1. 统一包名修改

This commit is contained in:
chenbowen
2025-09-22 11:55:27 +08:00
parent a001fc8f16
commit 0d46897482
2739 changed files with 512 additions and 512 deletions

25
zt-module-infra/pom.xml Normal file
View File

@@ -0,0 +1,25 @@
<?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">
<parent>
<groupId>com.zt.plat</groupId>
<artifactId>zt</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<modules>
<module>zt-module-infra-api</module>
<module>zt-module-infra-server</module>
</modules>
<artifactId>zt-module-infra</artifactId>
<packaging>pom</packaging>
<name>${project.artifactId}</name>
<description>
infra 模块,主要提供两块能力:
1. 我们放基础设施的运维与管理,支撑上层的通用与核心业务。 例如说:定时任务的管理、服务器的信息等等
2. 研发工具,提升研发效率与质量。 例如说:代码生成器、接口文档等等
</description>
</project>

View File

@@ -0,0 +1,47 @@
<?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">
<parent>
<groupId>com.zt.plat</groupId>
<artifactId>zt-module-infra</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>zt-module-infra-api</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>
infra 模块 API暴露给其它模块调用
</description>
<dependencies>
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-common</artifactId>
</dependency>
<!-- Web 相关 -->
<dependency>
<groupId>org.springdoc</groupId> <!-- 接口文档:使用最新版本的 Swagger 模型 -->
<artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
<scope>provided</scope>
</dependency>
<!-- 参数校验 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<optional>true</optional>
</dependency>
<!-- RPC 远程调用相关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,70 @@
package com.zt.plat.module.infra.api.businessfile;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.module.infra.api.businessfile.dto.BusinessFilePageReqDTO;
import com.zt.plat.module.infra.api.businessfile.dto.BusinessFileRespDTO;
import com.zt.plat.module.infra.api.businessfile.dto.BusinessFileSaveReqDTO;
import com.zt.plat.module.infra.api.businessfile.dto.BusinessFileWithUrlRespDTO;
import com.zt.plat.module.infra.enums.ApiConstants;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* @author chenbowen
*/
@FeignClient(name = ApiConstants.NAME)
@Tag(name = "RPC 服务 - 业务附件关联")
public interface BusinessFileApi {
String PREFIX = ApiConstants.PREFIX + "/business-file";
@PostMapping(PREFIX + "/create")
@Operation(summary = "创建业务附件关联")
CommonResult<Long> createBusinessFile(@Valid @RequestBody BusinessFileSaveReqDTO createReqDTO);
@PostMapping(PREFIX + "/batch-create")
@Operation(summary = "批量新增业务附件关联")
CommonResult<List<Long>> batchCreateBusinessFile(@RequestBody List<BusinessFileSaveReqDTO> createReqDTOList);
@PostMapping(PREFIX + "/update")
@Operation(summary = "更新业务附件关联")
CommonResult<Boolean> updateBusinessFile(@Valid @RequestBody BusinessFileSaveReqDTO updateReqDTO);
@DeleteMapping(PREFIX + "/delete")
@Operation(summary = "删除业务附件关联")
@Parameter(name = "id", description = "编号", required = true)
CommonResult<Boolean> deleteBusinessFile(@RequestParam("id") Long id);
@DeleteMapping(PREFIX + "/delete-list")
@Operation(summary = "批量删除业务附件关联")
@Parameter(name = "ids", description = "编号列表", required = true)
CommonResult<Boolean> deleteBusinessFileList(@RequestParam("ids") List<Long> ids);
@GetMapping(PREFIX + "/get")
@Operation(summary = "获得业务附件关联")
@Parameter(name = "id", description = "编号", required = true)
CommonResult<BusinessFileRespDTO> getBusinessFile(@RequestParam("id") Long id);
@PostMapping(PREFIX + "/page")
@Operation(summary = "获得业务附件关联分页")
CommonResult<PageResult<BusinessFileRespDTO>> getBusinessFilePage(@RequestBody BusinessFilePageReqDTO pageReqDTO);
@PostMapping(PREFIX + "/page-with-url")
@Operation(summary = "获得业务附件关联分页带URL")
CommonResult<PageResult<BusinessFileWithUrlRespDTO>> getBusinessFilePageWithUrl(@RequestBody BusinessFilePageReqDTO pageReqDTO);
@DeleteMapping(PREFIX + "/delete-by-business")
@Operation(summary = "根据业务Id和来源删除业务附件关联")
@Parameter(name = "businessId", description = "业务Id", required = true)
@Parameter(name = "source", description = "业务来源", required = true)
CommonResult<Boolean> deleteBusinessFileByBusinessIdAndSource(@RequestParam("businessId") Long businessId,
@RequestParam("source") String source);
}

View File

@@ -0,0 +1,46 @@
package com.zt.plat.module.infra.api.businessfile.dto;
import com.zt.plat.framework.common.pojo.PageParam;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 业务附件关联分页查询 DTO
*
* @author 后台管理
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class BusinessFilePageReqDTO extends PageParam implements Serializable {
/**
* 业务Id
*/
private Long businessId;
/**
* 业务编码
*/
private String businessCode;
/**
* 文件名
*/
private String fileName;
/**
* 业务来源
*/
private String source;
/**
* 创建时间
*/
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,62 @@
package com.zt.plat.module.infra.api.businessfile.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 业务附件关联响应 DTO
*
* @author 后台管理
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BusinessFileRespDTO implements Serializable {
/**
* 编号
*/
private Long id;
/**
* 业务Id
*/
private Long businessId;
/**
* 业务编码
*/
private String businessCode;
/**
* 文件名
*/
private String fileName;
/**
* 文件Id
*/
private Long fileId;
/**
* 业务来源
*/
private String source;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,29 @@
package com.zt.plat.module.infra.api.businessfile.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 业务附件关联保存请求 DTO
* @author chenbowen
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BusinessFileSaveReqDTO implements Serializable {
/** 业务Id */
private Long businessId;
/** 业务编码 */
private String businessCode;
/** 文件名 */
private String fileName;
private Long fileId;
/** 业务来源 */
private String source;
}

View File

@@ -0,0 +1,49 @@
package com.zt.plat.module.infra.api.businessfile.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
/**
* 业务附件关联带URL响应 DTO
*
* @author 后台管理
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class BusinessFileWithUrlRespDTO extends BusinessFileRespDTO {
/**
* 文件路径
*/
private String filePath;
/**
* 文件 URL
*/
private String fileUrl;
/**
* 附件预览地址
*/
private String previewUrl;
/**
* 是否加密
*/
private Boolean isEncrypted;
/**
* 文件MIME类型
*/
private String fileType;
/**
* 文件大小
*/
private Integer fileSize;
}

View File

@@ -0,0 +1,21 @@
package com.zt.plat.module.infra.api.config;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.module.infra.enums.ApiConstants;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@Tag(name = "RPC 服务 - 参数配置")
public interface ConfigApi {
String PREFIX = ApiConstants.PREFIX + "/config";
@GetMapping(PREFIX + "/get-value-by-key")
@Operation(summary = "根据参数键查询参数值")
CommonResult<String> getConfigValueByKey(@RequestParam("key") String key);
}

View File

@@ -0,0 +1,59 @@
package com.zt.plat.module.infra.api.file;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.module.infra.api.file.dto.FileCreateReqDTO;
import com.zt.plat.module.infra.enums.ApiConstants;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@Tag(name = "RPC 服务 - 文件")
public interface FileApi {
String PREFIX = ApiConstants.PREFIX + "/file";
/**
* 保存文件,并返回文件的访问路径
*
* @param content 文件内容
* @return 文件路径
*/
default String createFile(byte[] content) {
return createFile(content, null, null, null);
}
/**
* 保存文件,并返回文件的访问路径
*
* @param content 文件内容
* @param name 文件名称,允许空
* @return 文件路径
*/
default String createFile(byte[] content, String name) {
return createFile(content, name, null, null);
}
/**
* 保存文件,并返回文件的访问路径
*
* @param content 文件内容
* @param name 文件名称,允许空
* @param directory 目录,允许空
* @param type 文件的 MIME 类型,允许空
* @return 文件路径
*/
default String createFile(@NotEmpty(message = "文件内容不能为空") byte[] content,
String name, String directory, String type) {
return createFile(new FileCreateReqDTO().setName(name).setDirectory(directory).setType(type).setContent(content)).getCheckedData();
}
@PostMapping(PREFIX + "/create")
@Operation(summary = "保存文件,并返回文件的访问路径")
CommonResult<String> createFile(@Valid @RequestBody FileCreateReqDTO createReqDTO);
}

View File

@@ -0,0 +1,25 @@
package com.zt.plat.module.infra.api.file.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotEmpty;
@Schema(description = "RPC 服务 - 文件创建 Request DTO")
@Data
public class FileCreateReqDTO {
@Schema(description = "原文件名称", example = "xxx.png")
private String name;
@Schema(description = "文件目录", example = "xxx")
private String directory;
@Schema(description = "文件的 MIME 类型", example = "image/png")
private String type;
@Schema(description = "文件内容", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "文件内容不能为空")
private byte[] content;
}

View File

@@ -0,0 +1,4 @@
/**
* infra API 包,定义暴露给其它模块的 API
*/
package com.zt.plat.module.infra.api;

View File

@@ -0,0 +1,74 @@
package com.zt.plat.module.infra.api.websocket;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.util.json.JsonUtils;
import com.zt.plat.module.infra.api.websocket.dto.WebSocketSendReqDTO;
import com.zt.plat.module.infra.enums.ApiConstants;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import jakarta.validation.Valid;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@Tag(name = "RPC 服务 - WebSocket 发送器的") // 对 WebSocketMessageSender 进行封装,提供给其它模块使用
public interface WebSocketSenderApi {
String PREFIX = ApiConstants.PREFIX + "/websocket";
@PostMapping(PREFIX + "/send")
@Operation(summary = "发送 WebSocket 消息")
CommonResult<Boolean> send(@Valid @RequestBody WebSocketSendReqDTO message);
/**
* 发送消息给指定用户
*
* @param userType 用户类型
* @param userId 用户编号
* @param messageType 消息类型
* @param messageContent 消息内容JSON 格式
*/
default void send(Integer userType, Long userId, String messageType, String messageContent) {
send(new WebSocketSendReqDTO().setUserType(userType).setUserId(userId)
.setMessageType(messageType).setMessageContent(messageContent)).checkError();
}
/**
* 发送消息给指定用户类型
*
* @param userType 用户类型
* @param messageType 消息类型
* @param messageContent 消息内容JSON 格式
*/
default void send(Integer userType, String messageType, String messageContent) {
send(new WebSocketSendReqDTO().setUserType(userType)
.setMessageType(messageType).setMessageContent(messageContent)).checkError();
}
/**
* 发送消息给指定 Session
*
* @param sessionId Session 编号
* @param messageType 消息类型
* @param messageContent 消息内容JSON 格式
*/
default void send(String sessionId, String messageType, String messageContent) {
send(new WebSocketSendReqDTO().setSessionId(sessionId)
.setMessageType(messageType).setMessageContent(messageContent)).checkError();
}
default void sendObject(Integer userType, Long userId, String messageType, Object messageContent) {
send(userType, userId, messageType, JsonUtils.toJsonString(messageContent));
}
default void sendObject(Integer userType, String messageType, Object messageContent) {
send(userType, messageType, JsonUtils.toJsonString(messageContent));
}
default void sendObject(String sessionId, String messageType, Object messageContent) {
send(sessionId, messageType, JsonUtils.toJsonString(messageContent));
}
}

View File

@@ -0,0 +1,26 @@
package com.zt.plat.module.infra.api.websocket.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotEmpty;
@Schema(description = "RPC 服务 - WebSocket 消息发送 Request DTO")
@Data
public class WebSocketSendReqDTO {
@Schema(description = "Session 编号", example = "abc")
private String sessionId;
@Schema(description = "用户编号", example = "1024")
private Long userId;
@Schema(description = "用户类型", example = "1")
private Integer userType;
@Schema(description = "消息类型", example = "demo-message")
@NotEmpty(message = "消息类型不能为空")
private String messageType;
@Schema(description = "消息内容", example = "{\"name\":\"李四\"}}")
@NotEmpty(message = "消息内容不能为空")
private String messageContent;
}

View File

@@ -0,0 +1,23 @@
package com.zt.plat.module.infra.enums;
import com.zt.plat.framework.common.enums.RpcConstants;
/**
* API 相关的枚举
*
* @author ZT
*/
public class ApiConstants {
/**
* 服务名
*
* 注意,需要保证和 spring.application.name 保持一致
*/
public static final String NAME = "infra-server";
public static final String PREFIX = RpcConstants.RPC_API_PREFIX + "/infra";
public static final String VERSION = "1.0.0";
}

View File

@@ -0,0 +1,22 @@
package com.zt.plat.module.infra.enums;
/**
* Infra 字典类型的枚举类
*
* @author ZT
*/
public interface DictTypeConstants {
String USER_TYPE = "user_type"; // 用户类型
String JOB_STATUS = "infra_job_status"; // 定时任务状态的枚举
String JOB_LOG_STATUS = "infra_job_log_status"; // 定时任务日志状态的枚举
String API_ERROR_LOG_PROCESS_STATUS = "infra_api_error_log_process_status"; // API 错误日志的处理状态的枚举
String CONFIG_TYPE = "infra_config_type"; // 参数配置类型
String BOOLEAN_STRING = "infra_boolean_string"; // Boolean 是否类型
String OPERATE_TYPE = "infra_operate_type"; // 操作类型
}

View File

@@ -0,0 +1,90 @@
package com.zt.plat.module.infra.enums;
import com.zt.plat.framework.common.exception.ErrorCode;
/**
* Infra 错误码枚举类
*
* infra 系统,使用 1-001-000-000 段
*/
public interface ErrorCodeConstants {
// ========== 参数配置 1-001-000-000 ==========
ErrorCode CONFIG_NOT_EXISTS = new ErrorCode(1_001_000_001, "参数配置不存在");
ErrorCode CONFIG_KEY_DUPLICATE = new ErrorCode(1_001_000_002, "参数配置 key 重复");
ErrorCode CONFIG_CAN_NOT_DELETE_SYSTEM_TYPE = new ErrorCode(1_001_000_003, "不能删除类型为系统内置的参数配置");
ErrorCode CONFIG_GET_VALUE_ERROR_IF_VISIBLE = new ErrorCode(1_001_000_004, "获取参数配置失败,原因:不允许获取不可见配置");
// ========== 定时任务 1-001-001-000 ==========
ErrorCode JOB_NOT_EXISTS = new ErrorCode(1_001_001_000, "定时任务不存在");
ErrorCode JOB_HANDLER_EXISTS = new ErrorCode(1_001_001_001, "定时任务的处理器已经存在");
ErrorCode JOB_CHANGE_STATUS_INVALID = new ErrorCode(1_001_001_002, "只允许修改为开启或者关闭状态");
ErrorCode JOB_CHANGE_STATUS_EQUALS = new ErrorCode(1_001_001_003, "定时任务已经处于该状态,无需修改");
ErrorCode JOB_UPDATE_ONLY_NORMAL_STATUS = new ErrorCode(1_001_001_004, "只有开启状态的任务,才可以修改");
ErrorCode JOB_CRON_EXPRESSION_VALID = new ErrorCode(1_001_001_005, "CRON 表达式不正确");
ErrorCode JOB_HANDLER_BEAN_NOT_EXISTS = new ErrorCode(1_001_001_006, "定时任务的处理器 Bean 不存在,注意 Bean 默认首字母小写");
ErrorCode JOB_HANDLER_BEAN_TYPE_ERROR = new ErrorCode(1_001_001_007, "定时任务的处理器 Bean 类型不正确,未实现 JobHandler 接口");
// ========== API 错误日志 1-001-002-000 ==========
ErrorCode API_ERROR_LOG_NOT_FOUND = new ErrorCode(1_001_002_000, "API 错误日志不存在");
ErrorCode API_ERROR_LOG_PROCESSED = new ErrorCode(1_001_002_001, "API 错误日志已处理");
// ========= 文件相关 1-001-003-000 =================
ErrorCode FILE_PATH_EXISTS = new ErrorCode(1_001_003_000, "文件路径已存在");
ErrorCode FILE_NOT_EXISTS = new ErrorCode(1_001_003_001, "文件不存在");
ErrorCode FILE_IS_EMPTY = new ErrorCode(1_001_003_002, "文件为空");
// 文件验证码生成失败
ErrorCode FILE_CAPTCHA_GENERATE_FAIL = new ErrorCode(1_001_003_003, "生成验证码失败,请稍后再试!");
// 文件验证码有误
ErrorCode FILE_CAPTCHA_ERROR = new ErrorCode(1_001_003_004, "验证码错误");
// 文件解密失败
ErrorCode FILE_DECRYPT_FAIL = new ErrorCode(1_001_003_005, "文件解密失败,请检查文件是否有效");
// 同一附件60秒内只能获取一次验证码
ErrorCode FILE_CAPTCHA_EXISTS = new ErrorCode(1_001_003_006, "同一附件60秒内只能获取一次验证码");
// 同一用户最多同时只能有10个有效验证码
ErrorCode FILE_CAPTCHA_MAX = new ErrorCode(1_001_003_007, "同一用户最多只能有10个有效验证码");
// ========== 代码生成器 1-001-004-000 ==========
ErrorCode CODEGEN_TABLE_EXISTS = new ErrorCode(1_001_004_002, "表定义已经存在");
ErrorCode CODEGEN_IMPORT_TABLE_NULL = new ErrorCode(1_001_004_001, "导入的表不存在");
ErrorCode CODEGEN_IMPORT_COLUMNS_NULL = new ErrorCode(1_001_004_002, "导入的字段不存在");
ErrorCode CODEGEN_TABLE_NOT_EXISTS = new ErrorCode(1_001_004_004, "表定义不存在");
ErrorCode CODEGEN_COLUMN_NOT_EXISTS = new ErrorCode(1_001_004_005, "字段义不存在");
ErrorCode CODEGEN_SYNC_COLUMNS_NULL = new ErrorCode(1_001_004_006, "同步的字段不存在");
ErrorCode CODEGEN_SYNC_NONE_CHANGE = new ErrorCode(1_001_004_007, "同步失败,不存在改变");
ErrorCode CODEGEN_TABLE_INFO_TABLE_COMMENT_IS_NULL = new ErrorCode(1_001_004_008, "数据库的表注释未填写");
ErrorCode CODEGEN_TABLE_INFO_COLUMN_COMMENT_IS_NULL = new ErrorCode(1_001_004_009, "数据库的表字段({})注释未填写");
ErrorCode CODEGEN_MASTER_TABLE_NOT_EXISTS = new ErrorCode(1_001_004_010, "主表(id={})定义不存在,请检查");
ErrorCode CODEGEN_SUB_COLUMN_NOT_EXISTS = new ErrorCode(1_001_004_011, "子表的字段(id={})不存在,请检查");
ErrorCode CODEGEN_MASTER_GENERATION_FAIL_NO_SUB_TABLE = new ErrorCode(1_001_004_012, "主表生成代码失败,原因:它没有子表");
// ========== 文件配置 1-001-006-000 ==========
ErrorCode FILE_CONFIG_NOT_EXISTS = new ErrorCode(1_001_006_000, "文件配置不存在");
ErrorCode FILE_CONFIG_DELETE_FAIL_MASTER = new ErrorCode(1_001_006_001, "该文件配置不允许删除,原因:它是主配置,删除会导致无法上传文件");
// ========== 数据源配置 1-001-007-000 ==========
ErrorCode DATA_SOURCE_CONFIG_NOT_EXISTS = new ErrorCode(1_001_007_000, "数据源配置不存在");
ErrorCode DATA_SOURCE_CONFIG_NOT_OK = new ErrorCode(1_001_007_001, "数据源配置不正确,无法进行连接");
// ========== 学生 1-001-201-000 ==========
ErrorCode DEMO01_CONTACT_NOT_EXISTS = new ErrorCode(1_001_201_000, "示例联系人不存在");
ErrorCode DEMO02_CATEGORY_NOT_EXISTS = new ErrorCode(1_001_201_001, "示例分类不存在");
ErrorCode DEMO02_CATEGORY_EXITS_CHILDREN = new ErrorCode(1_001_201_002, "存在存在子示例分类,无法删除");
ErrorCode DEMO02_CATEGORY_PARENT_NOT_EXITS = new ErrorCode(1_001_201_003,"父级示例分类不存在");
ErrorCode DEMO02_CATEGORY_PARENT_ERROR = new ErrorCode(1_001_201_004, "不能设置自己为父示例分类");
ErrorCode DEMO02_CATEGORY_NAME_DUPLICATE = new ErrorCode(1_001_201_005, "已经存在该名字的示例分类");
ErrorCode DEMO02_CATEGORY_PARENT_IS_CHILD = new ErrorCode(1_001_201_006, "不能设置自己的子示例分类为父示例分类");
ErrorCode DEMO03_STUDENT_NOT_EXISTS = new ErrorCode(1_001_201_007, "学生不存在");
ErrorCode DEMO03_COURSE_NOT_EXISTS = new ErrorCode(1_001_201_008, "学生课程不存在");
ErrorCode DEMO03_GRADE_NOT_EXISTS = new ErrorCode(1_001_201_009, "学生班级不存在");
ErrorCode DEMO03_GRADE_EXISTS = new ErrorCode(1_001_201_010, "学生班级已存在");
// ========== 业务附件关联 ==========
ErrorCode BUSINESS_FILE_NOT_EXISTS = new ErrorCode(1_002_201_010, "业务附件关联不存在");
// ========== 数据命名与简写标准 ==========
ErrorCode STANDARD_NAME_NOT_EXISTS = new ErrorCode(1_002_030_000, "数据命名与简写标准不存在");
ErrorCode STD_ABBR_NOT_EXISTS = new ErrorCode(1_002_030_001, "字段名 {} 不存在命名规范定义,请核对字段定义");
ErrorCode DOC_NOT_EXISTS = new ErrorCode(1_002_031_000, "文档不存在");
}

View File

@@ -0,0 +1,29 @@
package com.zt.plat.module.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.zt.plat.module.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,31 @@
package com.zt.plat.module.infra.enums.codegen;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 代码生成的前端类型枚举
*
* @author ZT
*/
@AllArgsConstructor
@Getter
public enum CodegenFrontTypeEnum {
VUE2_ELEMENT_UI(10), // Vue2 Element UI 标准模版
VUE3_ELEMENT_PLUS(20), // Vue3 Element Plus 标准模版
VUE3_VBEN2_ANTD_SCHEMA(30), // Vue3 VBEN2 + ANTD + Schema 模版
VUE3_VBEN5_ANTD_SCHEMA(40), // Vue3 VBEN5 + ANTD + schema 模版
VUE3_VBEN5_ANTD_GENERAL(41), // Vue3 VBEN5 + ANTD 标准模版
;
/**
* 类型
*/
private final Integer type;
}

View File

@@ -0,0 +1,41 @@
package com.zt.plat.module.infra.enums.codegen;
import lombok.AllArgsConstructor;
import lombok.Getter;
import static cn.hutool.core.util.ArrayUtil.firstMatch;
/**
* 代码生成的场景枚举
*
* @author ZT
*/
@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.zt.plat.module.infra.enums.codegen;
import com.zt.plat.framework.common.util.object.ObjectUtils;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Objects;
/**
* 代码生成模板类型
*
* @author ZT
*/
@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,30 @@
package com.zt.plat.module.infra.enums.codegen;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 代码生成的 VO 类型枚举
*
* 目前的作用Controller 新增、修改、响应时,使用 VO 还是 DO
* 注意:不包括 Controller 的分页参数!
*
* @author ZT
*/
@AllArgsConstructor
@Getter
public enum CodegenVOTypeEnum {
VO(10, "VO"),
DO(20, "DO");
/**
* 场景
*/
private final Integer type;
/**
* 场景名
*/
private final String name;
}

View File

@@ -0,0 +1,21 @@
package com.zt.plat.module.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.zt.plat.module.infra.enums.logger;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* API 异常数据的处理状态
*
* @author ZT
*/
@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.zt.plat.module.infra.enums;

View File

@@ -0,0 +1,19 @@
## AdoptOpenJDK 停止发布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,提供更好的稳定性
FROM 172.16.46.66:10043/base-service/eclipse-temurin:21-jre
## 创建目录,并使用它作为工作目录
RUN mkdir -p /cloud-module-infra-server
WORKDIR /cloud-module-infra-server
## 将后端项目的 Jar 文件,复制到镜像中
COPY ./target/cloud-module-infra-server.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,172 @@
<?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">
<parent>
<groupId>com.zt.plat</groupId>
<artifactId>zt-module-infra</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>zt-module-infra-server</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>
infra 模块,主要提供两块能力:
1. 我们放基础设施的运维与管理,支撑上层的通用与核心业务。 例如说:定时任务的管理、服务器的信息等等
2. 研发工具,提升研发效率与质量。 例如说:代码生成器、接口文档等等
</description>
<dependencies>
<!-- Spring Cloud 基础 -->
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-spring-boot-starter-env</artifactId>
</dependency>
<!-- 依赖服务 -->
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-module-infra-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-module-system-api</artifactId>
<version>${revision}</version>
</dependency>
<!-- 业务组件 -->
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-spring-boot-starter-biz-tenant</artifactId>
</dependency>
<!-- Web 相关 -->
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-spring-boot-starter-websocket</artifactId>
</dependency>
<!-- DB 相关 -->
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-spring-boot-starter-mybatis</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId> <!-- 代码生成器,使用它解析表结构 -->
</dependency>
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-spring-boot-starter-redis</artifactId>
</dependency>
<!-- RPC 远程调用相关 -->
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-spring-boot-starter-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.zt.plat</groupId>
<artifactId>zt-spring-boot-starter-job</artifactId>
</dependency>
<!-- 消息队列相关 -->
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-spring-boot-starter-mq</artifactId>
</dependency>
<!-- Test 测试相关 -->
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 工具类相关 -->
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-spring-boot-starter-excel</artifactId>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId> <!-- 实现代码生成 -->
</dependency>
<!-- 监控相关 -->
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-spring-boot-starter-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>software.amazon.awssdk</groupId> <!-- 文件客户端解决阿里云、腾讯云、minio 等 S3 连接 -->
<artifactId>s3</artifactId>
</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,30 @@
package com.zt.plat.module.infra;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 项目的启动类
* <p>
* 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章
* 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章
* 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章
*
* @author ZT
*/
@SpringBootApplication
public class InfraServerApplication {
public static void main(String[] args) {
// 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章
// 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章
// 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章
SpringApplication.run(InfraServerApplication.class, args);
// 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章
// 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章
// 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章
}
}

View File

@@ -0,0 +1,87 @@
package com.zt.plat.module.infra.api.businessfile;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.module.infra.api.businessfile.dto.BusinessFilePageReqDTO;
import com.zt.plat.module.infra.api.businessfile.dto.BusinessFileRespDTO;
import com.zt.plat.module.infra.api.businessfile.dto.BusinessFileSaveReqDTO;
import com.zt.plat.module.infra.api.businessfile.dto.BusinessFileWithUrlRespDTO;
import com.zt.plat.module.infra.controller.admin.businessfile.vo.BusinessFilePageReqVO;
import com.zt.plat.module.infra.controller.admin.businessfile.vo.BusinessFileSaveReqVO;
import com.zt.plat.module.infra.controller.admin.businessfile.vo.BusinessFileWithUrlRespVO;
import com.zt.plat.module.infra.dal.dataobject.businessfile.BusinessFileDO;
import com.zt.plat.module.infra.service.businessfile.BusinessFileService;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
/**
* 业务附件关联 API 实现
* @author chenbowen
*/
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
public class BusinessFileApiImpl implements BusinessFileApi {
@Resource
private BusinessFileService businessFileService;
@Override
public CommonResult<Long> createBusinessFile(BusinessFileSaveReqDTO createReqDTO) {
return success(businessFileService.createBusinessFile(BeanUtils.toBean(createReqDTO, BusinessFileSaveReqVO.class)));
}
@Override
public CommonResult<List<Long>> batchCreateBusinessFile(List<BusinessFileSaveReqDTO> createReqDTOList) {
List<BusinessFileSaveReqVO> createReqVOList = BeanUtils.toBean(createReqDTOList, BusinessFileSaveReqVO.class);
List<Long> ids = businessFileService.batchCreateBusinessFile(createReqVOList);
return success(ids);
}
@Override
public CommonResult<Boolean> updateBusinessFile(BusinessFileSaveReqDTO updateReqDTO) {
businessFileService.updateBusinessFile(BeanUtils.toBean(updateReqDTO, BusinessFileSaveReqVO.class));
return success(true);
}
@Override
public CommonResult<Boolean> deleteBusinessFile(Long id) {
businessFileService.deleteBusinessFile(id);
return success(true);
}
@Override
public CommonResult<Boolean> deleteBusinessFileList(List<Long> ids) {
businessFileService.deleteBusinessFileListByIds(ids);
return success(true);
}
@Override
public CommonResult<BusinessFileRespDTO> getBusinessFile(Long id) {
BusinessFileDO businessFile = businessFileService.getBusinessFile(id);
return success(BeanUtils.toBean(businessFile, BusinessFileRespDTO.class));
}
@Override
public CommonResult<PageResult<BusinessFileRespDTO>> getBusinessFilePage(BusinessFilePageReqDTO pageReqDTO) {
PageResult<BusinessFileDO> pageResult = businessFileService.getBusinessFilePage(BeanUtils.toBean(pageReqDTO, BusinessFilePageReqVO.class));
return success(BeanUtils.toBean(pageResult, BusinessFileRespDTO.class));
}
@Override
public CommonResult<PageResult<BusinessFileWithUrlRespDTO>> getBusinessFilePageWithUrl(BusinessFilePageReqDTO pageReqDTO) {
PageResult<BusinessFileWithUrlRespVO> pageResult = businessFileService.getBusinessFilePageWithUrl(BeanUtils.toBean(pageReqDTO, BusinessFilePageReqVO.class));
return success(BeanUtils.toBean(pageResult, BusinessFileWithUrlRespDTO.class));
}
@Override
public CommonResult<Boolean> deleteBusinessFileByBusinessIdAndSource(Long businessId, String source) {
businessFileService.deleteBusinessFileByBusinessIdAndSource(businessId, source);
return success(true);
}
}

View File

@@ -0,0 +1,25 @@
package com.zt.plat.module.infra.api.config;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.module.infra.dal.dataobject.config.ConfigDO;
import com.zt.plat.module.infra.service.config.ConfigService;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import static com.zt.plat.framework.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,25 @@
package com.zt.plat.module.infra.api.file;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.module.infra.api.file.dto.FileCreateReqDTO;
import com.zt.plat.module.infra.service.file.FileService;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import static com.zt.plat.framework.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.getContent(), createReqDTO.getName(),
createReqDTO.getDirectory(), createReqDTO.getType(), false));
}
}

View File

@@ -0,0 +1,27 @@
package com.zt.plat.module.infra.api.logger;
import com.zt.plat.framework.common.biz.infra.logger.ApiAccessLogCommonApi;
import com.zt.plat.framework.common.biz.infra.logger.dto.ApiAccessLogCreateReqDTO;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.module.infra.service.logger.ApiAccessLogService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
public class ApiAccessLogApiImpl implements ApiAccessLogCommonApi {
@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.zt.plat.module.infra.api.logger;
import com.zt.plat.framework.common.biz.infra.logger.ApiErrorLogCommonApi;
import com.zt.plat.framework.common.biz.infra.logger.dto.ApiErrorLogCreateReqDTO;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.module.infra.service.logger.ApiErrorLogService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
public class ApiErrorLogApiImpl implements ApiErrorLogCommonApi {
@Resource
private ApiErrorLogService apiErrorLogService;
@Override
public CommonResult<Boolean> createApiErrorLog(ApiErrorLogCreateReqDTO createDTO) {
apiErrorLogService.createApiErrorLog(createDTO);
return success(true);
}
}

View File

@@ -0,0 +1 @@
package com.zt.plat.module.infra.api;

View File

@@ -0,0 +1,36 @@
package com.zt.plat.module.infra.api.websocket;
import cn.hutool.core.util.StrUtil;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.websocket.core.sender.WebSocketMessageSender;
import com.zt.plat.module.infra.api.websocket.dto.WebSocketSendReqDTO;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource;
import static com.zt.plat.framework.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,30 @@
package com.zt.plat.module.infra.config;
import com.zt.plat.module.infra.websocket.DocWebSocketHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import jakarta.annotation.Resource;
/**
* WebSocket 配置
*
* @author ZT
*/
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Resource
private DocWebSocketHandler docWebSocketHandler;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
// 注册文档协作WebSocket处理器
registry.addHandler(docWebSocketHandler, "/doc-collaboration")
.setAllowedOrigins("*") // 允许跨域,生产环境应该限制域名
.withSockJS(); // 启用SockJS支持
}
}

View File

@@ -0,0 +1,132 @@
package com.zt.plat.module.infra.controller.admin.businessfile;
import com.zt.plat.framework.apilog.core.annotation.ApiAccessLog;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageParam;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.framework.excel.core.util.ExcelUtils;
import com.zt.plat.module.infra.controller.admin.businessfile.vo.BusinessFilePageReqVO;
import com.zt.plat.module.infra.controller.admin.businessfile.vo.BusinessFileRespVO;
import com.zt.plat.module.infra.controller.admin.businessfile.vo.BusinessFileSaveReqVO;
import com.zt.plat.module.infra.controller.admin.businessfile.vo.BusinessFileWithUrlRespVO;
import com.zt.plat.module.infra.dal.dataobject.businessfile.BusinessFileDO;
import com.zt.plat.module.infra.service.businessfile.BusinessFileService;
import com.zt.plat.module.infra.service.file.FileService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.List;
import static com.zt.plat.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 业务附件关联")
@RestController
@RequestMapping("/infra/business-file")
@Validated
public class BusinessFileController {
@Resource
private BusinessFileService businessFileService;
@Resource
private FileService fileService;
@PostMapping("/create")
@Operation(summary = "创建业务附件关联")
@PreAuthorize("@ss.hasPermission('infra:business-file:create')")
public CommonResult<Long> createBusinessFile(@Valid @RequestBody BusinessFileSaveReqVO createReqVO) {
return success(businessFileService.createBusinessFile(createReqVO));
}
@PostMapping("/batch-create")
@Operation(summary = "批量创建业务附件关联")
@PreAuthorize("@ss.hasPermission('infra:business-file:create')")
public CommonResult<List<Long>> batchCreateBusinessFile(@Valid @RequestBody List<BusinessFileSaveReqVO> createReqVOList) {
return success(businessFileService.batchCreateBusinessFile(createReqVOList));
}
@PutMapping("/update")
@Operation(summary = "更新业务附件关联")
@PreAuthorize("@ss.hasPermission('infra:business-file:update')")
public CommonResult<Boolean> updateBusinessFile(@Valid @RequestBody BusinessFileSaveReqVO updateReqVO) {
businessFileService.updateBusinessFile(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除业务附件关联")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('infra:business-file:delete')")
public CommonResult<Boolean> deleteBusinessFile(@RequestParam("id") Long id) {
businessFileService.deleteBusinessFile(id);
return success(true);
}
@DeleteMapping("/delete-list")
@Parameter(name = "ids", description = "编号", required = true)
@Operation(summary = "批量删除业务附件关联")
@PreAuthorize("@ss.hasPermission('infra:business-file:delete')")
public CommonResult<Boolean> deleteBusinessFileList(@RequestParam("ids") List<Long> ids) {
businessFileService.deleteBusinessFileListByIds(ids);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得业务附件关联")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('infra:business-file:query')")
public CommonResult<BusinessFileRespVO> getBusinessFile(@RequestParam("id") Long id) {
BusinessFileDO businessFile = businessFileService.getBusinessFile(id);
return success(BeanUtils.toBean(businessFile, BusinessFileRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得业务附件关联分页")
@PreAuthorize("@ss.hasPermission('infra:business-file:query')")
public CommonResult<PageResult<BusinessFileRespVO>> getBusinessFilePage(@Valid BusinessFilePageReqVO pageReqVO) {
PageResult<BusinessFileDO> pageResult = businessFileService.getBusinessFilePage(pageReqVO);
return success(BeanUtils.toBean(pageResult, BusinessFileRespVO.class));
}
@GetMapping("/page-with-url")
@Operation(summary = "获得业务附件关联分页带URL")
@PreAuthorize("@ss.hasPermission('infra:business-file:query')")
public CommonResult<PageResult<BusinessFileWithUrlRespVO>> getBusinessFilePageWithUrl(@Valid BusinessFilePageReqVO pageReqVO) {
return success(businessFileService.getBusinessFilePageWithUrl(pageReqVO));
}
@GetMapping("/export-excel")
@Operation(summary = "导出业务附件关联 Excel")
@PreAuthorize("@ss.hasPermission('infra:business-file:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportBusinessFileExcel(@Valid BusinessFilePageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<BusinessFileDO> list = businessFileService.getBusinessFilePage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "业务附件关联.xls", "数据", BusinessFileRespVO.class,
BeanUtils.toBean(list, BusinessFileRespVO.class));
}
@DeleteMapping("/delete-by-business")
@Operation(summary = "根据业务Id和来源删除业务附件关联")
@Parameter(name = "businessId", description = "业务Id", required = true)
@Parameter(name = "source", description = "业务来源", required = true)
@PreAuthorize("@ss.hasPermission('infra:business-file:delete')")
public CommonResult<Boolean> deleteBusinessFileByBusinessIdAndSource(@RequestParam("businessId") Long businessId,
@RequestParam("source") String source) {
businessFileService.deleteBusinessFileByBusinessIdAndSource(businessId, source);
return success(true);
}
}

View File

@@ -0,0 +1,35 @@
package com.zt.plat.module.infra.controller.admin.businessfile.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import com.zt.plat.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 业务附件关联分页 Request VO")
@Data
public class BusinessFilePageReqVO extends PageParam {
@Schema(description = "业务Id", example = "24322")
private Long businessId;
@Schema(description = "业务编码")
private String businessCode;
@Schema(description = "附件fileId", example = "10125")
private Long fileId;
@Schema(description = "附件名称", example = "李四")
private String fileName;
@Schema(description = "附件来源")
private String source;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,43 @@
package com.zt.plat.module.infra.controller.admin.businessfile.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*;
@Schema(description = "管理后台 - 业务附件关联 Response VO")
@Data
@ExcelIgnoreUnannotated
public class BusinessFileRespVO {
@Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "29216")
@ExcelProperty("主键ID")
private Long id;
@Schema(description = "业务Id", requiredMode = Schema.RequiredMode.REQUIRED, example = "24322")
@ExcelProperty("业务Id")
private Long businessId;
@Schema(description = "业务编码")
@ExcelProperty("业务编码")
private String businessCode;
@Schema(description = "附件fileId", requiredMode = Schema.RequiredMode.REQUIRED, example = "10125")
@ExcelProperty("附件fileId")
private Long fileId;
@Schema(description = "附件名称", example = "李四")
@ExcelProperty("附件名称")
private String fileName;
@Schema(description = "附件来源")
@ExcelProperty("附件来源")
private String source;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,25 @@
package com.zt.plat.module.infra.controller.admin.businessfile.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 业务附件关联新增/修改 Request VO")
@Data
public class BusinessFileSaveReqVO {
@Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "29216")
private Long id;
@Schema(description = "附件Id", example = "李四")
private Long fileId;
@Schema(description = "业务Id", example = "李四")
private Long businessId;
@Schema(description = "附件名称", example = "李四")
private String fileName;
@Schema(description = "附件来源")
private String source;
}

View File

@@ -0,0 +1,97 @@
package com.zt.plat.module.infra.controller.admin.businessfile.vo;
import cn.hutool.core.date.DateUtil;
import com.zt.plat.framework.common.util.spring.SpringUtils;
import com.zt.plat.framework.security.core.util.SecurityFrameworkUtils;
import com.zt.plat.module.infra.framework.file.core.client.FileClient;
import com.zt.plat.module.infra.framework.file.core.client.s3.S3FileClient;
import com.zt.plat.module.infra.service.file.FileConfigService;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Base64;
import java.util.Date;
/**
* @author 后台管理
*/
@Schema(description = "管理后台 - 业务附件关联带URL Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class BusinessFileWithUrlRespVO extends BusinessFileRespVO {
@Schema(description = "文件路径", example = "cloud.jpg")
private String filePath;
@Schema(description = "文件 URL", example = "https://www.iocoder.cn/cloud.jpg")
private String fileUrl;
@Schema(description = "附件预览地址", example = "https://www.iocoder.cn/cloud.jpg")
private String previewUrl;
@Schema(description = "是否加密", example = "false")
private Boolean isEncrypted;
@Schema(description = "文件MIME类型", example = "application/octet-stream")
private String fileType;
@Schema(description = "文件大小", example = "2048")
private Integer fileSize;
/**
* 获取文件 URL参考 FileRespVO 的实现)
*/
public String getFileUrl() {
// 加密附件不返回 url
if (Boolean.TRUE.equals(this.isEncrypted)) {
return null;
}
// 如果 url 已经是临时下载地址(如预签名 URL直接返回
if (fileUrl != null && (fileUrl.contains("X-Amz-Signature") || fileUrl.contains("?sign="))) {
return fileUrl;
}
FileConfigService fileConfigService = SpringUtils.getBean(FileConfigService.class);
FileClient fileClient = fileConfigService.getMasterFileClient();
if (fileClient instanceof S3FileClient s3FileClient) {
String presignedDownloadUrl = s3FileClient.getPresignedDownloadUrl(this.filePath, null);
if (presignedDownloadUrl != null && !presignedDownloadUrl.isEmpty()) {
return presignedDownloadUrl;
}
}
return fileUrl;
}
/**
* 获取附件预览地址(参考 FileRespVO 的实现)
*/
public String getPreviewUrl() {
// 加密附件不返回 previewUrl
if (Boolean.TRUE.equals(this.isEncrypted)) {
return null;
}
// 仅当 url 不为空时生成
if (this.fileUrl == null || this.fileUrl.isEmpty()) {
return null;
}
// 这里的 onlinePreview 通过 SpringUtils 获取
String onlinePreview = SpringUtils.getProperty("cloud.kkfile");
if (onlinePreview == null || onlinePreview.isEmpty()) {
return null;
}
String presignedUrl = this.getFileUrl();
if (presignedUrl == null || presignedUrl.isEmpty()) {
return null;
}
String base64PresignedUrl = Base64.getEncoder().encodeToString(presignedUrl.getBytes(StandardCharsets.UTF_8));
String timestamp = String.valueOf(System.currentTimeMillis());
String loginUserNickname = SecurityFrameworkUtils.getLoginUserNickname();
String format = DateUtil.format(new Date(), "yyyy-MM-dd");
String watermark = SpringUtils.getProperty("aj.captcha.water-mark", loginUserNickname+" "+ format);
return onlinePreview + base64PresignedUrl + "&t=" + timestamp + "&watermarkTxt=" + watermark;
}
}

View File

@@ -0,0 +1,169 @@
package com.zt.plat.module.infra.controller.admin.codegen;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ZipUtil;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.module.infra.controller.admin.codegen.vo.CodegenCreateListReqVO;
import com.zt.plat.module.infra.controller.admin.codegen.vo.CodegenDetailRespVO;
import com.zt.plat.module.infra.controller.admin.codegen.vo.CodegenPreviewRespVO;
import com.zt.plat.module.infra.controller.admin.codegen.vo.CodegenUpdateReqVO;
import com.zt.plat.module.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO;
import com.zt.plat.module.infra.controller.admin.codegen.vo.table.CodegenTableRespVO;
import com.zt.plat.module.infra.controller.admin.codegen.vo.table.DatabaseTableRespVO;
import com.zt.plat.module.infra.convert.codegen.CodegenConvert;
import com.zt.plat.module.infra.dal.dataobject.codegen.CodegenColumnDO;
import com.zt.plat.module.infra.dal.dataobject.codegen.CodegenTableDO;
import com.zt.plat.module.infra.service.codegen.CodegenService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
import static com.zt.plat.framework.security.core.util.SecurityFrameworkUtils.getLoginUserNickname;
import static com.zt.plat.module.infra.framework.file.core.utils.FileTypeUtils.writeAttachment;
@Tag(name = "管理后台 - 代码生成器")
@RestController
@RequestMapping("/infra/codegen")
@Validated
public class CodegenController {
@Resource
private CodegenService codegenService;
@GetMapping("/db/table/list")
@Operation(summary = "获得数据库自带的表定义列表", description = "会过滤掉已经导入 Codegen 的表")
@Parameters({
@Parameter(name = "dataSourceConfigId", description = "数据源配置的编号", required = true, example = "1"),
@Parameter(name = "name", description = "表名,模糊匹配", example = "cloud"),
@Parameter(name = "comment", description = "描述,模糊匹配", example = "ZT")
})
@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")
@Operation(summary = "获得表定义列表")
@Parameter(name = "dataSourceConfigId", description = "数据源配置的编号", required = true, example = "1")
@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")
@Operation(summary = "获得表定义分页")
@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")
@Operation(summary = "获得表和字段的明细")
@Parameter(name = "tableId", description = "表编号", required = true, example = "1024")
@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));
}
@Operation(summary = "基于数据库的表结构,创建代码生成器的表和字段定义")
@PostMapping("/create-list")
@PreAuthorize("@ss.hasPermission('infra:codegen:create')")
public CommonResult<List<Long>> createCodegenList(@Valid @RequestBody CodegenCreateListReqVO reqVO) {
return success(codegenService.createCodegenList(getLoginUserNickname(), reqVO));
}
@Operation(summary = "更新数据库的表和字段定义")
@PutMapping("/update")
@PreAuthorize("@ss.hasPermission('infra:codegen:update')")
public CommonResult<Boolean> updateCodegen(@Valid @RequestBody CodegenUpdateReqVO updateReqVO) {
codegenService.updateCodegen(updateReqVO);
return success(true);
}
@Operation(summary = "基于数据库的表结构,同步数据库的表和字段定义")
@PutMapping("/sync-from-db")
@Parameter(name = "tableId", description = "表编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('infra:codegen:update')")
public CommonResult<Boolean> syncCodegenFromDB(@RequestParam("tableId") Long tableId) {
codegenService.syncCodegenFromDB(tableId);
return success(true);
}
@Operation(summary = "删除数据库的表和字段定义")
@DeleteMapping("/delete")
@Parameter(name = "tableId", description = "表编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('infra:codegen:delete')")
public CommonResult<Boolean> deleteCodegen(@RequestParam("tableId") Long tableId) {
codegenService.deleteCodegen(tableId);
return success(true);
}
@Operation(summary = "预览生成代码")
@GetMapping("/preview")
@Parameters({
@Parameter(name = "tableId", description = "表编号", required = true, example = "1024"),
@Parameter(name = "isBusiness", description = "是否业务基类", example = "false"),
@Parameter(name = "isStandardized", description = "是否标准化", example = "false"),
@Parameter(name = "isFileUpload", description = "是否包含附件上传能力", example = "false")
})
@PreAuthorize("@ss.hasPermission('infra:codegen:preview')")
public CommonResult<List<CodegenPreviewRespVO>> previewCodegen(
@RequestParam("tableId") Long tableId,
@RequestParam(value = "isBusiness", required = false, defaultValue = "false") Boolean isBusiness,
@RequestParam(value = "isStandardized", required = false, defaultValue = "false") Boolean isStandardized,
@RequestParam(value = "isFileUpload", required = false, defaultValue = "false") Boolean isFileUpload) {
Map<String, String> codes = codegenService.generationCodes(tableId, isBusiness, isStandardized, isFileUpload);
return success(CodegenConvert.INSTANCE.convert(codes));
}
@Operation(summary = "下载生成代码")
@GetMapping("/download")
@Parameters({
@Parameter(name = "tableId", description = "表编号", required = true, example = "1024"),
@Parameter(name = "isBusiness", description = "是否业务基类", example = "false"),
@Parameter(name = "isStandardized", description = "是否标准化", example = "false"),
@Parameter(name = "isFileUpload", description = "是否包含附件上传能力", example = "false")
})
@PreAuthorize("@ss.hasPermission('infra:codegen:download')")
public void downloadCodegen(
@RequestParam("tableId") Long tableId,
@RequestParam(value = "isBusiness", required = false, defaultValue = "false") Boolean isBusiness,
@RequestParam(value = "isStandardized", required = false, defaultValue = "false") Boolean isStandardized,
@RequestParam(value = "isFileUpload", required = false, defaultValue = "false") Boolean isFileUpload,
HttpServletResponse response) throws IOException {
// 生成代码,传递 isBusiness、isStandardized、isFileUpload
Map<String, String> codes = codegenService.generationCodes(tableId, isBusiness, isStandardized, isFileUpload);
// 构建 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,24 @@
package com.zt.plat.module.infra.controller.admin.codegen.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.List;
@Schema(description = "管理后台 - 基于数据库的表结构,创建代码生成器的表和字段定义 Request VO")
@Data
public class CodegenCreateListReqVO {
@Schema(description = "数据源配置的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "数据源配置的编号不能为空")
private Long dataSourceConfigId;
@Schema(description = "表名数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 2, 3]")
@NotNull(message = "表名数组不能为空")
private List<String> tableNames;
@Schema(description = "是否为规范缩写定义表", example = "true")
private Boolean isStandardized = false;
}

View File

@@ -0,0 +1,20 @@
package com.zt.plat.module.infra.controller.admin.codegen.vo;
import com.zt.plat.module.infra.controller.admin.codegen.vo.column.CodegenColumnRespVO;
import com.zt.plat.module.infra.controller.admin.codegen.vo.table.CodegenTableRespVO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Schema(description = "管理后台 - 代码生成表和字段的明细 Response VO")
@Data
public class CodegenDetailRespVO {
@Schema(description = "表定义")
private CodegenTableRespVO table;
@Schema(description = "字段定义")
private List<CodegenColumnRespVO> columns;
}

View File

@@ -0,0 +1,16 @@
package com.zt.plat.module.infra.controller.admin.codegen.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 代码生成预览 Response VO注意每个文件都是一个该对象")
@Data
public class CodegenPreviewRespVO {
@Schema(description = "文件路径", requiredMode = Schema.RequiredMode.REQUIRED, example = "java/com.zt.plat/adminserver/modules/system/controller/test/SysTestDemoController.java")
private String filePath;
@Schema(description = "代码", requiredMode = Schema.RequiredMode.REQUIRED, example = "Hello World")
private String code;
}

View File

@@ -0,0 +1,24 @@
package com.zt.plat.module.infra.controller.admin.codegen.vo;
import com.zt.plat.module.infra.controller.admin.codegen.vo.column.CodegenColumnSaveReqVO;
import com.zt.plat.module.infra.controller.admin.codegen.vo.table.CodegenTableSaveReqVO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import java.util.List;
@Schema(description = "管理后台 - 代码生成表和字段的修改 Request VO")
@Data
public class CodegenUpdateReqVO {
@Valid // 校验内嵌的字段
@NotNull(message = "表定义不能为空")
private CodegenTableSaveReqVO table;
@Valid // 校验内嵌的字段
@NotNull(message = "字段定义不能为空")
private List<CodegenColumnSaveReqVO> columns;
}

View File

@@ -0,0 +1,69 @@
package com.zt.plat.module.infra.controller.admin.codegen.vo.column;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 代码生成字段定义 Response VO")
@Data
public class CodegenColumnRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "表编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long tableId;
@Schema(description = "字段名", requiredMode = Schema.RequiredMode.REQUIRED, example = "user_age")
private String columnName;
@Schema(description = "字段类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "int(11)")
private String dataType;
@Schema(description = "字段描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "年龄")
private String columnComment;
@Schema(description = "是否允许为空", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean nullable;
@Schema(description = "是否主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
private Boolean primaryKey;
@Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
private Integer ordinalPosition;
@Schema(description = "Java 属性类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "userAge")
private String javaType;
@Schema(description = "Java 属性名", requiredMode = Schema.RequiredMode.REQUIRED, example = "Integer")
private String javaField;
@Schema(description = "字典类型", example = "sys_gender")
private String dictType;
@Schema(description = "数据示例", example = "1024")
private String example;
@Schema(description = "是否为 Create 创建操作的字段", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean createOperation;
@Schema(description = "是否为 Update 更新操作的字段", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
private Boolean updateOperation;
@Schema(description = "是否为 List 查询操作的字段", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean listOperation;
@Schema(description = "List 查询操作的条件类型,参见 CodegenColumnListConditionEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "LIKE")
private String listOperationCondition;
@Schema(description = "是否为 List 查询操作的返回字段", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean listOperationResult;
@Schema(description = "显示类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "input")
private String htmlType;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

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

View File

@@ -0,0 +1,29 @@
package com.zt.plat.module.infra.controller.admin.codegen.vo.table;
import com.zt.plat.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 表定义分页 Request VO")
@Data
public class CodegenTablePageReqVO extends PageParam {
@Schema(description = "表名称,模糊匹配", example = "cloud")
private String tableName;
@Schema(description = "表描述,模糊匹配", example = "ZT")
private String tableComment;
@Schema(description = "实体,模糊匹配", example = "Cloud")
private String className;
@Schema(description = "创建时间", example = "[2022-07-01 00:00:00,2022-07-01 23:59:59]")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

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

View File

@@ -0,0 +1,100 @@
package com.zt.plat.module.infra.controller.admin.codegen.vo.table;
import cn.hutool.core.util.ObjectUtil;
import com.zt.plat.module.infra.enums.codegen.CodegenSceneEnum;
import com.zt.plat.module.infra.enums.codegen.CodegenTemplateTypeEnum;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotNull;
@Schema(description = "管理后台 - 代码生成表定义创建/修改 Response VO")
@Data
public class CodegenTableSaveReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "生成场景,参见 CodegenSceneEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "导入类型不能为空")
private Integer scene;
@Schema(description = "表名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "cloud")
@NotNull(message = "表名称不能为空")
private String tableName;
@Schema(description = "表描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT")
@NotNull(message = "表描述不能为空")
private String tableComment;
@Schema(description = "备注", example = "我是备注")
private String remark;
@Schema(description = "模块名", requiredMode = Schema.RequiredMode.REQUIRED, example = "system")
@NotNull(message = "模块名不能为空")
private String moduleName;
@Schema(description = "业务名", requiredMode = Schema.RequiredMode.REQUIRED, example = "codegen")
@NotNull(message = "业务名不能为空")
private String businessName;
@Schema(description = "类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "CodegenTable")
@NotNull(message = "类名称不能为空")
private String className;
@Schema(description = "类描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "代码生成器的表定义")
@NotNull(message = "类描述不能为空")
private String classComment;
@Schema(description = "作者", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT源码")
@NotNull(message = "作者不能为空")
private String author;
@Schema(description = "模板类型,参见 CodegenTemplateTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "模板类型不能为空")
private Integer templateType;
@Schema(description = "前端类型,参见 CodegenFrontTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
@NotNull(message = "前端类型不能为空")
private Integer frontType;
@Schema(description = "父菜单编号", example = "1024")
private Long parentMenuId;
@Schema(description = "主表的编号", example = "2048")
private Long masterTableId;
@Schema(description = "子表关联主表的字段编号", example = "4096")
private Long subJoinColumnId;
@Schema(description = "主表与子表是否一对多", example = "4096")
private Boolean subJoinMany;
@Schema(description = "树表的父字段编号", example = "8192")
private Long treeParentColumnId;
@Schema(description = "树表的名字字段编号", example = "16384")
private Long treeNameColumnId;
@AssertTrue(message = "上级菜单不能为空,请前往 [修改生成配置 -> 生成信息] 界面,设置“上级菜单”字段")
@JsonIgnore
public boolean isParentMenuIdValid() {
// 生成场景为管理后台时,必须设置上级菜单,不然生成的菜单 SQL 是无父级菜单的
return ObjectUtil.notEqual(getScene(), CodegenSceneEnum.ADMIN.getScene())
|| getParentMenuId() != null;
}
@AssertTrue(message = "关联的父表信息不全")
@JsonIgnore
public boolean isSubValid() {
return ObjectUtil.notEqual(getTemplateType(), CodegenTemplateTypeEnum.SUB)
|| (ObjectUtil.isAllNotEmpty(masterTableId, subJoinColumnId, subJoinMany));
}
@AssertTrue(message = "关联的树表信息不全")
@JsonIgnore
public boolean isTreeValid() {
return ObjectUtil.notEqual(templateType, CodegenTemplateTypeEnum.TREE)
|| (ObjectUtil.isAllNotEmpty(treeParentColumnId, treeNameColumnId));
}
}

View File

@@ -0,0 +1,16 @@
package com.zt.plat.module.infra.controller.admin.codegen.vo.table;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 数据库的表定义 Response VO")
@Data
public class DatabaseTableRespVO {
@Schema(description = "表名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "yuanma")
private String name;
@Schema(description = "表描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT源码")
private String comment;
}

View File

@@ -0,0 +1,108 @@
package com.zt.plat.module.infra.controller.admin.config;
import com.zt.plat.framework.apilog.core.annotation.ApiAccessLog;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageParam;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.excel.core.util.ExcelUtils;
import com.zt.plat.module.infra.controller.admin.config.vo.ConfigPageReqVO;
import com.zt.plat.module.infra.controller.admin.config.vo.ConfigRespVO;
import com.zt.plat.module.infra.controller.admin.config.vo.ConfigSaveReqVO;
import com.zt.plat.module.infra.convert.config.ConfigConvert;
import com.zt.plat.module.infra.dal.dataobject.config.ConfigDO;
import com.zt.plat.module.infra.enums.ErrorCodeConstants;
import com.zt.plat.module.infra.service.config.ConfigService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.List;
import static com.zt.plat.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 参数配置")
@RestController
@RequestMapping("/infra/config")
@Validated
public class ConfigController {
@Resource
private ConfigService configService;
@PostMapping("/create")
@Operation(summary = "创建参数配置")
@PreAuthorize("@ss.hasPermission('infra:config:create')")
public CommonResult<Long> createConfig(@Valid @RequestBody ConfigSaveReqVO createReqVO) {
return success(configService.createConfig(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "修改参数配置")
@PreAuthorize("@ss.hasPermission('infra:config:update')")
public CommonResult<Boolean> updateConfig(@Valid @RequestBody ConfigSaveReqVO updateReqVO) {
configService.updateConfig(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除参数配置")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('infra:config:delete')")
public CommonResult<Boolean> deleteConfig(@RequestParam("id") Long id) {
configService.deleteConfig(id);
return success(true);
}
@GetMapping(value = "/get")
@Operation(summary = "获得参数配置")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('infra:config:query')")
public CommonResult<ConfigRespVO> getConfig(@RequestParam("id") Long id) {
return success(ConfigConvert.INSTANCE.convert(configService.getConfig(id)));
}
@GetMapping(value = "/get-value-by-key")
@Operation(summary = "根据参数键名查询参数值", description = "不可见的配置,不允许返回给前端")
@Parameter(name = "key", description = "参数键", required = true, example = "yunai.biz.username")
public CommonResult<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")
@Operation(summary = "获取参数配置分页")
@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")
@Operation(summary = "导出参数配置")
@PreAuthorize("@ss.hasPermission('infra:config:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportConfig(ConfigPageReqVO exportReqVO,
HttpServletResponse response) throws IOException {
exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<ConfigDO> list = configService.getConfigPage(exportReqVO).getList();
// 输出
ExcelUtils.write(response, "参数配置.xls", "数据", ConfigRespVO.class,
ConfigConvert.INSTANCE.convertList(list));
}
}

View File

@@ -0,0 +1,29 @@
package com.zt.plat.module.infra.controller.admin.config.vo;
import com.zt.plat.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 参数配置分页 Request VO")
@Data
public class ConfigPageReqVO extends PageParam {
@Schema(description = "数据源名称,模糊匹配", example = "名称")
private String name;
@Schema(description = "参数键名,模糊匹配", example = "yunai.db.username")
private String key;
@Schema(description = "参数类型,参见 SysConfigTypeEnum 枚举", example = "1")
private Integer type;
@Schema(description = "创建时间", example = "[2022-07-01 00:00:00,2022-07-01 23:59:59]")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,56 @@
package com.zt.plat.module.infra.controller.admin.config.vo;
import com.zt.plat.framework.excel.core.annotations.DictFormat;
import com.zt.plat.framework.excel.core.convert.DictConvert;
import com.zt.plat.module.infra.enums.DictTypeConstants;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 参数配置信息 Response VO")
@Data
@ExcelIgnoreUnannotated
public class ConfigRespVO {
@Schema(description = "参数配置序号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@ExcelProperty("参数配置序号")
private Long id;
@Schema(description = "参数分类", requiredMode = Schema.RequiredMode.REQUIRED, example = "biz")
@ExcelProperty("参数分类")
private String category;
@Schema(description = "参数名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "数据库名")
@ExcelProperty("参数名称")
private String name;
@Schema(description = "参数键名", requiredMode = Schema.RequiredMode.REQUIRED, example = "yunai.db.username")
@ExcelProperty("参数键名")
private String key;
@Schema(description = "参数键值", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@ExcelProperty("参数键值")
private String value;
@Schema(description = "参数类型,参见 SysConfigTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty(value = "参数类型", converter = DictConvert.class)
@DictFormat(DictTypeConstants.CONFIG_TYPE)
private Integer type;
@Schema(description = "是否可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@ExcelProperty(value = "是否可见", converter = DictConvert.class)
@DictFormat(DictTypeConstants.BOOLEAN_STRING)
private Boolean visible;
@Schema(description = "备注", example = "备注一下很帅气!")
@ExcelProperty("备注")
private String remark;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式")
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,45 @@
package com.zt.plat.module.infra.controller.admin.config.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
@Schema(description = "管理后台 - 参数配置创建/修改 Request VO")
@Data
public class ConfigSaveReqVO {
@Schema(description = "参数配置序号", example = "1024")
private Long id;
@Schema(description = "参数分组", requiredMode = Schema.RequiredMode.REQUIRED, example = "biz")
@NotEmpty(message = "参数分组不能为空")
@Size(max = 50, message = "参数名称不能超过 50 个字符")
private String category;
@Schema(description = "参数名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "数据库名")
@NotBlank(message = "参数名称不能为空")
@Size(max = 100, message = "参数名称不能超过 100 个字符")
private String name;
@Schema(description = "参数键名", requiredMode = Schema.RequiredMode.REQUIRED, example = "yunai.db.username")
@NotBlank(message = "参数键名长度不能为空")
@Size(max = 100, message = "参数键名长度不能超过 100 个字符")
private String key;
@Schema(description = "参数键值", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotBlank(message = "参数键值不能为空")
@Size(max = 500, message = "参数键值长度不能超过 500 个字符")
private String value;
@Schema(description = "是否可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@NotNull(message = "是否可见不能为空")
private Boolean visible;
@Schema(description = "备注", example = "备注一下很帅气!")
private String remark;
}

View File

@@ -0,0 +1,72 @@
package com.zt.plat.module.infra.controller.admin.db;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.module.infra.controller.admin.db.vo.DataSourceConfigRespVO;
import com.zt.plat.module.infra.controller.admin.db.vo.DataSourceConfigSaveReqVO;
import com.zt.plat.module.infra.dal.dataobject.db.DataSourceConfigDO;
import com.zt.plat.module.infra.service.db.DataSourceConfigService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import java.util.List;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 数据源配置")
@RestController
@RequestMapping("/infra/data-source-config")
@Validated
public class DataSourceConfigController {
@Resource
private DataSourceConfigService dataSourceConfigService;
@PostMapping("/create")
@Operation(summary = "创建数据源配置")
@PreAuthorize("@ss.hasPermission('infra:data-source-config:create')")
public CommonResult<Long> createDataSourceConfig(@Valid @RequestBody DataSourceConfigSaveReqVO createReqVO) {
return success(dataSourceConfigService.createDataSourceConfig(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新数据源配置")
@PreAuthorize("@ss.hasPermission('infra:data-source-config:update')")
public CommonResult<Boolean> updateDataSourceConfig(@Valid @RequestBody DataSourceConfigSaveReqVO updateReqVO) {
dataSourceConfigService.updateDataSourceConfig(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除数据源配置")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('infra:data-source-config:delete')")
public CommonResult<Boolean> deleteDataSourceConfig(@RequestParam("id") Long id) {
dataSourceConfigService.deleteDataSourceConfig(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得数据源配置")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('infra:data-source-config:query')")
public CommonResult<DataSourceConfigRespVO> getDataSourceConfig(@RequestParam("id") Long id) {
DataSourceConfigDO config = dataSourceConfigService.getDataSourceConfig(id);
return success(BeanUtils.toBean(config, DataSourceConfigRespVO.class));
}
@GetMapping("/list")
@Operation(summary = "获得数据源配置列表")
@PreAuthorize("@ss.hasPermission('infra:data-source-config:query')")
public CommonResult<List<DataSourceConfigRespVO>> getDataSourceConfigList() {
List<DataSourceConfigDO> list = dataSourceConfigService.getDataSourceConfigList();
return success(BeanUtils.toBean(list, DataSourceConfigRespVO.class));
}
}

View File

@@ -0,0 +1,27 @@
package com.zt.plat.module.infra.controller.admin.db.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 数据源配置 Response VO")
@Data
public class DataSourceConfigRespVO {
@Schema(description = "主键编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer id;
@Schema(description = "数据源名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "test")
private String name;
@Schema(description = "数据源连接", requiredMode = Schema.RequiredMode.REQUIRED, example = "jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro")
private String url;
@Schema(description = "用户名", requiredMode = Schema.RequiredMode.REQUIRED, example = "root")
private String username;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,31 @@
package com.zt.plat.module.infra.controller.admin.db.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotNull;
@Schema(description = "管理后台 - 数据源配置创建/修改 Request VO")
@Data
public class DataSourceConfigSaveReqVO {
@Schema(description = "主键编号", example = "1024")
private Long id;
@Schema(description = "数据源名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "test")
@NotNull(message = "数据源名称不能为空")
private String name;
@Schema(description = "数据源连接", requiredMode = Schema.RequiredMode.REQUIRED, example = "jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro")
@NotNull(message = "数据源连接不能为空")
private String url;
@Schema(description = "用户名", requiredMode = Schema.RequiredMode.REQUIRED, example = "root")
@NotNull(message = "用户名不能为空")
private String username;
@Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456")
@NotNull(message = "密码不能为空")
private String password;
}

View File

@@ -0,0 +1,102 @@
package com.zt.plat.module.infra.controller.admin.demo.demo01;
import com.zt.plat.framework.apilog.core.annotation.ApiAccessLog;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageParam;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.framework.excel.core.util.ExcelUtils;
import com.zt.plat.module.infra.controller.admin.demo.demo01.vo.Demo01ContactPageReqVO;
import com.zt.plat.module.infra.controller.admin.demo.demo01.vo.Demo01ContactRespVO;
import com.zt.plat.module.infra.controller.admin.demo.demo01.vo.Demo01ContactSaveReqVO;
import com.zt.plat.module.infra.dal.dataobject.demo.demo01.Demo01ContactDO;
import com.zt.plat.module.infra.service.demo.demo01.Demo01ContactService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.List;
import static com.zt.plat.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 示例联系人")
@RestController
@RequestMapping("/infra/demo01-contact")
@Validated
public class Demo01ContactController {
@Resource
private Demo01ContactService demo01ContactService;
@PostMapping("/create")
@Operation(summary = "创建示例联系人")
@PreAuthorize("@ss.hasPermission('infra:demo01-contact:create')")
public CommonResult<Long> createDemo01Contact(@Valid @RequestBody Demo01ContactSaveReqVO createReqVO) {
return success(demo01ContactService.createDemo01Contact(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新示例联系人")
@PreAuthorize("@ss.hasPermission('infra:demo01-contact:update')")
public CommonResult<Boolean> updateDemo01Contact(@Valid @RequestBody Demo01ContactSaveReqVO updateReqVO) {
demo01ContactService.updateDemo01Contact(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除示例联系人")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('infra:demo01-contact:delete')")
public CommonResult<Boolean> deleteDemo01Contact(@RequestParam("id") Long id) {
demo01ContactService.deleteDemo01Contact(id);
return success(true);
}
@DeleteMapping("/delete-list")
@Parameter(name = "ids", description = "编号", required = true)
@Operation(summary = "批量删除示例联系人")
@PreAuthorize("@ss.hasPermission('infra:demo01-contact:delete')")
public CommonResult<Boolean> deleteDemo0iContactList(@RequestParam("ids") List<Long> ids) {
demo01ContactService.deleteDemo0iContactListByIds(ids);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得示例联系人")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('infra:demo01-contact:query')")
public CommonResult<Demo01ContactRespVO> getDemo01Contact(@RequestParam("id") Long id) {
Demo01ContactDO demo01Contact = demo01ContactService.getDemo01Contact(id);
return success(BeanUtils.toBean(demo01Contact, Demo01ContactRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得示例联系人分页")
@PreAuthorize("@ss.hasPermission('infra:demo01-contact:query')")
public CommonResult<PageResult<Demo01ContactRespVO>> getDemo01ContactPage(@Valid Demo01ContactPageReqVO pageReqVO) {
PageResult<Demo01ContactDO> pageResult = demo01ContactService.getDemo01ContactPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, Demo01ContactRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出示例联系人 Excel")
@PreAuthorize("@ss.hasPermission('infra:demo01-contact:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportDemo01ContactExcel(@Valid Demo01ContactPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<Demo01ContactDO> list = demo01ContactService.getDemo01ContactPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "示例联系人.xls", "数据", Demo01ContactRespVO.class,
BeanUtils.toBean(list, Demo01ContactRespVO.class));
}
}

View File

@@ -0,0 +1,26 @@
package com.zt.plat.module.infra.controller.admin.demo.demo01.vo;
import com.zt.plat.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 示例联系人分页 Request VO")
@Data
public class Demo01ContactPageReqVO extends PageParam {
@Schema(description = "名字", example = "张三")
private String name;
@Schema(description = "性别", example = "1")
private Integer sex;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,46 @@
package com.zt.plat.module.infra.controller.admin.demo.demo01.vo;
import com.zt.plat.framework.excel.core.annotations.DictFormat;
import com.zt.plat.framework.excel.core.convert.DictConvert;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 示例联系人 Response VO")
@Data
@ExcelIgnoreUnannotated
public class Demo01ContactRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "21555")
@ExcelProperty("编号")
private Long id;
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
@ExcelProperty("名字")
private String name;
@Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty(value = "性别", converter = DictConvert.class)
@DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
private Integer sex;
@Schema(description = "出生年", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("出生年")
private LocalDateTime birthday;
@Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "你说的对")
@ExcelProperty("简介")
private String description;
@Schema(description = "头像")
@ExcelProperty("头像")
private String avatar;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,36 @@
package com.zt.plat.module.infra.controller.admin.demo.demo01.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 示例联系人新增/修改 Request VO")
@Data
public class Demo01ContactSaveReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "21555")
private Long id;
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
@NotEmpty(message = "名字不能为空")
private String name;
@Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "性别不能为空")
private Integer sex;
@Schema(description = "出生年", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "出生年不能为空")
private LocalDateTime birthday;
@Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "你说的对")
@NotEmpty(message = "简介不能为空")
private String description;
@Schema(description = "头像")
private String avatar;
}

View File

@@ -0,0 +1,90 @@
package com.zt.plat.module.infra.controller.admin.demo.demo02;
import com.zt.plat.framework.apilog.core.annotation.ApiAccessLog;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.framework.excel.core.util.ExcelUtils;
import com.zt.plat.module.infra.controller.admin.demo.demo02.vo.Demo02CategoryListReqVO;
import com.zt.plat.module.infra.controller.admin.demo.demo02.vo.Demo02CategoryRespVO;
import com.zt.plat.module.infra.controller.admin.demo.demo02.vo.Demo02CategorySaveReqVO;
import com.zt.plat.module.infra.dal.dataobject.demo.demo02.Demo02CategoryDO;
import com.zt.plat.module.infra.service.demo.demo02.Demo02CategoryService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.List;
import static com.zt.plat.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 示例分类")
@RestController
@RequestMapping("/infra/demo02-category")
@Validated
public class Demo02CategoryController {
@Resource
private Demo02CategoryService demo02CategoryService;
@PostMapping("/create")
@Operation(summary = "创建示例分类")
@PreAuthorize("@ss.hasPermission('infra:demo02-category:create')")
public CommonResult<Long> createDemo02Category(@Valid @RequestBody Demo02CategorySaveReqVO createReqVO) {
return success(demo02CategoryService.createDemo02Category(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新示例分类")
@PreAuthorize("@ss.hasPermission('infra:demo02-category:update')")
public CommonResult<Boolean> updateDemo02Category(@Valid @RequestBody Demo02CategorySaveReqVO updateReqVO) {
demo02CategoryService.updateDemo02Category(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除示例分类")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('infra:demo02-category:delete')")
public CommonResult<Boolean> deleteDemo02Category(@RequestParam("id") Long id) {
demo02CategoryService.deleteDemo02Category(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得示例分类")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('infra:demo02-category:query')")
public CommonResult<Demo02CategoryRespVO> getDemo02Category(@RequestParam("id") Long id) {
Demo02CategoryDO demo02Category = demo02CategoryService.getDemo02Category(id);
return success(BeanUtils.toBean(demo02Category, Demo02CategoryRespVO.class));
}
@GetMapping("/list")
@Operation(summary = "获得示例分类列表")
@PreAuthorize("@ss.hasPermission('infra:demo02-category:query')")
public CommonResult<List<Demo02CategoryRespVO>> getDemo02CategoryList(@Valid Demo02CategoryListReqVO listReqVO) {
List<Demo02CategoryDO> list = demo02CategoryService.getDemo02CategoryList(listReqVO);
return success(BeanUtils.toBean(list, Demo02CategoryRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出示例分类 Excel")
@PreAuthorize("@ss.hasPermission('infra:demo02-category:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportDemo02CategoryExcel(@Valid Demo02CategoryListReqVO listReqVO,
HttpServletResponse response) throws IOException {
List<Demo02CategoryDO> list = demo02CategoryService.getDemo02CategoryList(listReqVO);
// 导出 Excel
ExcelUtils.write(response, "示例分类.xls", "数据", Demo02CategoryRespVO.class,
BeanUtils.toBean(list, Demo02CategoryRespVO.class));
}
}

View File

@@ -0,0 +1,25 @@
package com.zt.plat.module.infra.controller.admin.demo.demo02.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 示例分类列表 Request VO")
@Data
public class Demo02CategoryListReqVO {
@Schema(description = "名字", example = "芋艿")
private String name;
@Schema(description = "父级编号", example = "6080")
private Long parentId;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,31 @@
package com.zt.plat.module.infra.controller.admin.demo.demo02.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 示例分类 Response VO")
@Data
@ExcelIgnoreUnannotated
public class Demo02CategoryRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10304")
@ExcelProperty("编号")
private Long id;
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
@ExcelProperty("名字")
private String name;
@Schema(description = "父级编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "6080")
@ExcelProperty("父级编号")
private Long parentId;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,23 @@
package com.zt.plat.module.infra.controller.admin.demo.demo02.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "管理后台 - 示例分类新增/修改 Request VO")
@Data
public class Demo02CategorySaveReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10304")
private Long id;
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
@NotEmpty(message = "名字不能为空")
private String name;
@Schema(description = "父级编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "6080")
@NotNull(message = "父级编号不能为空")
private Long parentId;
}

View File

@@ -0,0 +1,209 @@
package com.zt.plat.module.infra.controller.admin.demo.demo03.erp;
import com.zt.plat.framework.apilog.core.annotation.ApiAccessLog;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageParam;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.framework.excel.core.util.ExcelUtils;
import com.zt.plat.module.infra.controller.admin.demo.demo03.erp.vo.Demo03StudentErpPageReqVO;
import com.zt.plat.module.infra.controller.admin.demo.demo03.erp.vo.Demo03StudentErpRespVO;
import com.zt.plat.module.infra.controller.admin.demo.demo03.erp.vo.Demo03StudentErpSaveReqVO;
import com.zt.plat.module.infra.dal.dataobject.demo.demo03.Demo03CourseDO;
import com.zt.plat.module.infra.dal.dataobject.demo.demo03.Demo03GradeDO;
import com.zt.plat.module.infra.dal.dataobject.demo.demo03.Demo03StudentDO;
import com.zt.plat.module.infra.service.demo.demo03.erp.Demo03StudentErpService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.List;
import static com.zt.plat.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 学生")
@RestController
@RequestMapping("/infra/demo03-student-erp")
@Validated
public class Demo03StudentErpController {
@Resource
private Demo03StudentErpService demo03StudentErpService;
@PostMapping("/create")
@Operation(summary = "创建学生")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:create')")
public CommonResult<Long> createDemo03Student(@Valid @RequestBody Demo03StudentErpSaveReqVO createReqVO) {
return success(demo03StudentErpService.createDemo03Student(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新学生")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:update')")
public CommonResult<Boolean> updateDemo03Student(@Valid @RequestBody Demo03StudentErpSaveReqVO updateReqVO) {
demo03StudentErpService.updateDemo03Student(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除学生")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('infra:demo03-student:delete')")
public CommonResult<Boolean> deleteDemo03Student(@RequestParam("id") Long id) {
demo03StudentErpService.deleteDemo03Student(id);
return success(true);
}
@DeleteMapping("/delete-list")
@Parameter(name = "ids", description = "编号", required = true)
@Operation(summary = "批量删除学生")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:delete')")
public CommonResult<Boolean> deleteDemo03StudentList(@RequestParam("ids") List<Long> ids) {
// TODO @puhui999deleteDemo03StudentList
demo03StudentErpService.deleteDemo03StudentListByIds(ids);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得学生")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
public CommonResult<Demo03StudentErpRespVO> getDemo03Student(@RequestParam("id") Long id) {
Demo03StudentDO demo03Student = demo03StudentErpService.getDemo03Student(id);
return success(BeanUtils.toBean(demo03Student, Demo03StudentErpRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得学生分页")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
public CommonResult<PageResult<Demo03StudentErpRespVO>> getDemo03StudentPage(@Valid Demo03StudentErpPageReqVO pageReqVO) {
PageResult<Demo03StudentDO> pageResult = demo03StudentErpService.getDemo03StudentPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, Demo03StudentErpRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出学生 Excel")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportDemo03StudentExcel(@Valid Demo03StudentErpPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<Demo03StudentDO> list = demo03StudentErpService.getDemo03StudentPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "学生.xls", "数据", Demo03StudentErpRespVO.class,
BeanUtils.toBean(list, Demo03StudentErpRespVO.class));
}
// ==================== 子表(学生课程) ====================
@GetMapping("/demo03-course/page")
@Operation(summary = "获得学生课程分页")
@Parameter(name = "studentId", description = "学生编号")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
public CommonResult<PageResult<Demo03CourseDO>> getDemo03CoursePage(PageParam pageReqVO,
@RequestParam("studentId") Long studentId) {
return success(demo03StudentErpService.getDemo03CoursePage(pageReqVO, studentId));
}
@PostMapping("/demo03-course/create")
@Operation(summary = "创建学生课程")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:create')")
public CommonResult<Long> createDemo03Course(@Valid @RequestBody Demo03CourseDO demo03Course) {
return success(demo03StudentErpService.createDemo03Course(demo03Course));
}
@PutMapping("/demo03-course/update")
@Operation(summary = "更新学生课程")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:update')")
public CommonResult<Boolean> updateDemo03Course(@Valid @RequestBody Demo03CourseDO demo03Course) {
demo03StudentErpService.updateDemo03Course(demo03Course);
return success(true);
}
@DeleteMapping("/demo03-course/delete")
@Parameter(name = "id", description = "编号", required = true)
@Operation(summary = "删除学生课程")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:delete')")
public CommonResult<Boolean> deleteDemo03Course(@RequestParam("id") Long id) {
demo03StudentErpService.deleteDemo03Course(id);
return success(true);
}
@DeleteMapping("/demo03-course/delete-list")
@Parameter(name = "ids", description = "编号", required = true)
@Operation(summary = "批量删除学生课程")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:delete')")
public CommonResult<Boolean> deleteDemo03CourseList(@RequestParam("ids") List<Long> ids) {
demo03StudentErpService.deleteDemo03CourseListByIds(ids);
return success(true);
}
@GetMapping("/demo03-course/get")
@Operation(summary = "获得学生课程")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
public CommonResult<Demo03CourseDO> getDemo03Course(@RequestParam("id") Long id) {
return success(demo03StudentErpService.getDemo03Course(id));
}
// ==================== 子表(学生班级) ====================
@GetMapping("/demo03-grade/page")
@Operation(summary = "获得学生班级分页")
@Parameter(name = "studentId", description = "学生编号")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
public CommonResult<PageResult<Demo03GradeDO>> getDemo03GradePage(PageParam pageReqVO,
@RequestParam("studentId") Long studentId) {
return success(demo03StudentErpService.getDemo03GradePage(pageReqVO, studentId));
}
@PostMapping("/demo03-grade/create")
@Operation(summary = "创建学生班级")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:create')")
public CommonResult<Long> createDemo03Grade(@Valid @RequestBody Demo03GradeDO demo03Grade) {
return success(demo03StudentErpService.createDemo03Grade(demo03Grade));
}
@PutMapping("/demo03-grade/update")
@Operation(summary = "更新学生班级")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:update')")
public CommonResult<Boolean> updateDemo03Grade(@Valid @RequestBody Demo03GradeDO demo03Grade) {
demo03StudentErpService.updateDemo03Grade(demo03Grade);
return success(true);
}
@DeleteMapping("/demo03-grade/delete")
@Parameter(name = "id", description = "编号", required = true)
@Operation(summary = "删除学生班级")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:delete')")
public CommonResult<Boolean> deleteDemo03Grade(@RequestParam("id") Long id) {
demo03StudentErpService.deleteDemo03Grade(id);
return success(true);
}
@DeleteMapping("/demo03-grade/delete-list")
@Parameter(name = "ids", description = "编号", required = true)
@Operation(summary = "批量删除学生班级")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:delete')")
public CommonResult<Boolean> deleteDemo03GradeList(@RequestParam("ids") List<Long> ids) {
demo03StudentErpService.deleteDemo03GradeListByIds(ids);
return success(true);
}
@GetMapping("/demo03-grade/get")
@Operation(summary = "获得学生班级")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
public CommonResult<Demo03GradeDO> getDemo03Grade(@RequestParam("id") Long id) {
return success(demo03StudentErpService.getDemo03Grade(id));
}
}

View File

@@ -0,0 +1,29 @@
package com.zt.plat.module.infra.controller.admin.demo.demo03.erp.vo;
import com.zt.plat.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 学生分页 Request VO")
@Data
public class Demo03StudentErpPageReqVO extends PageParam {
@Schema(description = "名字", example = "芋艿")
private String name;
@Schema(description = "性别")
private Integer sex;
@Schema(description = "简介", example = "随便")
private String description;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,42 @@
package com.zt.plat.module.infra.controller.admin.demo.demo03.erp.vo;
import com.zt.plat.framework.excel.core.annotations.DictFormat;
import com.zt.plat.framework.excel.core.convert.DictConvert;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 学生 Response VO")
@Data
@ExcelIgnoreUnannotated
public class Demo03StudentErpRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8525")
@ExcelProperty("编号")
private Long id;
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
@ExcelProperty("名字")
private String name;
@Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty(value = "性别", converter = DictConvert.class)
@DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
private Integer sex;
@Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("出生日期")
private LocalDateTime birthday;
@Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "随便")
@ExcelProperty("简介")
private String description;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,33 @@
package com.zt.plat.module.infra.controller.admin.demo.demo03.erp.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 学生新增/修改 Request VO")
@Data
public class Demo03StudentErpSaveReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8525")
private Long id;
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
@NotEmpty(message = "名字不能为空")
private String name;
@Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "性别不能为空")
private Integer sex;
@Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "出生日期不能为空")
private LocalDateTime birthday;
@Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "随便")
@NotEmpty(message = "简介不能为空")
private String description;
}

View File

@@ -0,0 +1,124 @@
package com.zt.plat.module.infra.controller.admin.demo.demo03.inner;
import com.zt.plat.framework.apilog.core.annotation.ApiAccessLog;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageParam;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.framework.excel.core.util.ExcelUtils;
import com.zt.plat.module.infra.controller.admin.demo.demo03.inner.vo.Demo03StudentInnerPageReqVO;
import com.zt.plat.module.infra.controller.admin.demo.demo03.inner.vo.Demo03StudentInnerRespVO;
import com.zt.plat.module.infra.controller.admin.demo.demo03.inner.vo.Demo03StudentInnerSaveReqVO;
import com.zt.plat.module.infra.dal.dataobject.demo.demo03.Demo03CourseDO;
import com.zt.plat.module.infra.dal.dataobject.demo.demo03.Demo03GradeDO;
import com.zt.plat.module.infra.dal.dataobject.demo.demo03.Demo03StudentDO;
import com.zt.plat.module.infra.service.demo.demo03.inner.Demo03StudentInnerService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.List;
import static com.zt.plat.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 学生")
@RestController
@RequestMapping("/infra/demo03-student-inner")
@Validated
public class Demo03StudentInnerController {
@Resource
private Demo03StudentInnerService demo03StudentInnerService;
@PostMapping("/create")
@Operation(summary = "创建学生")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:create')")
public CommonResult<Long> createDemo03Student(@Valid @RequestBody Demo03StudentInnerSaveReqVO createReqVO) {
return success(demo03StudentInnerService.createDemo03Student(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新学生")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:update')")
public CommonResult<Boolean> updateDemo03Student(@Valid @RequestBody Demo03StudentInnerSaveReqVO updateReqVO) {
demo03StudentInnerService.updateDemo03Student(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除学生")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('infra:demo03-student:delete')")
public CommonResult<Boolean> deleteDemo03Student(@RequestParam("id") Long id) {
demo03StudentInnerService.deleteDemo03Student(id);
return success(true);
}
@DeleteMapping("/delete-list")
@Parameter(name = "ids", description = "编号", required = true)
@Operation(summary = "批量删除学生")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:delete')")
public CommonResult<Boolean> deleteDemo03StudentList(@RequestParam("ids") List<Long> ids) {
demo03StudentInnerService.deleteDemo03StudentListByIds(ids);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得学生")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
public CommonResult<Demo03StudentInnerRespVO> getDemo03Student(@RequestParam("id") Long id) {
Demo03StudentDO demo03Student = demo03StudentInnerService.getDemo03Student(id);
return success(BeanUtils.toBean(demo03Student, Demo03StudentInnerRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得学生分页")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
public CommonResult<PageResult<Demo03StudentInnerRespVO>> getDemo03StudentPage(@Valid Demo03StudentInnerPageReqVO pageReqVO) {
PageResult<Demo03StudentDO> pageResult = demo03StudentInnerService.getDemo03StudentPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, Demo03StudentInnerRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出学生 Excel")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportDemo03StudentExcel(@Valid Demo03StudentInnerPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<Demo03StudentDO> list = demo03StudentInnerService.getDemo03StudentPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "学生.xls", "数据", Demo03StudentInnerRespVO.class,
BeanUtils.toBean(list, Demo03StudentInnerRespVO.class));
}
// ==================== 子表(学生课程) ====================
@GetMapping("/demo03-course/list-by-student-id")
@Operation(summary = "获得学生课程列表")
@Parameter(name = "studentId", description = "学生编号")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
public CommonResult<List<Demo03CourseDO>> getDemo03CourseListByStudentId(@RequestParam("studentId") Long studentId) {
return success(demo03StudentInnerService.getDemo03CourseListByStudentId(studentId));
}
// ==================== 子表(学生班级) ====================
@GetMapping("/demo03-grade/get-by-student-id")
@Operation(summary = "获得学生班级")
@Parameter(name = "studentId", description = "学生编号")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
public CommonResult<Demo03GradeDO> getDemo03GradeByStudentId(@RequestParam("studentId") Long studentId) {
return success(demo03StudentInnerService.getDemo03GradeByStudentId(studentId));
}
}

View File

@@ -0,0 +1,29 @@
package com.zt.plat.module.infra.controller.admin.demo.demo03.inner.vo;
import com.zt.plat.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 学生分页 Request VO")
@Data
public class Demo03StudentInnerPageReqVO extends PageParam {
@Schema(description = "名字", example = "芋艿")
private String name;
@Schema(description = "性别")
private Integer sex;
@Schema(description = "简介", example = "随便")
private String description;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,42 @@
package com.zt.plat.module.infra.controller.admin.demo.demo03.inner.vo;
import com.zt.plat.framework.excel.core.annotations.DictFormat;
import com.zt.plat.framework.excel.core.convert.DictConvert;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 学生 Response VO")
@Data
@ExcelIgnoreUnannotated
public class Demo03StudentInnerRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8525")
@ExcelProperty("编号")
private Long id;
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
@ExcelProperty("名字")
private String name;
@Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty(value = "性别", converter = DictConvert.class)
@DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
private Integer sex;
@Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("出生日期")
private LocalDateTime birthday;
@Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "随便")
@ExcelProperty("简介")
private String description;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,42 @@
package com.zt.plat.module.infra.controller.admin.demo.demo03.inner.vo;
import com.zt.plat.module.infra.dal.dataobject.demo.demo03.Demo03CourseDO;
import com.zt.plat.module.infra.dal.dataobject.demo.demo03.Demo03GradeDO;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 学生新增/修改 Request VO")
@Data
public class Demo03StudentInnerSaveReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8525")
private Long id;
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
@NotEmpty(message = "名字不能为空")
private String name;
@Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "性别不能为空")
private Integer sex;
@Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "出生日期不能为空")
private LocalDateTime birthday;
@Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "随便")
@NotEmpty(message = "简介不能为空")
private String description;
@Schema(description = "学生课程列表")
private List<Demo03CourseDO> demo03Courses;
@Schema(description = "学生班级")
private Demo03GradeDO demo03Grade;
}

View File

@@ -0,0 +1,124 @@
package com.zt.plat.module.infra.controller.admin.demo.demo03.normal;
import com.zt.plat.framework.apilog.core.annotation.ApiAccessLog;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageParam;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.framework.excel.core.util.ExcelUtils;
import com.zt.plat.module.infra.controller.admin.demo.demo03.normal.vo.Demo03StudentNormalPageReqVO;
import com.zt.plat.module.infra.controller.admin.demo.demo03.normal.vo.Demo03StudentNormalRespVO;
import com.zt.plat.module.infra.controller.admin.demo.demo03.normal.vo.Demo03StudentNormalSaveReqVO;
import com.zt.plat.module.infra.dal.dataobject.demo.demo03.Demo03CourseDO;
import com.zt.plat.module.infra.dal.dataobject.demo.demo03.Demo03GradeDO;
import com.zt.plat.module.infra.dal.dataobject.demo.demo03.Demo03StudentDO;
import com.zt.plat.module.infra.service.demo.demo03.normal.Demo03StudentNormalService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.List;
import static com.zt.plat.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 学生")
@RestController
@RequestMapping("/infra/demo03-student-normal")
@Validated
public class Demo03StudentNormalController {
@Resource
private Demo03StudentNormalService demo03StudentNormalService;
@PostMapping("/create")
@Operation(summary = "创建学生")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:create')")
public CommonResult<Long> createDemo03Student(@Valid @RequestBody Demo03StudentNormalSaveReqVO createReqVO) {
return success(demo03StudentNormalService.createDemo03Student(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新学生")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:update')")
public CommonResult<Boolean> updateDemo03Student(@Valid @RequestBody Demo03StudentNormalSaveReqVO updateReqVO) {
demo03StudentNormalService.updateDemo03Student(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除学生")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('infra:demo03-student:delete')")
public CommonResult<Boolean> deleteDemo03Student(@RequestParam("id") Long id) {
demo03StudentNormalService.deleteDemo03Student(id);
return success(true);
}
@DeleteMapping("/delete-list")
@Parameter(name = "ids", description = "编号", required = true)
@Operation(summary = "批量删除学生")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:delete')")
public CommonResult<Boolean> deleteDemo03StudentList(@RequestParam("ids") List<Long> ids) {
demo03StudentNormalService.deleteDemo03StudentListByIds(ids);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得学生")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
public CommonResult<Demo03StudentNormalRespVO> getDemo03Student(@RequestParam("id") Long id) {
Demo03StudentDO demo03Student = demo03StudentNormalService.getDemo03Student(id);
return success(BeanUtils.toBean(demo03Student, Demo03StudentNormalRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得学生分页")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
public CommonResult<PageResult<Demo03StudentNormalRespVO>> getDemo03StudentPage(@Valid Demo03StudentNormalPageReqVO pageReqVO) {
PageResult<Demo03StudentDO> pageResult = demo03StudentNormalService.getDemo03StudentPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, Demo03StudentNormalRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出学生 Excel")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportDemo03StudentExcel(@Valid Demo03StudentNormalPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<Demo03StudentDO> list = demo03StudentNormalService.getDemo03StudentPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "学生.xls", "数据", Demo03StudentNormalRespVO.class,
BeanUtils.toBean(list, Demo03StudentNormalRespVO.class));
}
// ==================== 子表(学生课程) ====================
@GetMapping("/demo03-course/list-by-student-id")
@Operation(summary = "获得学生课程列表")
@Parameter(name = "studentId", description = "学生编号")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
public CommonResult<List<Demo03CourseDO>> getDemo03CourseListByStudentId(@RequestParam("studentId") Long studentId) {
return success(demo03StudentNormalService.getDemo03CourseListByStudentId(studentId));
}
// ==================== 子表(学生班级) ====================
@GetMapping("/demo03-grade/get-by-student-id")
@Operation(summary = "获得学生班级")
@Parameter(name = "studentId", description = "学生编号")
@PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
public CommonResult<Demo03GradeDO> getDemo03GradeByStudentId(@RequestParam("studentId") Long studentId) {
return success(demo03StudentNormalService.getDemo03GradeByStudentId(studentId));
}
}

View File

@@ -0,0 +1,29 @@
package com.zt.plat.module.infra.controller.admin.demo.demo03.normal.vo;
import com.zt.plat.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 学生分页 Request VO")
@Data
public class Demo03StudentNormalPageReqVO extends PageParam {
@Schema(description = "名字", example = "芋艿")
private String name;
@Schema(description = "性别")
private Integer sex;
@Schema(description = "简介", example = "随便")
private String description;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,42 @@
package com.zt.plat.module.infra.controller.admin.demo.demo03.normal.vo;
import com.zt.plat.framework.excel.core.annotations.DictFormat;
import com.zt.plat.framework.excel.core.convert.DictConvert;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 学生 Response VO")
@Data
@ExcelIgnoreUnannotated
public class Demo03StudentNormalRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8525")
@ExcelProperty("编号")
private Long id;
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
@ExcelProperty("名字")
private String name;
@Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty(value = "性别", converter = DictConvert.class)
@DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
private Integer sex;
@Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("出生日期")
private LocalDateTime birthday;
@Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "随便")
@ExcelProperty("简介")
private String description;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,42 @@
package com.zt.plat.module.infra.controller.admin.demo.demo03.normal.vo;
import com.zt.plat.module.infra.dal.dataobject.demo.demo03.Demo03CourseDO;
import com.zt.plat.module.infra.dal.dataobject.demo.demo03.Demo03GradeDO;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 学生新增/修改 Request VO")
@Data
public class Demo03StudentNormalSaveReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8525")
private Long id;
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
@NotEmpty(message = "名字不能为空")
private String name;
@Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "性别不能为空")
private Integer sex;
@Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "出生日期不能为空")
private LocalDateTime birthday;
@Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "随便")
@NotEmpty(message = "简介不能为空")
private String description;
@Schema(description = "学生课程列表")
private List<Demo03CourseDO> demo03Courses;
@Schema(description = "学生班级")
private Demo03GradeDO demo03Grade;
}

View File

@@ -0,0 +1,8 @@
/**
* 代码生成示例
*
* 1. demo01单表增删改查
* 2. demo02单表树形结构
* 3. demo03主子表标准模式+ 主子表ERP 模式)+ 主子表(内嵌模式)
*/
package com.zt.plat.module.infra.controller.admin.demo;

View File

@@ -0,0 +1,288 @@
package com.zt.plat.module.infra.controller.admin.doc;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.enums.UserTypeEnum;
import com.zt.plat.framework.security.core.LoginUser;
import com.zt.plat.framework.security.core.util.SecurityFrameworkUtils;
import com.zt.plat.framework.tenant.core.aop.TenantIgnore;
import com.zt.plat.module.infra.controller.admin.doc.vo.*;
import com.zt.plat.module.infra.convert.doc.DocFileConvert;
import com.zt.plat.module.infra.dal.dataobject.doc.DocFilePermissionDO;
import com.zt.plat.module.infra.dal.dataobject.doc.DocFileVersionDO;
import com.zt.plat.module.infra.service.doc.DocFileService;
import com.zt.plat.module.infra.service.file.FileService;
import cn.hutool.json.JSONUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.util.Map;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 在线文档")
@RestController
@RequestMapping("/infra/doc-file")
@Validated
@Slf4j
public class DocFileController {
@Resource
private DocFileService docFileService;
@Resource
private FileService fileService;
@PostMapping("/create")
@Operation(summary = "创建在线文档")
@PreAuthorize("@ss.hasPermission('infra:doc:create')")
public CommonResult<Long> createDocFile(@Valid @RequestBody DocFileCreateReqVO createReqVO) {
return success(docFileService.createDocFile(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "修改在线文档")
@PreAuthorize("@ss.hasPermission('infra:doc:update')")
public CommonResult<Boolean> updateDocFile(@Valid @RequestBody DocFileUpdateReqVO updateReqVO) {
docFileService.updateDocFile(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除在线文档")
@PreAuthorize("@ss.hasPermission('infra:doc:delete')")
public CommonResult<Boolean> deleteDocFile(@RequestParam("id") Long id) {
docFileService.deleteDocFile(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得在线文档")
@PreAuthorize("@ss.hasPermission('infra:doc:query')")
public CommonResult<DocFileRespVO> getDocFile(@RequestParam("id") Long id) {
DocFileRespVO doc = docFileService.getDocFileWithFileInfo(id);
return success(doc);
}
@GetMapping("/page")
@Operation(summary = "获得在线文档分页")
@PreAuthorize("@ss.hasPermission('infra:doc:query')")
public CommonResult<PageResult<DocFileRespVO>> getDocFilePage(@Valid DocFilePageReqVO pageReqVO) {
PageResult<DocFileRespVO> page = docFileService.getDocFilePageWithFileInfo(pageReqVO);
return success(page);
}
@PostMapping("/upload")
@Operation(summary = "上传文档文件")
@PreAuthorize("@ss.hasPermission('infra:doc:upload')")
public CommonResult<Long> upload(@RequestPart("file") MultipartFile file,
@RequestParam("title") String title,
@RequestParam("spaceType") Integer spaceType,
@RequestParam(value = "description", required = false) String description) {
return success(docFileService.uploadDocFile(file, title, spaceType, description));
}
@GetMapping("/editor-config")
@Operation(summary = "获取 OnlyOffice 编辑器配置")
@PreAuthorize("@ss.hasPermission('infra:doc:edit')")
public CommonResult<DocEditorConfigRespVO> getEditorConfig(@RequestParam("id") Long id) {
return success(docFileService.getEditorConfig(id));
}
@GetMapping("/file-content")
@Operation(summary = "获取文档文件内容")
@PermitAll
public void getFileContent(@RequestParam("fileId") Long fileId,
@RequestParam("token") String token,
HttpServletResponse response) {
docFileService.getDocFileContent(fileId, token, response);
}
@PostMapping("/callback")
@Operation(summary = "OnlyOffice 回调接口")
@PermitAll
@TenantIgnore
public Map<String,Object> callback(@RequestParam("docFileId") Long docFileId,
@RequestParam(value = "userId", required = false) Long triggerUserId,
@RequestBody Map<String,Object> body,
HttpServletRequest request) {
try {
log.info("=== OnlyOffice 回调接口被调用 ===");
log.info("请求URL: {}", request.getRequestURL());
log.info("请求URI: {}", request.getRequestURI());
log.info("请求方法: {}", request.getMethod());
log.info("Content-Type: {}", request.getContentType());
log.info("User-Agent: {}", request.getHeader("User-Agent"));
log.info("客户端IP: {}", getClientIpAddress(request));
log.info("OnlyOffice 回调请求: docFileId={}, triggerUserId={}, body={}", docFileId, triggerUserId, body);
// 如果传入了triggerUserId设置当前请求的登录用户为该用户
if (triggerUserId != null) {
LoginUser mockUser = new LoginUser();
mockUser.setId(triggerUserId);
mockUser.setUserType(UserTypeEnum.ADMIN.getValue());
mockUser.setTenantId(1L); // 默认租户
SecurityFrameworkUtils.setLoginUser(mockUser, request);
log.info("已设置登录用户上下文: userId={}", triggerUserId);
}
Integer status = (Integer) body.get("status");
String downloadUrl = (String) body.get("url");
// 扩展保存策略:更多状态下进行保存
boolean shouldSave = false;
String saveReason = "";
if (status != null) {
switch (status) {
case 1:
// 文档正在编辑中 - 记录活动但不保存
log.info("文档编辑中: docFileId={}, users={}", docFileId, body.get("users"));
break;
case 2:
// 文档已准备保存(用户主动保存或停止编辑)- 立即保存
shouldSave = true;
saveReason = "用户主动保存";
break;
case 3:
// 保存时出错 - 记录错误但不重试保存,避免循环
log.warn("OnlyOffice保存出错: docFileId={}, url={}", docFileId, downloadUrl);
break;
case 4:
// 文档关闭且无错误 - 只在有实际内容时保存
if (downloadUrl != null && !downloadUrl.trim().isEmpty()) {
shouldSave = true;
saveReason = "文档关闭保存";
}
break;
case 6:
// 强制保存(通常是协作冲突解决) - 立即保存
shouldSave = true;
saveReason = "协作保存";
break;
case 7:
// 保存时出错但继续编辑 - 记录错误但不重试
log.warn("OnlyOffice编辑中保存出错: docFileId={}, url={}", docFileId, downloadUrl);
break;
default:
log.info("OnlyOffice未处理的回调状态: docFileId={}, status={}", docFileId, status);
}
}
if (shouldSave) {
if (downloadUrl == null || downloadUrl.trim().isEmpty()) {
log.error("OnlyOffice 回调 URL 为空: docFileId={}, status={}, reason={}", docFileId, status, saveReason);
return Map.of("error", 1, "message", "下载URL为空");
}
log.info("开始保存文档: docFileId={}, triggerUserId={}, status={}, reason={}, url={}",
docFileId, triggerUserId, status, saveReason, downloadUrl);
docFileService.saveDocumentContent(docFileId, downloadUrl, saveReason, triggerUserId);
log.info("文档保存成功: docFileId={}, triggerUserId={}, reason={}", docFileId, triggerUserId, saveReason);
}
// 按 OnlyOffice 协议返回 {error:0}
return Map.of("error", 0);
} catch (Exception e) {
log.error("OnlyOffice 回调处理失败: docFileId={}, body={}", docFileId, body, e);
return Map.of("error", 1, "message", e.getMessage());
}
}
// ------------------- 权限 -------------------
@PostMapping("/permission/set")
@Operation(summary = "设置文档权限")
@PreAuthorize("@ss.hasPermission('infra:doc:permission')")
public CommonResult<Boolean> setPermission(@Valid @RequestBody DocFilePermissionReqVO permissionReqVO) {
docFileService.setDocFilePermission(permissionReqVO);
return success(true);
}
@GetMapping("/permission/list")
@Operation(summary = "获得文档权限列表")
@PreAuthorize("@ss.hasPermission('infra:doc:permission')")
public CommonResult<List<DocFilePermissionRespVO>> getPermissionList(@RequestParam("docFileId") Long docFileId) {
List<DocFilePermissionDO> list = docFileService.getDocFilePermissions(docFileId);
return success(DocFileConvert.INSTANCE.convertPermissionList(list));
}
@DeleteMapping("/permission/delete")
@Operation(summary = "删除文档权限")
@PreAuthorize("@ss.hasPermission('infra:doc:permission')")
public CommonResult<Boolean> deletePermission(@RequestParam("docFileId") Long docFileId,
@RequestParam("roleId") Long roleId) {
docFileService.deleteDocFilePermission(docFileId, roleId);
return success(true);
}
// ========== 版本管理 ==========
@GetMapping("/version/list")
@Operation(summary = "获取文档版本列表")
@PreAuthorize("@ss.hasPermission('infra:doc:query')")
public CommonResult<List<DocFileVersionRespVO>> getVersionList(@RequestParam("docFileId") Long docFileId) {
List<DocFileVersionDO> list = docFileService.getDocFileVersions(docFileId);
List<DocFileVersionRespVO> result = DocFileConvert.INSTANCE.convertVersionList(list);
// 填充文件信息
for (DocFileVersionRespVO vo : result) {
if (vo.getFileId() != null) {
try {
var fileInfo = fileService.getActiveFileById(vo.getFileId());
if (fileInfo != null) {
vo.setFileName(fileInfo.getName());
vo.setFileSize(fileInfo.getSize() != null ? fileInfo.getSize().longValue() : 0L);
}
} catch (Exception e) {
log.warn("获取版本文件信息失败: versionId={}, fileId={}", vo.getId(), vo.getFileId(), e);
}
}
}
return success(result);
}
@PostMapping("/version/restore")
@Operation(summary = "恢复到指定版本")
@PreAuthorize("@ss.hasPermission('infra:doc:update')")
public CommonResult<Boolean> restoreToVersion(@RequestParam("docFileId") Long docFileId,
@RequestParam("versionId") Long versionId) {
docFileService.restoreDocFileToVersion(docFileId, versionId);
return success(true);
}
/**
* 获取客户端真实IP地址
*/
private String getClientIpAddress(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
}

View File

@@ -0,0 +1,99 @@
package com.zt.plat.module.infra.controller.admin.doc.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - OnlyOffice 编辑器配置 Response VO")
@Data
public class DocEditorConfigRespVO {
@Schema(description = "文档类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "text")
private String documentType;
@Schema(description = "文档配置", requiredMode = Schema.RequiredMode.REQUIRED)
private DocumentConfig document;
@Schema(description = "编辑器配置", requiredMode = Schema.RequiredMode.REQUIRED)
private EditorConfig editorConfig;
@Schema(description = "文档高度", requiredMode = Schema.RequiredMode.REQUIRED, example = "100%")
private String height;
@Schema(description = "令牌", requiredMode = Schema.RequiredMode.REQUIRED)
private String token;
@Schema(description = "OnlyOffice服务器地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "http://localhost:8085")
private String documentServerUrl;
@Schema(description = "文档配置")
@Data
public static class DocumentConfig {
@Schema(description = "文件类型", example = "docx")
private String fileType;
@Schema(description = "文档key", example = "doc123")
private String key;
@Schema(description = "文档标题", example = "技术文档")
private String title;
@Schema(description = "文档地址")
private String url;
@Schema(description = "权限配置")
private Permissions permissions;
}
@Schema(description = "权限配置")
@Data
public static class Permissions {
@Schema(description = "是否可编辑", example = "true")
private Boolean edit;
@Schema(description = "是否可下载", example = "true")
private Boolean download;
@Schema(description = "是否可打印", example = "true")
private Boolean print;
}
@Schema(description = "编辑器配置")
@Data
public static class EditorConfig {
@Schema(description = "回调地址")
private String callbackUrl;
@Schema(description = "语言", example = "zh-CN")
private String lang;
@Schema(description = "模式", example = "edit")
private String mode;
@Schema(description = "用户配置")
private User user;
@Schema(description = "协作配置")
private CoEditing coEditing;
}
@Schema(description = "用户配置")
@Data
public static class User {
@Schema(description = "用户ID", example = "1")
private String id;
@Schema(description = "用户名", example = "admin")
private String name;
}
@Schema(description = "协作配置")
@Data
public static class CoEditing {
@Schema(description = "协作模式", example = "fast")
private String mode;
@Schema(description = "是否允许更改", example = "true")
private Boolean change;
}
}

View File

@@ -0,0 +1,31 @@
package com.zt.plat.module.infra.controller.admin.doc.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
@Schema(description = "管理后台 - 在线文档创建 Request VO")
@Data
public class DocFileCreateReqVO {
@Schema(description = "文档标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "技术文档")
@NotEmpty(message = "文档标题不能为空")
private String title;
@Schema(description = "文件类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "docx")
@NotEmpty(message = "文件类型不能为空")
private String fileType;
@Schema(description = "空间类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "空间类型不能为空")
private Integer spaceType;
@Schema(description = "文档描述", example = "这是一个技术文档")
private String description;
@Schema(description = "文件编号", example = "1")
private Long fileId;
}

View File

@@ -0,0 +1,36 @@
package com.zt.plat.module.infra.controller.admin.doc.vo;
import com.zt.plat.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 在线文档分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class DocFilePageReqVO extends PageParam {
@Schema(description = "文档标题", example = "技术文档")
private String title;
@Schema(description = "文件类型", example = "docx")
private String fileType;
@Schema(description = "空间类型", example = "1")
private Integer spaceType;
@Schema(description = "状态", example = "1")
private Integer status;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,28 @@
package com.zt.plat.module.infra.controller.admin.doc.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 文档权限 Request VO")
@Data
public class DocFilePermissionReqVO {
@Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "文档编号不能为空")
private Long docFileId;
@Schema(description = "角色编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "角色编号不能为空")
private Long roleId;
@Schema(description = "权限类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "权限类型不能为空")
private Integer permissionType;
@Schema(description = "过期时间", example = "2024-12-31 23:59:59")
private LocalDateTime expireTime;
}

View File

@@ -0,0 +1,33 @@
package com.zt.plat.module.infra.controller.admin.doc.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 文档权限 Response VO")
@Data
public class DocFilePermissionRespVO {
@Schema(description = "权限编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long docFileId;
@Schema(description = "角色编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long roleId;
@Schema(description = "角色名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "管理员")
private String roleName;
@Schema(description = "权限类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer permissionType;
@Schema(description = "过期时间", example = "2024-12-31 23:59:59")
private LocalDateTime expireTime;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,52 @@
package com.zt.plat.module.infra.controller.admin.doc.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 在线文档 Response VO")
@Data
public class DocFileRespVO {
@Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "文档标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "技术文档")
private String title;
@Schema(description = "文件编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
private Long fileId;
@Schema(description = "文件类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "docx")
private String fileType;
@Schema(description = "空间类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer spaceType;
@Schema(description = "文档描述", example = "这是一个技术文档")
private String description;
@Schema(description = "最新版本编号", example = "3072")
private Long latestVersionId;
@Schema(description = "所有者用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long ownerUserId;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
// 以下字段从 fileService 动态获取
@Schema(description = "文件名称", example = "document.docx")
private String fileName;
@Schema(description = "文件大小", example = "1024")
private Long fileSize;
@Schema(description = "文件访问地址", example = "http://127.0.0.1:48080/admin-api/infra/file/4/get/37e56010ecbee472cdd821ac073b8")
private String fileUrl;
}

View File

@@ -0,0 +1,22 @@
package com.zt.plat.module.infra.controller.admin.doc.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotNull;
@Schema(description = "管理后台 - 在线文档更新 Request VO")
@Data
public class DocFileUpdateReqVO {
@Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "文档编号不能为空")
private Long id;
@Schema(description = "文档标题", example = "技术文档")
private String title;
@Schema(description = "文档描述", example = "这是一个技术文档")
private String description;
}

View File

@@ -0,0 +1,40 @@
package com.zt.plat.module.infra.controller.admin.doc.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 文档版本 Response VO")
@Data
public class DocFileVersionRespVO {
@Schema(description = "版本编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long docFileId;
@Schema(description = "版本号", requiredMode = Schema.RequiredMode.REQUIRED, example = "v1")
private String versionNo;
@Schema(description = "文件编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long fileId;
@Schema(description = "变更说明", example = "修改文档内容")
private String changeDescription;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
@Schema(description = "创建者", example = "1")
private String creator;
// 额外字段,从文件服务获取
@Schema(description = "文件名称", example = "document.docx")
private String fileName;
@Schema(description = "文件大小", example = "1024")
private Long fileSize;
}

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.cloud.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.cloud.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,88 @@
package com.zt.plat.module.infra.controller.admin.file;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.module.infra.controller.admin.file.vo.config.FileConfigPageReqVO;
import com.zt.plat.module.infra.controller.admin.file.vo.config.FileConfigRespVO;
import com.zt.plat.module.infra.controller.admin.file.vo.config.FileConfigSaveReqVO;
import com.zt.plat.module.infra.dal.dataobject.file.FileConfigDO;
import com.zt.plat.module.infra.service.file.FileConfigService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 文件配置")
@RestController
@RequestMapping("/infra/file-config")
@Validated
public class FileConfigController {
@Resource
private FileConfigService fileConfigService;
@PostMapping("/create")
@Operation(summary = "创建文件配置")
@PreAuthorize("@ss.hasPermission('infra:file-config:create')")
public CommonResult<Long> createFileConfig(@Valid @RequestBody FileConfigSaveReqVO createReqVO) {
return success(fileConfigService.createFileConfig(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新文件配置")
@PreAuthorize("@ss.hasPermission('infra:file-config:update')")
public CommonResult<Boolean> updateFileConfig(@Valid @RequestBody FileConfigSaveReqVO updateReqVO) {
fileConfigService.updateFileConfig(updateReqVO);
return success(true);
}
@PutMapping("/update-master")
@Operation(summary = "更新文件配置为 Master")
@PreAuthorize("@ss.hasPermission('infra:file-config:update')")
public CommonResult<Boolean> updateFileConfigMaster(@RequestParam("id") Long id) {
fileConfigService.updateFileConfigMaster(id);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除文件配置")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('infra:file-config:delete')")
public CommonResult<Boolean> deleteFileConfig(@RequestParam("id") Long id) {
fileConfigService.deleteFileConfig(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得文件配置")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('infra:file-config:query')")
public CommonResult<FileConfigRespVO> getFileConfig(@RequestParam("id") Long id) {
FileConfigDO config = fileConfigService.getFileConfig(id);
return success(BeanUtils.toBean(config, FileConfigRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得文件配置分页")
@PreAuthorize("@ss.hasPermission('infra:file-config:query')")
public CommonResult<PageResult<FileConfigRespVO>> getFileConfigPage(@Valid FileConfigPageReqVO pageVO) {
PageResult<FileConfigDO> pageResult = fileConfigService.getFileConfigPage(pageVO);
return success(BeanUtils.toBean(pageResult, FileConfigRespVO.class));
}
@GetMapping("/test")
@Operation(summary = "测试文件配置是否正确")
@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,179 @@
package com.zt.plat.module.infra.controller.admin.file;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import com.zt.plat.framework.common.exception.ServiceException;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.framework.tenant.core.aop.TenantIgnore;
import com.zt.plat.module.infra.controller.admin.file.vo.file.*;
import com.zt.plat.module.infra.dal.dataobject.file.FileDO;
import com.zt.plat.module.infra.service.file.FileService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
import static com.zt.plat.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static com.zt.plat.module.infra.framework.file.core.utils.FileTypeUtils.writeAttachment;
/**
* @author chenbowen
*/
@Tag(name = "管理后台 - 文件存储")
@RestController
@RequestMapping("/infra/file")
@Validated
@Slf4j
public class FileController {
@Resource
private FileService fileService;
@GetMapping("/get")
@Operation(summary = "获取文件预览地址", description = "根据 fileId 返回文件预览 urlkkfile")
public CommonResult<FileRespVO> getPreviewUrl(@RequestParam("fileId") Long fileId) {
FileDO fileDO = fileService.getActiveFileById(fileId);
if (fileDO == null) {
return CommonResult.error(HttpStatus.NOT_FOUND.value(), "文件不存在");
}
// FileDO 转换为 FileRespVO
FileRespVO fileRespVO = BeanUtils.toBean(fileDO, FileRespVO.class);
return success(fileRespVO);
}
@PostMapping("/upload")
@Operation(summary = "上传文件", description = "模式一:后端上传文件")
public CommonResult<String> uploadFile(FileUploadReqVO uploadReqVO) throws Exception {
MultipartFile file = uploadReqVO.getFile();
byte[] content = IoUtil.readBytes(file.getInputStream());
return success(fileService.createFile(content, file.getOriginalFilename(), uploadReqVO.getDirectory(), file.getContentType(), uploadReqVO.getEncrypt()));
}
@PostMapping("/upload-with-return")
@Operation(summary = "上传文件携带返回实体", description = "上传文件携带返回实体")
public CommonResult<FileRespVO> uploadWithReturn(FileUploadReqVO uploadReqVO) throws IOException {
MultipartFile file = uploadReqVO.getFile();
byte[] content = IoUtil.readBytes(file.getInputStream());
FileRespVO fileWhitReturn = fileService.createFileWhitReturn(content, file.getOriginalFilename(), uploadReqVO.getDirectory(), file.getContentType(), uploadReqVO.getEncrypt());
return success(fileWhitReturn);
}
@GetMapping("/presigned-url")
@Operation(summary = "获取文件预签名地址", description = "模式二:前端上传文件:用于前端直接上传七牛、阿里云 OSS 等文件存储器")
@Parameters({
@Parameter(name = "name", description = "文件名称", required = true),
@Parameter(name = "directory", description = "文件目录")
})
public CommonResult<FilePresignedUrlRespVO> getFilePresignedUrl(
@RequestParam("name") String name,
@RequestParam(value = "directory", required = false) String directory) {
return success(fileService.getFilePresignedUrl(name, directory));
}
@PostMapping("/create")
@Operation(summary = "创建文件", description = "模式二:前端上传文件:配合 presigned-url 接口,记录上传了上传的文件")
public CommonResult<Long> createFile(@Valid @RequestBody FileCreateReqVO createReqVO) {
return success(fileService.createFile(createReqVO));
}
@DeleteMapping("/delete")
@Operation(summary = "删除文件")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('infra:file:delete')")
public CommonResult<Boolean> deleteFile(@RequestParam("id") Long id) throws Exception {
fileService.deleteFile(id);
return success(true);
}
@GetMapping("/{configId}/get/**")
@PermitAll
@TenantIgnore
@Operation(summary = "下载文件")
@Parameter(name = "configId", description = "配置编号", required = true)
public void getFileContent(HttpServletRequest request,
HttpServletResponse response,
@PathVariable("configId") Long configId) throws Exception {
// 获取请求的路径
String path = StrUtil.subAfter(request.getRequestURI(), "/get/", false);
if (StrUtil.isEmpty(path)) {
throw new IllegalArgumentException("结尾的 path 路径必须传递");
}
// 解码,解决中文路径的问题 https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/807/
path = URLUtil.decode(path);
// 读取内容
byte[] content = fileService.getFileContent(configId, path);
if (content == null) {
log.warn("[getFileContent][configId({}) path({}) 文件不存在]", configId, path);
response.setStatus(HttpStatus.NOT_FOUND.value());
return;
}
writeAttachment(response, path, content);
}
@GetMapping("/page")
@Operation(summary = "获得文件分页")
@PreAuthorize("@ss.hasPermission('infra:file:query')")
public CommonResult<PageResult<FileRespVO>> getFilePage(@Valid FilePageReqVO pageVO) {
PageResult<FileDO> pageResult = fileService.getFilePage(pageVO);
return success(BeanUtils.toBean(pageResult, FileRespVO.class));
}
@GetMapping("/verify-and-download")
@Operation(summary = "校验验证码并下载解密文件")
public void verifyAndDownload(@Valid @RequestParam Long fileId, @RequestParam String code, HttpServletResponse response) throws Exception {
Long userId = getLoginUserId();
byte[] content = fileService.verifyCodeAndGetFile(fileId, userId, code);
FileDO fileDO = fileService.getActiveFileById(fileId);
if (fileDO == null) {
response.setStatus(HttpStatus.NOT_FOUND.value());
return;
}
writeAttachment(response, fileDO.getName(), content);
}
@GetMapping("/generate-download-code")
@Operation(summary = "获取下载验证码")
public CommonResult<FileRespVO> preDownloadEncrypt(@RequestParam("fileId") Long fileId) {
Long userId = getLoginUserId();
FileDO activeFileById = fileService.getActiveFileById(fileId);
if (activeFileById == null) {
return CommonResult.error(HttpStatus.NOT_FOUND.value(), "文件不存在");
}
FileRespVO fileRespVO = BeanUtils.toBean(activeFileById, FileRespVO.class);
try {
fileService.generateFileVerificationCode(fileId, userId);
return CommonResult.customize(fileRespVO, HttpStatus.OK.value(), "验证码已生成,请使用验证码下载文件");
} catch (ServiceException e) {
return CommonResult.customize(fileRespVO, HttpStatus.OK.value(), e.getMessage());
}
}
@GetMapping("/verify-code")
@Operation(summary = "校验验证码")
public CommonResult<String> verifyCode(@Valid @RequestParam Long fileId, @RequestParam String code) throws Exception {
Long userId = getLoginUserId();
boolean flag = fileService.verifyCode(fileId, userId, code);
if(!flag){
return CommonResult.customize(null, HttpStatus.INTERNAL_SERVER_ERROR.value(), "验证码错误");
}
return CommonResult.customize(null, HttpStatus.OK.value(), "验证码校验通过");
}
}

View File

@@ -0,0 +1,26 @@
package com.zt.plat.module.infra.controller.admin.file.vo.config;
import com.zt.plat.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 文件配置分页 Request VO")
@Data
public class FileConfigPageReqVO extends PageParam {
@Schema(description = "配置名", example = "S3 - 阿里云")
private String name;
@Schema(description = "存储器", example = "1")
private Integer storage;
@Schema(description = "创建时间", example = "[2022-07-01 00:00:00, 2022-07-01 23:59:59]")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,34 @@
package com.zt.plat.module.infra.controller.admin.file.vo.config;
import com.zt.plat.module.infra.framework.file.core.client.FileClientConfig;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 文件配置 Response VO")
@Data
public class FileConfigRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "配置名", requiredMode = Schema.RequiredMode.REQUIRED, example = "S3 - 阿里云")
private String name;
@Schema(description = "存储器,参见 FileStorageEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer storage;
@Schema(description = "是否为主配置", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean master;
@Schema(description = "存储配置", requiredMode = Schema.RequiredMode.REQUIRED)
private FileClientConfig config;
@Schema(description = "备注", example = "我是备注")
private String remark;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,31 @@
package com.zt.plat.module.infra.controller.admin.file.vo.config;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotNull;
import java.util.Map;
@Schema(description = "管理后台 - 文件配置创建/修改 Request VO")
@Data
public class FileConfigSaveReqVO {
@Schema(description = "编号", example = "1")
private Long id;
@Schema(description = "配置名", requiredMode = Schema.RequiredMode.REQUIRED, example = "S3 - 阿里云")
@NotNull(message = "配置名不能为空")
private String name;
@Schema(description = "存储器,参见 FileStorageEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "存储器不能为空")
private Integer storage;
@Schema(description = "存储配置,配置是动态参数,所以使用 Map 接收", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "存储配置不能为空")
private Map<String, Object> config;
@Schema(description = "备注", example = "我是备注")
private String remark;
}

View File

@@ -0,0 +1,33 @@
package com.zt.plat.module.infra.controller.admin.file.vo.file;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "管理后台 - 文件创建 Request VO")
@Data
public class FileCreateReqVO {
@NotNull(message = "文件配置编号不能为空")
@Schema(description = "文件配置编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11")
private Long configId;
@NotNull(message = "文件路径不能为空")
@Schema(description = "文件路径", requiredMode = Schema.RequiredMode.REQUIRED, example = "cloud.jpg")
private String path;
@NotNull(message = "原文件名不能为空")
@Schema(description = "原文件名", requiredMode = Schema.RequiredMode.REQUIRED, example = "cloud.jpg")
private String name;
@NotNull(message = "文件 URL不能为空")
@Schema(description = "文件 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/cloud.jpg")
private String url;
@Schema(description = "文件 MIME 类型", example = "application/octet-stream")
private String type;
@Schema(description = "文件大小", example = "2048", requiredMode = Schema.RequiredMode.REQUIRED)
private Integer size;
}

View File

@@ -0,0 +1,26 @@
package com.zt.plat.module.infra.controller.admin.file.vo.file;
import com.zt.plat.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 文件分页 Request VO")
@Data
public class FilePageReqVO extends PageParam {
@Schema(description = "文件路径,模糊匹配", example = "cloud")
private String path;
@Schema(description = "文件类型,模糊匹配", example = "jpg")
private String type;
@Schema(description = "创建时间", example = "[2022-07-01 00:00:00, 2022-07-01 23:59:59]")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,38 @@
package com.zt.plat.module.infra.controller.admin.file.vo.file;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "管理后台 - 文件预签名地址 Response VO")
@Data
public class FilePresignedUrlRespVO {
@Schema(description = "配置编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11")
private Long configId;
@Schema(description = "文件上传 URL", requiredMode = Schema.RequiredMode.REQUIRED,
example = "https://s3.cn-south-1.qiniucs.com/ruoyi-vue-pro/758d3a5387507358c7236de4c8f96de1c7f5097ff6a7722b34772fb7b76b140f.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=3TvrJ70gl2Gt6IBe7_IZT1F6i_k0iMuRtyEv4EyS%2F20240217%2Fcn-south-1%2Fs3%2Faws4_request&X-Amz-Date=20240217T123222Z&X-Amz-Expires=600&X-Amz-SignedHeaders=host&X-Amz-Signature=a29f33770ab79bf523ccd4034d0752ac545f3c2a3b17baa1eb4e280cfdccfda5")
private String uploadUrl;
/**
* 为什么要返回 url 字段?
*
* 前端上传完文件后,需要使用该 URL 进行访问
*/
@Schema(description = "文件访问 URL", requiredMode = Schema.RequiredMode.REQUIRED,
example = "https://test.cloud.iocoder.cn/758d3a5387507358c7236de4c8f96de1c7f5097ff6a7722b34772fb7b76b140f.png")
private String url;
/**
* 为什么要返回 path 字段?
*
* 前端上传完文件后,需要调用 createFile 记录下 path 路径
*/
@Schema(description = "文件路径", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxx.png")
private String path;
}

View File

@@ -0,0 +1,102 @@
package com.zt.plat.module.infra.controller.admin.file.vo.file;
import cn.hutool.core.date.DateUtil;
import com.zt.plat.framework.common.util.spring.SpringUtils;
import com.zt.plat.framework.security.core.util.SecurityFrameworkUtils;
import com.zt.plat.module.infra.framework.file.core.client.FileClient;
import com.zt.plat.module.infra.framework.file.core.client.s3.S3FileClient;
import com.zt.plat.module.infra.service.file.FileConfigService;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Base64;
import java.util.Date;
/**
* @author chenbowen
*/
@Schema(description = "管理后台 - 文件 Response VO,不返回 content 字段,太大")
@Data
@Accessors(chain = true)
public class FileRespVO {
public String getUrl() {
// 加密附件不返回 url
if (Boolean.TRUE.equals(this.isEncrypted)) {
return null;
}
// 如果 url 已经是临时下载地址(如预签名 URL直接返回
if (url != null && (url.contains("X-Amz-Signature") || url.contains("?sign="))) {
return url;
}
FileConfigService fileConfigService = SpringUtils.getBean(FileConfigService.class);
FileClient fileClient = fileConfigService.getMasterFileClient();
if (fileClient instanceof S3FileClient s3FileClient) {
String presignedDownloadUrl = s3FileClient.getPresignedDownloadUrl(this.path, null);
if (presignedDownloadUrl != null && !presignedDownloadUrl.isEmpty()) {
return presignedDownloadUrl;
}
}
return url;
}
@Schema(description = "文件编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "配置编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11")
private Long configId;
@Schema(description = "文件路径", requiredMode = Schema.RequiredMode.REQUIRED, example = "cloud.jpg")
private String path;
@Schema(description = "原文件名", requiredMode = Schema.RequiredMode.REQUIRED, example = "cloud.jpg")
private String name;
@Schema(description = "文件 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/cloud.jpg")
private String url;
// 附件预览地址
@Schema(description = "附件预览地址", example = "https://www.iocoder.cn/cloud.jpg")
private String previewUrl;
public String getPreviewUrl() {
// 加密附件不返回 previewUrl
if (Boolean.TRUE.equals(this.isEncrypted)) {
return null;
}
// 仅当 url 不为空时生成
if (this.url == null || this.url.isEmpty()) {
return null;
}
// 这里的 onlinePreview 通过 SpringUtils 获取
String onlinePreview = SpringUtils.getProperty("cloud.kkfile");
if (onlinePreview == null || onlinePreview.isEmpty()) {
return null;
}
String presignedUrl = this.getUrl();
if (presignedUrl == null || presignedUrl.isEmpty()) {
return null;
}
String base64PresignedUrl = Base64.getEncoder().encodeToString(presignedUrl.getBytes(StandardCharsets.UTF_8));
String timestamp = String.valueOf(System.currentTimeMillis());
String loginUserNickname = SecurityFrameworkUtils.getLoginUserNickname();
String format = DateUtil.format(new Date(), "yyyy-MM-dd");
String watermark = SpringUtils.getProperty("aj.captcha.water-mark", loginUserNickname+" "+ format);
return onlinePreview + base64PresignedUrl + "&t=" + timestamp + "&watermarkTxt=" + watermark;
}
@Schema(description = "是否加密", example = "false")
private Boolean isEncrypted;
@Schema(description = "文件MIME类型", example = "application/octet-stream")
private String type;
@Schema(description = "文件大小", example = "2048", requiredMode = Schema.RequiredMode.REQUIRED)
private Integer size;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,25 @@
package com.zt.plat.module.infra.controller.admin.file.vo.file;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
/**
* @author chenbowen
*/
@Schema(description = "管理后台 - 上传文件 Request VO")
@Data
public class FileUploadReqVO {
@Schema(description = "文件附件", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "文件附件不能为空")
private MultipartFile file;
@Schema(description = "文件目录", example = "XXX/YYY")
private String directory;
@Schema(description = "是否加密", example = "")
private Boolean encrypt = false;
}

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