send(@Valid @RequestBody WebSocketSendReqDTO message);
+
+ /**
+ * 发送消息给指定用户
+ *
+ * @param userType 用户类型
+ * @param userId 用户编号
+ * @param messageType 消息类型
+ * @param messageContent 消息内容,JSON 格式
+ */
+ default void send(Integer userType, Long userId, String messageType, String messageContent) {
+ send(new WebSocketSendReqDTO().setUserType(userType).setUserId(userId)
+ .setMessageType(messageType).setMessageContent(messageContent)).checkError();
+ }
+
+ /**
+ * 发送消息给指定用户类型
+ *
+ * @param userType 用户类型
+ * @param messageType 消息类型
+ * @param messageContent 消息内容,JSON 格式
+ */
+ default void send(Integer userType, String messageType, String messageContent) {
+ send(new WebSocketSendReqDTO().setUserType(userType)
+ .setMessageType(messageType).setMessageContent(messageContent)).checkError();
+ }
+
+ /**
+ * 发送消息给指定 Session
+ *
+ * @param sessionId Session 编号
+ * @param messageType 消息类型
+ * @param messageContent 消息内容,JSON 格式
+ */
+ default void send(String sessionId, String messageType, String messageContent) {
+ send(new WebSocketSendReqDTO().setSessionId(sessionId)
+ .setMessageType(messageType).setMessageContent(messageContent)).checkError();
+ }
+
+ default void sendObject(Integer userType, Long userId, String messageType, Object messageContent) {
+ send(userType, userId, messageType, JsonUtils.toJsonString(messageContent));
+ }
+
+ default void sendObject(Integer userType, String messageType, Object messageContent) {
+ send(userType, messageType, JsonUtils.toJsonString(messageContent));
+ }
+
+ default void sendObject(String sessionId, String messageType, Object messageContent) {
+ send(sessionId, messageType, JsonUtils.toJsonString(messageContent));
+ }
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-api/src/main/java/com/tashow/cloud/infraapi/api/websocket/dto/WebSocketSendReqDTO.java b/tashow-module/tashow-module-infra/tashow-module-infra-api/src/main/java/com/tashow/cloud/infraapi/api/websocket/dto/WebSocketSendReqDTO.java
new file mode 100644
index 0000000..4f65487
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-api/src/main/java/com/tashow/cloud/infraapi/api/websocket/dto/WebSocketSendReqDTO.java
@@ -0,0 +1,26 @@
+package com.tashow.cloud.infraapi.api.websocket.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import jakarta.validation.constraints.NotEmpty;
+
+@Schema(description = "RPC 服务 - WebSocket 消息发送 Request DTO")
+@Data
+public class WebSocketSendReqDTO {
+
+ @Schema(description = "Session 编号", example = "abc")
+ private String sessionId;
+ @Schema(description = "用户编号", example = "1024")
+ private Long userId;
+ @Schema(description = "用户类型", example = "1")
+ private Integer userType;
+
+ @Schema(description = "消息类型", example = "demo-message")
+ @NotEmpty(message = "消息类型不能为空")
+ private String messageType;
+ @Schema(description = "消息内容", example = "{\"name\":\"李四\"}}")
+ @NotEmpty(message = "消息内容不能为空")
+ private String messageContent;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-api/src/main/java/com/tashow/cloud/infraapi/enums/ApiConstants.java b/tashow-module/tashow-module-infra/tashow-module-infra-api/src/main/java/com/tashow/cloud/infraapi/enums/ApiConstants.java
new file mode 100644
index 0000000..4b64329
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-api/src/main/java/com/tashow/cloud/infraapi/enums/ApiConstants.java
@@ -0,0 +1,24 @@
+package com.tashow.cloud.infraapi.enums;
+
+
+import com.tashow.cloud.common.enums.RpcConstants;
+
+/**
+ * API 相关的枚举
+ *
+ * @author 芋道源码
+ */
+public class ApiConstants {
+
+ /**
+ * 服务名
+ *
+ * 注意,需要保证和 spring.application.name 保持一致
+ */
+ public static final String NAME = "infra-server";
+
+ public static final String PREFIX = RpcConstants.RPC_API_PREFIX + "/infra";
+
+ public static final String VERSION = "1.0.0";
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-api/src/main/java/com/tashow/cloud/infraapi/enums/DictTypeConstants.java b/tashow-module/tashow-module-infra/tashow-module-infra-api/src/main/java/com/tashow/cloud/infraapi/enums/DictTypeConstants.java
new file mode 100644
index 0000000..7667950
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-api/src/main/java/com/tashow/cloud/infraapi/enums/DictTypeConstants.java
@@ -0,0 +1,20 @@
+package com.tashow.cloud.infraapi.enums;
+
+/**
+ * Infra 字典类型的枚举类
+ *
+ * @author 芋道源码
+ */
+public interface DictTypeConstants {
+
+ String JOB_STATUS = "infra_job_status"; // 定时任务状态的枚举
+ String JOB_LOG_STATUS = "infra_job_log_status"; // 定时任务日志状态的枚举
+
+ String API_ERROR_LOG_PROCESS_STATUS = "infra_api_error_log_process_status"; // API 错误日志的处理状态的枚举
+
+ String CONFIG_TYPE = "infra_config_type"; // 参数配置类型
+ String BOOLEAN_STRING = "infra_boolean_string"; // Boolean 是否类型
+
+ String OPERATE_TYPE = "infra_operate_type"; // 操作类型
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-api/src/main/java/com/tashow/cloud/infraapi/enums/ErrorCodeConstants.java b/tashow-module/tashow-module-infra/tashow-module-infra-api/src/main/java/com/tashow/cloud/infraapi/enums/ErrorCodeConstants.java
new file mode 100644
index 0000000..509a2de
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-api/src/main/java/com/tashow/cloud/infraapi/enums/ErrorCodeConstants.java
@@ -0,0 +1,72 @@
+package com.tashow.cloud.infraapi.enums;
+
+
+import com.tashow.cloud.common.exception.ErrorCode;
+
+/**
+ * Infra 错误码枚举类
+ *
+ * infra 系统,使用 1-001-000-000 段
+ */
+public interface ErrorCodeConstants {
+
+ // ========== 参数配置 1-001-000-000 ==========
+ ErrorCode CONFIG_NOT_EXISTS = new ErrorCode(1_001_000_001, "参数配置不存在");
+ ErrorCode CONFIG_KEY_DUPLICATE = new ErrorCode(1_001_000_002, "参数配置 key 重复");
+ ErrorCode CONFIG_CAN_NOT_DELETE_SYSTEM_TYPE = new ErrorCode(1_001_000_003, "不能删除类型为系统内置的参数配置");
+ ErrorCode CONFIG_GET_VALUE_ERROR_IF_VISIBLE = new ErrorCode(1_001_000_004, "获取参数配置失败,原因:不允许获取不可见配置");
+
+ // ========== 定时任务 1-001-001-000 ==========
+ ErrorCode JOB_NOT_EXISTS = new ErrorCode(1_001_001_000, "定时任务不存在");
+ ErrorCode JOB_HANDLER_EXISTS = new ErrorCode(1_001_001_001, "定时任务的处理器已经存在");
+ ErrorCode JOB_CHANGE_STATUS_INVALID = new ErrorCode(1_001_001_002, "只允许修改为开启或者关闭状态");
+ ErrorCode JOB_CHANGE_STATUS_EQUALS = new ErrorCode(1_001_001_003, "定时任务已经处于该状态,无需修改");
+ ErrorCode JOB_UPDATE_ONLY_NORMAL_STATUS = new ErrorCode(1_001_001_004, "只有开启状态的任务,才可以修改");
+ ErrorCode JOB_CRON_EXPRESSION_VALID = new ErrorCode(1_001_001_005, "CRON 表达式不正确");
+ ErrorCode JOB_HANDLER_BEAN_NOT_EXISTS = new ErrorCode(1_001_001_006, "定时任务的处理器 Bean 不存在,注意 Bean 默认首字母小写");
+ ErrorCode JOB_HANDLER_BEAN_TYPE_ERROR = new ErrorCode(1_001_001_007, "定时任务的处理器 Bean 类型不正确,未实现 JobHandler 接口");
+
+ // ========== API 错误日志 1-001-002-000 ==========
+ ErrorCode API_ERROR_LOG_NOT_FOUND = new ErrorCode(1_001_002_000, "API 错误日志不存在");
+ ErrorCode API_ERROR_LOG_PROCESSED = new ErrorCode(1_001_002_001, "API 错误日志已处理");
+
+ // ========= 文件相关 1-001-003-000 =================
+ ErrorCode FILE_PATH_EXISTS = new ErrorCode(1_001_003_000, "文件路径已存在");
+ ErrorCode FILE_NOT_EXISTS = new ErrorCode(1_001_003_001, "文件不存在");
+ ErrorCode FILE_IS_EMPTY = new ErrorCode(1_001_003_002, "文件为空");
+
+ // ========== 代码生成器 1-001-004-000 ==========
+ ErrorCode CODEGEN_TABLE_EXISTS = new ErrorCode(1_001_004_002, "表定义已经存在");
+ ErrorCode CODEGEN_IMPORT_TABLE_NULL = new ErrorCode(1_001_004_001, "导入的表不存在");
+ ErrorCode CODEGEN_IMPORT_COLUMNS_NULL = new ErrorCode(1_001_004_002, "导入的字段不存在");
+ ErrorCode CODEGEN_TABLE_NOT_EXISTS = new ErrorCode(1_001_004_004, "表定义不存在");
+ ErrorCode CODEGEN_COLUMN_NOT_EXISTS = new ErrorCode(1_001_004_005, "字段义不存在");
+ ErrorCode CODEGEN_SYNC_COLUMNS_NULL = new ErrorCode(1_001_004_006, "同步的字段不存在");
+ ErrorCode CODEGEN_SYNC_NONE_CHANGE = new ErrorCode(1_001_004_007, "同步失败,不存在改变");
+ ErrorCode CODEGEN_TABLE_INFO_TABLE_COMMENT_IS_NULL = new ErrorCode(1_001_004_008, "数据库的表注释未填写");
+ ErrorCode CODEGEN_TABLE_INFO_COLUMN_COMMENT_IS_NULL = new ErrorCode(1_001_004_009, "数据库的表字段({})注释未填写");
+ ErrorCode CODEGEN_MASTER_TABLE_NOT_EXISTS = new ErrorCode(1_001_004_010, "主表(id={})定义不存在,请检查");
+ ErrorCode CODEGEN_SUB_COLUMN_NOT_EXISTS = new ErrorCode(1_001_004_011, "子表的字段(id={})不存在,请检查");
+ ErrorCode CODEGEN_MASTER_GENERATION_FAIL_NO_SUB_TABLE = new ErrorCode(1_001_004_012, "主表生成代码失败,原因:它没有子表");
+
+ // ========== 文件配置 1-001-006-000 ==========
+ ErrorCode FILE_CONFIG_NOT_EXISTS = new ErrorCode(1_001_006_000, "文件配置不存在");
+ ErrorCode FILE_CONFIG_DELETE_FAIL_MASTER = new ErrorCode(1_001_006_001, "该文件配置不允许删除,原因:它是主配置,删除会导致无法上传文件");
+
+ // ========== 数据源配置 1-001-007-000 ==========
+ ErrorCode DATA_SOURCE_CONFIG_NOT_EXISTS = new ErrorCode(1_001_007_000, "数据源配置不存在");
+ ErrorCode DATA_SOURCE_CONFIG_NOT_OK = new ErrorCode(1_001_007_001, "数据源配置不正确,无法进行连接");
+
+ // ========== 学生 1-001-201-000 ==========
+ ErrorCode DEMO01_CONTACT_NOT_EXISTS = new ErrorCode(1_001_201_000, "示例联系人不存在");
+ ErrorCode DEMO02_CATEGORY_NOT_EXISTS = new ErrorCode(1_001_201_001, "示例分类不存在");
+ ErrorCode DEMO02_CATEGORY_EXITS_CHILDREN = new ErrorCode(1_001_201_002, "存在存在子示例分类,无法删除");
+ ErrorCode DEMO02_CATEGORY_PARENT_NOT_EXITS = new ErrorCode(1_001_201_003,"父级示例分类不存在");
+ ErrorCode DEMO02_CATEGORY_PARENT_ERROR = new ErrorCode(1_001_201_004, "不能设置自己为父示例分类");
+ ErrorCode DEMO02_CATEGORY_NAME_DUPLICATE = new ErrorCode(1_001_201_005, "已经存在该名字的示例分类");
+ ErrorCode DEMO02_CATEGORY_PARENT_IS_CHILD = new ErrorCode(1_001_201_006, "不能设置自己的子示例分类为父示例分类");
+ ErrorCode DEMO03_STUDENT_NOT_EXISTS = new ErrorCode(1_001_201_007, "学生不存在");
+ ErrorCode DEMO03_GRADE_NOT_EXISTS = new ErrorCode(1_001_201_008, "学生班级不存在");
+ ErrorCode DEMO03_GRADE_EXISTS = new ErrorCode(1_001_201_009, "学生班级已存在");
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/Dockerfile b/tashow-module/tashow-module-infra/tashow-module-infra-biz/Dockerfile
new file mode 100644
index 0000000..32d8274
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/Dockerfile
@@ -0,0 +1,19 @@
+## AdoptOpenJDK 停止发布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,提供更好的稳定性
+## 感谢复旦核博士的建议!灰子哥,牛皮!
+FROM eclipse-temurin:21-jre
+
+## 创建目录,并使用它作为工作目录
+RUN mkdir -p /yudao-module-infra-biz
+WORKDIR /yudao-module-infra-biz
+## 将后端项目的 Jar 文件,复制到镜像中
+COPY ./target/yudao-module-infra-biz.jar app.jar
+
+## 设置 TZ 时区
+## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖
+ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms512m -Xmx512m"
+
+## 暴露后端项目的 48080 端口
+EXPOSE 48082
+
+## 启动后端项目
+CMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/pom.xml b/tashow-module/tashow-module-infra/tashow-module-infra-biz/pom.xml
new file mode 100644
index 0000000..eafc02f
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/pom.xml
@@ -0,0 +1,164 @@
+
+
+ 4.0.0
+
+ com.tashow.cloud
+ tashow-module-infra
+ ${revision}
+
+ tashow-module-infra-biz
+ jar
+
+ ${project.artifactId}
+
+ infra 模块,主要提供两块能力:
+ 1. 我们放基础设施的运维与管理,支撑上层的通用与核心业务。 例如说:定时任务的管理、服务器的信息等等
+ 2. 研发工具,提升研发效率与质量。 例如说:代码生成器、接口文档等等
+
+
+
+
+
+ com.tashow.cloud
+ tashow-framework-env
+
+
+
+
+ com.tashow.cloud
+ tashow-module-system-api
+ ${revision}
+
+
+ com.tashow.cloud
+ tashow-module-infra-api
+ ${revision}
+
+
+
+
+ com.tashow.cloud
+ tashow-framework-tenant
+
+
+
+
+ com.tashow.cloud
+ tashow-framework-security
+
+
+
+ com.tashow.cloud
+ tashow-framework-websocket
+
+
+
+
+ com.tashow.cloud
+ tashow-data-mybatis
+
+
+ com.baomidou
+ mybatis-plus-generator
+
+
+
+ com.tashow.cloud
+ tashow-data-redis
+
+
+
+
+ com.tashow.cloud
+ tashow-framework-rpc
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-config
+
+
+
+
+ com.tashow.cloud
+ tashow-framework-job
+
+
+
+
+ com.tashow.cloud
+ tashow-framework-mq
+
+
+
+
+ com.tashow.cloud
+ tashow-data-excel
+
+
+
+ org.apache.velocity
+ velocity-engine-core
+
+
+
+
+ com.tashow.cloud
+ tashow-framework-monitor
+
+
+
+ de.codecentric
+ spring-boot-admin-starter-server
+
+
+
+
+ commons-net
+ commons-net
+
+
+ com.jcraft
+ jsch
+
+
+ com.amazonaws
+ aws-java-sdk-s3
+
+
+
+ org.apache.tika
+ tika-core
+
+
+
+
+
+ ${project.artifactId}
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring.boot.version}
+
+
+
+ repackage
+
+
+
+
+
+
+
+
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/InfraServerApplication.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/InfraServerApplication.java
new file mode 100644
index 0000000..bc69e5a
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/InfraServerApplication.java
@@ -0,0 +1,30 @@
+package com.tashow.cloud.infra;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * 项目的启动类
+ *
+ * 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ * 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ * 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ *
+ * @author 芋道源码
+ */
+@SpringBootApplication
+public class InfraServerApplication {
+
+ public static void main(String[] args) {
+ // 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ // 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ // 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+
+ SpringApplication.run(InfraServerApplication.class, args);
+
+ // 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ // 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ // 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ }
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/api/config/ConfigApiImpl.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/api/config/ConfigApiImpl.java
new file mode 100644
index 0000000..82f7268
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/api/config/ConfigApiImpl.java
@@ -0,0 +1,27 @@
+package com.tashow.cloud.infra.api.config;
+
+import com.tashow.cloud.common.pojo.CommonResult;
+import com.tashow.cloud.infra.service.config.ConfigService;
+import com.tashow.cloud.infraapi.api.config.ConfigApi;
+import com.tashow.cloud.infra.dal.dataobject.config.ConfigDO;
+import jakarta.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RestController;
+
+import static com.tashow.cloud.common.pojo.CommonResult.success;
+
+
+@RestController // 提供 RESTful API 接口,给 Feign 调用
+@Validated
+public class ConfigApiImpl implements ConfigApi {
+
+ @Resource
+ private ConfigService configService;
+
+ @Override
+ public CommonResult getConfigValueByKey(String key) {
+ ConfigDO config = configService.getConfigByKey(key);
+ return success(config != null ? config.getValue() : null);
+ }
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/api/file/FileApiImpl.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/api/file/FileApiImpl.java
new file mode 100644
index 0000000..cb57d40
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/api/file/FileApiImpl.java
@@ -0,0 +1,28 @@
+package com.tashow.cloud.infra.api.file;
+
+import com.tashow.cloud.common.pojo.CommonResult;
+import com.tashow.cloud.infra.service.file.FileService;
+import com.tashow.cloud.infraapi.api.file.FileApi;
+import com.tashow.cloud.infraapi.api.file.dto.FileCreateReqDTO;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RestController;
+
+import jakarta.annotation.Resource;
+
+import static com.tashow.cloud.common.pojo.CommonResult.success;
+
+
+@RestController // 提供 RESTful API 接口,给 Feign 调用
+@Validated
+public class FileApiImpl implements FileApi {
+
+ @Resource
+ private FileService fileService;
+
+ @Override
+ public CommonResult createFile(FileCreateReqDTO createReqDTO) {
+ return success(fileService.createFile(createReqDTO.getName(), createReqDTO.getPath(),
+ createReqDTO.getContent()));
+ }
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/api/logger/ApiAccessLogApiImpl.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/api/logger/ApiAccessLogApiImpl.java
new file mode 100644
index 0000000..00effcd
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/api/logger/ApiAccessLogApiImpl.java
@@ -0,0 +1,28 @@
+package com.tashow.cloud.infra.api.logger;
+
+import com.tashow.cloud.common.pojo.CommonResult;
+import com.tashow.cloud.infra.service.logger.ApiAccessLogService;
+import com.tashow.cloud.infraapi.api.logger.ApiAccessLogApi;
+import com.tashow.cloud.infraapi.api.logger.dto.ApiAccessLogCreateReqDTO;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RestController;
+
+import jakarta.annotation.Resource;
+
+import static com.tashow.cloud.common.pojo.CommonResult.success;
+
+
+@RestController // 提供 RESTful API 接口,给 Feign 调用
+@Validated
+public class ApiAccessLogApiImpl implements ApiAccessLogApi {
+
+ @Resource
+ private ApiAccessLogService apiAccessLogService;
+
+ @Override
+ public CommonResult createApiAccessLog(ApiAccessLogCreateReqDTO createDTO) {
+ apiAccessLogService.createApiAccessLog(createDTO);
+ return success(true);
+ }
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/api/logger/ApiErrorLogApiImpl.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/api/logger/ApiErrorLogApiImpl.java
new file mode 100644
index 0000000..24d6e56
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/api/logger/ApiErrorLogApiImpl.java
@@ -0,0 +1,27 @@
+package com.tashow.cloud.infra.api.logger;
+
+
+import com.tashow.cloud.common.pojo.CommonResult;
+import com.tashow.cloud.infra.service.logger.ApiErrorLogService;
+import com.tashow.cloud.infraapi.api.logger.ApiErrorLogApi;
+import com.tashow.cloud.infraapi.api.logger.dto.ApiErrorLogCreateReqDTO;
+import jakarta.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RestController;
+
+import static com.tashow.cloud.common.pojo.CommonResult.success;
+
+@RestController // 提供 RESTful API 接口,给 Feign 调用
+@Validated
+public class ApiErrorLogApiImpl implements ApiErrorLogApi {
+
+ @Resource
+ private ApiErrorLogService apiErrorLogService;
+
+ @Override
+ public CommonResult createApiErrorLog(ApiErrorLogCreateReqDTO createDTO) {
+ apiErrorLogService.createApiErrorLog(createDTO);
+ return success(true);
+ }
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/api/package-info.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/api/package-info.java
new file mode 100644
index 0000000..b389e58
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/api/package-info.java
@@ -0,0 +1 @@
+package com.tashow.cloud.infra.api;
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/api/websocket/WebSocketSenderApiImpl.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/api/websocket/WebSocketSenderApiImpl.java
new file mode 100644
index 0000000..9833991
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/api/websocket/WebSocketSenderApiImpl.java
@@ -0,0 +1,37 @@
+package com.tashow.cloud.infra.api.websocket;
+
+
+import cn.hutool.core.util.StrUtil;
+import com.tashow.cloud.common.pojo.CommonResult;
+import com.tashow.cloud.infraapi.api.websocket.WebSocketSenderApi;
+import com.tashow.cloud.infraapi.api.websocket.dto.WebSocketSendReqDTO;
+import com.tashow.cloud.websocket.core.sender.WebSocketMessageSender;
+import jakarta.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RestController;
+
+import static com.tashow.cloud.common.pojo.CommonResult.success;
+
+@RestController // 提供 RESTful API 接口,给 Feign 调用
+@Validated
+public class WebSocketSenderApiImpl implements WebSocketSenderApi {
+
+ @Resource
+ private WebSocketMessageSender webSocketMessageSender;
+
+ @Override
+ public CommonResult send(WebSocketSendReqDTO message) {
+ if (StrUtil.isNotEmpty(message.getSessionId())) {
+ webSocketMessageSender.send(message.getSessionId(),
+ message.getMessageType(), message.getMessageContent());
+ } else if (message.getUserType() != null && message.getUserId() != null) {
+ webSocketMessageSender.send(message.getUserType(), message.getUserId(),
+ message.getMessageType(), message.getMessageContent());
+ } else if (message.getUserType() != null) {
+ webSocketMessageSender.send(message.getUserType(),
+ message.getMessageType(), message.getMessageContent());
+ }
+ return success(true);
+ }
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/CodegenController.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/CodegenController.java
new file mode 100644
index 0000000..27c57d0
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/CodegenController.java
@@ -0,0 +1,151 @@
+package com.tashow.cloud.infra.controller.admin.codegen;
+
+import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.util.ZipUtil;
+import com.tashow.cloud.common.pojo.CommonResult;
+import com.tashow.cloud.common.pojo.PageResult;
+import com.tashow.cloud.common.util.object.BeanUtils;
+import com.tashow.cloud.infra.controller.admin.codegen.vo.CodegenCreateListReqVO;
+import com.tashow.cloud.infra.controller.admin.codegen.vo.CodegenDetailRespVO;
+import com.tashow.cloud.infra.controller.admin.codegen.vo.CodegenPreviewRespVO;
+import com.tashow.cloud.infra.controller.admin.codegen.vo.CodegenUpdateReqVO;
+import com.tashow.cloud.infra.convert.codegen.CodegenConvert;
+import com.tashow.cloud.infra.dal.dataobject.codegen.CodegenColumnDO;
+import com.tashow.cloud.infra.dal.dataobject.codegen.CodegenTableDO;
+import com.tashow.cloud.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO;
+import com.tashow.cloud.infra.controller.admin.codegen.vo.table.CodegenTableRespVO;
+import com.tashow.cloud.infra.controller.admin.codegen.vo.table.DatabaseTableRespVO;
+import com.tashow.cloud.infra.service.codegen.CodegenService;
+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 java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import static com.tashow.cloud.common.pojo.CommonResult.success;
+import static com.tashow.cloud.infra.framework.file.core.utils.FileTypeUtils.writeAttachment;
+import static com.tashow.cloud.security.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+
+@Tag(name = "管理后台 - 代码生成器")
+@RestController
+@RequestMapping("/infra/codegen")
+@Validated
+public class CodegenController {
+
+ @Resource
+ private CodegenService codegenService;
+
+ @GetMapping("/db/table/list")
+ @Operation(summary = "获得数据库自带的表定义列表", description = "会过滤掉已经导入 Codegen 的表")
+ @Parameters({
+ @Parameter(name = "dataSourceConfigId", description = "数据源配置的编号", required = true, example = "1"),
+ @Parameter(name = "name", description = "表名,模糊匹配", example = "yudao"),
+ @Parameter(name = "comment", description = "描述,模糊匹配", example = "芋道")
+ })
+ @PreAuthorize("@ss.hasPermission('infra:codegen:query')")
+ public CommonResult> getDatabaseTableList(
+ @RequestParam(value = "dataSourceConfigId") Long dataSourceConfigId,
+ @RequestParam(value = "name", required = false) String name,
+ @RequestParam(value = "comment", required = false) String comment) {
+ return success(codegenService.getDatabaseTableList(dataSourceConfigId, name, comment));
+ }
+
+ @GetMapping("/table/list")
+ @Operation(summary = "获得表定义列表")
+ @Parameter(name = "dataSourceConfigId", description = "数据源配置的编号", required = true, example = "1")
+ @PreAuthorize("@ss.hasPermission('infra:codegen:query')")
+ public CommonResult> getCodegenTableList(@RequestParam(value = "dataSourceConfigId") Long dataSourceConfigId) {
+ List list = codegenService.getCodegenTableList(dataSourceConfigId);
+ return success(BeanUtils.toBean(list, CodegenTableRespVO.class));
+ }
+
+ @GetMapping("/table/page")
+ @Operation(summary = "获得表定义分页")
+ @PreAuthorize("@ss.hasPermission('infra:codegen:query')")
+ public CommonResult> getCodegenTablePage(@Valid CodegenTablePageReqVO pageReqVO) {
+ PageResult pageResult = codegenService.getCodegenTablePage(pageReqVO);
+ return success(BeanUtils.toBean(pageResult, CodegenTableRespVO.class));
+ }
+
+ @GetMapping("/detail")
+ @Operation(summary = "获得表和字段的明细")
+ @Parameter(name = "tableId", description = "表编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('infra:codegen:query')")
+ public CommonResult getCodegenDetail(@RequestParam("tableId") Long tableId) {
+ CodegenTableDO table = codegenService.getCodegenTable(tableId);
+ List columns = codegenService.getCodegenColumnListByTableId(tableId);
+ // 拼装返回
+ return success(CodegenConvert.INSTANCE.convert(table, columns));
+ }
+
+ @Operation(summary = "基于数据库的表结构,创建代码生成器的表和字段定义")
+ @PostMapping("/create-list")
+ @PreAuthorize("@ss.hasPermission('infra:codegen:create')")
+ public CommonResult> createCodegenList(@Valid @RequestBody CodegenCreateListReqVO reqVO) {
+ return success(codegenService.createCodegenList(getLoginUserId(), reqVO));
+ }
+
+ @Operation(summary = "更新数据库的表和字段定义")
+ @PutMapping("/update")
+ @PreAuthorize("@ss.hasPermission('infra:codegen:update')")
+ public CommonResult updateCodegen(@Valid @RequestBody CodegenUpdateReqVO updateReqVO) {
+ codegenService.updateCodegen(updateReqVO);
+ return success(true);
+ }
+
+ @Operation(summary = "基于数据库的表结构,同步数据库的表和字段定义")
+ @PutMapping("/sync-from-db")
+ @Parameter(name = "tableId", description = "表编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('infra:codegen:update')")
+ public CommonResult syncCodegenFromDB(@RequestParam("tableId") Long tableId) {
+ codegenService.syncCodegenFromDB(tableId);
+ return success(true);
+ }
+
+ @Operation(summary = "删除数据库的表和字段定义")
+ @DeleteMapping("/delete")
+ @Parameter(name = "tableId", description = "表编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('infra:codegen:delete')")
+ public CommonResult deleteCodegen(@RequestParam("tableId") Long tableId) {
+ codegenService.deleteCodegen(tableId);
+ return success(true);
+ }
+
+ @Operation(summary = "预览生成代码")
+ @GetMapping("/preview")
+ @Parameter(name = "tableId", description = "表编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('infra:codegen:preview')")
+ public CommonResult> previewCodegen(@RequestParam("tableId") Long tableId) {
+ Map codes = codegenService.generationCodes(tableId);
+ return success(CodegenConvert.INSTANCE.convert(codes));
+ }
+
+ @Operation(summary = "下载生成代码")
+ @GetMapping("/download")
+ @Parameter(name = "tableId", description = "表编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('infra:codegen:download')")
+ public void downloadCodegen(@RequestParam("tableId") Long tableId,
+ HttpServletResponse response) throws IOException {
+ // 生成代码
+ Map codes = codegenService.generationCodes(tableId);
+ // 构建 zip 包
+ String[] paths = codes.keySet().toArray(new String[0]);
+ ByteArrayInputStream[] ins = codes.values().stream().map(IoUtil::toUtf8Stream).toArray(ByteArrayInputStream[]::new);
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ ZipUtil.zip(outputStream, paths, ins);
+ // 输出
+ writeAttachment(response, "codegen.zip", outputStream.toByteArray());
+ }
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/vo/CodegenCreateListReqVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/vo/CodegenCreateListReqVO.java
new file mode 100644
index 0000000..6db6e08
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/vo/CodegenCreateListReqVO.java
@@ -0,0 +1,21 @@
+package com.tashow.cloud.infra.controller.admin.codegen.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+
+@Schema(description = "管理后台 - 基于数据库的表结构,创建代码生成器的表和字段定义 Request VO")
+@Data
+public class CodegenCreateListReqVO {
+
+ @Schema(description = "数据源配置的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "数据源配置的编号不能为空")
+ private Long dataSourceConfigId;
+
+ @Schema(description = "表名数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 2, 3]")
+ @NotNull(message = "表名数组不能为空")
+ private List tableNames;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/vo/CodegenDetailRespVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/vo/CodegenDetailRespVO.java
new file mode 100644
index 0000000..8ceb95c
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/vo/CodegenDetailRespVO.java
@@ -0,0 +1,20 @@
+package com.tashow.cloud.infra.controller.admin.codegen.vo;
+
+import com.tashow.cloud.infra.controller.admin.codegen.vo.column.CodegenColumnRespVO;
+import com.tashow.cloud.infra.controller.admin.codegen.vo.table.CodegenTableRespVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.List;
+
+@Schema(description = "管理后台 - 代码生成表和字段的明细 Response VO")
+@Data
+public class CodegenDetailRespVO {
+
+ @Schema(description = "表定义")
+ private CodegenTableRespVO table;
+
+ @Schema(description = "字段定义")
+ private List columns;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/vo/CodegenPreviewRespVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/vo/CodegenPreviewRespVO.java
new file mode 100644
index 0000000..5744dc7
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/vo/CodegenPreviewRespVO.java
@@ -0,0 +1,16 @@
+package com.tashow.cloud.infra.controller.admin.codegen.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - 代码生成预览 Response VO,注意,每个文件都是一个该对象")
+@Data
+public class CodegenPreviewRespVO {
+
+ @Schema(description = "文件路径", requiredMode = Schema.RequiredMode.REQUIRED, example = "java/cn/iocoder/yudao/adminserver/modules/system/controller/test/SysTestDemoController.java")
+ private String filePath;
+
+ @Schema(description = "代码", requiredMode = Schema.RequiredMode.REQUIRED, example = "Hello World")
+ private String code;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/vo/CodegenUpdateReqVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/vo/CodegenUpdateReqVO.java
new file mode 100644
index 0000000..fb99274
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/vo/CodegenUpdateReqVO.java
@@ -0,0 +1,24 @@
+package com.tashow.cloud.infra.controller.admin.codegen.vo;
+
+import com.tashow.cloud.infra.controller.admin.codegen.vo.column.CodegenColumnSaveReqVO;
+import com.tashow.cloud.infra.controller.admin.codegen.vo.table.CodegenTableSaveReqVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+
+@Schema(description = "管理后台 - 代码生成表和字段的修改 Request VO")
+@Data
+public class CodegenUpdateReqVO {
+
+ @Valid // 校验内嵌的字段
+ @NotNull(message = "表定义不能为空")
+ private CodegenTableSaveReqVO table;
+
+ @Valid // 校验内嵌的字段
+ @NotNull(message = "字段定义不能为空")
+ private List columns;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/vo/column/CodegenColumnRespVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/vo/column/CodegenColumnRespVO.java
new file mode 100644
index 0000000..2e56cde
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/vo/column/CodegenColumnRespVO.java
@@ -0,0 +1,69 @@
+package com.tashow.cloud.infra.controller.admin.codegen.vo.column;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 代码生成字段定义 Response VO")
+@Data
+public class CodegenColumnRespVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ private Long id;
+
+ @Schema(description = "表编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ private Long tableId;
+
+ @Schema(description = "字段名", requiredMode = Schema.RequiredMode.REQUIRED, example = "user_age")
+ private String columnName;
+
+ @Schema(description = "字段类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "int(11)")
+ private String dataType;
+
+ @Schema(description = "字段描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "年龄")
+ private String columnComment;
+
+ @Schema(description = "是否允许为空", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+ private Boolean nullable;
+
+ @Schema(description = "是否主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
+ private Boolean primaryKey;
+
+ @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+ private Integer ordinalPosition;
+
+ @Schema(description = "Java 属性类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "userAge")
+ private String javaType;
+
+ @Schema(description = "Java 属性名", requiredMode = Schema.RequiredMode.REQUIRED, example = "Integer")
+ private String javaField;
+
+ @Schema(description = "字典类型", example = "sys_gender")
+ private String dictType;
+
+ @Schema(description = "数据示例", example = "1024")
+ private String example;
+
+ @Schema(description = "是否为 Create 创建操作的字段", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+ private Boolean createOperation;
+
+ @Schema(description = "是否为 Update 更新操作的字段", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
+ private Boolean updateOperation;
+
+ @Schema(description = "是否为 List 查询操作的字段", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+ private Boolean listOperation;
+
+ @Schema(description = "List 查询操作的条件类型,参见 CodegenColumnListConditionEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "LIKE")
+ private String listOperationCondition;
+
+ @Schema(description = "是否为 List 查询操作的返回字段", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+ private Boolean listOperationResult;
+
+ @Schema(description = "显示类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "input")
+ private String htmlType;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ private LocalDateTime createTime;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/vo/column/CodegenColumnSaveReqVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/vo/column/CodegenColumnSaveReqVO.java
new file mode 100644
index 0000000..1286724
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/vo/column/CodegenColumnSaveReqVO.java
@@ -0,0 +1,81 @@
+package com.tashow.cloud.infra.controller.admin.codegen.vo.column;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import jakarta.validation.constraints.NotNull;
+
+@Schema(description = "管理后台 - 代码生成字段定义创建/修改 Request VO")
+@Data
+public class CodegenColumnSaveReqVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ private Long id;
+
+ @Schema(description = "表编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "表编号不能为空")
+ private Long tableId;
+
+ @Schema(description = "字段名", requiredMode = Schema.RequiredMode.REQUIRED, example = "user_age")
+ @NotNull(message = "字段名不能为空")
+ private String columnName;
+
+ @Schema(description = "字段类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "int(11)")
+ @NotNull(message = "字段类型不能为空")
+ private String dataType;
+
+ @Schema(description = "字段描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "年龄")
+ @NotNull(message = "字段描述不能为空")
+ private String columnComment;
+
+ @Schema(description = "是否允许为空", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+ @NotNull(message = "是否允许为空不能为空")
+ private Boolean nullable;
+
+ @Schema(description = "是否主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
+ @NotNull(message = "是否主键不能为空")
+ private Boolean primaryKey;
+
+ @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+ @NotNull(message = "排序不能为空")
+ private Integer ordinalPosition;
+
+ @Schema(description = "Java 属性类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "userAge")
+ @NotNull(message = "Java 属性类型不能为空")
+ private String javaType;
+
+ @Schema(description = "Java 属性名", requiredMode = Schema.RequiredMode.REQUIRED, example = "Integer")
+ @NotNull(message = "Java 属性名不能为空")
+ private String javaField;
+
+ @Schema(description = "字典类型", example = "sys_gender")
+ private String dictType;
+
+ @Schema(description = "数据示例", example = "1024")
+ private String example;
+
+ @Schema(description = "是否为 Create 创建操作的字段", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+ @NotNull(message = "是否为 Create 创建操作的字段不能为空")
+ private Boolean createOperation;
+
+ @Schema(description = "是否为 Update 更新操作的字段", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
+ @NotNull(message = "是否为 Update 更新操作的字段不能为空")
+ private Boolean updateOperation;
+
+ @Schema(description = "是否为 List 查询操作的字段", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+ @NotNull(message = "是否为 List 查询操作的字段不能为空")
+ private Boolean listOperation;
+
+ @Schema(description = "List 查询操作的条件类型,参见 CodegenColumnListConditionEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "LIKE")
+ @NotNull(message = "List 查询操作的条件类型不能为空")
+ private String listOperationCondition;
+
+ @Schema(description = "是否为 List 查询操作的返回字段", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+ @NotNull(message = "是否为 List 查询操作的返回字段不能为空")
+ private Boolean listOperationResult;
+
+ @Schema(description = "显示类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "input")
+ @NotNull(message = "显示类型不能为空")
+ private String htmlType;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/vo/table/CodegenTablePageReqVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/vo/table/CodegenTablePageReqVO.java
new file mode 100644
index 0000000..eee84c5
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/vo/table/CodegenTablePageReqVO.java
@@ -0,0 +1,34 @@
+package com.tashow.cloud.infra.controller.admin.codegen.vo.table;
+
+import com.tashow.cloud.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 com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+
+@Schema(description = "管理后台 - 表定义分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class CodegenTablePageReqVO extends PageParam {
+
+ @Schema(description = "表名称,模糊匹配", example = "yudao")
+ private String tableName;
+
+ @Schema(description = "表描述,模糊匹配", example = "芋道")
+ private String tableComment;
+
+ @Schema(description = "实体,模糊匹配", example = "Yudao")
+ private String className;
+
+ @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/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/vo/table/CodegenTableRespVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/vo/table/CodegenTableRespVO.java
new file mode 100644
index 0000000..604420e
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/vo/table/CodegenTableRespVO.java
@@ -0,0 +1,72 @@
+package com.tashow.cloud.infra.controller.admin.codegen.vo.table;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 代码生成表定义 Response VO")
+@Data
+public class CodegenTableRespVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ private Long id;
+
+ @Schema(description = "生成场景,参见 CodegenSceneEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ private Integer scene;
+
+ @Schema(description = "表名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao")
+ private String tableName;
+
+ @Schema(description = "表描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
+ private String tableComment;
+
+ @Schema(description = "备注", example = "我是备注")
+ private String remark;
+
+ @Schema(description = "模块名", requiredMode = Schema.RequiredMode.REQUIRED, example = "system")
+ private String moduleName;
+
+ @Schema(description = "业务名", requiredMode = Schema.RequiredMode.REQUIRED, example = "codegen")
+ private String businessName;
+
+ @Schema(description = "类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "CodegenTable")
+ private String className;
+
+ @Schema(description = "类描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "代码生成器的表定义")
+ private String classComment;
+
+ @Schema(description = "作者", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
+ private String author;
+
+ @Schema(description = "模板类型,参见 CodegenTemplateTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ private Integer templateType;
+
+ @Schema(description = "前端类型,参见 CodegenFrontTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
+ private Integer frontType;
+
+ @Schema(description = "父菜单编号", example = "1024")
+ private Long parentMenuId;
+
+ @Schema(description = "主表的编号", example = "2048")
+ private Long masterTableId;
+ @Schema(description = "子表关联主表的字段编号", example = "4096")
+ private Long subJoinColumnId;
+ @Schema(description = "主表与子表是否一对多", example = "4096")
+ private Boolean subJoinMany;
+
+ @Schema(description = "树表的父字段编号", example = "8192")
+ private Long treeParentColumnId;
+ @Schema(description = "树表的名字字段编号", example = "16384")
+ private Long treeNameColumnId;
+
+ @Schema(description = "主键编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ private Integer dataSourceConfigId;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ private LocalDateTime createTime;
+
+ @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ private LocalDateTime updateTime;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/vo/table/CodegenTableSaveReqVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/vo/table/CodegenTableSaveReqVO.java
new file mode 100644
index 0000000..2204ab0
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/vo/table/CodegenTableSaveReqVO.java
@@ -0,0 +1,104 @@
+package com.tashow.cloud.infra.controller.admin.codegen.vo.table;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.tashow.cloud.infra.enums.codegen.CodegenSceneEnum;
+import com.tashow.cloud.infra.enums.codegen.CodegenTemplateTypeEnum;
+import com.tashow.cloud.infra.enums.codegen.CodegenSceneEnum;
+import com.tashow.cloud.infra.enums.codegen.CodegenTemplateTypeEnum;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.tashow.cloud.infra.enums.codegen.CodegenSceneEnum;
+import com.tashow.cloud.infra.enums.codegen.CodegenTemplateTypeEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import jakarta.validation.constraints.AssertTrue;
+import jakarta.validation.constraints.NotNull;
+
+@Schema(description = "管理后台 - 代码生成表定义创建/修改 Response VO")
+@Data
+public class CodegenTableSaveReqVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ private Long id;
+
+ @Schema(description = "生成场景,参见 CodegenSceneEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "导入类型不能为空")
+ private Integer scene;
+
+ @Schema(description = "表名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao")
+ @NotNull(message = "表名称不能为空")
+ private String tableName;
+
+ @Schema(description = "表描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
+ @NotNull(message = "表描述不能为空")
+ private String tableComment;
+
+ @Schema(description = "备注", example = "我是备注")
+ private String remark;
+
+ @Schema(description = "模块名", requiredMode = Schema.RequiredMode.REQUIRED, example = "system")
+ @NotNull(message = "模块名不能为空")
+ private String moduleName;
+
+ @Schema(description = "业务名", requiredMode = Schema.RequiredMode.REQUIRED, example = "codegen")
+ @NotNull(message = "业务名不能为空")
+ private String businessName;
+
+ @Schema(description = "类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "CodegenTable")
+ @NotNull(message = "类名称不能为空")
+ private String className;
+
+ @Schema(description = "类描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "代码生成器的表定义")
+ @NotNull(message = "类描述不能为空")
+ private String classComment;
+
+ @Schema(description = "作者", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
+ @NotNull(message = "作者不能为空")
+ private String author;
+
+ @Schema(description = "模板类型,参见 CodegenTemplateTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "模板类型不能为空")
+ private Integer templateType;
+
+ @Schema(description = "前端类型,参见 CodegenFrontTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
+ @NotNull(message = "前端类型不能为空")
+ private Integer frontType;
+
+ @Schema(description = "父菜单编号", example = "1024")
+ private Long parentMenuId;
+
+ @Schema(description = "主表的编号", example = "2048")
+ private Long masterTableId;
+ @Schema(description = "子表关联主表的字段编号", example = "4096")
+ private Long subJoinColumnId;
+ @Schema(description = "主表与子表是否一对多", example = "4096")
+ private Boolean subJoinMany;
+
+ @Schema(description = "树表的父字段编号", example = "8192")
+ private Long treeParentColumnId;
+ @Schema(description = "树表的名字字段编号", example = "16384")
+ private Long treeNameColumnId;
+
+ @AssertTrue(message = "上级菜单不能为空,请前往 [修改生成配置 -> 生成信息] 界面,设置“上级菜单”字段")
+ @JsonIgnore
+ public boolean isParentMenuIdValid() {
+ // 生成场景为管理后台时,必须设置上级菜单,不然生成的菜单 SQL 是无父级菜单的
+ return ObjectUtil.notEqual(getScene(), CodegenSceneEnum.ADMIN.getScene())
+ || getParentMenuId() != null;
+ }
+
+ @AssertTrue(message = "关联的父表信息不全")
+ @JsonIgnore
+ public boolean isSubValid() {
+ return ObjectUtil.notEqual(getTemplateType(), CodegenTemplateTypeEnum.SUB)
+ || (ObjectUtil.isAllNotEmpty(masterTableId, subJoinColumnId, subJoinMany));
+ }
+
+ @AssertTrue(message = "关联的树表信息不全")
+ @JsonIgnore
+ public boolean isTreeValid() {
+ return ObjectUtil.notEqual(templateType, CodegenTemplateTypeEnum.TREE)
+ || (ObjectUtil.isAllNotEmpty(treeParentColumnId, treeNameColumnId));
+ }
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/vo/table/DatabaseTableRespVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/vo/table/DatabaseTableRespVO.java
new file mode 100644
index 0000000..8020306
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/codegen/vo/table/DatabaseTableRespVO.java
@@ -0,0 +1,16 @@
+package com.tashow.cloud.infra.controller.admin.codegen.vo.table;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - 数据库的表定义 Response VO")
+@Data
+public class DatabaseTableRespVO {
+
+ @Schema(description = "表名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "yuanma")
+ private String name;
+
+ @Schema(description = "表描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
+ private String comment;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/config/ConfigController.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/config/ConfigController.java
new file mode 100644
index 0000000..f72c8b6
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/config/ConfigController.java
@@ -0,0 +1,109 @@
+package com.tashow.cloud.infra.controller.admin.config;
+
+import com.tashow.cloud.common.pojo.CommonResult;
+import com.tashow.cloud.common.pojo.PageParam;
+import com.tashow.cloud.common.pojo.PageResult;
+import com.tashow.cloud.excel.excel.core.util.ExcelUtils;
+import com.tashow.cloud.infra.controller.admin.config.vo.ConfigPageReqVO;
+import com.tashow.cloud.infra.controller.admin.config.vo.ConfigRespVO;
+import com.tashow.cloud.infra.convert.config.ConfigConvert;
+import com.tashow.cloud.infra.dal.dataobject.config.ConfigDO;
+import com.tashow.cloud.infra.service.config.ConfigService;
+import com.tashow.cloud.infra.controller.admin.config.vo.ConfigSaveReqVO;
+import com.tashow.cloud.infraapi.enums.ErrorCodeConstants;
+import com.tashow.cloud.web.apilog.core.annotation.ApiAccessLog;
+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 com.tashow.cloud.common.exception.util.ServiceExceptionUtil.exception;
+import static com.tashow.cloud.common.pojo.CommonResult.success;
+import static com.tashow.cloud.web.apilog.core.enums.OperateTypeEnum.EXPORT;
+
+
+@Tag(name = "管理后台 - 参数配置")
+@RestController
+@RequestMapping("/infra/config")
+@Validated
+public class ConfigController {
+
+ @Resource
+ private ConfigService configService;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建参数配置")
+ @PreAuthorize("@ss.hasPermission('infra:config:create')")
+ public CommonResult createConfig(@Valid @RequestBody ConfigSaveReqVO createReqVO) {
+ return success(configService.createConfig(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "修改参数配置")
+ @PreAuthorize("@ss.hasPermission('infra:config:update')")
+ public CommonResult updateConfig(@Valid @RequestBody ConfigSaveReqVO updateReqVO) {
+ configService.updateConfig(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除参数配置")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('infra:config:delete')")
+ public CommonResult deleteConfig(@RequestParam("id") Long id) {
+ configService.deleteConfig(id);
+ return success(true);
+ }
+
+ @GetMapping(value = "/get")
+ @Operation(summary = "获得参数配置")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('infra:config:query')")
+ public CommonResult getConfig(@RequestParam("id") Long id) {
+ return success(ConfigConvert.INSTANCE.convert(configService.getConfig(id)));
+ }
+
+ @GetMapping(value = "/get-value-by-key")
+ @Operation(summary = "根据参数键名查询参数值", description = "不可见的配置,不允许返回给前端")
+ @Parameter(name = "key", description = "参数键", required = true, example = "yunai.biz.username")
+ public CommonResult getConfigKey(@RequestParam("key") String key) {
+ ConfigDO config = configService.getConfigByKey(key);
+ if (config == null) {
+ return success(null);
+ }
+ if (!config.getVisible()) {
+ throw exception(ErrorCodeConstants.CONFIG_GET_VALUE_ERROR_IF_VISIBLE);
+ }
+ return success(config.getValue());
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获取参数配置分页")
+ @PreAuthorize("@ss.hasPermission('infra:config:query')")
+ public CommonResult> getConfigPage(@Valid ConfigPageReqVO pageReqVO) {
+ PageResult page = configService.getConfigPage(pageReqVO);
+ return success(ConfigConvert.INSTANCE.convertPage(page));
+ }
+
+ @GetMapping("/export")
+ @Operation(summary = "导出参数配置")
+ @PreAuthorize("@ss.hasPermission('infra:config:export')")
+ @ApiAccessLog(operateType = EXPORT)
+ public void exportConfig(ConfigPageReqVO exportReqVO,
+ HttpServletResponse response) throws IOException {
+ exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ List list = configService.getConfigPage(exportReqVO).getList();
+ // 输出
+ ExcelUtils.write(response, "参数配置.xls", "数据", ConfigRespVO.class,
+ ConfigConvert.INSTANCE.convertList(list));
+ }
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/config/vo/ConfigPageReqVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/config/vo/ConfigPageReqVO.java
new file mode 100644
index 0000000..c3994a1
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/config/vo/ConfigPageReqVO.java
@@ -0,0 +1,34 @@
+package com.tashow.cloud.infra.controller.admin.config.vo;
+
+import com.tashow.cloud.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 com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+
+@Schema(description = "管理后台 - 参数配置分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ConfigPageReqVO extends PageParam {
+
+ @Schema(description = "数据源名称,模糊匹配", example = "名称")
+ private String name;
+
+ @Schema(description = "参数键名,模糊匹配", example = "yunai.db.username")
+ private String key;
+
+ @Schema(description = "参数类型,参见 SysConfigTypeEnum 枚举", example = "1")
+ private Integer type;
+
+ @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/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/config/vo/ConfigRespVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/config/vo/ConfigRespVO.java
new file mode 100644
index 0000000..42dc916
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/config/vo/ConfigRespVO.java
@@ -0,0 +1,56 @@
+package com.tashow.cloud.infra.controller.admin.config.vo;
+
+import com.tashow.cloud.excel.excel.core.annotations.DictFormat;
+import com.tashow.cloud.excel.excel.core.convert.DictConvert;
+import com.tashow.cloud.infraapi.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 ConfigRespVO {
+
+ @Schema(description = "参数配置序号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ @ExcelProperty("参数配置序号")
+ private Long id;
+
+ @Schema(description = "参数分类", requiredMode = Schema.RequiredMode.REQUIRED, example = "biz")
+ @ExcelProperty("参数分类")
+ private String category;
+
+ @Schema(description = "参数名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "数据库名")
+ @ExcelProperty("参数名称")
+ private String name;
+
+ @Schema(description = "参数键名", requiredMode = Schema.RequiredMode.REQUIRED, example = "yunai.db.username")
+ @ExcelProperty("参数键名")
+ private String key;
+
+ @Schema(description = "参数键值", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ @ExcelProperty("参数键值")
+ private String value;
+
+ @Schema(description = "参数类型,参见 SysConfigTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @ExcelProperty(value = "参数类型", converter = DictConvert.class)
+ @DictFormat(DictTypeConstants.CONFIG_TYPE)
+ private Integer type;
+
+ @Schema(description = "是否可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+ @ExcelProperty(value = "是否可见", converter = DictConvert.class)
+ @DictFormat(DictTypeConstants.BOOLEAN_STRING)
+ private Boolean visible;
+
+ @Schema(description = "备注", example = "备注一下很帅气!")
+ @ExcelProperty("备注")
+ private String remark;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式")
+ @ExcelProperty("创建时间")
+ private LocalDateTime createTime;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/config/vo/ConfigSaveReqVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/config/vo/ConfigSaveReqVO.java
new file mode 100644
index 0000000..dfc0cdc
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/config/vo/ConfigSaveReqVO.java
@@ -0,0 +1,45 @@
+package com.tashow.cloud.infra.controller.admin.config.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+
+@Schema(description = "管理后台 - 参数配置创建/修改 Request VO")
+@Data
+public class ConfigSaveReqVO {
+
+ @Schema(description = "参数配置序号", example = "1024")
+ private Long id;
+
+ @Schema(description = "参数分组", requiredMode = Schema.RequiredMode.REQUIRED, example = "biz")
+ @NotEmpty(message = "参数分组不能为空")
+ @Size(max = 50, message = "参数名称不能超过 50 个字符")
+ private String category;
+
+ @Schema(description = "参数名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "数据库名")
+ @NotBlank(message = "参数名称不能为空")
+ @Size(max = 100, message = "参数名称不能超过 100 个字符")
+ private String name;
+
+ @Schema(description = "参数键名", requiredMode = Schema.RequiredMode.REQUIRED, example = "yunai.db.username")
+ @NotBlank(message = "参数键名长度不能为空")
+ @Size(max = 100, message = "参数键名长度不能超过 100 个字符")
+ private String key;
+
+ @Schema(description = "参数键值", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ @NotBlank(message = "参数键值不能为空")
+ @Size(max = 500, message = "参数键值长度不能超过 500 个字符")
+ private String value;
+
+ @Schema(description = "是否可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+ @NotNull(message = "是否可见不能为空")
+ private Boolean visible;
+
+ @Schema(description = "备注", example = "备注一下很帅气!")
+ private String remark;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/db/DataSourceConfigController.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/db/DataSourceConfigController.java
new file mode 100644
index 0000000..251bf72
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/db/DataSourceConfigController.java
@@ -0,0 +1,73 @@
+package com.tashow.cloud.infra.controller.admin.db;
+
+import com.tashow.cloud.common.pojo.CommonResult;
+import com.tashow.cloud.common.util.object.BeanUtils;
+import com.tashow.cloud.infra.controller.admin.db.vo.DataSourceConfigRespVO;
+import com.tashow.cloud.infra.controller.admin.db.vo.DataSourceConfigSaveReqVO;
+import com.tashow.cloud.infra.dal.dataobject.db.DataSourceConfigDO;
+import com.tashow.cloud.infra.service.db.DataSourceConfigService;
+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 com.tashow.cloud.common.pojo.CommonResult.success;
+
+
+@Tag(name = "管理后台 - 数据源配置")
+@RestController
+@RequestMapping("/infra/data-source-config")
+@Validated
+public class DataSourceConfigController {
+
+ @Resource
+ private DataSourceConfigService dataSourceConfigService;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建数据源配置")
+ @PreAuthorize("@ss.hasPermission('infra:data-source-config:create')")
+ public CommonResult createDataSourceConfig(@Valid @RequestBody DataSourceConfigSaveReqVO createReqVO) {
+ return success(dataSourceConfigService.createDataSourceConfig(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新数据源配置")
+ @PreAuthorize("@ss.hasPermission('infra:data-source-config:update')")
+ public CommonResult updateDataSourceConfig(@Valid @RequestBody DataSourceConfigSaveReqVO updateReqVO) {
+ dataSourceConfigService.updateDataSourceConfig(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除数据源配置")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('infra:data-source-config:delete')")
+ public CommonResult deleteDataSourceConfig(@RequestParam("id") Long id) {
+ dataSourceConfigService.deleteDataSourceConfig(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得数据源配置")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('infra:data-source-config:query')")
+ public CommonResult getDataSourceConfig(@RequestParam("id") Long id) {
+ DataSourceConfigDO config = dataSourceConfigService.getDataSourceConfig(id);
+ return success(BeanUtils.toBean(config, DataSourceConfigRespVO.class));
+ }
+
+ @GetMapping("/list")
+ @Operation(summary = "获得数据源配置列表")
+ @PreAuthorize("@ss.hasPermission('infra:data-source-config:query')")
+ public CommonResult> getDataSourceConfigList() {
+ List list = dataSourceConfigService.getDataSourceConfigList();
+ return success(BeanUtils.toBean(list, DataSourceConfigRespVO.class));
+ }
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/db/vo/DataSourceConfigRespVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/db/vo/DataSourceConfigRespVO.java
new file mode 100644
index 0000000..7e482d2
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/db/vo/DataSourceConfigRespVO.java
@@ -0,0 +1,27 @@
+package com.tashow.cloud.infra.controller.admin.db.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 数据源配置 Response VO")
+@Data
+public class DataSourceConfigRespVO {
+
+ @Schema(description = "主键编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ private Integer id;
+
+ @Schema(description = "数据源名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "test")
+ private String name;
+
+ @Schema(description = "数据源连接", requiredMode = Schema.RequiredMode.REQUIRED, example = "jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro")
+ private String url;
+
+ @Schema(description = "用户名", requiredMode = Schema.RequiredMode.REQUIRED, example = "root")
+ private String username;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ private LocalDateTime createTime;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/db/vo/DataSourceConfigSaveReqVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/db/vo/DataSourceConfigSaveReqVO.java
new file mode 100644
index 0000000..9948155
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/db/vo/DataSourceConfigSaveReqVO.java
@@ -0,0 +1,31 @@
+package com.tashow.cloud.infra.controller.admin.db.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import jakarta.validation.constraints.NotNull;
+
+@Schema(description = "管理后台 - 数据源配置创建/修改 Request VO")
+@Data
+public class DataSourceConfigSaveReqVO {
+
+ @Schema(description = "主键编号", example = "1024")
+ private Long id;
+
+ @Schema(description = "数据源名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "test")
+ @NotNull(message = "数据源名称不能为空")
+ private String name;
+
+ @Schema(description = "数据源连接", requiredMode = Schema.RequiredMode.REQUIRED, example = "jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro")
+ @NotNull(message = "数据源连接不能为空")
+ private String url;
+
+ @Schema(description = "用户名", requiredMode = Schema.RequiredMode.REQUIRED, example = "root")
+ @NotNull(message = "用户名不能为空")
+ private String username;
+
+ @Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456")
+ @NotNull(message = "密码不能为空")
+ private String password;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo01/Demo01ContactController.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo01/Demo01ContactController.java
new file mode 100644
index 0000000..d33b5e4
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo01/Demo01ContactController.java
@@ -0,0 +1,93 @@
+package com.tashow.cloud.infra.controller.admin.demo.demo01;
+
+import com.tashow.cloud.common.pojo.CommonResult;
+import com.tashow.cloud.common.pojo.PageParam;
+import com.tashow.cloud.common.pojo.PageResult;
+import com.tashow.cloud.common.util.object.BeanUtils;
+import com.tashow.cloud.excel.excel.core.util.ExcelUtils;
+import com.tashow.cloud.infra.controller.admin.demo.demo01.vo.Demo01ContactPageReqVO;
+import com.tashow.cloud.infra.controller.admin.demo.demo01.vo.Demo01ContactRespVO;
+import com.tashow.cloud.infra.dal.dataobject.demo.demo01.Demo01ContactDO;
+import com.tashow.cloud.infra.service.demo.demo01.Demo01ContactService;
+import com.tashow.cloud.infra.controller.admin.demo.demo01.vo.Demo01ContactSaveReqVO;
+import com.tashow.cloud.web.apilog.core.annotation.ApiAccessLog;
+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 com.tashow.cloud.common.pojo.CommonResult.success;
+import static com.tashow.cloud.web.apilog.core.enums.OperateTypeEnum.EXPORT;
+
+@Tag(name = "管理后台 - 示例联系人")
+@RestController
+@RequestMapping("/infra/demo01-contact")
+@Validated
+public class Demo01ContactController {
+
+ @Resource
+ private Demo01ContactService demo01ContactService;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建示例联系人")
+ @PreAuthorize("@ss.hasPermission('infra:demo01-contact:create')")
+ public CommonResult createDemo01Contact(@Valid @RequestBody Demo01ContactSaveReqVO createReqVO) {
+ return success(demo01ContactService.createDemo01Contact(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新示例联系人")
+ @PreAuthorize("@ss.hasPermission('infra:demo01-contact:update')")
+ public CommonResult updateDemo01Contact(@Valid @RequestBody Demo01ContactSaveReqVO updateReqVO) {
+ demo01ContactService.updateDemo01Contact(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除示例联系人")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('infra:demo01-contact:delete')")
+ public CommonResult deleteDemo01Contact(@RequestParam("id") Long id) {
+ demo01ContactService.deleteDemo01Contact(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得示例联系人")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('infra:demo01-contact:query')")
+ public CommonResult getDemo01Contact(@RequestParam("id") Long id) {
+ Demo01ContactDO demo01Contact = demo01ContactService.getDemo01Contact(id);
+ return success(BeanUtils.toBean(demo01Contact, Demo01ContactRespVO.class));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获得示例联系人分页")
+ @PreAuthorize("@ss.hasPermission('infra:demo01-contact:query')")
+ public CommonResult> getDemo01ContactPage(@Valid Demo01ContactPageReqVO pageReqVO) {
+ PageResult pageResult = demo01ContactService.getDemo01ContactPage(pageReqVO);
+ return success(BeanUtils.toBean(pageResult, Demo01ContactRespVO.class));
+ }
+
+ @GetMapping("/export-excel")
+ @Operation(summary = "导出示例联系人 Excel")
+ @PreAuthorize("@ss.hasPermission('infra:demo01-contact:export')")
+ @ApiAccessLog(operateType = EXPORT)
+ public void exportDemo01ContactExcel(@Valid Demo01ContactPageReqVO pageReqVO,
+ HttpServletResponse response) throws IOException {
+ pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ List list = demo01ContactService.getDemo01ContactPage(pageReqVO).getList();
+ // 导出 Excel
+ ExcelUtils.write(response, "示例联系人.xls", "数据", Demo01ContactRespVO.class,
+ BeanUtils.toBean(list, Demo01ContactRespVO.class));
+ }
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo01/vo/Demo01ContactPageReqVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo01/vo/Demo01ContactPageReqVO.java
new file mode 100644
index 0000000..f063d14
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo01/vo/Demo01ContactPageReqVO.java
@@ -0,0 +1,31 @@
+package com.tashow.cloud.infra.controller.admin.demo.demo01.vo;
+
+import com.tashow.cloud.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 com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+
+@Schema(description = "管理后台 - 示例联系人分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class Demo01ContactPageReqVO extends PageParam {
+
+ @Schema(description = "名字", example = "张三")
+ private String name;
+
+ @Schema(description = "性别", example = "1")
+ private Integer sex;
+
+ @Schema(description = "创建时间")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDateTime[] createTime;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo01/vo/Demo01ContactRespVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo01/vo/Demo01ContactRespVO.java
new file mode 100644
index 0000000..db1683b
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo01/vo/Demo01ContactRespVO.java
@@ -0,0 +1,46 @@
+package com.tashow.cloud.infra.controller.admin.demo.demo01.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.tashow.cloud.excel.excel.core.annotations.DictFormat;
+import com.tashow.cloud.excel.excel.core.convert.DictConvert;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 示例联系人 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class Demo01ContactRespVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "21555")
+ @ExcelProperty("编号")
+ private Long id;
+
+ @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
+ @ExcelProperty("名字")
+ private String name;
+
+ @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)
+ @ExcelProperty("出生年")
+ private LocalDateTime birthday;
+
+ @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "你说的对")
+ @ExcelProperty("简介")
+ private String description;
+
+ @Schema(description = "头像")
+ @ExcelProperty("头像")
+ private String avatar;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("创建时间")
+ private LocalDateTime createTime;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo01/vo/Demo01ContactSaveReqVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo01/vo/Demo01ContactSaveReqVO.java
new file mode 100644
index 0000000..0e6e639
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo01/vo/Demo01ContactSaveReqVO.java
@@ -0,0 +1,36 @@
+package com.tashow.cloud.infra.controller.admin.demo.demo01.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 示例联系人新增/修改 Request VO")
+@Data
+public class Demo01ContactSaveReqVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "21555")
+ private Long id;
+
+ @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
+ @NotEmpty(message = "名字不能为空")
+ private String name;
+
+ @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "性别不能为空")
+ private Integer sex;
+
+ @Schema(description = "出生年", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotNull(message = "出生年不能为空")
+ private LocalDateTime birthday;
+
+ @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "你说的对")
+ @NotEmpty(message = "简介不能为空")
+ private String description;
+
+ @Schema(description = "头像")
+ private String avatar;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo02/Demo02CategoryController.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo02/Demo02CategoryController.java
new file mode 100644
index 0000000..642f767
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo02/Demo02CategoryController.java
@@ -0,0 +1,90 @@
+package com.tashow.cloud.infra.controller.admin.demo.demo02;
+
+import com.tashow.cloud.common.pojo.CommonResult;
+import com.tashow.cloud.common.util.object.BeanUtils;
+import com.tashow.cloud.excel.excel.core.util.ExcelUtils;
+import com.tashow.cloud.infra.dal.dataobject.demo.demo02.Demo02CategoryDO;
+import com.tashow.cloud.infra.service.demo.demo02.Demo02CategoryService;
+import com.tashow.cloud.infra.controller.admin.demo.demo02.vo.Demo02CategoryListReqVO;
+import com.tashow.cloud.infra.controller.admin.demo.demo02.vo.Demo02CategoryRespVO;
+import com.tashow.cloud.infra.controller.admin.demo.demo02.vo.Demo02CategorySaveReqVO;
+import com.tashow.cloud.web.apilog.core.annotation.ApiAccessLog;
+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.servlet.http.HttpServletResponse;
+import jakarta.validation.Valid;
+import java.io.IOException;
+import java.util.List;
+
+import static com.tashow.cloud.web.apilog.core.enums.OperateTypeEnum.EXPORT;
+import static com.tashow.cloud.common.pojo.CommonResult.success;
+
+@Tag(name = "管理后台 - 示例分类")
+@RestController
+@RequestMapping("/infra/demo02-category")
+@Validated
+public class Demo02CategoryController {
+
+ @Resource
+ private Demo02CategoryService demo02CategoryService;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建示例分类")
+ @PreAuthorize("@ss.hasPermission('infra:demo02-category:create')")
+ public CommonResult createDemo02Category(@Valid @RequestBody Demo02CategorySaveReqVO createReqVO) {
+ return success(demo02CategoryService.createDemo02Category(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新示例分类")
+ @PreAuthorize("@ss.hasPermission('infra:demo02-category:update')")
+ public CommonResult updateDemo02Category(@Valid @RequestBody Demo02CategorySaveReqVO updateReqVO) {
+ demo02CategoryService.updateDemo02Category(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除示例分类")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('infra:demo02-category:delete')")
+ public CommonResult deleteDemo02Category(@RequestParam("id") Long id) {
+ demo02CategoryService.deleteDemo02Category(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得示例分类")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('infra:demo02-category:query')")
+ public CommonResult getDemo02Category(@RequestParam("id") Long id) {
+ Demo02CategoryDO demo02Category = demo02CategoryService.getDemo02Category(id);
+ return success(BeanUtils.toBean(demo02Category, Demo02CategoryRespVO.class));
+ }
+
+ @GetMapping("/list")
+ @Operation(summary = "获得示例分类列表")
+ @PreAuthorize("@ss.hasPermission('infra:demo02-category:query')")
+ public CommonResult> getDemo02CategoryList(@Valid Demo02CategoryListReqVO listReqVO) {
+ List list = demo02CategoryService.getDemo02CategoryList(listReqVO);
+ return success(BeanUtils.toBean(list, Demo02CategoryRespVO.class));
+ }
+
+ @GetMapping("/export-excel")
+ @Operation(summary = "导出示例分类 Excel")
+ @PreAuthorize("@ss.hasPermission('infra:demo02-category:export')")
+ @ApiAccessLog(operateType = EXPORT)
+ public void exportDemo02CategoryExcel(@Valid Demo02CategoryListReqVO listReqVO,
+ HttpServletResponse response) throws IOException {
+ List list = demo02CategoryService.getDemo02CategoryList(listReqVO);
+ // 导出 Excel
+ ExcelUtils.write(response, "示例分类.xls", "数据", Demo02CategoryRespVO.class,
+ BeanUtils.toBean(list, Demo02CategoryRespVO.class));
+ }
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo02/vo/Demo02CategoryListReqVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo02/vo/Demo02CategoryListReqVO.java
new file mode 100644
index 0000000..4e532d7
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo02/vo/Demo02CategoryListReqVO.java
@@ -0,0 +1,25 @@
+package com.tashow.cloud.infra.controller.admin.demo.demo02.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+import static com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 示例分类列表 Request VO")
+@Data
+public class Demo02CategoryListReqVO {
+
+ @Schema(description = "名字", example = "芋艿")
+ private String name;
+
+ @Schema(description = "父级编号", example = "6080")
+ private Long parentId;
+
+ @Schema(description = "创建时间")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDateTime[] createTime;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo02/vo/Demo02CategoryRespVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo02/vo/Demo02CategoryRespVO.java
new file mode 100644
index 0000000..5a623e5
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo02/vo/Demo02CategoryRespVO.java
@@ -0,0 +1,31 @@
+package com.tashow.cloud.infra.controller.admin.demo.demo02.vo;
+
+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 Demo02CategoryRespVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10304")
+ @ExcelProperty("编号")
+ private Long id;
+
+ @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
+ @ExcelProperty("名字")
+ private String name;
+
+ @Schema(description = "父级编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "6080")
+ @ExcelProperty("父级编号")
+ private Long parentId;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("创建时间")
+ private LocalDateTime createTime;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo02/vo/Demo02CategorySaveReqVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo02/vo/Demo02CategorySaveReqVO.java
new file mode 100644
index 0000000..02cfe29
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo02/vo/Demo02CategorySaveReqVO.java
@@ -0,0 +1,24 @@
+package com.tashow.cloud.infra.controller.admin.demo.demo02.vo;
+
+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 Demo02CategorySaveReqVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10304")
+ private Long id;
+
+ @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
+ @NotEmpty(message = "名字不能为空")
+ private String name;
+
+ @Schema(description = "父级编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "6080")
+ @NotNull(message = "父级编号不能为空")
+ private Long parentId;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo03/Demo03StudentController.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo03/Demo03StudentController.java
new file mode 100644
index 0000000..5b3ae53
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo03/Demo03StudentController.java
@@ -0,0 +1,211 @@
+package com.tashow.cloud.infra.controller.admin.demo.demo03;
+
+import com.tashow.cloud.web.apilog.core.annotation.ApiAccessLog;
+import com.tashow.cloud.common.pojo.CommonResult;
+import com.tashow.cloud.common.pojo.PageParam;
+import com.tashow.cloud.common.pojo.PageResult;
+import com.tashow.cloud.common.util.object.BeanUtils;
+import com.tashow.cloud.excel.excel.core.util.ExcelUtils;
+import com.tashow.cloud.infra.controller.admin.demo.demo03.vo.Demo03StudentPageReqVO;
+import com.tashow.cloud.infra.controller.admin.demo.demo03.vo.Demo03StudentRespVO;
+import com.tashow.cloud.infra.controller.admin.demo.demo03.vo.Demo03StudentSaveReqVO;
+import com.tashow.cloud.infra.dal.dataobject.demo.demo03.Demo03CourseDO;
+import com.tashow.cloud.infra.dal.dataobject.demo.demo03.Demo03GradeDO;
+import com.tashow.cloud.infra.dal.dataobject.demo.demo03.Demo03StudentDO;
+import com.tashow.cloud.infra.service.demo.demo03.Demo03StudentService;
+import com.tashow.cloud.infra.controller.admin.demo.demo03.vo.Demo03StudentPageReqVO;
+import com.tashow.cloud.infra.controller.admin.demo.demo03.vo.Demo03StudentRespVO;
+import com.tashow.cloud.infra.controller.admin.demo.demo03.vo.Demo03StudentSaveReqVO;
+import com.tashow.cloud.infra.dal.dataobject.demo.demo03.Demo03CourseDO;
+import com.tashow.cloud.infra.dal.dataobject.demo.demo03.Demo03GradeDO;
+import com.tashow.cloud.infra.dal.dataobject.demo.demo03.Demo03StudentDO;
+import com.tashow.cloud.infra.service.demo.demo03.Demo03StudentService;
+import com.tashow.cloud.infra.controller.admin.demo.demo03.vo.Demo03StudentPageReqVO;
+import com.tashow.cloud.infra.controller.admin.demo.demo03.vo.Demo03StudentRespVO;
+import com.tashow.cloud.infra.controller.admin.demo.demo03.vo.Demo03StudentSaveReqVO;
+import com.tashow.cloud.infra.dal.dataobject.demo.demo03.Demo03CourseDO;
+import com.tashow.cloud.infra.dal.dataobject.demo.demo03.Demo03GradeDO;
+import com.tashow.cloud.infra.dal.dataobject.demo.demo03.Demo03StudentDO;
+import com.tashow.cloud.infra.service.demo.demo03.Demo03StudentService;
+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 com.tashow.cloud.web.apilog.core.enums.OperateTypeEnum.EXPORT;
+import static com.tashow.cloud.common.pojo.CommonResult.success;
+
+@Tag(name = "管理后台 - 学生")
+@RestController
+@RequestMapping("/infra/demo03-student")
+@Validated
+public class Demo03StudentController {
+
+ @Resource
+ private Demo03StudentService demo03StudentService;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建学生")
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:create')")
+ public CommonResult createDemo03Student(@Valid @RequestBody Demo03StudentSaveReqVO createReqVO) {
+ return success(demo03StudentService.createDemo03Student(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新学生")
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:update')")
+ public CommonResult updateDemo03Student(@Valid @RequestBody Demo03StudentSaveReqVO updateReqVO) {
+ demo03StudentService.updateDemo03Student(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除学生")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:delete')")
+ public CommonResult deleteDemo03Student(@RequestParam("id") Long id) {
+ demo03StudentService.deleteDemo03Student(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得学生")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
+ public CommonResult getDemo03Student(@RequestParam("id") Long id) {
+ Demo03StudentDO demo03Student = demo03StudentService.getDemo03Student(id);
+ return success(BeanUtils.toBean(demo03Student, Demo03StudentRespVO.class));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获得学生分页")
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
+ public CommonResult> getDemo03StudentPage(@Valid Demo03StudentPageReqVO pageReqVO) {
+ PageResult pageResult = demo03StudentService.getDemo03StudentPage(pageReqVO);
+ return success(BeanUtils.toBean(pageResult, Demo03StudentRespVO.class));
+ }
+
+ @GetMapping("/export-excel")
+ @Operation(summary = "导出学生 Excel")
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:export')")
+ @ApiAccessLog(operateType = EXPORT)
+ public void exportDemo03StudentExcel(@Valid Demo03StudentPageReqVO pageReqVO,
+ HttpServletResponse response) throws IOException {
+ pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ List list = demo03StudentService.getDemo03StudentPage(pageReqVO).getList();
+ // 导出 Excel
+ ExcelUtils.write(response, "学生.xls", "数据", Demo03StudentRespVO.class,
+ BeanUtils.toBean(list, Demo03StudentRespVO.class));
+ }
+
+ // ==================== 子表(学生课程) ====================
+
+ @GetMapping("/demo03-course/page")
+ @Operation(summary = "获得学生课程分页")
+ @Parameter(name = "studentId", description = "学生编号")
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
+ public CommonResult> getDemo03CoursePage(PageParam pageReqVO,
+ @RequestParam("studentId") Long studentId) {
+ return success(demo03StudentService.getDemo03CoursePage(pageReqVO, studentId));
+ }
+
+ @PostMapping("/demo03-course/create")
+ @Operation(summary = "创建学生课程")
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:create')")
+ public CommonResult createDemo03Course(@Valid @RequestBody Demo03CourseDO demo03Course) {
+ return success(demo03StudentService.createDemo03Course(demo03Course));
+ }
+
+ @PutMapping("/demo03-course/update")
+ @Operation(summary = "更新学生课程")
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:update')")
+ public CommonResult updateDemo03Course(@Valid @RequestBody Demo03CourseDO demo03Course) {
+ demo03StudentService.updateDemo03Course(demo03Course);
+ return success(true);
+ }
+
+ @DeleteMapping("/demo03-course/delete")
+ @Parameter(name = "id", description = "编号", required = true)
+ @Operation(summary = "删除学生课程")
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:delete')")
+ public CommonResult deleteDemo03Course(@RequestParam("id") Long id) {
+ demo03StudentService.deleteDemo03Course(id);
+ return success(true);
+ }
+
+ @GetMapping("/demo03-course/get")
+ @Operation(summary = "获得学生课程")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
+ public CommonResult getDemo03Course(@RequestParam("id") Long id) {
+ return success(demo03StudentService.getDemo03Course(id));
+ }
+
+ @GetMapping("/demo03-course/list-by-student-id")
+ @Operation(summary = "获得学生课程列表")
+ @Parameter(name = "studentId", description = "学生编号")
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
+ public CommonResult> getDemo03CourseListByStudentId(@RequestParam("studentId") Long studentId) {
+ return success(demo03StudentService.getDemo03CourseListByStudentId(studentId));
+ }
+
+ // ==================== 子表(学生班级) ====================
+
+ @GetMapping("/demo03-grade/page")
+ @Operation(summary = "获得学生班级分页")
+ @Parameter(name = "studentId", description = "学生编号")
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
+ public CommonResult> getDemo03GradePage(PageParam pageReqVO,
+ @RequestParam("studentId") Long studentId) {
+ return success(demo03StudentService.getDemo03GradePage(pageReqVO, studentId));
+ }
+
+ @PostMapping("/demo03-grade/create")
+ @Operation(summary = "创建学生班级")
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:create')")
+ public CommonResult createDemo03Grade(@Valid @RequestBody Demo03GradeDO demo03Grade) {
+ return success(demo03StudentService.createDemo03Grade(demo03Grade));
+ }
+
+ @PutMapping("/demo03-grade/update")
+ @Operation(summary = "更新学生班级")
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:update')")
+ public CommonResult updateDemo03Grade(@Valid @RequestBody Demo03GradeDO demo03Grade) {
+ demo03StudentService.updateDemo03Grade(demo03Grade);
+ return success(true);
+ }
+
+ @DeleteMapping("/demo03-grade/delete")
+ @Parameter(name = "id", description = "编号", required = true)
+ @Operation(summary = "删除学生班级")
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:delete')")
+ public CommonResult deleteDemo03Grade(@RequestParam("id") Long id) {
+ demo03StudentService.deleteDemo03Grade(id);
+ return success(true);
+ }
+
+ @GetMapping("/demo03-grade/get")
+ @Operation(summary = "获得学生班级")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
+ public CommonResult getDemo03Grade(@RequestParam("id") Long id) {
+ return success(demo03StudentService.getDemo03Grade(id));
+ }
+
+ @GetMapping("/demo03-grade/get-by-student-id")
+ @Operation(summary = "获得学生班级")
+ @Parameter(name = "studentId", description = "学生编号")
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
+ public CommonResult getDemo03GradeByStudentId(@RequestParam("studentId") Long studentId) {
+ return success(demo03StudentService.getDemo03GradeByStudentId(studentId));
+ }
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo03/package-info.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo03/package-info.java
new file mode 100644
index 0000000..73e213f
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo03/package-info.java
@@ -0,0 +1 @@
+package com.tashow.cloud.infra.controller.admin.demo.demo03;
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo03/vo/Demo03StudentPageReqVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo03/vo/Demo03StudentPageReqVO.java
new file mode 100644
index 0000000..3bebca6
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo03/vo/Demo03StudentPageReqVO.java
@@ -0,0 +1,33 @@
+package com.tashow.cloud.infra.controller.admin.demo.demo03.vo;
+
+import com.tashow.cloud.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 com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 学生分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class Demo03StudentPageReqVO extends PageParam {
+
+ @Schema(description = "名字", example = "芋艿")
+ private String name;
+
+ @Schema(description = "性别")
+ private Integer sex;
+
+ @Schema(description = "简介", example = "随便")
+ private String description;
+
+ @Schema(description = "创建时间")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDateTime[] createTime;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo03/vo/Demo03StudentRespVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo03/vo/Demo03StudentRespVO.java
new file mode 100644
index 0000000..c0c6007
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo03/vo/Demo03StudentRespVO.java
@@ -0,0 +1,42 @@
+package com.tashow.cloud.infra.controller.admin.demo.demo03.vo;
+
+import com.tashow.cloud.excel.excel.core.annotations.DictFormat;
+import com.tashow.cloud.excel.excel.core.convert.DictConvert;
+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 Demo03StudentRespVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8525")
+ @ExcelProperty("编号")
+ private Long id;
+
+ @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
+ @ExcelProperty("名字")
+ private String name;
+
+ @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty(value = "性别", converter = DictConvert.class)
+ @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
+ private Integer sex;
+
+ @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("出生日期")
+ private LocalDateTime birthday;
+
+ @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "随便")
+ @ExcelProperty("简介")
+ private String description;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("创建时间")
+ private LocalDateTime createTime;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo03/vo/Demo03StudentSaveReqVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo03/vo/Demo03StudentSaveReqVO.java
new file mode 100644
index 0000000..03cb151
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/demo03/vo/Demo03StudentSaveReqVO.java
@@ -0,0 +1,45 @@
+package com.tashow.cloud.infra.controller.admin.demo.demo03.vo;
+
+import com.tashow.cloud.infra.dal.dataobject.demo.demo03.Demo03CourseDO;
+import com.tashow.cloud.infra.dal.dataobject.demo.demo03.Demo03GradeDO;
+import com.tashow.cloud.infra.dal.dataobject.demo.demo03.Demo03CourseDO;
+import com.tashow.cloud.infra.dal.dataobject.demo.demo03.Demo03GradeDO;
+import com.tashow.cloud.infra.dal.dataobject.demo.demo03.Demo03CourseDO;
+import com.tashow.cloud.infra.dal.dataobject.demo.demo03.Demo03GradeDO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Schema(description = "管理后台 - 学生新增/修改 Request VO")
+@Data
+public class Demo03StudentSaveReqVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8525")
+ private Long id;
+
+ @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
+ @NotEmpty(message = "名字不能为空")
+ private String name;
+
+ @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotNull(message = "性别不能为空")
+ private Integer sex;
+
+ @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotNull(message = "出生日期不能为空")
+ private LocalDateTime birthday;
+
+ @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "随便")
+ @NotEmpty(message = "简介不能为空")
+ private String description;
+
+
+ private List demo03Courses;
+
+ private Demo03GradeDO demo03Grade;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/package-info.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/package-info.java
new file mode 100644
index 0000000..0afb225
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/demo/package-info.java
@@ -0,0 +1,8 @@
+/**
+ * 代码生成示例
+ *
+ * 1. demo01:单表(增删改查)
+ * 2. demo02:单表(树形结构)
+ * 3. demo03:主子表(标准模式)+ 主子表(ERP 模式)+ 主子表(内嵌模式)
+ */
+package com.tashow.cloud.infra.controller.admin.demo;
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/FileConfigController.http b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/FileConfigController.http
new file mode 100644
index 0000000..14b6228
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/FileConfigController.http
@@ -0,0 +1,45 @@
+### 请求 /infra/file-config/create 接口 => 成功
+POST {{baseUrl}}/infra/file-config/create
+Content-Type: application/json
+tenant-id: {{adminTenantId}}
+Authorization: Bearer {{token}}
+
+{
+ "name": "S3 - 七牛云",
+ "remark": "",
+ "storage": 20,
+ "config": {
+ "accessKey": "b7yvuhBSAGjmtPhMFcn9iMOxUOY_I06cA_p0ZUx8",
+ "accessSecret": "kXM1l5ia1RvSX3QaOEcwI3RLz3Y2rmNszWonKZtP",
+ "bucket": "ruoyi-vue-pro",
+ "endpoint": "s3-cn-south-1.qiniucs.com",
+ "domain": "http://test.yudao.iocoder.cn",
+ "region": "oss-cn-beijing"
+ }
+}
+
+### 请求 /infra/file-config/update 接口 => 成功
+PUT {{baseUrl}}/infra/file-config/update
+Content-Type: application/json
+tenant-id: {{adminTenantId}}
+Authorization: Bearer {{token}}
+
+{
+ "id": 2,
+ "name": "S3 - 七牛云",
+ "remark": "",
+ "config": {
+ "accessKey": "b7yvuhBSAGjmtPhMFcn9iMOxUOY_I06cA_p0ZUx8",
+ "accessSecret": "kXM1l5ia1RvSX3QaOEcwI3RLz3Y2rmNszWonKZtP",
+ "bucket": "ruoyi-vue-pro",
+ "endpoint": "s3-cn-south-1.qiniucs.com",
+ "domain": "http://test.yudao.iocoder.cn",
+ "region": "oss-cn-beijing"
+ }
+}
+
+### 请求 /infra/file-config/test 接口 => 成功
+GET {{baseUrl}}/infra/file-config/test?id=2
+Content-Type: application/json
+tenant-id: {{adminTenantId}}
+Authorization: Bearer {{token}}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/FileConfigController.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/FileConfigController.java
new file mode 100644
index 0000000..f0efff1
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/FileConfigController.java
@@ -0,0 +1,92 @@
+package com.tashow.cloud.infra.controller.admin.file;
+
+import com.tashow.cloud.common.pojo.CommonResult;
+import com.tashow.cloud.common.pojo.PageResult;
+import com.tashow.cloud.common.util.object.BeanUtils;
+import com.tashow.cloud.infra.dal.dataobject.file.FileConfigDO;
+import com.tashow.cloud.infra.service.file.FileConfigService;
+import com.tashow.cloud.infra.controller.admin.file.vo.config.FileConfigPageReqVO;
+import com.tashow.cloud.infra.controller.admin.file.vo.config.FileConfigRespVO;
+import com.tashow.cloud.infra.controller.admin.file.vo.config.FileConfigSaveReqVO;
+import com.tashow.cloud.infra.dal.dataobject.file.FileConfigDO;
+import com.tashow.cloud.infra.service.file.FileConfigService;
+import com.tashow.cloud.infra.dal.dataobject.file.FileConfigDO;
+import com.tashow.cloud.infra.service.file.FileConfigService;
+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 com.tashow.cloud.common.pojo.CommonResult.success;
+
+@Tag(name = "管理后台 - 文件配置")
+@RestController
+@RequestMapping("/infra/file-config")
+@Validated
+public class FileConfigController {
+
+ @Resource
+ private FileConfigService fileConfigService;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建文件配置")
+ @PreAuthorize("@ss.hasPermission('infra:file-config:create')")
+ public CommonResult createFileConfig(@Valid @RequestBody FileConfigSaveReqVO createReqVO) {
+ return success(fileConfigService.createFileConfig(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新文件配置")
+ @PreAuthorize("@ss.hasPermission('infra:file-config:update')")
+ public CommonResult updateFileConfig(@Valid @RequestBody FileConfigSaveReqVO updateReqVO) {
+ fileConfigService.updateFileConfig(updateReqVO);
+ return success(true);
+ }
+
+ @PutMapping("/update-master")
+ @Operation(summary = "更新文件配置为 Master")
+ @PreAuthorize("@ss.hasPermission('infra:file-config:update')")
+ public CommonResult updateFileConfigMaster(@RequestParam("id") Long id) {
+ fileConfigService.updateFileConfigMaster(id);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除文件配置")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('infra:file-config:delete')")
+ public CommonResult deleteFileConfig(@RequestParam("id") Long id) {
+ fileConfigService.deleteFileConfig(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得文件配置")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('infra:file-config:query')")
+ public CommonResult getFileConfig(@RequestParam("id") Long id) {
+ FileConfigDO config = fileConfigService.getFileConfig(id);
+ return success(BeanUtils.toBean(config, FileConfigRespVO.class));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获得文件配置分页")
+ @PreAuthorize("@ss.hasPermission('infra:file-config:query')")
+ public CommonResult> getFileConfigPage(@Valid FileConfigPageReqVO pageVO) {
+ PageResult pageResult = fileConfigService.getFileConfigPage(pageVO);
+ return success(BeanUtils.toBean(pageResult, FileConfigRespVO.class));
+ }
+
+ @GetMapping("/test")
+ @Operation(summary = "测试文件配置是否正确")
+ @PreAuthorize("@ss.hasPermission('infra:file-config:query')")
+ public CommonResult testFileConfig(@RequestParam("id") Long id) throws Exception {
+ String url = fileConfigService.testFileConfig(id);
+ return success(url);
+ }
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/FileController.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/FileController.java
new file mode 100644
index 0000000..94a68e5
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/FileController.java
@@ -0,0 +1,102 @@
+package com.tashow.cloud.infra.controller.admin.file;
+
+import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.core.util.URLUtil;
+import com.tashow.cloud.common.pojo.CommonResult;
+import com.tashow.cloud.common.pojo.PageResult;
+import com.tashow.cloud.common.util.object.BeanUtils;
+import com.tashow.cloud.infra.controller.admin.file.vo.file.*;
+import com.tashow.cloud.infra.dal.dataobject.file.FileDO;
+import com.tashow.cloud.infra.service.file.FileService;
+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.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.Valid;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+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 static com.tashow.cloud.common.pojo.CommonResult.success;
+import static com.tashow.cloud.infra.framework.file.core.utils.FileTypeUtils.writeAttachment;
+
+@Tag(name = "管理后台 - 文件存储")
+@RestController
+@RequestMapping("/infra/file")
+@Validated
+@Slf4j
+public class FileController {
+
+ @Resource
+ private FileService fileService;
+
+ @PostMapping("/upload")
+ @Operation(summary = "上传文件", description = "模式一:后端上传文件")
+ public CommonResult uploadFile(FileUploadReqVO uploadReqVO) throws Exception {
+ MultipartFile file = uploadReqVO.getFile();
+ String path = uploadReqVO.getPath();
+ return success(fileService.createFile(file.getOriginalFilename(), path, IoUtil.readBytes(file.getInputStream())));
+ }
+
+ @GetMapping("/presigned-url")
+ @Operation(summary = "获取文件预签名地址", description = "模式二:前端上传文件:用于前端直接上传七牛、阿里云 OSS 等文件存储器")
+ public CommonResult getFilePresignedUrl(@RequestParam("path") String path) throws Exception {
+ return success(fileService.getFilePresignedUrl(path));
+ }
+
+ @PostMapping("/create")
+ @Operation(summary = "创建文件", description = "模式二:前端上传文件:配合 presigned-url 接口,记录上传了上传的文件")
+ public CommonResult createFile(@Valid @RequestBody FileCreateReqVO createReqVO) {
+ return success(fileService.createFile(createReqVO));
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除文件")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('infra:file:delete')")
+ public CommonResult deleteFile(@RequestParam("id") Long id) throws Exception {
+ fileService.deleteFile(id);
+ return success(true);
+ }
+
+ @GetMapping("/{configId}/get/**")
+ @PermitAll
+ @Operation(summary = "下载文件")
+ @Parameter(name = "configId", description = "配置编号", required = true)
+ public void getFileContent(HttpServletRequest request,
+ HttpServletResponse response,
+ @PathVariable("configId") Long configId) throws Exception {
+ // 获取请求的路径
+ String path = StrUtil.subAfter(request.getRequestURI(), "/get/", false);
+ if (StrUtil.isEmpty(path)) {
+ throw new IllegalArgumentException("结尾的 path 路径必须传递");
+ }
+ // 解码,解决中文路径的问题 https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/807/
+ path = URLUtil.decode(path);
+
+ // 读取内容
+ byte[] content = fileService.getFileContent(configId, path);
+ if (content == null) {
+ log.warn("[getFileContent][configId({}) path({}) 文件不存在]", configId, path);
+ response.setStatus(HttpStatus.NOT_FOUND.value());
+ return;
+ }
+ writeAttachment(response, path, content);
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获得文件分页")
+ @PreAuthorize("@ss.hasPermission('infra:file:query')")
+ public CommonResult> getFilePage(@Valid FilePageReqVO pageVO) {
+ PageResult pageResult = fileService.getFilePage(pageVO);
+ return success(BeanUtils.toBean(pageResult, FileRespVO.class));
+ }
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/vo/config/FileConfigPageReqVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/vo/config/FileConfigPageReqVO.java
new file mode 100644
index 0000000..cd841b7
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/vo/config/FileConfigPageReqVO.java
@@ -0,0 +1,30 @@
+package com.tashow.cloud.infra.controller.admin.file.vo.config;
+
+import com.tashow.cloud.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 com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 文件配置分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class FileConfigPageReqVO extends PageParam {
+
+ @Schema(description = "配置名", example = "S3 - 阿里云")
+ private String name;
+
+ @Schema(description = "存储器", example = "1")
+ private Integer storage;
+
+ @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/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/vo/config/FileConfigRespVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/vo/config/FileConfigRespVO.java
new file mode 100644
index 0000000..430b844
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/vo/config/FileConfigRespVO.java
@@ -0,0 +1,34 @@
+package com.tashow.cloud.infra.controller.admin.file.vo.config;
+
+import com.tashow.cloud.infra.framework.file.core.client.FileClientConfig;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 文件配置 Response VO")
+@Data
+public class FileConfigRespVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ private Long id;
+
+ @Schema(description = "配置名", requiredMode = Schema.RequiredMode.REQUIRED, example = "S3 - 阿里云")
+ private String name;
+
+ @Schema(description = "存储器,参见 FileStorageEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ private Integer storage;
+
+ @Schema(description = "是否为主配置", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+ private Boolean master;
+
+ @Schema(description = "存储配置", requiredMode = Schema.RequiredMode.REQUIRED)
+ private FileClientConfig config;
+
+ @Schema(description = "备注", example = "我是备注")
+ private String remark;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ private LocalDateTime createTime;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/vo/config/FileConfigSaveReqVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/vo/config/FileConfigSaveReqVO.java
new file mode 100644
index 0000000..1fdd35d
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/vo/config/FileConfigSaveReqVO.java
@@ -0,0 +1,31 @@
+package com.tashow.cloud.infra.controller.admin.file.vo.config;
+
+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 FileConfigSaveReqVO {
+
+ @Schema(description = "编号", example = "1")
+ private Long id;
+
+ @Schema(description = "配置名", requiredMode = Schema.RequiredMode.REQUIRED, example = "S3 - 阿里云")
+ @NotNull(message = "配置名不能为空")
+ private String name;
+
+ @Schema(description = "存储器,参见 FileStorageEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "存储器不能为空")
+ private Integer storage;
+
+ @Schema(description = "存储配置,配置是动态参数,所以使用 Map 接收", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotNull(message = "存储配置不能为空")
+ private Map config;
+
+ @Schema(description = "备注", example = "我是备注")
+ private String remark;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/vo/file/FileCreateReqVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/vo/file/FileCreateReqVO.java
new file mode 100644
index 0000000..138140f
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/vo/file/FileCreateReqVO.java
@@ -0,0 +1,33 @@
+package com.tashow.cloud.infra.controller.admin.file.vo.file;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "管理后台 - 文件创建 Request VO")
+@Data
+public class FileCreateReqVO {
+
+ @NotNull(message = "文件配置编号不能为空")
+ @Schema(description = "文件配置编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11")
+ private Long configId;
+
+ @NotNull(message = "文件路径不能为空")
+ @Schema(description = "文件路径", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao.jpg")
+ private String path;
+
+ @NotNull(message = "原文件名不能为空")
+ @Schema(description = "原文件名", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao.jpg")
+ private String name;
+
+ @NotNull(message = "文件 URL不能为空")
+ @Schema(description = "文件 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/yudao.jpg")
+ private String url;
+
+ @Schema(description = "文件 MIME 类型", example = "application/octet-stream")
+ private String type;
+
+ @Schema(description = "文件大小", example = "2048", requiredMode = Schema.RequiredMode.REQUIRED)
+ private Integer size;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/vo/file/FilePageReqVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/vo/file/FilePageReqVO.java
new file mode 100644
index 0000000..9abc3e5
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/vo/file/FilePageReqVO.java
@@ -0,0 +1,30 @@
+package com.tashow.cloud.infra.controller.admin.file.vo.file;
+
+import com.tashow.cloud.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 com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 文件分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class FilePageReqVO extends PageParam {
+
+ @Schema(description = "文件路径,模糊匹配", example = "yudao")
+ private String path;
+
+ @Schema(description = "文件类型,模糊匹配", example = "jpg")
+ private String type;
+
+ @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/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/vo/file/FilePresignedUrlRespVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/vo/file/FilePresignedUrlRespVO.java
new file mode 100644
index 0000000..866da64
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/vo/file/FilePresignedUrlRespVO.java
@@ -0,0 +1,29 @@
+package com.tashow.cloud.infra.controller.admin.file.vo.file;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Schema(description = "管理后台 - 文件预签名地址 Response VO")
+@Data
+public class FilePresignedUrlRespVO {
+
+ @Schema(description = "配置编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11")
+ private Long configId;
+
+ @Schema(description = "文件上传 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://s3.cn-south-1.qiniucs.com/ruoyi-vue-pro/758d3a5387507358c7236de4c8f96de1c7f5097ff6a7722b34772fb7b76b140f.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=3TvrJ70gl2Gt6IBe7_IZT1F6i_k0iMuRtyEv4EyS%2F20240217%2Fcn-south-1%2Fs3%2Faws4_request&X-Amz-Date=20240217T123222Z&X-Amz-Expires=600&X-Amz-SignedHeaders=host&X-Amz-Signature=a29f33770ab79bf523ccd4034d0752ac545f3c2a3b17baa1eb4e280cfdccfda5")
+ private String uploadUrl;
+
+ /**
+ * 为什么要返回 url 字段?
+ *
+ * 前端上传完文件后,需要使用该 URL 进行访问
+ */
+ @Schema(description = "文件访问 URL", requiredMode = Schema.RequiredMode.REQUIRED,
+ example = "https://test.yudao.iocoder.cn/758d3a5387507358c7236de4c8f96de1c7f5097ff6a7722b34772fb7b76b140f.png")
+ private String url;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/vo/file/FileRespVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/vo/file/FileRespVO.java
new file mode 100644
index 0000000..24a09ec
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/vo/file/FileRespVO.java
@@ -0,0 +1,36 @@
+package com.tashow.cloud.infra.controller.admin.file.vo.file;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 文件 Response VO,不返回 content 字段,太大")
+@Data
+public class FileRespVO {
+
+ @Schema(description = "文件编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ private Long id;
+
+ @Schema(description = "配置编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11")
+ private Long configId;
+
+ @Schema(description = "文件路径", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao.jpg")
+ private String path;
+
+ @Schema(description = "原文件名", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao.jpg")
+ private String name;
+
+ @Schema(description = "文件 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/yudao.jpg")
+ private String url;
+
+ @Schema(description = "文件MIME类型", example = "application/octet-stream")
+ private String type;
+
+ @Schema(description = "文件大小", example = "2048", requiredMode = Schema.RequiredMode.REQUIRED)
+ private Integer size;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ private LocalDateTime createTime;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/vo/file/FileUploadReqVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/vo/file/FileUploadReqVO.java
new file mode 100644
index 0000000..71e21a9
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/file/vo/file/FileUploadReqVO.java
@@ -0,0 +1,20 @@
+package com.tashow.cloud.infra.controller.admin.file.vo.file;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.web.multipart.MultipartFile;
+
+import jakarta.validation.constraints.NotNull;
+
+@Schema(description = "管理后台 - 上传文件 Request VO")
+@Data
+public class FileUploadReqVO {
+
+ @Schema(description = "文件附件", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotNull(message = "文件附件不能为空")
+ private MultipartFile file;
+
+ @Schema(description = "文件附件", example = "yudaoyuanma.png")
+ private String path;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/job/JobController.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/job/JobController.java
new file mode 100644
index 0000000..6af6118
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/job/JobController.java
@@ -0,0 +1,28 @@
+package com.tashow.cloud.infra.controller.admin.job;
+
+import com.tashow.cloud.common.pojo.CommonResult;
+import io.swagger.v3.oas.annotations.Operation;
+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.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import static com.tashow.cloud.common.pojo.CommonResult.error;
+
+
+@Tag(name = "管理后台 - 定时任务")
+@RestController
+@RequestMapping("/infra/job")
+@Validated
+public class JobController {
+
+ @GetMapping("/page")
+ @Operation(summary = "获得定时任务分页")
+ @PreAuthorize("@ss.hasPermission('infra:job:query')")
+ public CommonResult getJobPage() {
+ return error(-1, "Cloud 版本使用 XXL-Job 作为定时任务!请参考 https://cloud.iocoder.cn/job/ 文档操作");
+ }
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/logger/ApiAccessLogController.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/logger/ApiAccessLogController.java
new file mode 100644
index 0000000..17096d7
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/logger/ApiAccessLogController.java
@@ -0,0 +1,68 @@
+package com.tashow.cloud.infra.controller.admin.logger;
+
+import com.tashow.cloud.web.apilog.core.annotation.ApiAccessLog;
+import com.tashow.cloud.common.pojo.CommonResult;
+import com.tashow.cloud.common.pojo.PageParam;
+import com.tashow.cloud.common.pojo.PageResult;
+import com.tashow.cloud.common.util.object.BeanUtils;
+import com.tashow.cloud.excel.excel.core.util.ExcelUtils;
+import com.tashow.cloud.infra.controller.admin.logger.vo.apiaccesslog.ApiAccessLogPageReqVO;
+import com.tashow.cloud.infra.controller.admin.logger.vo.apiaccesslog.ApiAccessLogRespVO;
+import com.tashow.cloud.infra.dal.dataobject.logger.ApiAccessLogDO;
+import com.tashow.cloud.infra.service.logger.ApiAccessLogService;
+import com.tashow.cloud.infra.controller.admin.logger.vo.apiaccesslog.ApiAccessLogPageReqVO;
+import com.tashow.cloud.infra.controller.admin.logger.vo.apiaccesslog.ApiAccessLogRespVO;
+import com.tashow.cloud.infra.dal.dataobject.logger.ApiAccessLogDO;
+import com.tashow.cloud.infra.service.logger.ApiAccessLogService;
+import com.tashow.cloud.infra.controller.admin.logger.vo.apiaccesslog.ApiAccessLogPageReqVO;
+import com.tashow.cloud.infra.controller.admin.logger.vo.apiaccesslog.ApiAccessLogRespVO;
+import com.tashow.cloud.infra.dal.dataobject.logger.ApiAccessLogDO;
+import com.tashow.cloud.infra.service.logger.ApiAccessLogService;
+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 com.tashow.cloud.web.apilog.core.enums.OperateTypeEnum.EXPORT;
+import static com.tashow.cloud.common.pojo.CommonResult.success;
+
+@Tag(name = "管理后台 - API 访问日志")
+@RestController
+@RequestMapping("/infra/api-access-log")
+@Validated
+public class ApiAccessLogController {
+
+ @Resource
+ private ApiAccessLogService apiAccessLogService;
+
+ @GetMapping("/page")
+ @Operation(summary = "获得API 访问日志分页")
+ @PreAuthorize("@ss.hasPermission('infra:api-access-log:query')")
+ public CommonResult> getApiAccessLogPage(@Valid ApiAccessLogPageReqVO pageReqVO) {
+ PageResult pageResult = apiAccessLogService.getApiAccessLogPage(pageReqVO);
+ return success(BeanUtils.toBean(pageResult, ApiAccessLogRespVO.class));
+ }
+
+ @GetMapping("/export-excel")
+ @Operation(summary = "导出API 访问日志 Excel")
+ @PreAuthorize("@ss.hasPermission('infra:api-access-log:export')")
+ @ApiAccessLog(operateType = EXPORT)
+ public void exportApiAccessLogExcel(@Valid ApiAccessLogPageReqVO exportReqVO,
+ HttpServletResponse response) throws IOException {
+ exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ List list = apiAccessLogService.getApiAccessLogPage(exportReqVO).getList();
+ // 导出 Excel
+ ExcelUtils.write(response, "API 访问日志.xls", "数据", ApiAccessLogRespVO.class,
+ BeanUtils.toBean(list, ApiAccessLogRespVO.class));
+ }
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/logger/ApiErrorLogController.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/logger/ApiErrorLogController.java
new file mode 100644
index 0000000..b0e0929
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/logger/ApiErrorLogController.java
@@ -0,0 +1,74 @@
+package com.tashow.cloud.infra.controller.admin.logger;
+
+import com.tashow.cloud.web.apilog.core.annotation.ApiAccessLog;
+import com.tashow.cloud.common.pojo.CommonResult;
+import com.tashow.cloud.common.pojo.PageParam;
+import com.tashow.cloud.common.pojo.PageResult;
+import com.tashow.cloud.common.util.object.BeanUtils;
+import com.tashow.cloud.excel.excel.core.util.ExcelUtils;
+import com.tashow.cloud.infra.controller.admin.logger.vo.apierrorlog.ApiErrorLogPageReqVO;
+import com.tashow.cloud.infra.dal.dataobject.logger.ApiErrorLogDO;
+import com.tashow.cloud.infra.service.logger.ApiErrorLogService;
+import com.tashow.cloud.infra.controller.admin.logger.vo.apierrorlog.ApiErrorLogRespVO;
+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 java.io.IOException;
+import java.util.List;
+
+import static com.tashow.cloud.web.apilog.core.enums.OperateTypeEnum.EXPORT;
+import static com.tashow.cloud.common.pojo.CommonResult.success;
+import static com.tashow.cloud.web.web.core.util.WebFrameworkUtils.getLoginUserId;
+
+@Tag(name = "管理后台 - API 错误日志")
+@RestController
+@RequestMapping("/infra/api-error-log")
+@Validated
+public class ApiErrorLogController {
+
+ @Resource
+ private ApiErrorLogService apiErrorLogService;
+
+ @PutMapping("/update-status")
+ @Operation(summary = "更新 API 错误日志的状态")
+ @Parameters({
+ @Parameter(name = "id", description = "编号", required = true, example = "1024"),
+ @Parameter(name = "processStatus", description = "处理状态", required = true, example = "1")
+ })
+ @PreAuthorize("@ss.hasPermission('infra:api-error-log:update-status')")
+ public CommonResult updateApiErrorLogProcess(@RequestParam("id") Long id,
+ @RequestParam("processStatus") Integer processStatus) {
+ apiErrorLogService.updateApiErrorLogProcess(id, processStatus, getLoginUserId());
+ return success(true);
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获得 API 错误日志分页")
+ @PreAuthorize("@ss.hasPermission('infra:api-error-log:query')")
+ public CommonResult> getApiErrorLogPage(@Valid ApiErrorLogPageReqVO pageReqVO) {
+ PageResult pageResult = apiErrorLogService.getApiErrorLogPage(pageReqVO);
+ return success(BeanUtils.toBean(pageResult, ApiErrorLogRespVO.class));
+ }
+
+ @GetMapping("/export-excel")
+ @Operation(summary = "导出 API 错误日志 Excel")
+ @PreAuthorize("@ss.hasPermission('infra:api-error-log:export')")
+ @ApiAccessLog(operateType = EXPORT)
+ public void exportApiErrorLogExcel(@Valid ApiErrorLogPageReqVO exportReqVO,
+ HttpServletResponse response) throws IOException {
+ exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ List list = apiErrorLogService.getApiErrorLogPage(exportReqVO).getList();
+ // 导出 Excel
+ ExcelUtils.write(response, "API 错误日志.xls", "数据", ApiErrorLogRespVO.class,
+ BeanUtils.toBean(list, ApiErrorLogRespVO.class));
+ }
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogPageReqVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogPageReqVO.java
new file mode 100644
index 0000000..db048bf
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogPageReqVO.java
@@ -0,0 +1,42 @@
+package com.tashow.cloud.infra.controller.admin.logger.vo.apiaccesslog;
+
+import com.tashow.cloud.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 com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - API 访问日志分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ApiAccessLogPageReqVO extends PageParam {
+
+ @Schema(description = "用户编号", example = "666")
+ private Long userId;
+
+ @Schema(description = "用户类型", example = "2")
+ private Integer userType;
+
+ @Schema(description = "应用名", example = "dashboard")
+ private String applicationName;
+
+ @Schema(description = "请求地址,模糊匹配", example = "/xxx/yyy")
+ private String requestUrl;
+
+ @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[] beginTime;
+
+ @Schema(description = "执行时长,大于等于,单位:毫秒", example = "100")
+ private Integer duration;
+
+ @Schema(description = "结果码", example = "0")
+ private Integer resultCode;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogRespVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogRespVO.java
new file mode 100644
index 0000000..3dd8752
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogRespVO.java
@@ -0,0 +1,99 @@
+package com.tashow.cloud.infra.controller.admin.logger.vo.apiaccesslog;
+
+import com.tashow.cloud.excel.excel.core.annotations.DictFormat;
+import com.tashow.cloud.excel.excel.core.convert.DictConvert;
+import com.tashow.cloud.systemapi.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 = "管理后台 - API 访问日志 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class ApiAccessLogRespVO {
+
+ @Schema(description = "日志主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ @ExcelProperty("日志主键")
+ private Long id;
+
+ @Schema(description = "链路追踪编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "66600cb6-7852-11eb-9439-0242ac130002")
+ @ExcelProperty("链路追踪编号")
+ private String traceId;
+
+ @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "666")
+ @ExcelProperty("用户编号")
+ private Long userId;
+
+ @Schema(description = "用户类型,参见 UserTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
+ @ExcelProperty(value = "用户类型", converter = DictConvert.class)
+ @DictFormat(DictTypeConstants.USER_TYPE)
+ private Integer userType;
+
+ @Schema(description = "应用名", requiredMode = Schema.RequiredMode.REQUIRED, example = "dashboard")
+ @ExcelProperty("应用名")
+ private String applicationName;
+
+ @Schema(description = "请求方法名", requiredMode = Schema.RequiredMode.REQUIRED, example = "GET")
+ @ExcelProperty("请求方法名")
+ private String requestMethod;
+
+ @Schema(description = "请求地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "/xxx/yyy")
+ @ExcelProperty("请求地址")
+ private String requestUrl;
+
+ @Schema(description = "请求参数")
+ @ExcelProperty("请求参数")
+ private String requestParams;
+
+ @Schema(description = "响应结果")
+ @ExcelProperty("响应结果")
+ private String responseBody;
+
+ @Schema(description = "用户 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "127.0.0.1")
+ @ExcelProperty("用户 IP")
+ private String userIp;
+
+ @Schema(description = "浏览器 UA", requiredMode = Schema.RequiredMode.REQUIRED, example = "Mozilla/5.0")
+ @ExcelProperty("浏览器 UA")
+ private String userAgent;
+
+ @Schema(description = "操作模块", requiredMode = Schema.RequiredMode.REQUIRED, example = "商品模块")
+ @ExcelProperty("操作模块")
+ private String operateModule;
+
+ @Schema(description = "操作名", requiredMode = Schema.RequiredMode.REQUIRED, example = "创建商品")
+ @ExcelProperty("操作名")
+ private String operateName;
+
+ @Schema(description = "操作分类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @ExcelProperty(value = "操作分类", converter = DictConvert.class)
+ @DictFormat(com.tashow.cloud.infraapi.enums.DictTypeConstants.OPERATE_TYPE)
+ private Integer operateType;
+
+ @Schema(description = "开始请求时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("开始请求时间")
+ private LocalDateTime beginTime;
+
+ @Schema(description = "结束请求时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("结束请求时间")
+ private LocalDateTime endTime;
+
+ @Schema(description = "执行时长", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
+ @ExcelProperty("执行时长")
+ private Integer duration;
+
+ @Schema(description = "结果码", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
+ @ExcelProperty("结果码")
+ private Integer resultCode;
+
+ @Schema(description = "结果提示", example = "芋道源码,牛逼!")
+ @ExcelProperty("结果提示")
+ private String resultMsg;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ private LocalDateTime createTime;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogPageReqVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogPageReqVO.java
new file mode 100644
index 0000000..fbb7376
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogPageReqVO.java
@@ -0,0 +1,39 @@
+package com.tashow.cloud.infra.controller.admin.logger.vo.apierrorlog;
+
+import com.tashow.cloud.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 com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - API 错误日志分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ApiErrorLogPageReqVO extends PageParam {
+
+ @Schema(description = "用户编号", example = "666")
+ private Long userId;
+
+ @Schema(description = "用户类型", example = "1")
+ private Integer userType;
+
+ @Schema(description = "应用名", example = "dashboard")
+ private String applicationName;
+
+ @Schema(description = "请求地址", example = "/xx/yy")
+ private String requestUrl;
+
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ @Schema(description = "异常发生时间")
+ private LocalDateTime[] exceptionTime;
+
+ @Schema(description = "处理状态", example = "0")
+ private Integer processStatus;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogRespVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogRespVO.java
new file mode 100644
index 0000000..1796a72
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogRespVO.java
@@ -0,0 +1,113 @@
+package com.tashow.cloud.infra.controller.admin.logger.vo.apierrorlog;
+
+import com.tashow.cloud.excel.excel.core.annotations.DictFormat;
+import com.tashow.cloud.excel.excel.core.convert.DictConvert;
+import com.tashow.cloud.infraapi.enums.DictTypeConstants;
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.tashow.cloud.infraapi.enums.DictTypeConstants;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - API 错误日志 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class ApiErrorLogRespVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ @ExcelProperty("编号")
+ private Long id;
+
+ @Schema(description = "链路追踪编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "66600cb6-7852-11eb-9439-0242ac130002")
+ @ExcelProperty("链路追踪编号")
+ private String traceId;
+
+ @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "666")
+ @ExcelProperty("用户编号")
+ private Long userId;
+
+ @Schema(description = "用户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @ExcelProperty(value = "用户类型", converter = DictConvert.class)
+ @DictFormat(com.tashow.cloud.systemapi.enums.DictTypeConstants.USER_TYPE)
+ private Integer userType;
+
+ @Schema(description = "应用名", requiredMode = Schema.RequiredMode.REQUIRED, example = "dashboard")
+ @ExcelProperty("应用名")
+ private String applicationName;
+
+ @Schema(description = "请求方法名", requiredMode = Schema.RequiredMode.REQUIRED, example = "GET")
+ @ExcelProperty("请求方法名")
+ private String requestMethod;
+
+ @Schema(description = "请求地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "/xx/yy")
+ @ExcelProperty("请求地址")
+ private String requestUrl;
+
+ @Schema(description = "请求参数", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("请求参数")
+ private String requestParams;
+
+ @Schema(description = "用户 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "127.0.0.1")
+ @ExcelProperty("用户 IP")
+ private String userIp;
+
+ @Schema(description = "浏览器 UA", requiredMode = Schema.RequiredMode.REQUIRED, example = "Mozilla/5.0")
+ @ExcelProperty("浏览器 UA")
+ private String userAgent;
+
+ @Schema(description = "异常发生时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("异常发生时间")
+ private LocalDateTime exceptionTime;
+
+ @Schema(description = "异常名", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("异常名")
+ private String exceptionName;
+
+ @Schema(description = "异常导致的消息", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("异常导致的消息")
+ private String exceptionMessage;
+
+ @Schema(description = "异常导致的根消息", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("异常导致的根消息")
+ private String exceptionRootCauseMessage;
+
+ @Schema(description = "异常的栈轨迹", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("异常的栈轨迹")
+ private String exceptionStackTrace;
+
+ @Schema(description = "异常发生的类全名", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("异常发生的类全名")
+ private String exceptionClassName;
+
+ @Schema(description = "异常发生的类文件", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("异常发生的类文件")
+ private String exceptionFileName;
+
+ @Schema(description = "异常发生的方法名", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("异常发生的方法名")
+ private String exceptionMethodName;
+
+ @Schema(description = "异常发生的方法所在行", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("异常发生的方法所在行")
+ private Integer exceptionLineNumber;
+
+ @Schema(description = "处理状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
+ @ExcelProperty(value = "处理状态", converter = DictConvert.class)
+ @DictFormat(DictTypeConstants.API_ERROR_LOG_PROCESS_STATUS)
+ private Integer processStatus;
+
+ @Schema(description = "处理时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("处理时间")
+ private LocalDateTime processTime;
+
+ @Schema(description = "处理用户编号", example = "233")
+ @ExcelProperty("处理用户编号")
+ private Integer processUserId;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("创建时间")
+ private LocalDateTime createTime;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/redis/RedisController.http b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/redis/RedisController.http
new file mode 100644
index 0000000..68b5487
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/redis/RedisController.http
@@ -0,0 +1,4 @@
+### 请求 /infra/redis/get-monitor-info 接口 => 成功
+GET {{baseUrl}}/infra/redis/get-monitor-info
+Authorization: Bearer {{token}}
+tenant-id: {{adminTenantId}}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/redis/RedisController.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/redis/RedisController.java
new file mode 100644
index 0000000..5cb9b31
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/redis/RedisController.java
@@ -0,0 +1,47 @@
+package com.tashow.cloud.infra.controller.admin.redis;
+
+import com.tashow.cloud.common.pojo.CommonResult;
+import com.tashow.cloud.infra.controller.admin.redis.vo.RedisMonitorRespVO;
+import com.tashow.cloud.infra.convert.redis.RedisConvert;
+import com.tashow.cloud.infra.controller.admin.redis.vo.RedisMonitorRespVO;
+import com.tashow.cloud.infra.convert.redis.RedisConvert;
+import com.tashow.cloud.infra.controller.admin.redis.vo.RedisMonitorRespVO;
+import com.tashow.cloud.infra.convert.redis.RedisConvert;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.data.redis.connection.RedisServerCommands;
+import org.springframework.data.redis.core.RedisCallback;
+import org.springframework.data.redis.core.StringRedisTemplate;
+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.RestController;
+
+import jakarta.annotation.Resource;
+import java.util.Properties;
+
+import static com.tashow.cloud.common.pojo.CommonResult.success;
+
+@Tag(name = "管理后台 - Redis 监控")
+@RestController
+@RequestMapping("/infra/redis")
+public class RedisController {
+
+ @Resource
+ private StringRedisTemplate stringRedisTemplate;
+
+ @GetMapping("/get-monitor-info")
+ @Operation(summary = "获得 Redis 监控信息")
+ @PreAuthorize("@ss.hasPermission('infra:redis:get-monitor-info')")
+ public CommonResult getRedisMonitorInfo() {
+ // 获得 Redis 统计信息
+ Properties info = stringRedisTemplate.execute((RedisCallback) RedisServerCommands::info);
+ Long dbSize = stringRedisTemplate.execute(RedisServerCommands::dbSize);
+ Properties commandStats = stringRedisTemplate.execute((
+ RedisCallback) connection -> connection.serverCommands().info("commandstats"));
+ assert commandStats != null; // 断言,避免警告
+ // 拼接结果返回
+ return success(RedisConvert.INSTANCE.build(info, dbSize, commandStats));
+ }
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/redis/vo/RedisMonitorRespVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/redis/vo/RedisMonitorRespVO.java
new file mode 100644
index 0000000..802d374
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/admin/redis/vo/RedisMonitorRespVO.java
@@ -0,0 +1,43 @@
+package com.tashow.cloud.infra.controller.admin.redis.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+
+import java.util.List;
+import java.util.Properties;
+
+@Schema(description = "管理后台 - Redis 监控信息 Response VO")
+@Data
+@Builder
+@AllArgsConstructor
+public class RedisMonitorRespVO {
+
+ @Schema(description = "Redis info 指令结果,具体字段,查看 Redis 文档", requiredMode = Schema.RequiredMode.REQUIRED)
+ private Properties info;
+
+ @Schema(description = "Redis key 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ private Long dbSize;
+
+ @Schema(description = "CommandStat 数组", requiredMode = Schema.RequiredMode.REQUIRED)
+ private List commandStats;
+
+ @Schema(description = "Redis 命令统计结果")
+ @Data
+ @Builder
+ @AllArgsConstructor
+ public static class CommandStat {
+
+ @Schema(description = "Redis 命令", requiredMode = Schema.RequiredMode.REQUIRED, example = "get")
+ private String command;
+
+ @Schema(description = "调用次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ private Long calls;
+
+ @Schema(description = "消耗 CPU 秒数", requiredMode = Schema.RequiredMode.REQUIRED, example = "666")
+ private Long usec;
+
+ }
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/app/file/AppFileController.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/app/file/AppFileController.java
new file mode 100644
index 0000000..6fada29
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/app/file/AppFileController.java
@@ -0,0 +1,56 @@
+package com.tashow.cloud.infra.controller.app.file;
+
+import cn.hutool.core.io.IoUtil;
+import com.tashow.cloud.common.pojo.CommonResult;
+import com.tashow.cloud.infra.service.file.FileService;
+import com.tashow.cloud.infra.controller.admin.file.vo.file.FileCreateReqVO;
+import com.tashow.cloud.infra.controller.admin.file.vo.file.FilePresignedUrlRespVO;
+import com.tashow.cloud.infra.controller.app.file.vo.AppFileUploadReqVO;
+import com.tashow.cloud.infra.service.file.FileService;
+import com.tashow.cloud.infra.service.file.FileService;
+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.validation.Valid;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import static com.tashow.cloud.common.pojo.CommonResult.success;
+
+@Tag(name = "用户 App - 文件存储")
+@RestController
+@RequestMapping("/infra/file")
+@Validated
+@Slf4j
+public class AppFileController {
+
+ @Resource
+ private FileService fileService;
+
+ @PostMapping("/upload")
+ @Operation(summary = "上传文件")
+ @PermitAll
+ public CommonResult uploadFile(AppFileUploadReqVO uploadReqVO) throws Exception {
+ MultipartFile file = uploadReqVO.getFile();
+ String path = uploadReqVO.getPath();
+ return success(fileService.createFile(file.getOriginalFilename(), path, IoUtil.readBytes(file.getInputStream())));
+ }
+
+ @GetMapping("/presigned-url")
+ @Operation(summary = "获取文件预签名地址", description = "模式二:前端上传文件:用于前端直接上传七牛、阿里云 OSS 等文件存储器")
+ @PermitAll
+ public CommonResult getFilePresignedUrl(@RequestParam("path") String path) throws Exception {
+ return success(fileService.getFilePresignedUrl(path));
+ }
+
+ @PostMapping("/create")
+ @Operation(summary = "创建文件", description = "模式二:前端上传文件:配合 presigned-url 接口,记录上传了上传的文件")
+ @PermitAll
+ public CommonResult createFile(@Valid @RequestBody FileCreateReqVO createReqVO) {
+ return success(fileService.createFile(createReqVO));
+ }
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/app/file/vo/AppFileUploadReqVO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/app/file/vo/AppFileUploadReqVO.java
new file mode 100644
index 0000000..5a8f788
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/app/file/vo/AppFileUploadReqVO.java
@@ -0,0 +1,20 @@
+package com.tashow.cloud.infra.controller.app.file.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.web.multipart.MultipartFile;
+
+import jakarta.validation.constraints.NotNull;
+
+@Schema(description = "用户 App - 上传文件 Request VO")
+@Data
+public class AppFileUploadReqVO {
+
+ @Schema(description = "文件附件", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotNull(message = "文件附件不能为空")
+ private MultipartFile file;
+
+ @Schema(description = "文件附件", example = "yudaoyuanma.png")
+ private String path;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/app/package-info.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/app/package-info.java
new file mode 100644
index 0000000..e3cb81c
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/app/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * 占位
+ */
+package com.tashow.cloud.infra.controller.app;
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/package-info.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/controller/package-info.java
new file mode 100644
index 0000000..149a690
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/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 com.tashow.cloud.infra.controller;
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/convert/codegen/CodegenConvert.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/convert/codegen/CodegenConvert.java
new file mode 100644
index 0000000..c8d8285
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/convert/codegen/CodegenConvert.java
@@ -0,0 +1,68 @@
+package com.tashow.cloud.infra.convert.codegen;
+
+import com.tashow.cloud.common.util.collection.CollectionUtils;
+import com.tashow.cloud.common.util.object.BeanUtils;
+import com.tashow.cloud.infra.controller.admin.codegen.vo.CodegenDetailRespVO;
+import com.tashow.cloud.infra.controller.admin.codegen.vo.CodegenPreviewRespVO;
+import com.tashow.cloud.infra.controller.admin.codegen.vo.column.CodegenColumnRespVO;
+import com.tashow.cloud.infra.controller.admin.codegen.vo.table.CodegenTableRespVO;
+import com.tashow.cloud.infra.dal.dataobject.codegen.CodegenColumnDO;
+import com.tashow.cloud.infra.dal.dataobject.codegen.CodegenTableDO;
+import com.baomidou.mybatisplus.generator.config.po.TableField;
+import com.baomidou.mybatisplus.generator.config.po.TableInfo;
+import org.apache.ibatis.type.JdbcType;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.Mappings;
+import org.mapstruct.Named;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+import java.util.Map;
+
+@Mapper
+public interface CodegenConvert {
+
+ CodegenConvert INSTANCE = Mappers.getMapper(CodegenConvert.class);
+
+ // ========== TableInfo 相关 ==========
+
+ @Mappings({
+ @Mapping(source = "name", target = "tableName"),
+ @Mapping(source = "comment", target = "tableComment"),
+ })
+ CodegenTableDO convert(TableInfo bean);
+
+ List convertList(List list);
+
+ @Mappings({
+ @Mapping(source = "name", target = "columnName"),
+ @Mapping(source = "metaInfo.jdbcType", target = "dataType", qualifiedByName = "getDataType"),
+ @Mapping(source = "comment", target = "columnComment"),
+ @Mapping(source = "metaInfo.nullable", target = "nullable"),
+ @Mapping(source = "keyFlag", target = "primaryKey"),
+ @Mapping(source = "columnType.type", target = "javaType"),
+ @Mapping(source = "propertyName", target = "javaField"),
+ })
+ CodegenColumnDO convert(TableField bean);
+
+ @Named("getDataType")
+ default String getDataType(JdbcType jdbcType) {
+ return jdbcType.name();
+ }
+
+ // ========== 其它 ==========
+
+ default CodegenDetailRespVO convert(CodegenTableDO table, List columns) {
+ CodegenDetailRespVO respVO = new CodegenDetailRespVO();
+ respVO.setTable(BeanUtils.toBean(table, CodegenTableRespVO.class));
+ respVO.setColumns(BeanUtils.toBean(columns, CodegenColumnRespVO.class));
+ return respVO;
+ }
+
+ default List convert(Map codes) {
+ return CollectionUtils.convertList(codes.entrySet(),
+ entry -> new CodegenPreviewRespVO().setFilePath(entry.getKey()).setCode(entry.getValue()));
+ }
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/convert/config/ConfigConvert.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/convert/config/ConfigConvert.java
new file mode 100644
index 0000000..8548296
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/convert/config/ConfigConvert.java
@@ -0,0 +1,28 @@
+package com.tashow.cloud.infra.convert.config;
+
+import com.tashow.cloud.common.pojo.PageResult;
+import com.tashow.cloud.infra.controller.admin.config.vo.ConfigRespVO;
+import com.tashow.cloud.infra.controller.admin.config.vo.ConfigSaveReqVO;
+import com.tashow.cloud.infra.dal.dataobject.config.ConfigDO;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+
+@Mapper
+public interface ConfigConvert {
+
+ ConfigConvert INSTANCE = Mappers.getMapper(ConfigConvert.class);
+
+ PageResult convertPage(PageResult page);
+
+ List convertList(List list);
+
+ @Mapping(source = "configKey", target = "key")
+ ConfigRespVO convert(ConfigDO bean);
+
+ @Mapping(source = "key", target = "configKey")
+ ConfigDO convert(ConfigSaveReqVO bean);
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/convert/file/FileConfigConvert.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/convert/file/FileConfigConvert.java
new file mode 100644
index 0000000..ed141ec
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/convert/file/FileConfigConvert.java
@@ -0,0 +1,22 @@
+package com.tashow.cloud.infra.convert.file;
+
+import com.tashow.cloud.infra.controller.admin.file.vo.config.FileConfigSaveReqVO;
+import com.tashow.cloud.infra.dal.dataobject.file.FileConfigDO;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.factory.Mappers;
+
+/**
+ * 文件配置 Convert
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface FileConfigConvert {
+
+ FileConfigConvert INSTANCE = Mappers.getMapper(FileConfigConvert.class);
+
+ @Mapping(target = "config", ignore = true)
+ FileConfigDO convert(FileConfigSaveReqVO bean);
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/convert/package-info.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/convert/package-info.java
new file mode 100644
index 0000000..dc3d339
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/convert/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * 提供 POJO 类的实体转换
+ *
+ * 目前使用 MapStruct 框架
+ */
+package com.tashow.cloud.infra.convert;
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/convert/redis/RedisConvert.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/convert/redis/RedisConvert.java
new file mode 100644
index 0000000..00cc775
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/convert/redis/RedisConvert.java
@@ -0,0 +1,29 @@
+package com.tashow.cloud.infra.convert.redis;
+
+import cn.hutool.core.util.StrUtil;
+import com.tashow.cloud.infra.controller.admin.redis.vo.RedisMonitorRespVO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.ArrayList;
+import java.util.Properties;
+
+@Mapper
+public interface RedisConvert {
+
+ RedisConvert INSTANCE = Mappers.getMapper(RedisConvert.class);
+
+ default RedisMonitorRespVO build(Properties info, Long dbSize, Properties commandStats) {
+ RedisMonitorRespVO respVO = RedisMonitorRespVO.builder().info(info).dbSize(dbSize)
+ .commandStats(new ArrayList<>(commandStats.size())).build();
+ commandStats.forEach((key, value) -> {
+ respVO.getCommandStats().add(RedisMonitorRespVO.CommandStat.builder()
+ .command(StrUtil.subAfter((String) key, "cmdstat_", false))
+ .calls(Long.valueOf(StrUtil.subBetween((String) value, "calls=", ",")))
+ .usec(Long.valueOf(StrUtil.subBetween((String) value, "usec=", ",")))
+ .build());
+ });
+ return respVO;
+ }
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md
new file mode 100644
index 0000000..8153487
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md
@@ -0,0 +1 @@
+
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/dal/dataobject/codegen/CodegenColumnDO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/dal/dataobject/codegen/CodegenColumnDO.java
new file mode 100644
index 0000000..1b0b322
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/dal/dataobject/codegen/CodegenColumnDO.java
@@ -0,0 +1,140 @@
+package com.tashow.cloud.infra.dal.dataobject.codegen;
+
+import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
+import com.tashow.cloud.infra.enums.codegen.CodegenColumnHtmlTypeEnum;
+import com.tashow.cloud.infra.enums.codegen.CodegenColumnListConditionEnum;
+import com.tashow.cloud.infra.enums.codegen.CodegenColumnHtmlTypeEnum;
+import com.tashow.cloud.infra.enums.codegen.CodegenColumnListConditionEnum;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.generator.config.po.TableField;
+import com.tashow.cloud.infra.enums.codegen.CodegenColumnHtmlTypeEnum;
+import com.tashow.cloud.infra.enums.codegen.CodegenColumnListConditionEnum;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+/**
+ * 代码生成 column 字段定义
+ *
+ * @author 芋道源码
+ */
+@TableName(value = "infra_codegen_column", autoResultMap = true)
+@KeySequence("infra_codegen_column_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@Accessors(chain = true)
+@EqualsAndHashCode(callSuper = true)
+public class CodegenColumnDO extends BaseDO {
+
+ /**
+ * ID 编号
+ */
+ @TableId
+ private Long id;
+ /**
+ * 表编号
+ *
+ * 关联 {@link CodegenTableDO#getId()}
+ */
+ private Long tableId;
+
+ // ========== 表相关字段 ==========
+
+ /**
+ * 字段名
+ *
+ * 关联 {@link TableField#getName()}
+ */
+ private String columnName;
+ /**
+ * 数据库字段类型
+ *
+ * 关联 {@link TableField.MetaInfo#getJdbcType()}
+ */
+ private String dataType;
+ /**
+ * 字段描述
+ *
+ * 关联 {@link TableField#getComment()}
+ */
+ private String columnComment;
+ /**
+ * 是否允许为空
+ *
+ * 关联 {@link TableField.MetaInfo#isNullable()}
+ */
+ private Boolean nullable;
+ /**
+ * 是否主键
+ *
+ * 关联 {@link TableField#isKeyFlag()}
+ */
+ private Boolean primaryKey;
+ /**
+ * 排序
+ */
+ private Integer ordinalPosition;
+
+ // ========== Java 相关字段 ==========
+
+ /**
+ * Java 属性类型
+ *
+ * 例如说 String、Boolean 等等
+ *
+ * 关联 {@link TableField#getColumnType()}
+ */
+ private String javaType;
+ /**
+ * Java 属性名
+ *
+ * 关联 {@link TableField#getPropertyName()}
+ */
+ private String javaField;
+ /**
+ * 字典类型
+ *
+ * 关联 DictTypeDO 的 type 属性
+ */
+ private String dictType;
+ /**
+ * 数据示例,主要用于生成 Swagger 注解的 example 字段
+ */
+ private String example;
+
+ // ========== CRUD 相关字段 ==========
+
+ /**
+ * 是否为 Create 创建操作的字段
+ */
+ private Boolean createOperation;
+ /**
+ * 是否为 Update 更新操作的字段
+ */
+ private Boolean updateOperation;
+ /**
+ * 是否为 List 查询操作的字段
+ */
+ private Boolean listOperation;
+ /**
+ * List 查询操作的条件类型
+ *
+ * 枚举 {@link CodegenColumnListConditionEnum}
+ */
+ private String listOperationCondition;
+ /**
+ * 是否为 List 查询操作的返回字段
+ */
+ private Boolean listOperationResult;
+
+ // ========== UI 相关字段 ==========
+
+ /**
+ * 显示类型
+ *
+ * 枚举 {@link CodegenColumnHtmlTypeEnum}
+ */
+ private String htmlType;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/dal/dataobject/codegen/CodegenTableDO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/dal/dataobject/codegen/CodegenTableDO.java
new file mode 100644
index 0000000..416258f
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/dal/dataobject/codegen/CodegenTableDO.java
@@ -0,0 +1,164 @@
+package com.tashow.cloud.infra.dal.dataobject.codegen;
+
+import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
+import com.tashow.cloud.infra.enums.codegen.CodegenFrontTypeEnum;
+import com.tashow.cloud.infra.enums.codegen.CodegenSceneEnum;
+import com.tashow.cloud.infra.enums.codegen.CodegenTemplateTypeEnum;
+import com.tashow.cloud.infra.dal.dataobject.db.DataSourceConfigDO;
+import com.tashow.cloud.infra.enums.codegen.CodegenFrontTypeEnum;
+import com.tashow.cloud.infra.enums.codegen.CodegenSceneEnum;
+import com.tashow.cloud.infra.enums.codegen.CodegenTemplateTypeEnum;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.generator.config.po.TableInfo;
+import com.tashow.cloud.infra.enums.codegen.CodegenFrontTypeEnum;
+import com.tashow.cloud.infra.enums.codegen.CodegenSceneEnum;
+import com.tashow.cloud.infra.enums.codegen.CodegenTemplateTypeEnum;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+/**
+ * 代码生成 table 表定义
+ *
+ * @author 芋道源码
+ */
+@TableName(value = "infra_codegen_table", autoResultMap = true)
+@KeySequence("infra_codegen_table_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@Accessors(chain = true)
+@EqualsAndHashCode(callSuper = true)
+public class CodegenTableDO extends BaseDO {
+
+ /**
+ * ID 编号
+ */
+ @TableId
+ private Long id;
+
+ /**
+ * 数据源编号
+ *
+ * 关联 {@link DataSourceConfigDO#getId()}
+ */
+ private Long dataSourceConfigId;
+ /**
+ * 生成场景
+ *
+ * 枚举 {@link CodegenSceneEnum}
+ */
+ private Integer scene;
+
+ // ========== 表相关字段 ==========
+
+ /**
+ * 表名称
+ *
+ * 关联 {@link TableInfo#getName()}
+ */
+ private String tableName;
+ /**
+ * 表描述
+ *
+ * 关联 {@link TableInfo#getComment()}
+ */
+ private String tableComment;
+ /**
+ * 备注
+ */
+ private String remark;
+
+ // ========== 类相关字段 ==========
+
+ /**
+ * 模块名,即一级目录
+ *
+ * 例如说,system、infra、tool 等等
+ */
+ private String moduleName;
+ /**
+ * 业务名,即二级目录
+ *
+ * 例如说,user、permission、dict 等等
+ */
+ private String businessName;
+ /**
+ * 类名称(首字母大写)
+ *
+ * 例如说,SysUser、SysMenu、SysDictData 等等
+ */
+ private String className;
+ /**
+ * 类描述
+ */
+ private String classComment;
+ /**
+ * 作者
+ */
+ private String author;
+
+ // ========== 生成相关字段 ==========
+
+ /**
+ * 模板类型
+ *
+ * 枚举 {@link CodegenTemplateTypeEnum}
+ */
+ private Integer templateType;
+ /**
+ * 代码生成的前端类型
+ *
+ * 枚举 {@link CodegenFrontTypeEnum}
+ */
+ private Integer frontType;
+
+ // ========== 菜单相关字段 ==========
+
+ /**
+ * 父菜单编号
+ *
+ * 关联 MenuDO 的 id 属性
+ */
+ private Long parentMenuId;
+
+ // ========== 主子表相关字段 ==========
+
+ /**
+ * 主表的编号
+ *
+ * 关联 {@link CodegenTableDO#getId()}
+ */
+ private Long masterTableId;
+ /**
+ * 【自己】子表关联主表的字段编号
+ *
+ * 关联 {@link CodegenColumnDO#getId()}
+ */
+ private Long subJoinColumnId;
+ /**
+ * 主表与子表是否一对多
+ *
+ * true:一对多
+ * false:一对一
+ */
+ private Boolean subJoinMany;
+
+ // ========== 树表相关字段 ==========
+
+ /**
+ * 树表的父字段编号
+ *
+ * 关联 {@link CodegenColumnDO#getId()}
+ */
+ private Long treeParentColumnId;
+ /**
+ * 树表的名字字段编号
+ *
+ * 名字的用途:新增或修改时,select 框展示的字段
+ *
+ * 关联 {@link CodegenColumnDO#getId()}
+ */
+ private Long treeNameColumnId;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/dal/dataobject/config/ConfigDO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/dal/dataobject/config/ConfigDO.java
new file mode 100644
index 0000000..1834f48
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/dal/dataobject/config/ConfigDO.java
@@ -0,0 +1,66 @@
+package com.tashow.cloud.infra.dal.dataobject.config;
+
+import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
+import com.tashow.cloud.infra.enums.config.ConfigTypeEnum;
+import com.tashow.cloud.infra.enums.config.ConfigTypeEnum;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.tashow.cloud.infra.enums.config.ConfigTypeEnum;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+/**
+ * 参数配置表
+ *
+ * @author 芋道源码
+ */
+@TableName("infra_config")
+@KeySequence("infra_config_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ConfigDO extends BaseDO {
+
+ /**
+ * 参数主键
+ */
+ @TableId
+ private Long id;
+ /**
+ * 参数分类
+ */
+ private String category;
+ /**
+ * 参数名称
+ */
+ private String name;
+ /**
+ * 参数键名
+ *
+ * 支持多 DB 类型时,无法直接使用 key + @TableField("config_key") 来实现转换,原因是 "config_key" AS key 而存在报错
+ */
+ private String configKey;
+ /**
+ * 参数键值
+ */
+ private String value;
+ /**
+ * 参数类型
+ *
+ * 枚举 {@link ConfigTypeEnum}
+ */
+ private Integer type;
+ /**
+ * 是否可见
+ *
+ * 不可见的参数,一般是敏感参数,前端不可获取
+ */
+ private Boolean visible;
+ /**
+ * 备注
+ */
+ private String remark;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/dal/dataobject/db/DataSourceConfigDO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/dal/dataobject/db/DataSourceConfigDO.java
new file mode 100644
index 0000000..39dfa3d
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/dal/dataobject/db/DataSourceConfigDO.java
@@ -0,0 +1,48 @@
+package com.tashow.cloud.infra.dal.dataobject.db;
+
+import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.tashow.cloud.mybatis.mybatis.core.type.EncryptTypeHandler;
+import lombok.Data;
+
+/**
+ * 数据源配置
+ *
+ * @author 芋道源码
+ */
+@TableName(value = "infra_data_source_config", autoResultMap = true)
+@KeySequence("infra_data_source_config_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+public class DataSourceConfigDO extends BaseDO {
+
+ /**
+ * 主键编号 - Master 数据源
+ */
+ public static final Long ID_MASTER = 0L;
+
+ /**
+ * 主键编号
+ */
+ private Long id;
+ /**
+ * 连接名
+ */
+ private String name;
+
+ /**
+ * 数据源连接
+ */
+ private String url;
+ /**
+ * 用户名
+ */
+ private String username;
+ /**
+ * 密码
+ */
+ @TableField(typeHandler = EncryptTypeHandler.class)
+ private String password;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/dal/dataobject/demo/demo01/Demo01ContactDO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/dal/dataobject/demo/demo01/Demo01ContactDO.java
new file mode 100644
index 0000000..35d72c1
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/dal/dataobject/demo/demo01/Demo01ContactDO.java
@@ -0,0 +1,54 @@
+package com.tashow.cloud.infra.dal.dataobject.demo.demo01;
+
+import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.time.LocalDateTime;
+
+/**
+ * 示例联系人 DO
+ *
+ * @author 芋道源码
+ */
+@TableName("yudao_demo01_contact")
+@KeySequence("yudao_demo01_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class Demo01ContactDO extends BaseDO {
+
+ /**
+ * 编号
+ */
+ @TableId
+ private Long id;
+ /**
+ * 名字
+ */
+ private String name;
+ /**
+ * 性别
+ *
+ * 枚举 {@link TODO system_user_sex 对应的类}
+ */
+ private Integer sex;
+ /**
+ * 出生年
+ */
+ private LocalDateTime birthday;
+ /**
+ * 简介
+ */
+ private String description;
+ /**
+ * 头像
+ */
+ private String avatar;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/dal/dataobject/demo/demo02/Demo02CategoryDO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/dal/dataobject/demo/demo02/Demo02CategoryDO.java
new file mode 100644
index 0000000..1b6bb2b
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/dal/dataobject/demo/demo02/Demo02CategoryDO.java
@@ -0,0 +1,40 @@
+package com.tashow.cloud.infra.dal.dataobject.demo.demo02;
+
+import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+/**
+ * 示例分类 DO
+ *
+ * @author 芋道源码
+ */
+@TableName("yudao_demo02_category")
+@KeySequence("yudao_demo02_category_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class Demo02CategoryDO extends BaseDO {
+
+ public static final Long PARENT_ID_ROOT = 0L;
+
+ /**
+ * 编号
+ */
+ @TableId
+ private Long id;
+ /**
+ * 名字
+ */
+ private String name;
+ /**
+ * 父级编号
+ */
+ private Long parentId;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/dal/dataobject/demo/demo03/Demo03CourseDO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/dal/dataobject/demo/demo03/Demo03CourseDO.java
new file mode 100644
index 0000000..488cba5
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/dal/dataobject/demo/demo03/Demo03CourseDO.java
@@ -0,0 +1,42 @@
+package com.tashow.cloud.infra.dal.dataobject.demo.demo03;
+
+import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+/**
+ * 学生课程 DO
+ *
+ * @author 芋道源码
+ */
+@TableName("yudao_demo03_course")
+@KeySequence("yudao_demo03_course_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class Demo03CourseDO extends BaseDO {
+
+ /**
+ * 编号
+ */
+ @TableId
+ private Long id;
+ /**
+ * 学生编号
+ */
+ private Long studentId;
+ /**
+ * 名字
+ */
+ private String name;
+ /**
+ * 分数
+ */
+ private Integer score;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/dal/dataobject/demo/demo03/Demo03GradeDO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/dal/dataobject/demo/demo03/Demo03GradeDO.java
new file mode 100644
index 0000000..3254e22
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/dal/dataobject/demo/demo03/Demo03GradeDO.java
@@ -0,0 +1,42 @@
+package com.tashow.cloud.infra.dal.dataobject.demo.demo03;
+
+import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+/**
+ * 学生班级 DO
+ *
+ * @author 芋道源码
+ */
+@TableName("yudao_demo03_grade")
+@KeySequence("yudao_demo03_grade_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class Demo03GradeDO extends BaseDO {
+
+ /**
+ * 编号
+ */
+ @TableId
+ private Long id;
+ /**
+ * 学生编号
+ */
+ private Long studentId;
+ /**
+ * 名字
+ */
+ private String name;
+ /**
+ * 班主任
+ */
+ private String teacher;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/dal/dataobject/demo/demo03/Demo03StudentDO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/dal/dataobject/demo/demo03/Demo03StudentDO.java
new file mode 100644
index 0000000..b3397a1
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/dal/dataobject/demo/demo03/Demo03StudentDO.java
@@ -0,0 +1,50 @@
+package com.tashow.cloud.infra.dal.dataobject.demo.demo03;
+
+import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.time.LocalDateTime;
+
+/**
+ * 学生 DO
+ *
+ * @author 芋道源码
+ */
+@TableName("yudao_demo03_student")
+@KeySequence("yudao_demo03_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class Demo03StudentDO extends BaseDO {
+
+ /**
+ * 编号
+ */
+ @TableId
+ private Long id;
+ /**
+ * 名字
+ */
+ private String name;
+ /**
+ * 性别
+ *
+ * 枚举 {@link TODO system_user_sex 对应的类}
+ */
+ private Integer sex;
+ /**
+ * 出生日期
+ */
+ private LocalDateTime birthday;
+ /**
+ * 简介
+ */
+ private String description;
+
+}
diff --git a/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/dal/dataobject/file/FileConfigDO.java b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/dal/dataobject/file/FileConfigDO.java
new file mode 100644
index 0000000..e18b8ca
--- /dev/null
+++ b/tashow-module/tashow-module-infra/tashow-module-infra-biz/src/main/java/com/tashow/cloud/infra/dal/dataobject/file/FileConfigDO.java
@@ -0,0 +1,111 @@
+package com.tashow.cloud.infra.dal.dataobject.file;
+
+import cn.hutool.core.util.StrUtil;
+import com.tashow.cloud.common.util.json.JsonUtils;
+import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
+import com.tashow.cloud.infra.framework.file.core.client.FileClientConfig;
+import com.tashow.cloud.infra.framework.file.core.client.db.DBFileClientConfig;
+import com.tashow.cloud.infra.framework.file.core.client.ftp.FtpFileClientConfig;
+import com.tashow.cloud.infra.framework.file.core.client.local.LocalFileClientConfig;
+import com.tashow.cloud.infra.framework.file.core.client.s3.S3FileClientConfig;
+import com.tashow.cloud.infra.framework.file.core.client.sftp.SftpFileClientConfig;
+import com.tashow.cloud.infra.framework.file.core.enums.FileStorageEnum;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler;
+import com.fasterxml.jackson.core.type.TypeReference;
+import lombok.*;
+
+import java.lang.reflect.Field;
+
+/**
+ * 文件配置表
+ *
+ * @author 芋道源码
+ */
+@TableName(value = "infra_file_config", autoResultMap = true)
+@KeySequence("infra_file_config_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class FileConfigDO extends BaseDO {
+
+ /**
+ * 配置编号,数据库自增
+ */
+ private Long id;
+ /**
+ * 配置名
+ */
+ private String name;
+ /**
+ * 存储器
+ *
+ * 枚举 {@link FileStorageEnum}
+ */
+ private Integer storage;
+ /**
+ * 备注
+ */
+ private String remark;
+ /**
+ * 是否为主配置
+ *
+ * 由于我们可以配置多个文件配置,默认情况下,使用主配置进行文件的上传
+ */
+ private Boolean master;
+
+ /**
+ * 支付渠道配置
+ */
+ @TableField(typeHandler = FileClientConfigTypeHandler.class)
+ private FileClientConfig config;
+
+ public static class FileClientConfigTypeHandler extends AbstractJsonTypeHandler