初始化

This commit is contained in:
xuelijun
2025-09-20 18:41:07 +08:00
commit a2d1fcde12
1437 changed files with 95746 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
## AdoptOpenJDK 停止发布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,提供更好的稳定性
## 感谢复旦核博士的建议!灰子哥,牛皮!
FROM eclipse-temurin:17-jre
## 创建目录,并使用它作为工作目录
RUN mkdir -p /home/java-work/system-infra
WORKDIR /home/java-work/system-infra
## 将后端项目的 Jar 文件,复制到镜像中
COPY ./target/tashow-module-infra.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

View File

@@ -0,0 +1,179 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-module</artifactId>
<version>${revision}</version>
</parent>
<artifactId>tashow-module-infra</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>
infra 模块,主要提供两块能力:
1. 我们放基础设施的运维与管理,支撑上层的通用与核心业务。 例如说:定时任务的管理、服务器的信息等等
2. 研发工具,提升研发效率与质量。 例如说:代码生成器、接口文档等等
</description>
<dependencies>
<!-- Spring Cloud 基础 -->
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-framework-env</artifactId>
</dependency>
<!-- 依赖服务 -->
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-system-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-infra-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.alibaba.otter</groupId>
<artifactId>canal.client</artifactId>
<version>1.1.0</version>
</dependency>
<!-- 业务组件 -->
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-framework-tenant</artifactId>
</dependency>
<!-- Web 相关 -->
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-framework-security</artifactId>
</dependency>
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-framework-websocket</artifactId>
</dependency>
<!-- DB 相关 -->
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-data-mybatis</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId> <!-- 代码生成器,使用它解析表结构 -->
</dependency>
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-data-redis</artifactId>
</dependency>
<!--<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-data-canal</artifactId>
<version>1.0.0</version>
<scope>compile</scope>
</dependency>-->
<!-- RPC 远程调用相关 -->
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-framework-rpc</artifactId>
</dependency>
<!-- Registry 注册中心相关 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Config 配置中心相关 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- Job 定时任务相关 -->
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-framework-job</artifactId>
</dependency>
<!-- 消息队列相关 -->
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-framework-mq</artifactId>
</dependency>
<!-- 工具类相关 -->
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-data-excel</artifactId>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId> <!-- 实现代码生成 -->
</dependency>
<!-- 监控相关 -->
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-framework-monitor</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId> <!-- 实现 Spring Boot Admin Server 服务端 -->
</dependency>
<!-- 三方云服务相关 -->
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId> <!-- 文件客户端:解决 ftp 连接 -->
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId> <!-- 文件客户端:解决 sftp 连接 -->
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId><!-- 文件客户端解决阿里云、腾讯云、minio 等 S3 连接 -->
</dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId> <!-- 文件客户端:文件类型的识别 -->
</dependency>
</dependencies>
<build>
<!-- 设置构建的 jar 包名 -->
<finalName>${project.artifactId}</finalName>
<plugins>
<!-- 打包 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,17 @@
package com.tashow.cloud.infra;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 项目的启动类
* @author 芋道源码
*/
@SpringBootApplication
public class InfraServerApplication {
public static void main(String[] args) {
SpringApplication.run(InfraServerApplication.class, args);
}
}

View File

@@ -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<String> getConfigValueByKey(String key) {
ConfigDO config = configService.getConfigByKey(key);
return success(config != null ? config.getValue() : null);
}
}

View File

@@ -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<String> createFile(FileCreateReqDTO createReqDTO) {
return success(fileService.createFile(createReqDTO.getName(), createReqDTO.getPath(),
createReqDTO.getContent()));
}
}

View File

@@ -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<Boolean> createApiAccessLog(ApiAccessLogCreateReqDTO createDTO) {
apiAccessLogService.createApiAccessLog(createDTO);
return success(true);
}
}

View File

@@ -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<Boolean> createApiErrorLog(ApiErrorLogCreateReqDTO createDTO) {
apiErrorLogService.createApiErrorLog(createDTO);
return success(true);
}
}

View File

@@ -0,0 +1 @@
package com.tashow.cloud.infra.api;

View File

@@ -0,0 +1,36 @@
package com.tashow.cloud.infra.api.websocket;
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<Boolean> 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);
}
}

View File

@@ -0,0 +1,138 @@
package com.tashow.cloud.infra.controller.admin.codegen;
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;
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.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.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.service.codegen.CodegenService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/** 管理后台 - 代码生成器 */
@RestController
@RequestMapping("/infra/codegen")
@Validated
public class CodegenController {
@Resource private CodegenService codegenService;
/** 获得数据库自带的表定义列表", description = "会过滤掉已经导入 Codegen 的表 */
@GetMapping("/db/table/list")
@PreAuthorize("@ss.hasPermission('infra:codegen:query')")
public CommonResult<List<DatabaseTableRespVO>> 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")
@PreAuthorize("@ss.hasPermission('infra:codegen:query')")
public CommonResult<List<CodegenTableRespVO>> getCodegenTableList(
@RequestParam(value = "dataSourceConfigId") Long dataSourceConfigId) {
List<CodegenTableDO> list = codegenService.getCodegenTableList(dataSourceConfigId);
return success(BeanUtils.toBean(list, CodegenTableRespVO.class));
}
/** 获得表定义分页 */
@GetMapping("/table/page")
@PreAuthorize("@ss.hasPermission('infra:codegen:query')")
public CommonResult<PageResult<CodegenTableRespVO>> getCodegenTablePage(
@Valid CodegenTablePageReqVO pageReqVO) {
PageResult<CodegenTableDO> pageResult = codegenService.getCodegenTablePage(pageReqVO);
return success(BeanUtils.toBean(pageResult, CodegenTableRespVO.class));
}
/** 获得表和字段的明细 */
@GetMapping("/detail")
@PreAuthorize("@ss.hasPermission('infra:codegen:query')")
public CommonResult<CodegenDetailRespVO> getCodegenDetail(@RequestParam("tableId") Long tableId) {
CodegenTableDO table = codegenService.getCodegenTable(tableId);
List<CodegenColumnDO> columns = codegenService.getCodegenColumnListByTableId(tableId);
// 拼装返回
return success(CodegenConvert.INSTANCE.convert(table, columns));
}
/** 基于数据库的表结构,创建代码生成器的表和字段定义 */
@PostMapping("/create-list")
@PreAuthorize("@ss.hasPermission('infra:codegen:create')")
public CommonResult<List<Long>> createCodegenList(
@Valid @RequestBody CodegenCreateListReqVO reqVO) {
return success(codegenService.createCodegenList(getLoginUserId(), reqVO));
}
/** 更新数据库的表和字段定义 */
@PutMapping("/update")
@PreAuthorize("@ss.hasPermission('infra:codegen:update')")
public CommonResult<Boolean> updateCodegen(@Valid @RequestBody CodegenUpdateReqVO updateReqVO) {
codegenService.updateCodegen(updateReqVO);
return success(true);
}
/** 基于数据库的表结构,同步数据库的表和字段定义 */
@PutMapping("/sync-from-db")
@PreAuthorize("@ss.hasPermission('infra:codegen:update')")
public CommonResult<Boolean> syncCodegenFromDB(@RequestParam("tableId") Long tableId) {
codegenService.syncCodegenFromDB(tableId);
return success(true);
}
/** 删除数据库的表和字段定义 */
@DeleteMapping("/delete")
@PreAuthorize("@ss.hasPermission('infra:codegen:delete')")
public CommonResult<Boolean> deleteCodegen(@RequestParam("tableId") Long tableId) {
codegenService.deleteCodegen(tableId);
return success(true);
}
/** 预览生成代码 */
@GetMapping("/preview")
@PreAuthorize("@ss.hasPermission('infra:codegen:preview')")
public CommonResult<List<CodegenPreviewRespVO>> previewCodegen(
@RequestParam("tableId") Long tableId) {
Map<String, String> codes = codegenService.generationCodes(tableId);
return success(CodegenConvert.INSTANCE.convert(codes));
}
/** 下载生成代码 */
@GetMapping("/download")
@PreAuthorize("@ss.hasPermission('infra:codegen:download')")
public void downloadCodegen(@RequestParam("tableId") Long tableId, HttpServletResponse response)
throws IOException {
// 生成代码
Map<String, String> 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());
}
}

View File

@@ -0,0 +1,18 @@
package com.tashow.cloud.infra.controller.admin.codegen.vo;
import jakarta.validation.constraints.NotNull;
import java.util.List;
import lombok.Data;
/** 管理后台 - 基于数据库的表结构,创建代码生成器的表和字段定义 Request VO */
@Data
public class CodegenCreateListReqVO {
/** 数据源配置的编号" */
@NotNull(message = "数据源配置的编号不能为空")
private Long dataSourceConfigId;
/** 表名数组", example = "[1, 2, 3] */
@NotNull(message = "表名数组不能为空")
private List<String> tableNames;
}

View File

@@ -0,0 +1,17 @@
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 java.util.List;
import lombok.Data;
/** 管理后台 - 代码生成表和字段的明细 Response VO */
@Data
public class CodegenDetailRespVO {
/** 表定义 */
private CodegenTableRespVO table;
/** 字段定义 */
private List<CodegenColumnRespVO> columns;
}

View File

@@ -0,0 +1,14 @@
package com.tashow.cloud.infra.controller.admin.codegen.vo;
import lombok.Data;
/** 管理后台 - 代码生成预览 Response VO注意每个文件都是一个该对象 */
@Data
public class CodegenPreviewRespVO {
/** 文件路径" */
private String filePath;
/** 代码" */
private String code;
}

View File

@@ -0,0 +1,21 @@
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 jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import java.util.List;
import lombok.Data;
/** 管理后台 - 代码生成表和字段的修改 Request VO */
@Data
public class CodegenUpdateReqVO {
@Valid // 校验内嵌的字段
@NotNull(message = "表定义不能为空")
private CodegenTableSaveReqVO table;
@Valid // 校验内嵌的字段
@NotNull(message = "字段定义不能为空")
private List<CodegenColumnSaveReqVO> columns;
}

View File

@@ -0,0 +1,66 @@
package com.tashow.cloud.infra.controller.admin.codegen.vo.column;
import java.time.LocalDateTime;
import lombok.Data;
/** 管理后台 - 代码生成字段定义 Response VO */
@Data
public class CodegenColumnRespVO {
/** 编号" */
private Long id;
/** 表编号" */
private Long tableId;
/** 字段名" */
private String columnName;
/** 字段类型" */
private String dataType;
/** 字段描述", example = "年龄 */
private String columnComment;
/** 是否允许为空" */
private Boolean nullable;
/** 是否主键" */
private Boolean primaryKey;
/** 排序" */
private Integer ordinalPosition;
/** Java 属性类型" */
private String javaType;
/** Java 属性名" */
private String javaField;
/** 字典类型 */
private String dictType;
/** 数据示例 */
private String example;
/** 是否为 Create 创建操作的字段" */
private Boolean createOperation;
/** 是否为 Update 更新操作的字段" */
private Boolean updateOperation;
/** 是否为 List 查询操作的字段" */
private Boolean listOperation;
/** List 查询操作的条件类型,参见 CodegenColumnListConditionEnum 枚举" */
private String listOperationCondition;
/** 是否为 List 查询操作的返回字段" */
private Boolean listOperationResult;
/** 显示类型" */
private String htmlType;
/** 创建时间 */
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,78 @@
package com.tashow.cloud.infra.controller.admin.codegen.vo.column;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/** 管理后台 - 代码生成字段定义创建/修改 Request VO */
@Data
public class CodegenColumnSaveReqVO {
/** 编号" */
private Long id;
/** 表编号" */
@NotNull(message = "表编号不能为空")
private Long tableId;
/** 字段名" */
@NotNull(message = "字段名不能为空")
private String columnName;
/** 字段类型" */
@NotNull(message = "字段类型不能为空")
private String dataType;
/** 字段描述", example = "年龄 */
@NotNull(message = "字段描述不能为空")
private String columnComment;
/** 是否允许为空" */
@NotNull(message = "是否允许为空不能为空")
private Boolean nullable;
/** 是否主键" */
@NotNull(message = "是否主键不能为空")
private Boolean primaryKey;
/** 排序" */
@NotNull(message = "排序不能为空")
private Integer ordinalPosition;
/** Java 属性类型" */
@NotNull(message = "Java 属性类型不能为空")
private String javaType;
/** Java 属性名" */
@NotNull(message = "Java 属性名不能为空")
private String javaField;
/** 字典类型 */
private String dictType;
/** 数据示例 */
private String example;
/** 是否为 Create 创建操作的字段" */
@NotNull(message = "是否为 Create 创建操作的字段不能为空")
private Boolean createOperation;
/** 是否为 Update 更新操作的字段" */
@NotNull(message = "是否为 Update 更新操作的字段不能为空")
private Boolean updateOperation;
/** 是否为 List 查询操作的字段" */
@NotNull(message = "是否为 List 查询操作的字段不能为空")
private Boolean listOperation;
/** List 查询操作的条件类型,参见 CodegenColumnListConditionEnum 枚举" */
@NotNull(message = "List 查询操作的条件类型不能为空")
private String listOperationCondition;
/** 是否为 List 查询操作的返回字段" */
@NotNull(message = "是否为 List 查询操作的返回字段不能为空")
private Boolean listOperationResult;
/** 显示类型" */
@NotNull(message = "显示类型不能为空")
private String htmlType;
}

View File

@@ -0,0 +1,30 @@
package com.tashow.cloud.infra.controller.admin.codegen.vo.table;
import static com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
import com.tashow.cloud.common.pojo.PageParam;
import java.time.LocalDateTime;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
/** 管理后台 - 表定义分页 Request VO */
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CodegenTablePageReqVO extends PageParam {
/** 表名称,模糊匹配 */
private String tableName;
/** 表描述,模糊匹配 */
private String tableComment;
/** 实体,模糊匹配 */
private String className;
/** 创建时间 */
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,72 @@
package com.tashow.cloud.infra.controller.admin.codegen.vo.table;
import java.time.LocalDateTime;
import lombok.Data;
/** 管理后台 - 代码生成表定义 Response VO */
@Data
public class CodegenTableRespVO {
/** 编号" */
private Long id;
/** 生成场景,参见 CodegenSceneEnum 枚举" */
private Integer scene;
/** 表名称" */
private String tableName;
/** 表描述", example = "芋道 */
private String tableComment;
/** 备注 */
private String remark;
/** 模块名" */
private String moduleName;
/** 业务名" */
private String businessName;
/** 类名称" */
private String className;
/** 类描述", example = "代码生成器的表定义 */
private String classComment;
/** 作者", example = "芋道源码 */
private String author;
/** 模板类型,参见 CodegenTemplateTypeEnum 枚举" */
private Integer templateType;
/** 前端类型,参见 CodegenFrontTypeEnum 枚举" */
private Integer frontType;
/** 父菜单编号 */
private Long parentMenuId;
/** 主表的编号 */
private Long masterTableId;
/** 子表关联主表的字段编号 */
private Long subJoinColumnId;
/** 主表与子表是否一对多 */
private Boolean subJoinMany;
/** 树表的父字段编号 */
private Long treeParentColumnId;
/** 树表的名字字段编号 */
private Long treeNameColumnId;
/** 主键编号" */
private Integer dataSourceConfigId;
/** 创建时间 */
private LocalDateTime createTime;
/** 更新时间 */
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,100 @@
package com.tashow.cloud.infra.controller.admin.codegen.vo.table;
import cn.hutool.core.util.ObjectUtil;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.tashow.cloud.infra.enums.codegen.CodegenSceneEnum;
import com.tashow.cloud.infra.enums.codegen.CodegenTemplateTypeEnum;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/** 管理后台 - 代码生成表定义创建/修改 Response VO */
@Data
public class CodegenTableSaveReqVO {
/** 编号" */
private Long id;
/** 生成场景,参见 CodegenSceneEnum 枚举" */
@NotNull(message = "导入类型不能为空")
private Integer scene;
/** 表名称" */
@NotNull(message = "表名称不能为空")
private String tableName;
/** 表描述", example = "芋道 */
@NotNull(message = "表描述不能为空")
private String tableComment;
/** 备注 */
private String remark;
/** 模块名" */
@NotNull(message = "模块名不能为空")
private String moduleName;
/** 业务名" */
@NotNull(message = "业务名不能为空")
private String businessName;
/** 类名称" */
@NotNull(message = "类名称不能为空")
private String className;
/** 类描述", example = "代码生成器的表定义 */
@NotNull(message = "类描述不能为空")
private String classComment;
/** 作者", example = "芋道源码 */
@NotNull(message = "作者不能为空")
private String author;
/** 模板类型,参见 CodegenTemplateTypeEnum 枚举" */
@NotNull(message = "模板类型不能为空")
private Integer templateType;
/** 前端类型,参见 CodegenFrontTypeEnum 枚举" */
@NotNull(message = "前端类型不能为空")
private Integer frontType;
/** 父菜单编号 */
private Long parentMenuId;
/** 主表的编号 */
private Long masterTableId;
/** 子表关联主表的字段编号 */
private Long subJoinColumnId;
/** 主表与子表是否一对多 */
private Boolean subJoinMany;
/** 树表的父字段编号 */
private Long treeParentColumnId;
/** 树表的名字字段编号 */
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));
}
}

View File

@@ -0,0 +1,14 @@
package com.tashow.cloud.infra.controller.admin.codegen.vo.table;
import lombok.Data;
/** 管理后台 - 数据库的表定义 Response VO */
@Data
public class DatabaseTableRespVO {
/** 表名称" */
private String name;
/** 表描述", example = "芋道源码 */
private String comment;
}

View File

@@ -0,0 +1,99 @@
package com.tashow.cloud.infra.controller.admin.config;
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;
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.controller.admin.config.vo.ConfigSaveReqVO;
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.infraapi.enums.ErrorCodeConstants;
import com.tashow.cloud.web.apilog.core.annotation.ApiAccessLog;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import java.io.IOException;
import java.util.List;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/** 管理后台 - 参数配置 */
@RestController
@RequestMapping("/infra/config")
@Validated
public class ConfigController {
@Resource private ConfigService configService;
/** 创建参数配置 */
@PostMapping("/create")
@PreAuthorize("@ss.hasPermission('infra:config:create')")
public CommonResult<Long> createConfig(@Valid @RequestBody ConfigSaveReqVO createReqVO) {
return success(configService.createConfig(createReqVO));
}
/** 修改参数配置 */
@PutMapping("/update")
@PreAuthorize("@ss.hasPermission('infra:config:update')")
public CommonResult<Boolean> updateConfig(@Valid @RequestBody ConfigSaveReqVO updateReqVO) {
configService.updateConfig(updateReqVO);
return success(true);
}
/** 删除参数配置 */
@DeleteMapping("/delete")
@PreAuthorize("@ss.hasPermission('infra:config:delete')")
public CommonResult<Boolean> deleteConfig(@RequestParam("id") Long id) {
configService.deleteConfig(id);
return success(true);
}
/** 获得参数配置 */
@GetMapping(value = "/get")
@PreAuthorize("@ss.hasPermission('infra:config:query')")
public CommonResult<ConfigRespVO> getConfig(@RequestParam("id") Long id) {
return success(ConfigConvert.INSTANCE.convert(configService.getConfig(id)));
}
/** 根据参数键名查询参数值", description = "不可见的配置,不允许返回给前端 */
@GetMapping(value = "/get-value-by-key")
public CommonResult<String> 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")
@PreAuthorize("@ss.hasPermission('infra:config:query')")
public CommonResult<PageResult<ConfigRespVO>> getConfigPage(@Valid ConfigPageReqVO pageReqVO) {
PageResult<ConfigDO> page = configService.getConfigPage(pageReqVO);
return success(ConfigConvert.INSTANCE.convertPage(page));
}
/** 导出参数配置 */
@GetMapping("/export")
@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<ConfigDO> list = configService.getConfigPage(exportReqVO).getList();
// 输出
ExcelUtils.write(
response, "参数配置.xls", "数据", ConfigRespVO.class, ConfigConvert.INSTANCE.convertList(list));
}
}

View File

@@ -0,0 +1,30 @@
package com.tashow.cloud.infra.controller.admin.config.vo;
import static com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
import com.tashow.cloud.common.pojo.PageParam;
import java.time.LocalDateTime;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
/** 管理后台 - 参数配置分页 Request VO */
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ConfigPageReqVO extends PageParam {
/** 数据源名称,模糊匹配 */
private String name;
/** 参数键名,模糊匹配 */
private String key;
/** 参数类型,参见 SysConfigTypeEnum 枚举 */
private Integer type;
/** 创建时间 */
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,53 @@
package com.tashow.cloud.infra.controller.admin.config.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 com.tashow.cloud.infraapi.enums.DictTypeConstants;
import java.time.LocalDateTime;
import lombok.Data;
/** 管理后台 - 参数配置信息 Response VO */
@Data
@ExcelIgnoreUnannotated
public class ConfigRespVO {
/** 参数配置序号" */
@ExcelProperty("参数配置序号")
private Long id;
/** 参数分类" */
@ExcelProperty("参数分类")
private String category;
/** 参数名称", example = "数据库名 */
@ExcelProperty("参数名称")
private String name;
/** 参数键名" */
@ExcelProperty("参数键名")
private String key;
/** 参数键值" */
@ExcelProperty("参数键值")
private String value;
/** 参数类型,参见 SysConfigTypeEnum 枚举" */
@ExcelProperty(value = "参数类型", converter = DictConvert.class)
@DictFormat(DictTypeConstants.CONFIG_TYPE)
private Integer type;
/** 是否可见" */
@ExcelProperty(value = "是否可见", converter = DictConvert.class)
@DictFormat(DictTypeConstants.BOOLEAN_STRING)
private Boolean visible;
/** 备注 */
@ExcelProperty("备注")
private String remark;
/** 创建时间", example = "时间戳格式 */
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,42 @@
package com.tashow.cloud.infra.controller.admin.config.vo;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
/** 管理后台 - 参数配置创建/修改 Request VO */
@Data
public class ConfigSaveReqVO {
/** 参数配置序号 */
private Long id;
/** 参数分组" */
@NotEmpty(message = "参数分组不能为空")
@Size(max = 50, message = "参数名称不能超过 50 个字符")
private String category;
/** 参数名称", example = "数据库名 */
@NotBlank(message = "参数名称不能为空")
@Size(max = 100, message = "参数名称不能超过 100 个字符")
private String name;
/** 参数键名" */
@NotBlank(message = "参数键名长度不能为空")
@Size(max = 100, message = "参数键名长度不能超过 100 个字符")
private String key;
/** 参数键值" */
@NotBlank(message = "参数键值不能为空")
@Size(max = 500, message = "参数键值长度不能超过 500 个字符")
private String value;
/** 是否可见" */
@NotNull(message = "是否可见不能为空")
private Boolean visible;
/** 备注 */
private String remark;
}

View File

@@ -0,0 +1,66 @@
package com.tashow.cloud.infra.controller.admin.db;
import static com.tashow.cloud.common.pojo.CommonResult.success;
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 jakarta.annotation.Resource;
import jakarta.validation.Valid;
import java.util.List;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/** 管理后台 - 数据源配置 */
@RestController
@RequestMapping("/infra/data-source-config")
@Validated
public class DataSourceConfigController {
@Resource private DataSourceConfigService dataSourceConfigService;
/** 创建数据源配置 */
@PostMapping("/create")
@PreAuthorize("@ss.hasPermission('infra:data-source-config:create')")
public CommonResult<Long> createDataSourceConfig(
@Valid @RequestBody DataSourceConfigSaveReqVO createReqVO) {
return success(dataSourceConfigService.createDataSourceConfig(createReqVO));
}
/** 更新数据源配置 */
@PutMapping("/update")
@PreAuthorize("@ss.hasPermission('infra:data-source-config:update')")
public CommonResult<Boolean> updateDataSourceConfig(
@Valid @RequestBody DataSourceConfigSaveReqVO updateReqVO) {
dataSourceConfigService.updateDataSourceConfig(updateReqVO);
return success(true);
}
/** 删除数据源配置 */
@DeleteMapping("/delete")
@PreAuthorize("@ss.hasPermission('infra:data-source-config:delete')")
public CommonResult<Boolean> deleteDataSourceConfig(@RequestParam("id") Long id) {
dataSourceConfigService.deleteDataSourceConfig(id);
return success(true);
}
/** 获得数据源配置 */
@GetMapping("/get")
@PreAuthorize("@ss.hasPermission('infra:data-source-config:query')")
public CommonResult<DataSourceConfigRespVO> getDataSourceConfig(@RequestParam("id") Long id) {
DataSourceConfigDO config = dataSourceConfigService.getDataSourceConfig(id);
return success(BeanUtils.toBean(config, DataSourceConfigRespVO.class));
}
/** 获得数据源配置列表 */
@GetMapping("/list")
@PreAuthorize("@ss.hasPermission('infra:data-source-config:query')")
public CommonResult<List<DataSourceConfigRespVO>> getDataSourceConfigList() {
List<DataSourceConfigDO> list = dataSourceConfigService.getDataSourceConfigList();
return success(BeanUtils.toBean(list, DataSourceConfigRespVO.class));
}
}

View File

@@ -0,0 +1,26 @@
package com.tashow.cloud.infra.controller.admin.db.vo;
import java.time.LocalDateTime;
import lombok.Data;
/** 管理后台 - 数据源配置 Response VO */
@Data
public class DataSourceConfigRespVO {
/** 主键编号" */
private Integer id;
/** 数据源名称" */
private String name;
/** 数据源连接" */
private String url;
/** 用户名" */
private String username;
/**
* 创建时间
*/
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,28 @@
package com.tashow.cloud.infra.controller.admin.db.vo;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/** 管理后台 - 数据源配置创建/修改 Request VO */
@Data
public class DataSourceConfigSaveReqVO {
/** 主键编号 */
private Long id;
/** 数据源名称" */
@NotNull(message = "数据源名称不能为空")
private String name;
/** 数据源连接" */
@NotNull(message = "数据源连接不能为空")
private String url;
/** 用户名" */
@NotNull(message = "用户名不能为空")
private String username;
/** 密码" */
@NotNull(message = "密码不能为空")
private String password;
}

View File

@@ -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}}

View File

@@ -0,0 +1,83 @@
package com.tashow.cloud.infra.controller.admin.file;
import static com.tashow.cloud.common.pojo.CommonResult.success;
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.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 jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/** 管理后台 - 文件配置 */
@RestController
@RequestMapping("/infra/file-config")
@Validated
public class FileConfigController {
@Resource private FileConfigService fileConfigService;
/** 创建文件配置 */
@PostMapping("/create")
@PreAuthorize("@ss.hasPermission('infra:file-config:create')")
public CommonResult<Long> createFileConfig(@Valid @RequestBody FileConfigSaveReqVO createReqVO) {
return success(fileConfigService.createFileConfig(createReqVO));
}
/** 更新文件配置 */
@PutMapping("/update")
@PreAuthorize("@ss.hasPermission('infra:file-config:update')")
public CommonResult<Boolean> updateFileConfig(
@Valid @RequestBody FileConfigSaveReqVO updateReqVO) {
fileConfigService.updateFileConfig(updateReqVO);
return success(true);
}
/** 更新文件配置为 Master */
@PutMapping("/update-master")
@PreAuthorize("@ss.hasPermission('infra:file-config:update')")
public CommonResult<Boolean> updateFileConfigMaster(@RequestParam("id") Long id) {
fileConfigService.updateFileConfigMaster(id);
return success(true);
}
/** 删除文件配置 */
@DeleteMapping("/delete")
@PreAuthorize("@ss.hasPermission('infra:file-config:delete')")
public CommonResult<Boolean> deleteFileConfig(@RequestParam("id") Long id) {
fileConfigService.deleteFileConfig(id);
return success(true);
}
/** 获得文件配置 */
@GetMapping("/get")
@PreAuthorize("@ss.hasPermission('infra:file-config:query')")
public CommonResult<FileConfigRespVO> getFileConfig(@RequestParam("id") Long id) {
FileConfigDO config = fileConfigService.getFileConfig(id);
return success(BeanUtils.toBean(config, FileConfigRespVO.class));
}
/** 获得文件配置分页 */
@GetMapping("/page")
@PreAuthorize("@ss.hasPermission('infra:file-config:query')")
public CommonResult<PageResult<FileConfigRespVO>> getFileConfigPage(
@Valid FileConfigPageReqVO pageVO) {
PageResult<FileConfigDO> pageResult = fileConfigService.getFileConfigPage(pageVO);
return success(BeanUtils.toBean(pageResult, FileConfigRespVO.class));
}
/** 测试文件配置是否正确 */
@GetMapping("/test")
@PreAuthorize("@ss.hasPermission('infra:file-config:query')")
public CommonResult<String> testFileConfig(@RequestParam("id") Long id) throws Exception {
String url = fileConfigService.testFileConfig(id);
return success(url);
}
}

View File

@@ -0,0 +1,100 @@
package com.tashow.cloud.infra.controller.admin.file;
import static com.tashow.cloud.common.pojo.CommonResult.success;
import static com.tashow.cloud.infra.framework.file.core.utils.FileTypeUtils.writeAttachment;
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 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;
/** 管理后台 - 文件存储 */
@RestController
@RequestMapping("/infra/file")
@Validated
@Slf4j
public class FileController {
@Resource private FileService fileService;
/** 上传文件", description = "模式一:后端上传文件 */
@PostMapping("/upload")
public CommonResult<String> uploadFile(FileUploadReqVO uploadReqVO) throws Exception {
MultipartFile file = uploadReqVO.getFile();
String path = uploadReqVO.getPath();
return success(
fileService.createFile(
file.getOriginalFilename(), path, IoUtil.readBytes(file.getInputStream())));
}
/** 获取文件预签名地址", description = "模式二:前端上传文件:用于前端直接上传七牛、阿里云 OSS 等文件存储器 */
@GetMapping("/presigned-url")
public CommonResult<FilePresignedUrlRespVO> getFilePresignedUrl(@RequestParam("path") String path)
throws Exception {
return success(fileService.getFilePresignedUrl(path));
}
/** 创建文件", description = "模式二:前端上传文件:配合 presigned-url 接口,记录上传了上传的文件 */
@PostMapping("/create")
public CommonResult<Long> createFile(@Valid @RequestBody FileCreateReqVO createReqVO) {
return success(fileService.createFile(createReqVO));
}
/** 删除文件 */
@DeleteMapping("/delete")
@PreAuthorize("@ss.hasPermission('infra:file:delete')")
public CommonResult<Boolean> deleteFile(@RequestParam("id") Long id) throws Exception {
fileService.deleteFile(id);
return success(true);
}
/** 下载文件 */
@GetMapping("/{configId}/get/**")
@PermitAll
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")
@PreAuthorize("@ss.hasPermission('infra:file:query')")
public CommonResult<PageResult<FileRespVO>> getFilePage(@Valid FilePageReqVO pageVO) {
PageResult<FileDO> pageResult = fileService.getFilePage(pageVO);
return success(BeanUtils.toBean(pageResult, FileRespVO.class));
}
}

View File

@@ -0,0 +1,27 @@
package com.tashow.cloud.infra.controller.admin.file.vo.config;
import static com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
import com.tashow.cloud.common.pojo.PageParam;
import java.time.LocalDateTime;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
/** 管理后台 - 文件配置分页 Request VO */
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class FileConfigPageReqVO extends PageParam {
/** 配置名 */
private String name;
/** 存储器 */
private Integer storage;
/** 创建时间 */
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,31 @@
package com.tashow.cloud.infra.controller.admin.file.vo.config;
import com.tashow.cloud.infra.framework.file.core.client.FileClientConfig;
import java.time.LocalDateTime;
import lombok.Data;
/** 管理后台 - 文件配置 Response VO */
@Data
public class FileConfigRespVO {
/** 编号" */
private Long id;
/** 配置名" */
private String name;
/** 存储器,参见 FileStorageEnum 枚举类" */
private Integer storage;
/** 是否为主配置" */
private Boolean master;
/** 存储配置 */
private FileClientConfig config;
/** 备注 */
private String remark;
/** 创建时间 */
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,28 @@
package com.tashow.cloud.infra.controller.admin.file.vo.config;
import jakarta.validation.constraints.NotNull;
import java.util.Map;
import lombok.Data;
/** 管理后台 - 文件配置创建/修改 Request VO */
@Data
public class FileConfigSaveReqVO {
/** 编号 */
private Long id;
/** 配置名" */
@NotNull(message = "配置名不能为空")
private String name;
/** 存储器,参见 FileStorageEnum 枚举类" */
@NotNull(message = "存储器不能为空")
private Integer storage;
/** 存储配置,配置是动态参数,所以使用 Map 接收 */
@NotNull(message = "存储配置不能为空")
private Map<String, Object> config;
/** 备注 */
private String remark;
}

View File

@@ -0,0 +1,33 @@
package com.tashow.cloud.infra.controller.admin.file.vo.file;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/** 管理后台 - 文件创建 Request VO */
@Data
public class FileCreateReqVO {
@NotNull(message = "文件配置编号不能为空")
/** 文件配置编号" */
private Long configId;
@NotNull(message = "文件路径不能为空")
/** 文件路径" */
private String path;
@NotNull(message = "原文件名不能为空")
/** 原文件名" */
private String name;
@NotNull(message = "文件 URL不能为空")
/** 文件 URL" */
private String url;
/** 文件 MIME 类型 */
private String type;
/**
* 文件大小", example = "2048
*/
private Integer size;
}

View File

@@ -0,0 +1,27 @@
package com.tashow.cloud.infra.controller.admin.file.vo.file;
import static com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
import com.tashow.cloud.common.pojo.PageParam;
import java.time.LocalDateTime;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
/** 管理后台 - 文件分页 Request VO */
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class FilePageReqVO extends PageParam {
/** 文件路径,模糊匹配 */
private String path;
/** 文件类型,模糊匹配 */
private String type;
/** 创建时间 */
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,25 @@
package com.tashow.cloud.infra.controller.admin.file.vo.file;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
/** 管理后台 - 文件预签名地址 Response VO */
@Data
public class FilePresignedUrlRespVO {
/** 配置编号" */
private Long configId;
/** 文件上传 URL" */
private String uploadUrl;
/**
* 文件访问 URL
*
* <p>前端上传完文件后,需要使用该 URL 进行访问
*/
private String url;
}

View File

@@ -0,0 +1,33 @@
package com.tashow.cloud.infra.controller.admin.file.vo.file;
import java.time.LocalDateTime;
import lombok.Data;
/** 管理后台 - 文件 Response VO,不返回 content 字段,太大 */
@Data
public class FileRespVO {
/** 文件编号" */
private Long id;
/** 配置编号" */
private Long configId;
/** 文件路径" */
private String path;
/** 原文件名" */
private String name;
/** 文件 URL" */
private String url;
/** 文件MIME类型 */
private String type;
/** 文件大小", example = "2048 */
private Integer size;
/** 创建时间 */
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,17 @@
package com.tashow.cloud.infra.controller.admin.file.vo.file;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
/** 管理后台 - 上传文件 Request VO */
@Data
public class FileUploadReqVO {
/** 文件附件 */
@NotNull(message = "文件附件不能为空")
private MultipartFile file;
/** 文件附件 */
private String path;
}

View File

@@ -0,0 +1,24 @@
package com.tashow.cloud.infra.controller.admin.job;
import static com.tashow.cloud.common.pojo.CommonResult.error;
import com.tashow.cloud.common.pojo.CommonResult;
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;
/** 管理后台 - 定时任务 */
@RestController
@RequestMapping("/infra/job")
@Validated
public class JobController {
/** 获得定时任务分页 */
@GetMapping("/page")
@PreAuthorize("@ss.hasPermission('infra:job:query')")
public CommonResult<String> getJobPage() {
return error(-1, "Cloud 版本使用 XXL-Job 作为定时任务!请参考 https://cloud.iocoder.cn/job/ 文档操作");
}
}

View File

@@ -0,0 +1,60 @@
package com.tashow.cloud.infra.controller.admin.logger;
import static com.tashow.cloud.common.pojo.CommonResult.success;
import static com.tashow.cloud.web.apilog.core.enums.OperateTypeEnum.EXPORT;
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.web.apilog.core.annotation.ApiAccessLog;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import java.io.IOException;
import java.util.List;
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;
/** 管理后台 - API 访问日志 */
@RestController
@RequestMapping("/infra/api-access-log")
@Validated
public class ApiAccessLogController {
@Resource private ApiAccessLogService apiAccessLogService;
/** 获得API 访问日志分页 */
@GetMapping("/page")
@PreAuthorize("@ss.hasPermission('infra:api-access-log:query')")
public CommonResult<PageResult<ApiAccessLogRespVO>> getApiAccessLogPage(
@Valid ApiAccessLogPageReqVO pageReqVO) {
PageResult<ApiAccessLogDO> pageResult = apiAccessLogService.getApiAccessLogPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, ApiAccessLogRespVO.class));
}
/** 导出API 访问日志 Excel */
@GetMapping("/export-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<ApiAccessLogDO> list = apiAccessLogService.getApiAccessLogPage(exportReqVO).getList();
// 导出 Excel
ExcelUtils.write(
response,
"API 访问日志.xls",
"数据",
ApiAccessLogRespVO.class,
BeanUtils.toBean(list, ApiAccessLogRespVO.class));
}
}

View File

@@ -0,0 +1,68 @@
package com.tashow.cloud.infra.controller.admin.logger;
import static com.tashow.cloud.common.pojo.CommonResult.success;
import static com.tashow.cloud.web.apilog.core.enums.OperateTypeEnum.EXPORT;
import static com.tashow.cloud.web.web.core.util.WebFrameworkUtils.getLoginUserId;
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.controller.admin.logger.vo.apierrorlog.ApiErrorLogRespVO;
import com.tashow.cloud.infra.dal.dataobject.logger.ApiErrorLogDO;
import com.tashow.cloud.infra.service.logger.ApiErrorLogService;
import com.tashow.cloud.web.apilog.core.annotation.ApiAccessLog;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import java.io.IOException;
import java.util.List;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/** 管理后台 - API 错误日志 */
@RestController
@RequestMapping("/infra/api-error-log")
@Validated
public class ApiErrorLogController {
@Resource private ApiErrorLogService apiErrorLogService;
/** 更新 API 错误日志的状态 */
@PutMapping("/update-status")
@PreAuthorize("@ss.hasPermission('infra:api-error-log:update-status')")
public CommonResult<Boolean> updateApiErrorLogProcess(
@RequestParam("id") Long id, @RequestParam("processStatus") Integer processStatus) {
apiErrorLogService.updateApiErrorLogProcess(id, processStatus, getLoginUserId());
return success(true);
}
/** 获得 API 错误日志分页 */
@GetMapping("/page")
@PreAuthorize("@ss.hasPermission('infra:api-error-log:query')")
public CommonResult<PageResult<ApiErrorLogRespVO>> getApiErrorLogPage(
@Valid ApiErrorLogPageReqVO pageReqVO) {
PageResult<ApiErrorLogDO> pageResult = apiErrorLogService.getApiErrorLogPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, ApiErrorLogRespVO.class));
}
/** 导出 API 错误日志 Excel */
@GetMapping("/export-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<ApiErrorLogDO> list = apiErrorLogService.getApiErrorLogPage(exportReqVO).getList();
// 导出 Excel
ExcelUtils.write(
response,
"API 错误日志.xls",
"数据",
ApiErrorLogRespVO.class,
BeanUtils.toBean(list, ApiErrorLogRespVO.class));
}
}

View File

@@ -0,0 +1,39 @@
package com.tashow.cloud.infra.controller.admin.logger.vo.apiaccesslog;
import static com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
import com.tashow.cloud.common.pojo.PageParam;
import java.time.LocalDateTime;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
/** 管理后台 - API 访问日志分页 Request VO */
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ApiAccessLogPageReqVO extends PageParam {
/** 用户编号 */
private Long userId;
/** 用户类型 */
private Integer userType;
/** 应用名 */
private String applicationName;
/** 请求地址,模糊匹配 */
private String requestUrl;
/** 开始时间 */
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] beginTime;
/** 执行时长,大于等于,单位:毫秒 */
private Integer duration;
/** 结果码 */
private Integer resultCode;
}

View File

@@ -0,0 +1,96 @@
package com.tashow.cloud.infra.controller.admin.logger.vo.apiaccesslog;
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 com.tashow.cloud.systemapi.enums.DictTypeConstants;
import java.time.LocalDateTime;
import lombok.Data;
/** 管理后台 - API 访问日志 Response VO */
@Data
@ExcelIgnoreUnannotated
public class ApiAccessLogRespVO {
/** 日志主键" */
@ExcelProperty("日志主键")
private Long id;
/** 链路追踪编号" */
@ExcelProperty("链路追踪编号")
private String traceId;
/** 用户编号" */
@ExcelProperty("用户编号")
private Long userId;
/** 用户类型,参见 UserTypeEnum 枚举" */
@ExcelProperty(value = "用户类型", converter = DictConvert.class)
@DictFormat(DictTypeConstants.USER_TYPE)
private Integer userType;
/** 应用名" */
@ExcelProperty("应用名")
private String applicationName;
/** 请求方法名" */
@ExcelProperty("请求方法名")
private String requestMethod;
/** 请求地址", example = "/xxx/yyy */
@ExcelProperty("请求地址")
private String requestUrl;
/** 请求参数 */
@ExcelProperty("请求参数")
private String requestParams;
/** 响应结果 */
@ExcelProperty("响应结果")
private String responseBody;
/** 用户 IP" */
@ExcelProperty("用户 IP")
private String userIp;
/** 浏览器 UA" */
@ExcelProperty("浏览器 UA")
private String userAgent;
/** 操作模块", example = "商品模块 */
@ExcelProperty("操作模块")
private String operateModule;
/** 操作名", example = "创建商品 */
@ExcelProperty("操作名")
private String operateName;
/** 操作分类" */
@ExcelProperty(value = "操作分类", converter = DictConvert.class)
@DictFormat(com.tashow.cloud.infraapi.enums.DictTypeConstants.OPERATE_TYPE)
private Integer operateType;
/** 开始请求时间 */
@ExcelProperty("开始请求时间")
private LocalDateTime beginTime;
/** 结束请求时间 */
@ExcelProperty("结束请求时间")
private LocalDateTime endTime;
/** 执行时长" */
@ExcelProperty("执行时长")
private Integer duration;
/** 结果码" */
@ExcelProperty("结果码")
private Integer resultCode;
/** 结果提示 */
@ExcelProperty("结果提示")
private String resultMsg;
/** 创建时间 */
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,36 @@
package com.tashow.cloud.infra.controller.admin.logger.vo.apierrorlog;
import static com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
import com.tashow.cloud.common.pojo.PageParam;
import java.time.LocalDateTime;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
/** 管理后台 - API 错误日志分页 Request VO */
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ApiErrorLogPageReqVO extends PageParam {
/** 用户编号 */
private Long userId;
/** 用户类型 */
private Integer userType;
/** 应用名 */
private String applicationName;
/** 请求地址 */
private String requestUrl;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
/** 异常发生时间 */
private LocalDateTime[] exceptionTime;
/** 处理状态 */
private Integer processStatus;
}

View File

@@ -0,0 +1,109 @@
package com.tashow.cloud.infra.controller.admin.logger.vo.apierrorlog;
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 com.tashow.cloud.infraapi.enums.DictTypeConstants;
import java.time.LocalDateTime;
import lombok.Data;
/** 管理后台 - API 错误日志 Response VO */
@Data
@ExcelIgnoreUnannotated
public class ApiErrorLogRespVO {
/** 编号" */
@ExcelProperty("编号")
private Long id;
/** 链路追踪编号" */
@ExcelProperty("链路追踪编号")
private String traceId;
/** 用户编号" */
@ExcelProperty("用户编号")
private Long userId;
/** 用户类型" */
@ExcelProperty(value = "用户类型", converter = DictConvert.class)
@DictFormat(com.tashow.cloud.systemapi.enums.DictTypeConstants.USER_TYPE)
private Integer userType;
/** 应用名" */
@ExcelProperty("应用名")
private String applicationName;
/** 请求方法名" */
@ExcelProperty("请求方法名")
private String requestMethod;
/** 请求地址", example = "/xx/yy */
@ExcelProperty("请求地址")
private String requestUrl;
/** 请求参数 */
@ExcelProperty("请求参数")
private String requestParams;
/** 用户 IP" */
@ExcelProperty("用户 IP")
private String userIp;
/** 浏览器 UA" */
@ExcelProperty("浏览器 UA")
private String userAgent;
/** 异常发生时间 */
@ExcelProperty("异常发生时间")
private LocalDateTime exceptionTime;
/** 异常名 */
@ExcelProperty("异常名")
private String exceptionName;
/** 异常导致的消息 */
@ExcelProperty("异常导致的消息")
private String exceptionMessage;
/** 异常导致的根消息 */
@ExcelProperty("异常导致的根消息")
private String exceptionRootCauseMessage;
/** 异常的栈轨迹 */
@ExcelProperty("异常的栈轨迹")
private String exceptionStackTrace;
/** 异常发生的类全名 */
@ExcelProperty("异常发生的类全名")
private String exceptionClassName;
/** 异常发生的类文件 */
@ExcelProperty("异常发生的类文件")
private String exceptionFileName;
/** 异常发生的方法名 */
@ExcelProperty("异常发生的方法名")
private String exceptionMethodName;
/** 异常发生的方法所在行 */
@ExcelProperty("异常发生的方法所在行")
private Integer exceptionLineNumber;
/** 处理状态" */
@ExcelProperty(value = "处理状态", converter = DictConvert.class)
@DictFormat(DictTypeConstants.API_ERROR_LOG_PROCESS_STATUS)
private Integer processStatus;
/** 处理时间 */
@ExcelProperty("处理时间")
private LocalDateTime processTime;
/** 处理用户编号 */
@ExcelProperty("处理用户编号")
private Integer processUserId;
/** 创建时间 */
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,4 @@
### 请求 /infra/redis/get-monitor-info 接口 => 成功
GET {{baseUrl}}/infra/redis/get-monitor-info
Authorization: Bearer {{token}}
tenant-id: {{adminTenantId}}

View File

@@ -0,0 +1,41 @@
package com.tashow.cloud.infra.controller.admin.redis;
import static com.tashow.cloud.common.pojo.CommonResult.success;
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 jakarta.annotation.Resource;
import java.util.Properties;
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;
/** 管理后台 - Redis 监控 */
@RestController
@RequestMapping("/infra/redis")
public class RedisController {
@Resource private StringRedisTemplate stringRedisTemplate;
/** 获得 Redis 监控信息 */
@GetMapping("/get-monitor-info")
@PreAuthorize("@ss.hasPermission('infra:redis:get-monitor-info')")
public CommonResult<RedisMonitorRespVO> getRedisMonitorInfo() {
// 获得 Redis 统计信息
Properties info =
stringRedisTemplate.execute((RedisCallback<Properties>) RedisServerCommands::info);
Long dbSize = stringRedisTemplate.execute(RedisServerCommands::dbSize);
Properties commandStats =
stringRedisTemplate.execute(
(RedisCallback<Properties>)
connection -> connection.serverCommands().info("commandstats"));
assert commandStats != null; // 断言,避免警告
// 拼接结果返回
return success(RedisConvert.INSTANCE.build(info, dbSize, commandStats));
}
}

View File

@@ -0,0 +1,38 @@
package com.tashow.cloud.infra.controller.admin.redis.vo;
import java.util.List;
import java.util.Properties;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
/** 管理后台 - Redis 监控信息 Response VO */
@Data
@Builder
@AllArgsConstructor
public class RedisMonitorRespVO {
private Properties info;
/** Redis key 数量" */
private Long dbSize;
/** CommandStat 数组 */
private List<CommandStat> commandStats;
/** Redis 命令统计结果 */
@Data
@Builder
@AllArgsConstructor
public static class CommandStat {
/** Redis 命令" */
private String command;
/** 调用次数" */
private Long calls;
/** 消耗 CPU 秒数" */
private Long usec;
}
}

View File

@@ -0,0 +1,53 @@
package com.tashow.cloud.infra.controller.app.file;
import static com.tashow.cloud.common.pojo.CommonResult.success;
import cn.hutool.core.io.IoUtil;
import com.tashow.cloud.common.pojo.CommonResult;
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 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;
/** 用户 App - 文件存储 */
@RestController
@RequestMapping("/infra/file")
@Validated
@Slf4j
public class AppFileController {
@Resource private FileService fileService;
/** 上传文件 */
@PostMapping("/upload")
@PermitAll
public CommonResult<String> uploadFile(AppFileUploadReqVO uploadReqVO) throws Exception {
MultipartFile file = uploadReqVO.getFile();
String path = uploadReqVO.getPath();
return success(
fileService.createFile(
file.getOriginalFilename(), path, IoUtil.readBytes(file.getInputStream())));
}
/** 获取文件预签名地址", description = "模式二:前端上传文件:用于前端直接上传七牛、阿里云 OSS 等文件存储器 */
@GetMapping("/presigned-url")
@PermitAll
public CommonResult<FilePresignedUrlRespVO> getFilePresignedUrl(@RequestParam("path") String path)
throws Exception {
return success(fileService.getFilePresignedUrl(path));
}
/** 创建文件", description = "模式二:前端上传文件:配合 presigned-url 接口,记录上传了上传的文件 */
@PostMapping("/create")
@PermitAll
public CommonResult<Long> createFile(@Valid @RequestBody FileCreateReqVO createReqVO) {
return success(fileService.createFile(createReqVO));
}
}

View File

@@ -0,0 +1,17 @@
package com.tashow.cloud.infra.controller.app.file.vo;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
/** 用户 App - 上传文件 Request VO */
@Data
public class AppFileUploadReqVO {
/** 文件附件 */
@NotNull(message = "文件附件不能为空")
private MultipartFile file;
/** 文件附件 */
private String path;
}

View File

@@ -0,0 +1,4 @@
/**
* 占位
*/
package com.tashow.cloud.infra.controller.app;

View File

@@ -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;

View File

@@ -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<CodegenColumnDO> convertList(List<TableField> 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<CodegenColumnDO> columns) {
CodegenDetailRespVO respVO = new CodegenDetailRespVO();
respVO.setTable(BeanUtils.toBean(table, CodegenTableRespVO.class));
respVO.setColumns(BeanUtils.toBean(columns, CodegenColumnRespVO.class));
return respVO;
}
default List<CodegenPreviewRespVO> convert(Map<String, String> codes) {
return CollectionUtils.convertList(codes.entrySet(),
entry -> new CodegenPreviewRespVO().setFilePath(entry.getKey()).setCode(entry.getValue()));
}
}

View File

@@ -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<ConfigRespVO> convertPage(PageResult<ConfigDO> page);
List<ConfigRespVO> convertList(List<ConfigDO> list);
@Mapping(source = "configKey", target = "key")
ConfigRespVO convert(ConfigDO bean);
@Mapping(source = "key", target = "configKey")
ConfigDO convert(ConfigSaveReqVO bean);
}

View File

@@ -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);
}

View File

@@ -0,0 +1,6 @@
/**
* 提供 POJO 类的实体转换
*
* 目前使用 MapStruct 框架
*/
package com.tashow.cloud.infra.convert;

View File

@@ -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;
}
}

View File

@@ -0,0 +1 @@
<http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao>

View File

@@ -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;
/**
* 表编号
* <p>
* 关联 {@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;
/**
* 字典类型
* <p>
* 关联 DictTypeDO 的 type 属性
*/
private String dictType;
/**
* 数据示例,主要用于生成 Swagger 注解的 example 字段
*/
private String example;
// ========== CRUD 相关字段 ==========
/**
* 是否为 Create 创建操作的字段
*/
private Boolean createOperation;
/**
* 是否为 Update 更新操作的字段
*/
private Boolean updateOperation;
/**
* 是否为 List 查询操作的字段
*/
private Boolean listOperation;
/**
* List 查询操作的条件类型
* <p>
* 枚举 {@link CodegenColumnListConditionEnum}
*/
private String listOperationCondition;
/**
* 是否为 List 查询操作的返回字段
*/
private Boolean listOperationResult;
// ========== UI 相关字段 ==========
/**
* 显示类型
* <p>
* 枚举 {@link CodegenColumnHtmlTypeEnum}
*/
private String htmlType;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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<Object> {
public FileClientConfigTypeHandler(Class<?> type) {
super(type);
}
public FileClientConfigTypeHandler(Class<?> type, Field field) {
super(type, field);
}
@Override
public Object parse(String json) {
FileClientConfig config = JsonUtils.parseObjectQuietly(json, new TypeReference<>() {});
if (config != null) {
return config;
}
// 兼容老版本的包路径
String className = JsonUtils.parseObject(json, "@class", String.class);
className = StrUtil.subAfter(className, ".", true);
switch (className) {
case "DBFileClientConfig":
return JsonUtils.parseObject2(json, DBFileClientConfig.class);
case "FtpFileClientConfig":
return JsonUtils.parseObject2(json, FtpFileClientConfig.class);
case "LocalFileClientConfig":
return JsonUtils.parseObject2(json, LocalFileClientConfig.class);
case "SftpFileClientConfig":
return JsonUtils.parseObject2(json, SftpFileClientConfig.class);
case "S3FileClientConfig":
return JsonUtils.parseObject2(json, S3FileClientConfig.class);
default:
throw new IllegalArgumentException("未知的 FileClientConfig 类型:" + json);
}
}
@Override
public String toJson(Object obj) {
return JsonUtils.toJsonString(obj);
}
}
}

View File

@@ -0,0 +1,48 @@
package com.tashow.cloud.infra.dal.dataobject.file;
import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
import com.tashow.cloud.infra.framework.file.core.client.db.DBFileClient;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
/**
* 文件内容表
*
* 专门用于存储 {@link DBFileClient} 的文件内容
*
* @author 芋道源码
*/
@TableName("infra_file_content")
@KeySequence("infra_file_content_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class FileContentDO extends BaseDO {
/**
* 编号,数据库自增
*/
@TableId
private Long id;
/**
* 配置编号
*
* 关联 {@link FileConfigDO#getId()}
*/
private Long configId;
/**
* 路径,即文件名
*/
private String path;
/**
* 文件内容
*/
private byte[] content;
}

View File

@@ -0,0 +1,55 @@
package com.tashow.cloud.infra.dal.dataobject.file;
import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
/**
* 文件表
* 每次文件上传,都会记录一条记录到该表中
*
* @author 芋道源码
*/
@TableName("infra_file")
@KeySequence("infra_file_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class FileDO extends BaseDO {
/**
* 编号,数据库自增
*/
private Long id;
/**
* 配置编号
*
* 关联 {@link FileConfigDO#getId()}
*/
private Long configId;
/**
* 原文件名
*/
private String name;
/**
* 路径,即文件名
*/
private String path;
/**
* 访问地址
*/
private String url;
/**
* 文件的 MIME 类型,例如 "application/octet-stream"
*/
private String type;
/**
* 文件大小
*/
private Integer size;
}

View File

@@ -0,0 +1,140 @@
package com.tashow.cloud.infra.dal.dataobject.logger;
import com.tashow.cloud.common.enums.UserTypeEnum;
import com.tashow.cloud.common.pojo.CommonResult;
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 com.tashow.cloud.web.apilog.core.enums.OperateTypeEnum;
import lombok.*;
import java.time.LocalDateTime;
/**
* API 访问日志
*
* @author 芋道源码
*/
@TableName("infra_api_access_log")
@KeySequence(value = "infra_api_access_log_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ApiAccessLogDO extends BaseDO {
/**
* {@link #requestParams} 的最大长度
*/
public static final Integer REQUEST_PARAMS_MAX_LENGTH = 8000;
/**
* {@link #resultMsg} 的最大长度
*/
public static final Integer RESULT_MSG_MAX_LENGTH = 512;
/**
* 编号
*/
@TableId
private Long id;
/**
* 链路追踪编号
*
* 一般来说通过链路追踪编号可以将访问日志错误日志链路追踪日志logger 打印日志等,结合在一起,从而进行排错。
*/
private String traceId;
/**
* 用户编号
*/
private Long userId;
/**
* 用户类型
*
* 枚举 {@link UserTypeEnum}
*/
private Integer userType;
/**
* 应用名
*
* 目前读取 `spring.application.name` 配置项
*/
private String applicationName;
// ========== 请求相关字段 ==========
/**
* 请求方法名
*/
private String requestMethod;
/**
* 访问地址
*/
private String requestUrl;
/**
* 请求参数
*
* query: Query String
* body: Quest Body
*/
private String requestParams;
/**
* 响应结果
*/
private String responseBody;
/**
* 用户 IP
*/
private String userIp;
/**
* 浏览器 UA
*/
private String userAgent;
// ========== 执行相关字段 ==========
/**
* 操作模块
*/
private String operateModule;
/**
* 操作名
*/
private String operateName;
/**
* 操作分类
*
* 枚举 {@link OperateTypeEnum}
*/
private Integer operateType;
/**
* 开始请求时间
*/
private LocalDateTime beginTime;
/**
* 结束请求时间
*/
private LocalDateTime endTime;
/**
* 执行时长,单位:毫秒
*/
private Integer duration;
/**
* 结果码
*
* 目前使用的 {@link CommonResult#getCode()} 属性
*/
private Integer resultCode;
/**
* 结果提示
*
* 目前使用的 {@link CommonResult#getMsg()} 属性
*/
private String resultMsg;
}

View File

@@ -0,0 +1,163 @@
package com.tashow.cloud.infra.dal.dataobject.logger;
import com.tashow.cloud.common.enums.UserTypeEnum;
import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
import com.tashow.cloud.infra.enums.logger.ApiErrorLogProcessStatusEnum;
import com.tashow.cloud.infra.enums.logger.ApiErrorLogProcessStatusEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.tashow.cloud.infra.enums.logger.ApiErrorLogProcessStatusEnum;
import lombok.*;
import java.time.LocalDateTime;
/**
* API 异常数据
*
* @author 芋道源码
*/
@TableName("infra_api_error_log")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
@KeySequence(value = "infra_api_error_log_seq")
public class ApiErrorLogDO extends BaseDO {
/**
* {@link #requestParams} 的最大长度
*/
public static final Integer REQUEST_PARAMS_MAX_LENGTH = 8000;
/**
* 编号
*/
@TableId
private Long id;
/**
* 用户编号
*/
private Long userId;
/**
* 链路追踪编号
*
* 一般来说通过链路追踪编号可以将访问日志错误日志链路追踪日志logger 打印日志等,结合在一起,从而进行排错。
*/
private String traceId;
/**
* 用户类型
*
* 枚举 {@link UserTypeEnum}
*/
private Integer userType;
/**
* 应用名
*
* 目前读取 spring.application.name
*/
private String applicationName;
// ========== 请求相关字段 ==========
/**
* 请求方法名
*/
private String requestMethod;
/**
* 访问地址
*/
private String requestUrl;
/**
* 请求参数
*
* query: Query String
* body: Quest Body
*/
private String requestParams;
/**
* 用户 IP
*/
private String userIp;
/**
* 浏览器 UA
*/
private String userAgent;
// ========== 异常相关字段 ==========
/**
* 异常发生时间
*/
private LocalDateTime exceptionTime;
/**
* 异常名
*
* {@link Throwable#getClass()} 的类全名
*/
private String exceptionName;
/**
* 异常导致的消息
*
* {@link cn.hutool.core.exceptions.ExceptionUtil#getMessage(Throwable)}
*/
private String exceptionMessage;
/**
* 异常导致的根消息
*
* {@link cn.hutool.core.exceptions.ExceptionUtil#getRootCauseMessage(Throwable)}
*/
private String exceptionRootCauseMessage;
/**
* 异常的栈轨迹
*
* {@link org.apache.commons.lang3.exception.ExceptionUtils#getStackTrace(Throwable)}
*/
private String exceptionStackTrace;
/**
* 异常发生的类全名
*
* {@link StackTraceElement#getClassName()}
*/
private String exceptionClassName;
/**
* 异常发生的类文件
*
* {@link StackTraceElement#getFileName()}
*/
private String exceptionFileName;
/**
* 异常发生的方法名
*
* {@link StackTraceElement#getMethodName()}
*/
private String exceptionMethodName;
/**
* 异常发生的方法所在行
*
* {@link StackTraceElement#getLineNumber()}
*/
private Integer exceptionLineNumber;
// ========== 处理相关字段 ==========
/**
* 处理状态
*
* 枚举 {@link ApiErrorLogProcessStatusEnum}
*/
private Integer processStatus;
/**
* 处理时间
*/
private LocalDateTime processTime;
/**
* 处理用户编号
*
* 关联 cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.user.SysUserDO.SysUserDO#getId()
*/
private Long processUserId;
}

View File

@@ -0,0 +1,26 @@
package com.tashow.cloud.infra.dal.mysql.codegen;
import com.tashow.cloud.mybatis.mybatis.core.mapper.BaseMapperX;
import com.tashow.cloud.mybatis.mybatis.core.query.LambdaQueryWrapperX;
import com.tashow.cloud.infra.dal.dataobject.codegen.CodegenColumnDO;
import com.tashow.cloud.infra.dal.dataobject.codegen.CodegenColumnDO;
import com.tashow.cloud.infra.dal.dataobject.codegen.CodegenColumnDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface CodegenColumnMapper extends BaseMapperX<CodegenColumnDO> {
default List<CodegenColumnDO> selectListByTableId(Long tableId) {
return selectList(new LambdaQueryWrapperX<CodegenColumnDO>()
.eq(CodegenColumnDO::getTableId, tableId)
.orderByAsc(CodegenColumnDO::getOrdinalPosition));
}
default void deleteListByTableId(Long tableId) {
delete(new LambdaQueryWrapperX<CodegenColumnDO>()
.eq(CodegenColumnDO::getTableId, tableId));
}
}

View File

@@ -0,0 +1,43 @@
package com.tashow.cloud.infra.dal.mysql.codegen;
import com.tashow.cloud.common.pojo.PageResult;
import com.tashow.cloud.mybatis.mybatis.core.mapper.BaseMapperX;
import com.tashow.cloud.mybatis.mybatis.core.query.LambdaQueryWrapperX;
import com.tashow.cloud.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO;
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.dal.dataobject.codegen.CodegenTableDO;
import com.tashow.cloud.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO;
import com.tashow.cloud.infra.dal.dataobject.codegen.CodegenTableDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface CodegenTableMapper extends BaseMapperX<CodegenTableDO> {
default CodegenTableDO selectByTableNameAndDataSourceConfigId(String tableName, Long dataSourceConfigId) {
return selectOne(CodegenTableDO::getTableName, tableName,
CodegenTableDO::getDataSourceConfigId, dataSourceConfigId);
}
default PageResult<CodegenTableDO> selectPage(CodegenTablePageReqVO pageReqVO) {
return selectPage(pageReqVO, new LambdaQueryWrapperX<CodegenTableDO>()
.likeIfPresent(CodegenTableDO::getTableName, pageReqVO.getTableName())
.likeIfPresent(CodegenTableDO::getTableComment, pageReqVO.getTableComment())
.likeIfPresent(CodegenTableDO::getClassName, pageReqVO.getClassName())
.betweenIfPresent(CodegenTableDO::getCreateTime, pageReqVO.getCreateTime())
.orderByDesc(CodegenTableDO::getUpdateTime)
);
}
default List<CodegenTableDO> selectListByDataSourceConfigId(Long dataSourceConfigId) {
return selectList(CodegenTableDO::getDataSourceConfigId, dataSourceConfigId);
}
default List<CodegenTableDO> selectListByTemplateTypeAndMasterTableId(Integer templateType, Long masterTableId) {
return selectList(CodegenTableDO::getTemplateType, templateType,
CodegenTableDO::getMasterTableId, masterTableId);
}
}

View File

@@ -0,0 +1,27 @@
package com.tashow.cloud.infra.dal.mysql.config;
import com.tashow.cloud.common.pojo.PageResult;
import com.tashow.cloud.mybatis.mybatis.core.mapper.BaseMapperX;
import com.tashow.cloud.mybatis.mybatis.core.query.LambdaQueryWrapperX;
import com.tashow.cloud.infra.dal.dataobject.config.ConfigDO;
import com.tashow.cloud.infra.controller.admin.config.vo.ConfigPageReqVO;
import com.tashow.cloud.infra.dal.dataobject.config.ConfigDO;
import com.tashow.cloud.infra.dal.dataobject.config.ConfigDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ConfigMapper extends BaseMapperX<ConfigDO> {
default ConfigDO selectByKey(String key) {
return selectOne(ConfigDO::getConfigKey, key);
}
default PageResult<ConfigDO> selectPage(ConfigPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<ConfigDO>()
.likeIfPresent(ConfigDO::getName, reqVO.getName())
.likeIfPresent(ConfigDO::getConfigKey, reqVO.getKey())
.eqIfPresent(ConfigDO::getType, reqVO.getType())
.betweenIfPresent(ConfigDO::getCreateTime, reqVO.getCreateTime()));
}
}

View File

@@ -0,0 +1,14 @@
package com.tashow.cloud.infra.dal.mysql.db;
import com.tashow.cloud.mybatis.mybatis.core.mapper.BaseMapperX;
import com.tashow.cloud.infra.dal.dataobject.db.DataSourceConfigDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 数据源配置 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface DataSourceConfigMapper extends BaseMapperX<DataSourceConfigDO> {
}

View File

@@ -0,0 +1,27 @@
package com.tashow.cloud.infra.dal.mysql.file;
import com.tashow.cloud.common.pojo.PageResult;
import com.tashow.cloud.mybatis.mybatis.core.mapper.BaseMapperX;
import com.tashow.cloud.mybatis.mybatis.core.query.LambdaQueryWrapperX;
import com.tashow.cloud.infra.dal.dataobject.file.FileConfigDO;
import com.tashow.cloud.infra.controller.admin.file.vo.config.FileConfigPageReqVO;
import com.tashow.cloud.infra.dal.dataobject.file.FileConfigDO;
import com.tashow.cloud.infra.dal.dataobject.file.FileConfigDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface FileConfigMapper extends BaseMapperX<FileConfigDO> {
default PageResult<FileConfigDO> selectPage(FileConfigPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<FileConfigDO>()
.likeIfPresent(FileConfigDO::getName, reqVO.getName())
.eqIfPresent(FileConfigDO::getStorage, reqVO.getStorage())
.betweenIfPresent(FileConfigDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(FileConfigDO::getId));
}
default FileConfigDO selectByMaster() {
return selectOne(FileConfigDO::getMaster, true);
}
}

View File

@@ -0,0 +1,27 @@
package com.tashow.cloud.infra.dal.mysql.file;
import com.tashow.cloud.infra.dal.dataobject.file.FileContentDO;
import com.tashow.cloud.infra.dal.dataobject.file.FileContentDO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.tashow.cloud.infra.dal.dataobject.file.FileContentDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface FileContentMapper extends BaseMapper<FileContentDO> {
default void deleteByConfigIdAndPath(Long configId, String path) {
this.delete(new LambdaQueryWrapper<FileContentDO>()
.eq(FileContentDO::getConfigId, configId)
.eq(FileContentDO::getPath, path));
}
default List<FileContentDO> selectListByConfigIdAndPath(Long configId, String path) {
return selectList(new LambdaQueryWrapper<FileContentDO>()
.eq(FileContentDO::getConfigId, configId)
.eq(FileContentDO::getPath, path));
}
}

View File

@@ -0,0 +1,28 @@
package com.tashow.cloud.infra.dal.mysql.file;
import com.tashow.cloud.common.pojo.PageResult;
import com.tashow.cloud.mybatis.mybatis.core.mapper.BaseMapperX;
import com.tashow.cloud.mybatis.mybatis.core.query.LambdaQueryWrapperX;
import com.tashow.cloud.infra.dal.dataobject.file.FileDO;
import com.tashow.cloud.infra.controller.admin.file.vo.file.FilePageReqVO;
import com.tashow.cloud.infra.dal.dataobject.file.FileDO;
import com.tashow.cloud.infra.dal.dataobject.file.FileDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 文件操作 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface FileMapper extends BaseMapperX<FileDO> {
default PageResult<FileDO> selectPage(FilePageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<FileDO>()
.likeIfPresent(FileDO::getPath, reqVO.getPath())
.likeIfPresent(FileDO::getType, reqVO.getType())
.betweenIfPresent(FileDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(FileDO::getId));
}
}

View File

@@ -0,0 +1,49 @@
package com.tashow.cloud.infra.dal.mysql.logger;
import com.tashow.cloud.common.pojo.PageResult;
import com.tashow.cloud.mybatis.mybatis.core.mapper.BaseMapperX;
import com.tashow.cloud.mybatis.mybatis.core.query.LambdaQueryWrapperX;
import com.tashow.cloud.infra.controller.admin.logger.vo.apiaccesslog.ApiAccessLogPageReqVO;
import com.tashow.cloud.infra.dal.dataobject.logger.ApiAccessLogDO;
import com.tashow.cloud.infra.controller.admin.logger.vo.apiaccesslog.ApiAccessLogPageReqVO;
import com.tashow.cloud.infra.dal.dataobject.logger.ApiAccessLogDO;
import com.tashow.cloud.infra.controller.admin.logger.vo.apiaccesslog.ApiAccessLogPageReqVO;
import com.tashow.cloud.infra.dal.dataobject.logger.ApiAccessLogDO;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
/**
* API 访问日志 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface ApiAccessLogMapper extends BaseMapperX<ApiAccessLogDO> {
default PageResult<ApiAccessLogDO> selectPage(ApiAccessLogPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<ApiAccessLogDO>()
.eqIfPresent(ApiAccessLogDO::getUserId, reqVO.getUserId())
.eqIfPresent(ApiAccessLogDO::getUserType, reqVO.getUserType())
.eqIfPresent(ApiAccessLogDO::getApplicationName, reqVO.getApplicationName())
.likeIfPresent(ApiAccessLogDO::getRequestUrl, reqVO.getRequestUrl())
.betweenIfPresent(ApiAccessLogDO::getBeginTime, reqVO.getBeginTime())
.geIfPresent(ApiAccessLogDO::getDuration, reqVO.getDuration())
.eqIfPresent(ApiAccessLogDO::getResultCode, reqVO.getResultCode())
.orderByDesc(ApiAccessLogDO::getId)
);
}
/**
* 物理删除指定时间之前的日志
*
* @param createTime 最大时间
* @param limit 删除条数,防止一次删除太多
* @return 删除条数
*/
@Delete("DELETE FROM infra_api_access_log WHERE create_time < #{createTime} LIMIT #{limit}")
Integer deleteByCreateTimeLt(@Param("createTime") LocalDateTime createTime, @Param("limit") Integer limit);
}

View File

@@ -0,0 +1,48 @@
package com.tashow.cloud.infra.dal.mysql.logger;
import com.tashow.cloud.common.pojo.PageResult;
import com.tashow.cloud.mybatis.mybatis.core.mapper.BaseMapperX;
import com.tashow.cloud.mybatis.mybatis.core.query.LambdaQueryWrapperX;
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.controller.admin.logger.vo.apierrorlog.ApiErrorLogPageReqVO;
import com.tashow.cloud.infra.dal.dataobject.logger.ApiErrorLogDO;
import com.tashow.cloud.infra.controller.admin.logger.vo.apierrorlog.ApiErrorLogPageReqVO;
import com.tashow.cloud.infra.dal.dataobject.logger.ApiErrorLogDO;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
/**
* API 错误日志 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface ApiErrorLogMapper extends BaseMapperX<ApiErrorLogDO> {
default PageResult<ApiErrorLogDO> selectPage(ApiErrorLogPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<ApiErrorLogDO>()
.eqIfPresent(ApiErrorLogDO::getUserId, reqVO.getUserId())
.eqIfPresent(ApiErrorLogDO::getUserType, reqVO.getUserType())
.eqIfPresent(ApiErrorLogDO::getApplicationName, reqVO.getApplicationName())
.likeIfPresent(ApiErrorLogDO::getRequestUrl, reqVO.getRequestUrl())
.betweenIfPresent(ApiErrorLogDO::getExceptionTime, reqVO.getExceptionTime())
.eqIfPresent(ApiErrorLogDO::getProcessStatus, reqVO.getProcessStatus())
.orderByDesc(ApiErrorLogDO::getId)
);
}
/**
* 物理删除指定时间之前的日志
*
* @param createTime 最大时间
* @param limit 删除条数,防止一次删除太多
* @return 删除条数
*/
@Delete("DELETE FROM infra_api_error_log WHERE create_time < #{createTime} LIMIT #{limit}")
Integer deleteByCreateTimeLt(@Param("createTime") LocalDateTime createTime, @Param("limit")Integer limit);
}

View File

@@ -0,0 +1,29 @@
package com.tashow.cloud.infra.enums.codegen;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 代码生成器的字段 HTML 展示枚举
*/
@AllArgsConstructor
@Getter
public enum CodegenColumnHtmlTypeEnum {
INPUT("input"), // 文本框
TEXTAREA("textarea"), // 文本域
SELECT("select"), // 下拉框
RADIO("radio"), // 单选框
CHECKBOX("checkbox"), // 复选框
DATETIME("datetime"), // 日期控件
IMAGE_UPLOAD("imageUpload"), // 上传图片
FILE_UPLOAD("fileUpload"), // 上传文件
EDITOR("editor"), // 富文本控件
;
/**
* 条件
*/
private final String type;
}

View File

@@ -0,0 +1,27 @@
package com.tashow.cloud.infra.enums.codegen;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 代码生成器的字段过滤条件枚举
*/
@AllArgsConstructor
@Getter
public enum CodegenColumnListConditionEnum {
EQ("="),
NE("!="),
GT(">"),
GTE(">="),
LT("<"),
LTE("<="),
LIKE("LIKE"),
BETWEEN("BETWEEN");
/**
* 条件
*/
private final String condition;
}

View File

@@ -0,0 +1,25 @@
package com.tashow.cloud.infra.enums.codegen;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 代码生成的前端类型枚举
*
* @author 芋道源码
*/
@AllArgsConstructor
@Getter
public enum CodegenFrontTypeEnum {
VUE2(10), // Vue2 Element UI 标准模版
VUE3(20), // Vue3 Element Plus 标准模版
VUE3_VBEN(30), // Vue3 VBEN 模版
;
/**
* 类型
*/
private final Integer type;
}

View File

@@ -0,0 +1,41 @@
package com.tashow.cloud.infra.enums.codegen;
import lombok.AllArgsConstructor;
import lombok.Getter;
import static cn.hutool.core.util.ArrayUtil.*;
/**
* 代码生成的场景枚举
*
* @author 芋道源码
*/
@AllArgsConstructor
@Getter
public enum CodegenSceneEnum {
ADMIN(1, "管理后台", "admin", ""),
APP(2, "用户 APP", "app", "App");
/**
* 场景
*/
private final Integer scene;
/**
* 场景名
*/
private final String name;
/**
* 基础包名
*/
private final String basePackage;
/**
* Controller 和 VO 类的前缀
*/
private final String prefixClass;
public static CodegenSceneEnum valueOf(Integer scene) {
return firstMatch(sceneEnum -> sceneEnum.getScene().equals(scene), values());
}
}

View File

@@ -0,0 +1,53 @@
package com.tashow.cloud.infra.enums.codegen;
import com.tashow.cloud.common.util.object.ObjectUtils;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Objects;
/**
* 代码生成模板类型
*
* @author 芋道源码
*/
@AllArgsConstructor
@Getter
public enum CodegenTemplateTypeEnum {
ONE(1), // 单表(增删改查)
TREE(2), // 树表(增删改查)
MASTER_NORMAL(10), // 主子表 - 主表 - 普通模式
MASTER_ERP(11), // 主子表 - 主表 - ERP 模式
MASTER_INNER(12), // 主子表 - 主表 - 内嵌模式
SUB(15), // 主子表 - 子表
;
/**
* 类型
*/
private final Integer type;
/**
* 是否为主表
*
* @param type 类型
* @return 是否主表
*/
public static boolean isMaster(Integer type) {
return ObjectUtils.equalsAny(type,
MASTER_NORMAL.type, MASTER_ERP.type, MASTER_INNER.type);
}
/**
* 是否为树表
*
* @param type 类型
* @return 是否树表
*/
public static boolean isTree(Integer type) {
return Objects.equals(type, TREE.type);
}
}

View File

@@ -0,0 +1,21 @@
package com.tashow.cloud.infra.enums.config;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum ConfigTypeEnum {
/**
* 系统配置
*/
SYSTEM(1),
/**
* 自定义配置
*/
CUSTOM(2);
private final Integer type;
}

View File

@@ -0,0 +1,28 @@
package com.tashow.cloud.infra.enums.logger;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* API 异常数据的处理状态
*
* @author 芋道源码
*/
@AllArgsConstructor
@Getter
public enum ApiErrorLogProcessStatusEnum {
INIT(0, "未处理"),
DONE(1, "已处理"),
IGNORE(2, "已忽略");
/**
* 状态
*/
private final Integer status;
/**
* 资源类型名
*/
private final String name;
}

View File

@@ -0,0 +1,4 @@
/**
* 占位
*/
package com.tashow.cloud.infra.enums;

View File

@@ -0,0 +1,204 @@
package com.tashow.cloud.infra.framework;
import com.alibaba.otter.canal.client.CanalConnector;
import com.alibaba.otter.canal.client.CanalConnectors;
import com.alibaba.otter.canal.protocol.CanalEntry.*;
import com.alibaba.otter.canal.protocol.Message;
import com.google.protobuf.InvalidProtocolBufferException;
import org.springframework.stereotype.Component;
import java.net.InetSocketAddress;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
@Component
public class CanalClient {
//sql队列
private Queue<String> SQL_QUEUE = new ConcurrentLinkedQueue<>();
/**
* canal入库方法
*/
public void run() {
CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress("43.139.42.137",
11111), "example", "", "");
int batchSize = 1000;
try {
connector.connect();
// connector.subscribe(".*\\..*");
connector.subscribe("tashow-platform");
connector.rollback();
try {
while (true) {
//尝试从master那边拉去数据batchSize条记录有多少取多少
Message message = connector.getWithoutAck(batchSize);
long batchId = message.getId();
int size = message.getEntries().size();
if (batchId == -1 || size == 0) {
Thread.sleep(1000);
} else {
dataHandle(message.getEntries());
}
connector.ack(batchId);
//当队列里面堆积的sql大于一定数值的时候就模拟执行
if (SQL_QUEUE.size() >= 1) {
executeQueueSql();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
} finally {
connector.disconnect();
}
}
/**
* 模拟执行队列里面的sql语句
*/
public void executeQueueSql() {
int size = SQL_QUEUE.size();
for (int i = 0; i < size; i++) {
String sql = SQL_QUEUE.poll();
System.out.println("[sql]----> " + sql);
this.execute(sql);
}
}
/**
* 数据处理
*
* @param entrys
*/
private void dataHandle(List<Entry> entrys) throws InvalidProtocolBufferException {
for (Entry entry : entrys) {
if(entry.getHeader().getSchemaName().equals("hc")){
return;
}
if (EntryType.ROWDATA == entry.getEntryType()) {
RowChange rowChange = RowChange.parseFrom(entry.getStoreValue());
EventType eventType = rowChange.getEventType();
if (eventType == EventType.DELETE) {
saveDeleteSql(entry);
} else if (eventType == EventType.UPDATE) {
saveUpdateSql(entry);
} else if (eventType == EventType.INSERT) {
saveInsertSql(entry);
}
}
}
}
/**
* 保存更新语句
*
* @param entry
*/
private void saveUpdateSql(Entry entry) {
try {
RowChange rowChange = RowChange.parseFrom(entry.getStoreValue());
List<RowData> rowDatasList = rowChange.getRowDatasList();
for (RowData rowData : rowDatasList) {
List<Column> newColumnList = rowData.getAfterColumnsList();
StringBuffer sql = new StringBuffer("update " + entry.getHeader().getTableName() + " set ");
for (int i = 0; i < newColumnList.size(); i++) {
sql.append(" " + newColumnList.get(i).getName()
+ " = '" + newColumnList.get(i).getValue() + "'");
if (i != newColumnList.size() - 1) {
sql.append(",");
}
}
sql.append(" where ");
List<Column> oldColumnList = rowData.getBeforeColumnsList();
for (Column column : oldColumnList) {
if (column.getIsKey()) {
//暂时只支持单一主键
sql.append(column.getName() + "=" + column.getValue());
break;
}
}
SQL_QUEUE.add(sql.toString());
}
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
}
/**
* 保存删除语句
*
* @param entry
*/
private void saveDeleteSql(Entry entry) {
try {
RowChange rowChange = RowChange.parseFrom(entry.getStoreValue());
List<RowData> rowDatasList = rowChange.getRowDatasList();
for (RowData rowData : rowDatasList) {
List<Column> columnList = rowData.getBeforeColumnsList();
StringBuffer sql = new StringBuffer("delete from " + entry.getHeader().getTableName() + " where ");
for (Column column : columnList) {
if (column.getIsKey()) {
//暂时只支持单一主键
sql.append(column.getName() + "=" + column.getValue());
break;
}
}
SQL_QUEUE.add(sql.toString());
}
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
}
/**
* 保存插入语句
*
* @param entry
*/
private void saveInsertSql(Entry entry) {
try {
RowChange rowChange = RowChange.parseFrom(entry.getStoreValue());
List<RowData> rowDatasList = rowChange.getRowDatasList();
for (RowData rowData : rowDatasList) {
List<Column> columnList = rowData.getAfterColumnsList();
StringBuffer sql = new StringBuffer("insert into " + entry.getHeader().getTableName() + " (");
for (int i = 0; i < columnList.size(); i++) {
sql.append(columnList.get(i).getName());
if (i != columnList.size() - 1) {
sql.append(",");
}
}
sql.append(") VALUES (");
for (int i = 0; i < columnList.size(); i++) {
sql.append("'" + columnList.get(i).getValue() + "'");
if (i != columnList.size() - 1) {
sql.append(",");
}
}
sql.append(")");
SQL_QUEUE.add(sql.toString());
}
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
}
/**
* 入库
* @param sql
*/
public void execute(String sql) {
System.out.println("sql======="+sql);
}
}

View File

@@ -0,0 +1,9 @@
package com.tashow.cloud.infra.framework.codegen.config;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(CodegenProperties.class)
public class CodegenConfiguration {
}

View File

@@ -0,0 +1,45 @@
package com.tashow.cloud.infra.framework.codegen.config;
import com.tashow.cloud.infra.enums.codegen.CodegenFrontTypeEnum;
import com.tashow.cloud.infra.enums.codegen.CodegenFrontTypeEnum;
import com.tashow.cloud.infra.enums.codegen.CodegenFrontTypeEnum;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.Collection;
@ConfigurationProperties(prefix = "tashow.codegen")
@Validated
@Data
public class CodegenProperties {
/**
* 生成的 Java 代码的基础包
*/
@NotNull(message = "Java 代码的基础包不能为空")
private String basePackage;
/**
* 数据库名数组
*/
@NotEmpty(message = "数据库不能为空")
private Collection<String> dbSchemas;
/**
* 代码生成的前端类型(默认)
*
* 枚举 {@link CodegenFrontTypeEnum#getType()}
*/
@NotNull(message = "代码生成的前端类型不能为空")
private Integer frontType;
/**
* 是否生成单元测试
*/
@NotNull(message = "是否生成单元测试不能为空")
private Boolean unitTestEnable;
}

View File

@@ -0,0 +1,4 @@
/**
* 代码生成器
*/
package com.tashow.cloud.infra.framework.codegen;

View File

@@ -0,0 +1,25 @@
package com.tashow.cloud.infra.framework.file.config;
import com.tashow.cloud.infra.framework.file.core.client.FileClientFactory;
import com.tashow.cloud.infra.framework.file.core.client.FileClientFactoryImpl;
import com.tashow.cloud.infra.framework.file.core.client.FileClientFactory;
import com.tashow.cloud.infra.framework.file.core.client.FileClientFactoryImpl;
import com.tashow.cloud.infra.framework.file.core.client.FileClientFactory;
import com.tashow.cloud.infra.framework.file.core.client.FileClientFactoryImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 文件配置类
*
* @author 芋道源码
*/
@Configuration(proxyBeanMethods = false)
public class YudaoFileAutoConfiguration {
@Bean
public FileClientFactory fileClientFactory() {
return new FileClientFactoryImpl();
}
}

View File

@@ -0,0 +1,69 @@
package com.tashow.cloud.infra.framework.file.core.client;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
/**
* 文件客户端的抽象类,提供模板方法,减少子类的冗余代码
*
* @author 芋道源码
*/
@Slf4j
public abstract class AbstractFileClient<Config extends FileClientConfig> implements FileClient {
/**
* 配置编号
*/
private final Long id;
/**
* 文件配置
*/
protected Config config;
public AbstractFileClient(Long id, Config config) {
this.id = id;
this.config = config;
}
/**
* 初始化
*/
public final void init() {
doInit();
log.debug("[init][配置({}) 初始化完成]", config);
}
/**
* 自定义初始化
*/
protected abstract void doInit();
public final void refresh(Config config) {
// 判断是否更新
if (config.equals(this.config)) {
return;
}
log.info("[refresh][配置({})发生变化,重新初始化]", config);
this.config = config;
// 初始化
this.init();
}
@Override
public Long getId() {
return id;
}
/**
* 格式化文件的 URL 访问地址
* 使用场景local、ftp、db通过 FileController 的 getFile 来获取文件内容
*
* @param domain 自定义域名
* @param path 文件路径
* @return URL 访问地址
*/
protected String formatFileUrl(String domain, String path) {
return StrUtil.format("{}/admin-api/infra/file/{}/get/{}", domain, getId(), path);
}
}

View File

@@ -0,0 +1,57 @@
package com.tashow.cloud.infra.framework.file.core.client;
import com.tashow.cloud.infra.framework.file.core.client.s3.FilePresignedUrlRespDTO;
import com.tashow.cloud.infra.framework.file.core.client.s3.FilePresignedUrlRespDTO;
import com.tashow.cloud.infra.framework.file.core.client.s3.FilePresignedUrlRespDTO;
/**
* 文件客户端
*
* @author 芋道源码
*/
public interface FileClient {
/**
* 获得客户端编号
*
* @return 客户端编号
*/
Long getId();
/**
* 上传文件
*
* @param content 文件流
* @param path 相对路径
* @return 完整路径,即 HTTP 访问地址
* @throws Exception 上传文件时,抛出 Exception 异常
*/
String upload(byte[] content, String path, String type) throws Exception;
/**
* 删除文件
*
* @param path 相对路径
* @throws Exception 删除文件时,抛出 Exception 异常
*/
void delete(String path) throws Exception;
/**
* 获得文件的内容
*
* @param path 相对路径
* @return 文件的内容
*/
byte[] getContent(String path) throws Exception;
/**
* 获得文件预签名地址
*
* @param path 相对路径
* @return 文件预签名地址
*/
default FilePresignedUrlRespDTO getPresignedObjectUrl(String path) throws Exception {
throw new UnsupportedOperationException("不支持的操作");
}
}

View File

@@ -0,0 +1,16 @@
package com.tashow.cloud.infra.framework.file.core.client;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
/**
* 文件客户端的配置
* 不同实现的客户端,需要不同的配置,通过子类来定义
*
* @author 芋道源码
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
// @JsonTypeInfo 注解的作用Jackson 多态
// 1. 序列化到时数据库时,增加 @class 属性。
// 2. 反序列化到内存对象时,通过 @class 属性,可以创建出正确的类型
public interface FileClientConfig {
}

View File

@@ -0,0 +1,26 @@
package com.tashow.cloud.infra.framework.file.core.client;
import com.tashow.cloud.infra.framework.file.core.enums.FileStorageEnum;
import com.tashow.cloud.infra.framework.file.core.enums.FileStorageEnum;
import com.tashow.cloud.infra.framework.file.core.enums.FileStorageEnum;
public interface FileClientFactory {
/**
* 获得文件客户端
*
* @param configId 配置编号
* @return 文件客户端
*/
FileClient getFileClient(Long configId);
/**
* 创建文件客户端
*
* @param configId 配置编号
* @param storage 存储器的枚举 {@link FileStorageEnum}
* @param config 文件配置
*/
<Config extends FileClientConfig> void createOrUpdateFileClient(Long configId, Integer storage, Config config);
}

View File

@@ -0,0 +1,58 @@
package com.tashow.cloud.infra.framework.file.core.client;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ReflectUtil;
import com.tashow.cloud.infra.framework.file.core.enums.FileStorageEnum;
import com.tashow.cloud.infra.framework.file.core.enums.FileStorageEnum;
import com.tashow.cloud.infra.framework.file.core.enums.FileStorageEnum;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* 文件客户端的工厂实现类
*
* @author 芋道源码
*/
@Slf4j
public class FileClientFactoryImpl implements FileClientFactory {
/**
* 文件客户端 Map
* key配置编号
*/
private final ConcurrentMap<Long, AbstractFileClient<?>> clients = new ConcurrentHashMap<>();
@Override
public FileClient getFileClient(Long configId) {
AbstractFileClient<?> client = clients.get(configId);
if (client == null) {
log.error("[getFileClient][配置编号({}) 找不到客户端]", configId);
}
return client;
}
@Override
@SuppressWarnings("unchecked")
public <Config extends FileClientConfig> void createOrUpdateFileClient(Long configId, Integer storage, Config config) {
AbstractFileClient<Config> client = (AbstractFileClient<Config>) clients.get(configId);
if (client == null) {
client = this.createFileClient(configId, storage, config);
client.init();
clients.put(client.getId(), client);
} else {
client.refresh(config);
}
}
@SuppressWarnings("unchecked")
private <Config extends FileClientConfig> AbstractFileClient<Config> createFileClient(
Long configId, Integer storage, Config config) {
FileStorageEnum storageEnum = FileStorageEnum.getByStorage(storage);
Assert.notNull(storageEnum, String.format("文件配置(%s) 为空", storageEnum));
// 创建客户端
return (AbstractFileClient<Config>) ReflectUtil.newInstance(storageEnum.getClientClass(), configId, config);
}
}

View File

@@ -0,0 +1,59 @@
package com.tashow.cloud.infra.framework.file.core.client.db;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.tashow.cloud.infra.dal.dataobject.file.FileContentDO;
import com.tashow.cloud.infra.dal.mysql.file.FileContentMapper;
import com.tashow.cloud.infra.dal.dataobject.file.FileContentDO;
import com.tashow.cloud.infra.dal.mysql.file.FileContentMapper;
import com.tashow.cloud.infra.framework.file.core.client.AbstractFileClient;
import com.tashow.cloud.infra.dal.dataobject.file.FileContentDO;
import com.tashow.cloud.infra.dal.mysql.file.FileContentMapper;
import java.util.Comparator;
import java.util.List;
/**
* 基于 DB 存储的文件客户端的配置类
*
* @author 芋道源码
*/
public class DBFileClient extends AbstractFileClient<DBFileClientConfig> {
private FileContentMapper fileContentMapper;
public DBFileClient(Long id, DBFileClientConfig config) {
super(id, config);
}
@Override
protected void doInit() {
fileContentMapper = SpringUtil.getBean(FileContentMapper.class);
}
@Override
public String upload(byte[] content, String path, String type) {
FileContentDO contentDO = new FileContentDO().setConfigId(getId())
.setPath(path).setContent(content);
fileContentMapper.insert(contentDO);
// 拼接返回路径
return super.formatFileUrl(config.getDomain(), path);
}
@Override
public void delete(String path) {
fileContentMapper.deleteByConfigIdAndPath(getId(), path);
}
@Override
public byte[] getContent(String path) {
List<FileContentDO> list = fileContentMapper.selectListByConfigIdAndPath(getId(), path);
if (CollUtil.isEmpty(list)) {
return null;
}
// 排序后,拿 id 最大的,即最后上传的
list.sort(Comparator.comparing(FileContentDO::getId));
return CollUtil.getLast(list).getContent();
}
}

Some files were not shown because too many files have changed in this diff Show More