新增快照功能 优化商品查询逻辑 快递对接完成 延时售后问题修复

This commit is contained in:
徐唯轩
2023-11-22 19:06:46 +08:00
parent 3495a23492
commit 690f9cfe4c
328 changed files with 17752 additions and 0 deletions

View File

@ -0,0 +1,41 @@
package co.yixiang.yshop.module.infra.api.file;
/**
* 文件 API 接口
*
* @author yshop
*/
public interface FileApi {
/**
* 保存文件,并返回文件的访问路径
*
* @param content 文件内容
* @return 文件路径
*/
default String createFile(byte[] content) {
return createFile(null, null, content);
}
/**
* 保存文件,并返回文件的访问路径
*
* @param path 文件路径
* @param content 文件内容
* @return 文件路径
*/
default String createFile(String path, byte[] content) {
return createFile(null, path, content);
}
/**
* 保存文件,并返回文件的访问路径
*
* @param name 文件名称
* @param path 文件路径
* @param content 文件内容
* @return 文件路径
*/
String createFile(String name, String path, byte[] content);
}

View File

@ -0,0 +1,21 @@
package co.yixiang.yshop.module.infra.api.logger;
import co.yixiang.yshop.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO;
import javax.validation.Valid;
/**
* API 访问日志的 API 接口
*
* @author yshop
*/
public interface ApiAccessLogApi {
/**
* 创建 API 访问日志
*
* @param createDTO 创建信息
*/
void createApiAccessLog(@Valid ApiAccessLogCreateReqDTO createDTO);
}

View File

@ -0,0 +1,21 @@
package co.yixiang.yshop.module.infra.api.logger;
import co.yixiang.yshop.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO;
import javax.validation.Valid;
/**
* API 错误日志的 API 接口
*
* @author yshop
*/
public interface ApiErrorLogApi {
/**
* 创建 API 错误日志
*
* @param createDTO 创建信息
*/
void createApiErrorLog(@Valid ApiErrorLogCreateReqDTO createDTO);
}

View File

@ -0,0 +1,85 @@
package co.yixiang.yshop.module.infra.api.logger.dto;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
/**
* API 访问日志
*
* @author yshop
*/
@Data
public class ApiAccessLogCreateReqDTO {
/**
* 链路追踪编号
*/
private String traceId;
/**
* 用户编号
*/
private Long userId;
/**
* 用户类型
*/
private Integer userType;
/**
* 应用名
*/
@NotNull(message = "应用名不能为空")
private String applicationName;
/**
* 请求方法名
*/
@NotNull(message = "http 请求方法不能为空")
private String requestMethod;
/**
* 访问地址
*/
@NotNull(message = "访问地址不能为空")
private String requestUrl;
/**
* 请求参数
*/
@NotNull(message = "请求参数不能为空")
private String requestParams;
/**
* 用户 IP
*/
@NotNull(message = "ip 不能为空")
private String userIp;
/**
* 浏览器 UA
*/
@NotNull(message = "User-Agent 不能为空")
private String userAgent;
/**
* 开始请求时间
*/
@NotNull(message = "开始请求时间不能为空")
private LocalDateTime beginTime;
/**
* 结束请求时间
*/
@NotNull(message = "结束请求时间不能为空")
private LocalDateTime endTime;
/**
* 执行时长,单位:毫秒
*/
@NotNull(message = "执行时长不能为空")
private Integer duration;
/**
* 结果码
*/
@NotNull(message = "错误码不能为空")
private Integer resultCode;
/**
* 结果提示
*/
private String resultMsg;
}

View File

@ -0,0 +1,107 @@
package co.yixiang.yshop.module.infra.api.logger.dto;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
/**
* API 错误日志
*
* @author yshop
*/
@Data
public class ApiErrorLogCreateReqDTO {
/**
* 链路编号
*/
private String traceId;
/**
* 账号编号
*/
private Long userId;
/**
* 用户类型
*/
private Integer userType;
/**
* 应用名
*/
@NotNull(message = "应用名不能为空")
private String applicationName;
/**
* 请求方法名
*/
@NotNull(message = "http 请求方法不能为空")
private String requestMethod;
/**
* 访问地址
*/
@NotNull(message = "访问地址不能为空")
private String requestUrl;
/**
* 请求参数
*/
@NotNull(message = "请求参数不能为空")
private String requestParams;
/**
* 用户 IP
*/
@NotNull(message = "ip 不能为空")
private String userIp;
/**
* 浏览器 UA
*/
@NotNull(message = "User-Agent 不能为空")
private String userAgent;
/**
* 异常时间
*/
@NotNull(message = "异常时间不能为空")
private LocalDateTime exceptionTime;
/**
* 异常名
*/
@NotNull(message = "异常名不能为空")
private String exceptionName;
/**
* 异常发生的类全名
*/
@NotNull(message = "异常发生的类全名不能为空")
private String exceptionClassName;
/**
* 异常发生的类文件
*/
@NotNull(message = "异常发生的类文件不能为空")
private String exceptionFileName;
/**
* 异常发生的方法名
*/
@NotNull(message = "异常发生的方法名不能为空")
private String exceptionMethodName;
/**
* 异常发生的方法所在行
*/
@NotNull(message = "异常发生的方法所在行不能为空")
private Integer exceptionLineNumber;
/**
* 异常的栈轨迹异常的栈轨迹
*/
@NotNull(message = "异常的栈轨迹不能为空")
private String exceptionStackTrace;
/**
* 异常导致的根消息
*/
@NotNull(message = "异常导致的根消息不能为空")
private String exceptionRootCauseMessage;
/**
* 异常导致的消息
*/
@NotNull(message = "异常导致的消息不能为空")
private String exceptionMessage;
}

View File

@ -0,0 +1,26 @@
package co.yixiang.yshop.module.infra.api.file;
import co.yixiang.yshop.module.infra.service.file.FileService;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
/**
* 文件 API 实现类
*
* @author yshop
*/
@Service
@Validated
public class FileApiImpl implements FileApi {
@Resource
private FileService fileService;
@Override
public String createFile(String name, String path, byte[] content) {
return fileService.createFile(name, path, content);
}
}

View File

@ -0,0 +1,27 @@
package co.yixiang.yshop.module.infra.api.logger;
import co.yixiang.yshop.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO;
import co.yixiang.yshop.module.infra.service.logger.ApiAccessLogService;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
/**
* API 访问日志的 API 实现类
*
* @author yshop
*/
@Service
@Validated
public class ApiAccessLogApiImpl implements ApiAccessLogApi {
@Resource
private ApiAccessLogService apiAccessLogService;
@Override
public void createApiAccessLog(ApiAccessLogCreateReqDTO createDTO) {
apiAccessLogService.createApiAccessLog(createDTO);
}
}

View File

@ -0,0 +1,27 @@
package co.yixiang.yshop.module.infra.api.logger;
import co.yixiang.yshop.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO;
import co.yixiang.yshop.module.infra.service.logger.ApiErrorLogService;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
/**
* API 访问日志的 API 接口
*
* @author yshop
*/
@Service
@Validated
public class ApiErrorLogApiImpl implements ApiErrorLogApi {
@Resource
private ApiErrorLogService apiErrorLogService;
@Override
public void createApiErrorLog(ApiErrorLogCreateReqDTO createDTO) {
apiErrorLogService.createApiErrorLog(createDTO);
}
}

View File

@ -0,0 +1 @@
package co.yixiang.yshop.module.infra.api;

View File

@ -0,0 +1,21 @@
package co.yixiang.yshop.module.infra.controller.admin.codegen.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.util.List;
@Schema(description = "管理后台 - 基于数据库的表结构,创建代码生成器的表和字段定义 Request VO")
@Data
public class CodegenCreateListReqVO {
@Schema(description = "数据源配置的编号", required = true, example = "1")
@NotNull(message = "数据源配置的编号不能为空")
private Long dataSourceConfigId;
@Schema(description = "表名数组", required = true, example = "[1, 2, 3]")
@NotNull(message = "表名数组不能为空")
private List<String> tableNames;
}

View File

@ -0,0 +1,20 @@
package co.yixiang.yshop.module.infra.controller.admin.codegen.vo;
import co.yixiang.yshop.module.infra.controller.admin.codegen.vo.column.CodegenColumnRespVO;
import co.yixiang.yshop.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 co.yixiang.yshop.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 = "文件路径", required = true, example = "java/co.yixiang.yshop/adminserver/modules/system/controller/test/SysTestDemoController.java")
private String filePath;
@Schema(description = "代码", required = true, example = "Hello World")
private String code;
}

View File

@ -0,0 +1,59 @@
package co.yixiang.yshop.module.infra.controller.admin.codegen.vo;
import cn.hutool.core.util.ObjectUtil;
import co.yixiang.yshop.module.infra.controller.admin.codegen.vo.column.CodegenColumnBaseVO;
import co.yixiang.yshop.module.infra.controller.admin.codegen.vo.table.CodegenTableBaseVO;
import co.yixiang.yshop.module.infra.enums.codegen.CodegenSceneEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.Valid;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.NotNull;
import java.util.List;
@Schema(description = "管理后台 - 代码生成表和字段的修改 Request VO")
@Data
public class CodegenUpdateReqVO {
@Valid // 校验内嵌的字段
@NotNull(message = "表定义不能为空")
private Table table;
@Valid // 校验内嵌的字段
@NotNull(message = "字段定义不能为空")
private List<Column> columns;
@Schema(description = "更新表定义")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Valid
public static class Table extends CodegenTableBaseVO {
@Schema(description = "编号", required = true, example = "1")
private Long id;
@AssertTrue(message = "上级菜单不能为空,请前往 [修改生成配置 -> 生成信息] 界面,设置“上级菜单”字段")
public boolean isParentMenuIdValid() {
// 生成场景为管理后台时,必须设置上级菜单,不然生成的菜单 SQL 是无父级菜单的
return ObjectUtil.notEqual(getScene(), CodegenSceneEnum.ADMIN.getScene())
|| getParentMenuId() != null;
}
}
@Schema(description = "更新表定义")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public static class Column extends CodegenColumnBaseVO {
@Schema(description = "编号", required = true, example = "1")
private Long id;
}
}

View File

@ -0,0 +1,84 @@
package co.yixiang.yshop.module.infra.controller.admin.codegen.vo.column;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
* 代码生成字段定义 Base VO提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class CodegenColumnBaseVO {
@Schema(description = "表编号", required = true, example = "1")
@NotNull(message = "表编号不能为空")
private Long tableId;
@Schema(description = "字段名", required = true, example = "user_age")
@NotNull(message = "字段名不能为空")
private String columnName;
@Schema(description = "字段类型", required = true, example = "int(11)")
@NotNull(message = "字段类型不能为空")
private String dataType;
@Schema(description = "字段描述", required = true, example = "年龄")
@NotNull(message = "字段描述不能为空")
private String columnComment;
@Schema(description = "是否允许为空", required = true, example = "true")
@NotNull(message = "是否允许为空不能为空")
private Boolean nullable;
@Schema(description = "是否主键", required = true, example = "false")
@NotNull(message = "是否主键不能为空")
private Boolean primaryKey;
@Schema(description = "是否自增", required = true, example = "true")
@NotNull(message = "是否自增不能为空")
private String autoIncrement;
@Schema(description = "排序", required = true, example = "10")
@NotNull(message = "排序不能为空")
private Integer ordinalPosition;
@Schema(description = "Java 属性类型", required = true, example = "userAge")
@NotNull(message = "Java 属性类型不能为空")
private String javaType;
@Schema(description = "Java 属性名", required = true, 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 创建操作的字段", required = true, example = "true")
@NotNull(message = "是否为 Create 创建操作的字段不能为空")
private Boolean createOperation;
@Schema(description = "是否为 Update 更新操作的字段", required = true, example = "false")
@NotNull(message = "是否为 Update 更新操作的字段不能为空")
private Boolean updateOperation;
@Schema(description = "是否为 List 查询操作的字段", required = true, example = "true")
@NotNull(message = "是否为 List 查询操作的字段不能为空")
private Boolean listOperation;
@Schema(description = "List 查询操作的条件类型,参见 CodegenColumnListConditionEnum 枚举", required = true, example = "LIKE")
@NotNull(message = "List 查询操作的条件类型不能为空")
private String listOperationCondition;
@Schema(description = "是否为 List 查询操作的返回字段", required = true, example = "true")
@NotNull(message = "是否为 List 查询操作的返回字段不能为空")
private Boolean listOperationResult;
@Schema(description = "显示类型", required = true, example = "input")
@NotNull(message = "显示类型不能为空")
private String htmlType;
}

View File

@ -0,0 +1,33 @@
package co.yixiang.yshop.module.infra.controller.admin.codegen.vo.table;
import co.yixiang.yshop.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 co.yixiang.yshop.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 CodegenTablePageReqVO extends PageParam {
@Schema(description = "表名称,模糊匹配", example = "yshop")
private String tableName;
@Schema(description = "表描述,模糊匹配", example = "yshop")
private String tableComment;
@Schema(description = "实体,模糊匹配", example = "Yshop")
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,28 @@
package co.yixiang.yshop.module.infra.controller.admin.codegen.vo.table;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 代码生成表定义 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CodegenTableRespVO extends CodegenTableBaseVO {
@Schema(description = "编号", required = true, example = "1")
private Long id;
@Schema(description = "主键编号", required = true, example = "1024")
private Integer dataSourceConfigId;
@Schema(description = "创建时间", required = true)
private LocalDateTime createTime;
@Schema(description = "更新时间", required = true)
private LocalDateTime updateTime;
}

View File

@ -0,0 +1,16 @@
package co.yixiang.yshop.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 = "表名称", required = true, example = "yuanma")
private String name;
@Schema(description = "表描述", required = true, example = "yshop")
private String comment;
}

View File

@ -0,0 +1,105 @@
package co.yixiang.yshop.module.infra.controller.admin.config;
import co.yixiang.yshop.framework.common.pojo.CommonResult;
import co.yixiang.yshop.framework.common.pojo.PageResult;
import co.yixiang.yshop.framework.excel.core.util.ExcelUtils;
import co.yixiang.yshop.framework.operatelog.core.annotations.OperateLog;
import co.yixiang.yshop.module.infra.controller.admin.config.vo.*;
import co.yixiang.yshop.module.infra.convert.config.ConfigConvert;
import co.yixiang.yshop.module.infra.dal.dataobject.config.ConfigDO;
import co.yixiang.yshop.module.infra.enums.ErrorCodeConstants;
import co.yixiang.yshop.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 org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.List;
import static co.yixiang.yshop.framework.common.exception.util.ServiceExceptionUtil.exception;
import static co.yixiang.yshop.framework.common.pojo.CommonResult.success;
import static co.yixiang.yshop.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
@Tag(name = "管理后台 - 参数配置")
@RestController
@RequestMapping("/infra/config")
@Validated
public class ConfigController {
@Resource
private ConfigService configService;
@PostMapping("/create")
@Operation(summary = "创建参数配置")
@PreAuthorize("@ss.hasPermission('infra:config:create')")
public CommonResult<Long> createConfig(@Valid @RequestBody ConfigCreateReqVO reqVO) {
return success(configService.createConfig(reqVO));
}
@PutMapping("/update")
@Operation(summary = "修改参数配置")
@PreAuthorize("@ss.hasPermission('infra:config:update')")
public CommonResult<Boolean> updateConfig(@Valid @RequestBody ConfigUpdateReqVO reqVO) {
configService.updateConfig(reqVO);
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 = "yshop.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 reqVO) {
PageResult<ConfigDO> page = configService.getConfigPage(reqVO);
return success(ConfigConvert.INSTANCE.convertPage(page));
}
@GetMapping("/export")
@Operation(summary = "导出参数配置")
@PreAuthorize("@ss.hasPermission('infra:config:export')")
@OperateLog(type = EXPORT)
public void exportConfig(@Valid ConfigExportReqVO reqVO,
HttpServletResponse response) throws IOException {
List<ConfigDO> list = configService.getConfigList(reqVO);
// 拼接数据
List<ConfigExcelVO> datas = ConfigConvert.INSTANCE.convertList(list);
// 输出
ExcelUtils.write(response, "参数配置.xls", "数据", ConfigExcelVO.class, datas);
}
}

View File

@ -0,0 +1,39 @@
package co.yixiang.yshop.module.infra.controller.admin.config.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
/**
* 参数配置 Base VO提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class ConfigBaseVO {
@Schema(description = "参数分组", required = true, example = "biz")
@NotEmpty(message = "参数分组不能为空")
@Size(max = 50, message = "参数名称不能超过50个字符")
private String category;
@Schema(description = "参数名称", required = true, example = "数据库名")
@NotBlank(message = "参数名称不能为空")
@Size(max = 100, message = "参数名称不能超过100个字符")
private String name;
@Schema(description = "参数键值", required = true, example = "1024")
@NotBlank(message = "参数键值不能为空")
@Size(max = 500, message = "参数键值长度不能超过500个字符")
private String value;
@Schema(description = "是否可见", required = true, example = "true")
@NotNull(message = "是否可见不能为空")
private Boolean visible;
@Schema(description = "备注", example = "备注一下很帅气!")
private String remark;
}

View File

@ -0,0 +1,46 @@
package co.yixiang.yshop.module.infra.controller.admin.config.vo;
import co.yixiang.yshop.framework.excel.core.annotations.DictFormat;
import co.yixiang.yshop.framework.excel.core.convert.DictConvert;
import co.yixiang.yshop.module.infra.enums.DictTypeConstants;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 参数配置 Excel 导出响应 VO
*/
@Data
public class ConfigExcelVO {
@ExcelProperty("参数配置序号")
private Long id;
@ExcelProperty("参数键名")
private String configKey;
@ExcelProperty("参数分类")
private String category;
@ExcelProperty("参数名称")
private String name;
@ExcelProperty("参数键值")
private String value;
@ExcelProperty(value = "参数类型", converter = DictConvert.class)
@DictFormat(DictTypeConstants.CONFIG_TYPE)
private Integer type;
@ExcelProperty(value = "是否可见", converter = DictConvert.class)
@DictFormat(DictTypeConstants.BOOLEAN_STRING)
private Boolean visible;
@ExcelProperty("备注")
private String remark;
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@ -0,0 +1,28 @@
package co.yixiang.yshop.module.infra.controller.admin.config.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static co.yixiang.yshop.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 参数配置导出 Request VO")
@Data
public class ConfigExportReqVO {
@Schema(description = "参数名称", example = "模糊匹配")
private String name;
@Schema(description = "参数键名,模糊匹配", example = "yshop.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,33 @@
package co.yixiang.yshop.module.infra.controller.admin.config.vo;
import co.yixiang.yshop.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 co.yixiang.yshop.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 ConfigPageReqVO extends PageParam {
@Schema(description = "数据源名称,模糊匹配", example = "名称")
private String name;
@Schema(description = "参数键名,模糊匹配", example = "yshop.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,30 @@
package co.yixiang.yshop.module.infra.controller.admin.config.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 参数配置信息 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class ConfigRespVO extends ConfigBaseVO {
@Schema(description = "参数配置序号", required = true, example = "1024")
private Long id;
@Schema(description = "参数键名", required = true, example = "yshop.db.username")
@NotBlank(message = "参数键名长度不能为空")
@Size(max = 100, message = "参数键名长度不能超过100个字符")
private String key;
@Schema(description = "参数类型,参见 SysConfigTypeEnum 枚举", required = true, example = "1")
private Integer type;
@Schema(description = "创建时间", required = true, example = "时间戳格式")
private LocalDateTime createTime;
}

View File

@ -0,0 +1,73 @@
package co.yixiang.yshop.module.infra.controller.admin.db;
import co.yixiang.yshop.framework.common.pojo.CommonResult;
import co.yixiang.yshop.module.infra.controller.admin.db.vo.DataSourceConfigCreateReqVO;
import co.yixiang.yshop.module.infra.controller.admin.db.vo.DataSourceConfigRespVO;
import co.yixiang.yshop.module.infra.controller.admin.db.vo.DataSourceConfigUpdateReqVO;
import co.yixiang.yshop.module.infra.convert.db.DataSourceConfigConvert;
import co.yixiang.yshop.module.infra.dal.dataobject.db.DataSourceConfigDO;
import co.yixiang.yshop.module.infra.service.db.DataSourceConfigService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
import static co.yixiang.yshop.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 DataSourceConfigCreateReqVO createReqVO) {
return success(dataSourceConfigService.createDataSourceConfig(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新数据源配置")
@PreAuthorize("@ss.hasPermission('infra:data-source-config:update')")
public CommonResult<Boolean> updateDataSourceConfig(@Valid @RequestBody DataSourceConfigUpdateReqVO 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 dataSourceConfig = dataSourceConfigService.getDataSourceConfig(id);
return success(DataSourceConfigConvert.INSTANCE.convert(dataSourceConfig));
}
@GetMapping("/list")
@Operation(summary = "获得数据源配置列表")
@PreAuthorize("@ss.hasPermission('infra:data-source-config:query')")
public CommonResult<List<DataSourceConfigRespVO>> getDataSourceConfigList() {
List<DataSourceConfigDO> list = dataSourceConfigService.getDataSourceConfigList();
return success(DataSourceConfigConvert.INSTANCE.convertList(list));
}
}

View File

@ -0,0 +1,25 @@
package co.yixiang.yshop.module.infra.controller.admin.db.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import javax.validation.constraints.*;
/**
* 数据源配置 Base VO提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class DataSourceConfigBaseVO {
@Schema(description = "数据源名称", required = true, example = "test")
@NotNull(message = "数据源名称不能为空")
private String name;
@Schema(description = "数据源连接", required = true, example = "jdbc:mysql://127.0.0.1:3306/yshop-pro")
@NotNull(message = "数据源连接不能为空")
private String url;
@Schema(description = "用户名", required = true, example = "root")
@NotNull(message = "用户名不能为空")
private String username;
}

View File

@ -0,0 +1,16 @@
package co.yixiang.yshop.module.infra.controller.admin.db.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import javax.validation.constraints.*;
@Schema(description = "管理后台 - 数据源配置创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class DataSourceConfigCreateReqVO extends DataSourceConfigBaseVO {
@Schema(description = "密码", required = true, example = "123456")
@NotNull(message = "密码不能为空")
private String password;
}

View File

@ -0,0 +1,19 @@
package co.yixiang.yshop.module.infra.controller.admin.db.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 数据源配置 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class DataSourceConfigRespVO extends DataSourceConfigBaseVO {
@Schema(description = "主键编号", required = true, example = "1024")
private Integer id;
@Schema(description = "创建时间", required = true)
private LocalDateTime createTime;
}

View File

@ -0,0 +1,20 @@
package co.yixiang.yshop.module.infra.controller.admin.db.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import javax.validation.constraints.*;
@Schema(description = "管理后台 - 数据源配置更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class DataSourceConfigUpdateReqVO extends DataSourceConfigBaseVO {
@Schema(description = "主键编号", required = true, example = "1024")
@NotNull(message = "主键编号不能为空")
private Long id;
@Schema(description = "密码", required = true, example = "123456")
@NotNull(message = "密码不能为空")
private String password;
}

View File

@ -0,0 +1,89 @@
package co.yixiang.yshop.module.infra.controller.admin.file;
import co.yixiang.yshop.framework.common.pojo.CommonResult;
import co.yixiang.yshop.framework.common.pojo.PageResult;
import co.yixiang.yshop.module.infra.controller.admin.file.vo.config.FileConfigCreateReqVO;
import co.yixiang.yshop.module.infra.controller.admin.file.vo.config.FileConfigPageReqVO;
import co.yixiang.yshop.module.infra.controller.admin.file.vo.config.FileConfigRespVO;
import co.yixiang.yshop.module.infra.controller.admin.file.vo.config.FileConfigUpdateReqVO;
import co.yixiang.yshop.module.infra.convert.file.FileConfigConvert;
import co.yixiang.yshop.module.infra.dal.dataobject.file.FileConfigDO;
import co.yixiang.yshop.module.infra.service.file.FileConfigService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import static co.yixiang.yshop.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 FileConfigCreateReqVO createReqVO) {
return success(fileConfigService.createFileConfig(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新文件配置")
@PreAuthorize("@ss.hasPermission('infra:file-config:update')")
public CommonResult<Boolean> updateFileConfig(@Valid @RequestBody FileConfigUpdateReqVO 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 fileConfig = fileConfigService.getFileConfig(id);
return success(FileConfigConvert.INSTANCE.convert(fileConfig));
}
@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(FileConfigConvert.INSTANCE.convertPage(pageResult));
}
@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,21 @@
package co.yixiang.yshop.module.infra.controller.admin.file.vo.config;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
* 文件配置 Base VO提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class FileConfigBaseVO {
@Schema(description = "配置名", required = true, example = "S3 - 阿里云")
@NotNull(message = "配置名不能为空")
private String name;
@Schema(description = "备注", example = "我是备注")
private String remark;
}

View File

@ -0,0 +1,25 @@
package co.yixiang.yshop.module.infra.controller.admin.file.vo.config;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.constraints.NotNull;
import java.util.Map;
@Schema(description = "管理后台 - 文件配置创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class FileConfigCreateReqVO extends FileConfigBaseVO {
@Schema(description = "存储器,参见 FileStorageEnum 枚举类参见 FileStorageEnum 枚举类", required = true, example = "1")
@NotNull(message = "存储器不能为空")
private Integer storage;
@Schema(description = "存储配置,配置是动态参数,所以使用 Map 接收", required = true)
@NotNull(message = "存储配置不能为空")
private Map<String, Object> config;
}

View File

@ -0,0 +1,30 @@
package co.yixiang.yshop.module.infra.controller.admin.file.vo.config;
import co.yixiang.yshop.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 co.yixiang.yshop.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 FileConfigPageReqVO extends PageParam {
@Schema(description = "配置名", example = "S3 - 阿里云")
private String name;
@Schema(description = "存储器", example = "1")
private Integer storage;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@Schema(description = "创建时间")
private LocalDateTime[] createTime;
}

View File

@ -0,0 +1,30 @@
package co.yixiang.yshop.module.infra.controller.admin.file.vo.file;
import co.yixiang.yshop.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 co.yixiang.yshop.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 FilePageReqVO extends PageParam {
@Schema(description = "文件路径,模糊匹配", example = "yshop")
private String path;
@Schema(description = "文件类型,模糊匹配", example = "application/octet-stream")
private String type;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@Schema(description = "创建时间")
private LocalDateTime[] createTime;
}

View File

@ -0,0 +1,36 @@
package co.yixiang.yshop.module.infra.controller.admin.file.vo.file;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 文件 Response VO,不返回 content 字段,太大")
@Data
public class FileRespVO {
@Schema(description = "文件编号", required = true, example = "1024")
private Long id;
@Schema(description = "配置编号", required = true, example = "11")
private Long configId;
@Schema(description = "文件路径", required = true, example = "yshop.jpg")
private String path;
@Schema(description = "原文件名", required = true, example = "yshop.jpg")
private String name;
@Schema(description = "文件 URL", required = true, example = "yshop.jpg")
private String url;
@Schema(description = "文件MIME类型", example = "application/octet-stream")
private String type;
@Schema(description = "文件大小", example = "2048", required = true)
private Integer size;
@Schema(description = "创建时间", required = true)
private LocalDateTime createTime;
}

View File

@ -0,0 +1,20 @@
package co.yixiang.yshop.module.infra.controller.admin.file.vo.file;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
import javax.validation.constraints.NotNull;
@Schema(description = "管理后台 - 上传文件 Request VO")
@Data
public class FileUploadReqVO {
@Schema(description = "文件附件", required = true)
@NotNull(message = "文件附件不能为空")
private MultipartFile file;
@Schema(description = "文件附件", example = "yshopyuanma.png")
private String path;
}

View File

@ -0,0 +1,145 @@
package co.yixiang.yshop.module.infra.controller.admin.job;
import co.yixiang.yshop.framework.common.pojo.CommonResult;
import co.yixiang.yshop.framework.common.pojo.PageResult;
import co.yixiang.yshop.framework.excel.core.util.ExcelUtils;
import co.yixiang.yshop.framework.operatelog.core.annotations.OperateLog;
import co.yixiang.yshop.framework.quartz.core.util.CronUtils;
import co.yixiang.yshop.module.infra.controller.admin.job.vo.job.*;
import co.yixiang.yshop.module.infra.convert.job.JobConvert;
import co.yixiang.yshop.module.infra.dal.dataobject.job.JobDO;
import co.yixiang.yshop.module.infra.service.job.JobService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.Operation;
import org.quartz.SchedulerException;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static co.yixiang.yshop.framework.common.pojo.CommonResult.success;
import static co.yixiang.yshop.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
@Tag(name = "管理后台 - 定时任务")
@RestController
@RequestMapping("/infra/job")
@Validated
public class JobController {
@Resource
private JobService jobService;
@PostMapping("/create")
@Operation(summary = "创建定时任务")
@PreAuthorize("@ss.hasPermission('infra:job:create')")
public CommonResult<Long> createJob(@Valid @RequestBody JobCreateReqVO createReqVO)
throws SchedulerException {
return success(jobService.createJob(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新定时任务")
@PreAuthorize("@ss.hasPermission('infra:job:update')")
public CommonResult<Boolean> updateJob(@Valid @RequestBody JobUpdateReqVO updateReqVO)
throws SchedulerException {
jobService.updateJob(updateReqVO);
return success(true);
}
@PutMapping("/update-status")
@Operation(summary = "更新定时任务的状态")
@Parameters({
@Parameter(name = "id", description = "编号", required = true, example = "1024"),
@Parameter(name = "status", description = "状态", required = true, example = "1"),
})
@PreAuthorize("@ss.hasPermission('infra:job:update')")
public CommonResult<Boolean> updateJobStatus(@RequestParam(value = "id") Long id, @RequestParam("status") Integer status)
throws SchedulerException {
jobService.updateJobStatus(id, status);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除定时任务")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('infra:job:delete')")
public CommonResult<Boolean> deleteJob(@RequestParam("id") Long id)
throws SchedulerException {
jobService.deleteJob(id);
return success(true);
}
@PutMapping("/trigger")
@Operation(summary = "触发定时任务")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('infra:job:trigger')")
public CommonResult<Boolean> triggerJob(@RequestParam("id") Long id) throws SchedulerException {
jobService.triggerJob(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得定时任务")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('infra:job:query')")
public CommonResult<JobRespVO> getJob(@RequestParam("id") Long id) {
JobDO job = jobService.getJob(id);
return success(JobConvert.INSTANCE.convert(job));
}
@GetMapping("/list")
@Operation(summary = "获得定时任务列表")
@Parameter(name = "ids", description = "编号列表", required = true)
@PreAuthorize("@ss.hasPermission('infra:job:query')")
public CommonResult<List<JobRespVO>> getJobList(@RequestParam("ids") Collection<Long> ids) {
List<JobDO> list = jobService.getJobList(ids);
return success(JobConvert.INSTANCE.convertList(list));
}
@GetMapping("/page")
@Operation(summary = "获得定时任务分页")
@PreAuthorize("@ss.hasPermission('infra:job:query')")
public CommonResult<PageResult<JobRespVO>> getJobPage(@Valid JobPageReqVO pageVO) {
PageResult<JobDO> pageResult = jobService.getJobPage(pageVO);
return success(JobConvert.INSTANCE.convertPage(pageResult));
}
@GetMapping("/export-excel")
@Operation(summary = "导出定时任务 Excel")
@PreAuthorize("@ss.hasPermission('infra:job:export')")
@OperateLog(type = EXPORT)
public void exportJobExcel(@Valid JobExportReqVO exportReqVO,
HttpServletResponse response) throws IOException {
List<JobDO> list = jobService.getJobList(exportReqVO);
// 导出 Excel
List<JobExcelVO> datas = JobConvert.INSTANCE.convertList02(list);
ExcelUtils.write(response, "定时任务.xls", "数据", JobExcelVO.class, datas);
}
@GetMapping("/get_next_times")
@Operation(summary = "获得定时任务的下 n 次执行时间")
@Parameters({
@Parameter(name = "id", description = "编号", required = true, example = "1024"),
@Parameter(name = "count", description = "数量", example = "5")
})
@PreAuthorize("@ss.hasPermission('infra:job:query')")
public CommonResult<List<LocalDateTime>> getJobNextTimes(@RequestParam("id") Long id,
@RequestParam(value = "count", required = false, defaultValue = "5") Integer count) {
JobDO job = jobService.getJob(id);
if (job == null) {
return success(Collections.emptyList());
}
return success(CronUtils.getNextTimes(job.getCronExpression(), count));
}
}

View File

@ -0,0 +1,81 @@
package co.yixiang.yshop.module.infra.controller.admin.job;
import co.yixiang.yshop.framework.common.pojo.CommonResult;
import co.yixiang.yshop.framework.common.pojo.PageResult;
import co.yixiang.yshop.framework.excel.core.util.ExcelUtils;
import co.yixiang.yshop.framework.operatelog.core.annotations.OperateLog;
import co.yixiang.yshop.module.infra.controller.admin.job.vo.log.JobLogExcelVO;
import co.yixiang.yshop.module.infra.controller.admin.job.vo.log.JobLogExportReqVO;
import co.yixiang.yshop.module.infra.controller.admin.job.vo.log.JobLogPageReqVO;
import co.yixiang.yshop.module.infra.controller.admin.job.vo.log.JobLogRespVO;
import co.yixiang.yshop.module.infra.convert.job.JobLogConvert;
import co.yixiang.yshop.module.infra.dal.dataobject.job.JobLogDO;
import co.yixiang.yshop.module.infra.service.job.JobLogService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import static co.yixiang.yshop.framework.common.pojo.CommonResult.success;
import static co.yixiang.yshop.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
@Tag(name = "管理后台 - 定时任务日志")
@RestController
@RequestMapping("/infra/job-log")
@Validated
public class JobLogController {
@Resource
private JobLogService jobLogService;
@GetMapping("/get")
@Operation(summary = "获得定时任务日志")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('infra:job:query')")
public CommonResult<JobLogRespVO> getJobLog(@RequestParam("id") Long id) {
JobLogDO jobLog = jobLogService.getJobLog(id);
return success(JobLogConvert.INSTANCE.convert(jobLog));
}
@GetMapping("/list")
@Operation(summary = "获得定时任务日志列表")
@Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
@PreAuthorize("@ss.hasPermission('infra:job:query')")
public CommonResult<List<JobLogRespVO>> getJobLogList(@RequestParam("ids") Collection<Long> ids) {
List<JobLogDO> list = jobLogService.getJobLogList(ids);
return success(JobLogConvert.INSTANCE.convertList(list));
}
@GetMapping("/page")
@Operation(summary = "获得定时任务日志分页")
@PreAuthorize("@ss.hasPermission('infra:job:query')")
public CommonResult<PageResult<JobLogRespVO>> getJobLogPage(@Valid JobLogPageReqVO pageVO) {
PageResult<JobLogDO> pageResult = jobLogService.getJobLogPage(pageVO);
return success(JobLogConvert.INSTANCE.convertPage(pageResult));
}
@GetMapping("/export-excel")
@Operation(summary = "导出定时任务日志 Excel")
@PreAuthorize("@ss.hasPermission('infra:job:export')")
@OperateLog(type = EXPORT)
public void exportJobLogExcel(@Valid JobLogExportReqVO exportReqVO,
HttpServletResponse response) throws IOException {
List<JobLogDO> list = jobLogService.getJobLogList(exportReqVO);
// 导出 Excel
List<JobLogExcelVO> datas = JobLogConvert.INSTANCE.convertList02(list);
ExcelUtils.write(response, "任务日志.xls", "数据", JobLogExcelVO.class, datas);
}
}

View File

@ -0,0 +1,36 @@
package co.yixiang.yshop.module.infra.controller.admin.job.vo.job;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
* 定时任务 Base VO提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class JobBaseVO {
@Schema(description = "任务名称", required = true, example = "测试任务")
@NotNull(message = "任务名称不能为空")
private String name;
@Schema(description = "处理器的参数", example = "yshop")
private String handlerParam;
@Schema(description = "CRON 表达式", required = true, example = "0/10 * * * * ? *")
@NotNull(message = "CRON 表达式不能为空")
private String cronExpression;
@Schema(description = "重试次数", required = true, example = "3")
@NotNull(message = "重试次数不能为空")
private Integer retryCount;
@Schema(description = "重试间隔", required = true, example = "1000")
@NotNull(message = "重试间隔不能为空")
private Integer retryInterval;
@Schema(description = "监控超时时间", example = "1000")
private Integer monitorTimeout;
}

View File

@ -0,0 +1,20 @@
package co.yixiang.yshop.module.infra.controller.admin.job.vo.job;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.constraints.NotNull;
@Schema(description = "管理后台 - 定时任务创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class JobCreateReqVO extends JobBaseVO {
@Schema(description = "处理器的名字", required = true, example = "sysUserSessionTimeoutJob")
@NotNull(message = "处理器的名字不能为空")
private String handlerName;
}

View File

@ -0,0 +1,19 @@
package co.yixiang.yshop.module.infra.controller.admin.job.vo.job;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 定时任务 Excel 导出 Request VO-参数和 JobPageReqVO 是一致的")
@Data
public class JobExportReqVO {
@Schema(description = "任务名称-模糊匹配", example = "测试任务")
private String name;
@Schema(description = "任务状态-参见 JobStatusEnum 枚举", example = "1")
private Integer status;
@Schema(description = "处理器的名字-模糊匹配", example = "UserSessionTimeoutJob")
private String handlerName;
}

View File

@ -0,0 +1,30 @@
package co.yixiang.yshop.module.infra.controller.admin.job.vo.job;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 定时任务 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class JobRespVO extends JobBaseVO {
@Schema(description = "任务编号", required = true, example = "1024")
private Long id;
@Schema(description = "任务状态", required = true, example = "1")
private Integer status;
@Schema(description = "处理器的名字", required = true, example = "sysUserSessionTimeoutJob")
@NotNull(message = "处理器的名字不能为空")
private String handlerName;
@Schema(description = "创建时间", required = true)
private LocalDateTime createTime;
}

View File

@ -0,0 +1,20 @@
package co.yixiang.yshop.module.infra.controller.admin.job.vo.job;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.constraints.NotNull;
@Schema(description = "管理后台 - 定时任务更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class JobUpdateReqVO extends JobBaseVO {
@Schema(description = "任务编号", required = true, example = "1024")
@NotNull(message = "任务编号不能为空")
private Long id;
}

View File

@ -0,0 +1,52 @@
package co.yixiang.yshop.module.infra.controller.admin.job.vo.log;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
import static co.yixiang.yshop.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* 定时任务日志 Base VO提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class JobLogBaseVO {
@Schema(description = "任务编号", required = true, example = "1024")
@NotNull(message = "任务编号不能为空")
private Long jobId;
@Schema(description = "处理器的名字", required = true, example = "sysUserSessionTimeoutJob")
@NotNull(message = "处理器的名字不能为空")
private String handlerName;
@Schema(description = "处理器的参数", example = "yshop")
private String handlerParam;
@Schema(description = "第几次执行", required = true, example = "1")
@NotNull(message = "第几次执行不能为空")
private Integer executeIndex;
@Schema(description = "开始执行时间", required = true)
@NotNull(message = "开始执行时间不能为空")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime beginTime;
@Schema(description = "结束执行时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime endTime;
@Schema(description = "执行时长", example = "123")
private Integer duration;
@Schema(description = "任务状态,参见 JobLogStatusEnum 枚举", required = true, example = "1")
@NotNull(message = "任务状态不能为空")
private Integer status;
@Schema(description = "结果数据", example = "执行成功")
private String result;
}

View File

@ -0,0 +1,53 @@
package co.yixiang.yshop.module.infra.controller.admin.job.vo.log;
import co.yixiang.yshop.framework.excel.core.annotations.DictFormat;
import co.yixiang.yshop.framework.excel.core.convert.DictConvert;
import co.yixiang.yshop.module.infra.enums.DictTypeConstants;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 定时任务 Excel VO
*
* @author yshop
*/
@Data
public class JobLogExcelVO {
@ExcelProperty("日志编号")
private Long id;
@ExcelProperty("任务编号")
private Long jobId;
@ExcelProperty("处理器的名字")
private String handlerName;
@ExcelProperty("处理器的参数")
private String handlerParam;
@ExcelProperty("第几次执行")
private Integer executeIndex;
@ExcelProperty("开始执行时间")
private LocalDateTime beginTime;
@ExcelProperty("结束执行时间")
private LocalDateTime endTime;
@ExcelProperty("执行时长")
private Integer duration;
@ExcelProperty(value = "任务状态", converter = DictConvert.class)
@DictFormat(DictTypeConstants.JOB_STATUS)
private Integer status;
@ExcelProperty("结果数据")
private String result;
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@ -0,0 +1,32 @@
package co.yixiang.yshop.module.infra.controller.admin.job.vo.log;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static co.yixiang.yshop.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 定时任务 Excel 导出 Request VO,参数和 JobLogPageReqVO 是一致的")
@Data
public class JobLogExportReqVO {
@Schema(description = "任务编号", example = "10")
private Long jobId;
@Schema(description = "处理器的名字,模糊匹配")
private String handlerName;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@Schema(description = "开始执行时间")
private LocalDateTime beginTime;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@Schema(description = "结束执行时间")
private LocalDateTime endTime;
@Schema(description = "任务状态,参见 JobLogStatusEnum 枚举")
private Integer status;
}

View File

@ -0,0 +1,37 @@
package co.yixiang.yshop.module.infra.controller.admin.job.vo.log;
import co.yixiang.yshop.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 co.yixiang.yshop.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 JobLogPageReqVO extends PageParam {
@Schema(description = "任务编号", example = "10")
private Long jobId;
@Schema(description = "处理器的名字,模糊匹配")
private String handlerName;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@Schema(description = "开始执行时间")
private LocalDateTime beginTime;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@Schema(description = "结束执行时间")
private LocalDateTime endTime;
@Schema(description = "任务状态,参见 JobLogStatusEnum 枚举")
private Integer status;
}

View File

@ -0,0 +1,22 @@
package co.yixiang.yshop.module.infra.controller.admin.job.vo.log;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 定时任务日志 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class JobLogRespVO extends JobLogBaseVO {
@Schema(description = "日志编号", required = true, example = "1024")
private Long id;
@Schema(description = "创建时间", required = true)
private LocalDateTime createTime;
}

View File

@ -0,0 +1,74 @@
package co.yixiang.yshop.module.infra.controller.admin.logger.vo.apiaccesslog;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
import static co.yixiang.yshop.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* API 访问日志 Base VO提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class ApiAccessLogBaseVO {
@Schema(description = "链路追踪编号", required = true, example = "66600cb6-7852-11eb-9439-0242ac130002")
@NotNull(message = "链路追踪编号不能为空")
private String traceId;
@Schema(description = "用户编号", required = true, example = "666")
@NotNull(message = "用户编号不能为空")
private Long userId;
@Schema(description = "用户类型,参见 UserTypeEnum 枚举", required = true, example = "2")
@NotNull(message = "用户类型不能为空")
private Integer userType;
@Schema(description = "应用名", required = true, example = "dashboard")
@NotNull(message = "应用名不能为空")
private String applicationName;
@Schema(description = "请求方法名", required = true, example = "GET")
@NotNull(message = "请求方法名不能为空")
private String requestMethod;
@Schema(description = "请求地址", required = true, example = "/xxx/yyy")
@NotNull(message = "请求地址不能为空")
private String requestUrl;
@Schema(description = "请求参数")
private String requestParams;
@Schema(description = "用户 IP", required = true, example = "127.0.0.1")
@NotNull(message = "用户 IP不能为空")
private String userIp;
@Schema(description = "浏览器 UA", required = true, example = "Mozilla/5.0")
@NotNull(message = "浏览器 UA不能为空")
private String userAgent;
@Schema(description = "开始请求时间", required = true)
@NotNull(message = "开始请求时间不能为空")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime beginTime;
@Schema(description = "结束请求时间", required = true)
@NotNull(message = "结束请求时间不能为空")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime endTime;
@Schema(description = "执行时长", required = true, example = "100")
@NotNull(message = "执行时长不能为空")
private Integer duration;
@Schema(description = "结果码", required = true, example = "0")
@NotNull(message = "结果码不能为空")
private Integer resultCode;
@Schema(description = "结果提示", example = "yshop牛逼")
private String resultMsg;
}

View File

@ -0,0 +1,65 @@
package co.yixiang.yshop.module.infra.controller.admin.logger.vo.apiaccesslog;
import co.yixiang.yshop.framework.excel.core.annotations.DictFormat;
import co.yixiang.yshop.framework.excel.core.convert.DictConvert;
import co.yixiang.yshop.module.system.enums.DictTypeConstants;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.time.LocalDateTime;
/**
* API 访问日志 Excel VO
*
* @author yshop
*/
@Data
public class ApiAccessLogExcelVO {
@ExcelProperty("日志主键")
private Long id;
@ExcelProperty("链路追踪编号")
private String traceId;
@ExcelProperty("用户编号")
private Long userId;
@ExcelProperty(value = "用户类型", converter = DictConvert.class)
@DictFormat(DictTypeConstants.USER_TYPE)
private Integer userType;
@ExcelProperty("应用名")
private String applicationName;
@ExcelProperty("请求方法名")
private String requestMethod;
@ExcelProperty("请求地址")
private String requestUrl;
@ExcelProperty("请求参数")
private String requestParams;
@ExcelProperty("用户 IP")
private String userIp;
@ExcelProperty("浏览器 UA")
private String userAgent;
@ExcelProperty("开始请求时间")
private LocalDateTime beginTime;
@ExcelProperty("结束请求时间")
private LocalDateTime endTime;
@ExcelProperty("执行时长")
private Integer duration;
@ExcelProperty("结果码")
private Integer resultCode;
@ExcelProperty("结果提示")
private String resultMsg;
}

View File

@ -0,0 +1,22 @@
package co.yixiang.yshop.module.infra.controller.admin.logger.vo.apiaccesslog;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - API 访问日志 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ApiAccessLogRespVO extends ApiAccessLogBaseVO {
@Schema(description = "日志主键", required = true, example = "1024")
private Long id;
@Schema(description = "创建时间", required = true)
private LocalDateTime createTime;
}

View File

@ -0,0 +1,90 @@
package co.yixiang.yshop.module.infra.controller.admin.logger.vo.apierrorlog;
import co.yixiang.yshop.framework.excel.core.annotations.DictFormat;
import co.yixiang.yshop.framework.excel.core.convert.DictConvert;
import co.yixiang.yshop.module.infra.enums.DictTypeConstants;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.time.LocalDateTime;
/**
* API 错误日志 Excel VO
*
* @author yshop
*/
@Data
public class ApiErrorLogExcelVO {
@ExcelProperty("编号")
private Integer id;
@ExcelProperty("链路追踪编号")
private String traceId;
@ExcelProperty("用户编号")
private Integer userId;
@ExcelProperty(value = "用户类型", converter = DictConvert.class)
@DictFormat(co.yixiang.yshop.module.system.enums.DictTypeConstants.USER_TYPE)
private Integer userType;
@ExcelProperty("应用名")
private String applicationName;
@ExcelProperty("请求方法名")
private String requestMethod;
@ExcelProperty("请求地址")
private String requestUrl;
@ExcelProperty("请求参数")
private String requestParams;
@ExcelProperty("用户 IP")
private String userIp;
@ExcelProperty("浏览器 UA")
private String userAgent;
@ExcelProperty("异常发生时间")
private LocalDateTime exceptionTime;
@ExcelProperty("异常名")
private String exceptionName;
@ExcelProperty("异常导致的消息")
private String exceptionMessage;
@ExcelProperty("异常导致的根消息")
private String exceptionRootCauseMessage;
@ExcelProperty("异常的栈轨迹")
private String exceptionStackTrace;
@ExcelProperty("异常发生的类全名")
private String exceptionClassName;
@ExcelProperty("异常发生的类文件")
private String exceptionFileName;
@ExcelProperty("异常发生的方法名")
private String exceptionMethodName;
@ExcelProperty("异常发生的方法所在行")
private Integer exceptionLineNumber;
@ExcelProperty("创建时间")
private LocalDateTime createTime;
@ExcelProperty(value = "处理状态", converter = DictConvert.class)
@DictFormat(DictTypeConstants.API_ERROR_LOG_PROCESS_STATUS)
private Integer processStatus;
@ExcelProperty("处理时间")
private LocalDateTime processTime;
@ExcelProperty("处理用户编号")
private Integer processUserId;
}

View File

@ -0,0 +1,34 @@
package co.yixiang.yshop.module.infra.controller.admin.logger.vo.apierrorlog;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static co.yixiang.yshop.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - API 错误日志 Excel 导出 Request VO,参数和 ApiErrorLogPageReqVO 是一致的")
@Data
public class ApiErrorLogExportReqVO {
@Schema(description = "用户编号", example = "666")
private Long userId;
@Schema(description = "用户类型", example = "1")
private Integer userType;
@Schema(description = "应用名", example = "dashboard")
private String applicationName;
@Schema(description = "请求地址", example = "/xx/yy")
private String requestUrl;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@Schema(description = "异常发生时间")
private LocalDateTime[] exceptionTime;
@Schema(description = "处理状态", example = "0")
private Integer processStatus;
}

View File

@ -0,0 +1,39 @@
package co.yixiang.yshop.module.infra.controller.admin.logger.vo.apierrorlog;
import co.yixiang.yshop.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 co.yixiang.yshop.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - API 错误日志分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ApiErrorLogPageReqVO extends PageParam {
@Schema(description = "用户编号", example = "666")
private Long userId;
@Schema(description = "用户类型", example = "1")
private Integer userType;
@Schema(description = "应用名", example = "dashboard")
private String applicationName;
@Schema(description = "请求地址", example = "/xx/yy")
private String requestUrl;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@Schema(description = "异常发生时间")
private LocalDateTime[] exceptionTime;
@Schema(description = "处理状态", example = "0")
private Integer processStatus;
}

View File

@ -0,0 +1,28 @@
package co.yixiang.yshop.module.infra.controller.admin.logger.vo.apierrorlog;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - API 错误日志 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ApiErrorLogRespVO extends ApiErrorLogBaseVO {
@Schema(description = "编号", required = true, example = "1024")
private Integer id;
@Schema(description = "创建时间", required = true)
private LocalDateTime createTime;
@Schema(description = "处理时间", required = true)
private LocalDateTime processTime;
@Schema(description = "处理用户编号", example = "233")
private Integer processUserId;
}

View File

@ -0,0 +1,112 @@
package co.yixiang.yshop.module.infra.controller.admin.redis;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import co.yixiang.yshop.framework.common.pojo.CommonResult;
import co.yixiang.yshop.framework.redis.core.RedisKeyDefine;
import co.yixiang.yshop.framework.redis.core.RedisKeyRegistry;
import co.yixiang.yshop.module.infra.controller.admin.redis.vo.RedisKeyDefineRespVO;
import co.yixiang.yshop.module.infra.controller.admin.redis.vo.RedisKeyValueRespVO;
import co.yixiang.yshop.module.infra.controller.admin.redis.vo.RedisMonitorRespVO;
import co.yixiang.yshop.module.infra.convert.redis.RedisConvert;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.data.redis.connection.RedisServerCommands;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.*;
import static co.yixiang.yshop.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - Redis 监控")
@RestController
@RequestMapping("/infra/redis")
public class RedisController {
@Resource
private StringRedisTemplate stringRedisTemplate;
@GetMapping("/get-monitor-info")
@Operation(summary = "获得 Redis 监控信息")
@PreAuthorize("@ss.hasPermission('infra:redis:get-monitor-info')")
public CommonResult<RedisMonitorRespVO> getRedisMonitorInfo() {
// 获得 Redis 统计信息
Properties info = stringRedisTemplate.execute((RedisCallback<Properties>) RedisServerCommands::info);
Long dbSize = stringRedisTemplate.execute(RedisServerCommands::dbSize);
Properties commandStats = stringRedisTemplate.execute((
RedisCallback<Properties>) connection -> connection.info("commandstats"));
assert commandStats != null; // 断言,避免警告
// 拼接结果返回
return success(RedisConvert.INSTANCE.build(info, dbSize, commandStats));
}
@GetMapping("/get-key-define-list")
@Operation(summary = "获得 Redis Key 模板列表")
@PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')")
public CommonResult<List<RedisKeyDefineRespVO>> getKeyDefineList() {
List<RedisKeyDefine> keyDefines = RedisKeyRegistry.list();
return success(RedisConvert.INSTANCE.convertList(keyDefines));
}
@GetMapping("/get-key-list")
@Operation(summary = "获得 Redis keys 键名列表")
@Parameter(name = "keyTemplate", description = "Redis Key 定义", example = "true")
@PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')")
public CommonResult<Set<String>> getKeyDefineList(@RequestParam("keyTemplate") String keyTemplate) {
return success(getKeyDefineList0(keyTemplate));
}
private Set<String> getKeyDefineList0(String keyTemplate) {
// key 格式化
String key = StrUtil.replace(keyTemplate, "%[s|c|b|d|x|o|f|a|e|g]", parameter -> "*");
// scan 扫描 key
Set<String> keys = new LinkedHashSet<>();
stringRedisTemplate.execute((RedisCallback<Set<String>>) connection -> {
try (Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().match(key).count(100).build())) {
cursor.forEachRemaining(value -> keys.add(StrUtil.utf8Str(value)));
} catch (Exception e) {
throw new RuntimeException(e);
}
return keys;
});
return keys;
}
@GetMapping("/get-key-value")
@Operation(summary = "获得 Redis key 内容")
@Parameter(name = "key", description = "Redis Key", example = "oauth2_access_token:233")
@PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')")
public CommonResult<RedisKeyValueRespVO> getKeyValue(@RequestParam("key") String key) {
String value = stringRedisTemplate.opsForValue().get(key);
return success(new RedisKeyValueRespVO(key, value));
}
@DeleteMapping("/delete-key")
@Operation(summary = "删除 Redis Key")
@Parameter(name = "key", description = "Redis Key", example = "oauth2_access_token:233")
@PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')")
public CommonResult<Boolean> deleteKey(@RequestParam("key") String key) {
stringRedisTemplate.delete(key);
return success(Boolean.TRUE);
}
@DeleteMapping("/delete-keys")
@Operation(summary = "删除 Redis Key 根据模板")
@Parameter(name = "keyTemplate", description = "Redis Key 定义", example = "true")
@PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')")
public CommonResult<Boolean> deleteKeys(@RequestParam("keyTemplate") String keyTemplate) {
Set<String> keys = getKeyDefineList0(keyTemplate);
if (CollUtil.isNotEmpty(keys)) {
stringRedisTemplate.delete(keys);
}
return success(Boolean.TRUE);
}
}

View File

@ -0,0 +1,35 @@
package co.yixiang.yshop.module.infra.controller.admin.redis.vo;
import co.yixiang.yshop.framework.redis.core.RedisKeyDefine;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import java.time.Duration;
@Schema(description = "管理后台 - Redis Key 信息 Response VO")
@Data
@Builder
@AllArgsConstructor
public class RedisKeyDefineRespVO {
@Schema(description = "Key 模板", required = true, example = "login_user:%s")
private String keyTemplate;
@Schema(description = "Key 类型的枚举", required = true, example = "String")
private RedisKeyDefine.KeyTypeEnum keyType;
@Schema(description = "Value 类型", required = true, example = "java.lang.String")
private Class<?> valueType;
@Schema(description = "超时类型", required = true, example = "1")
private RedisKeyDefine.TimeoutTypeEnum timeoutType;
@Schema(description = "过期时间,单位:毫秒", required = true, example = "1024")
private Duration timeout;
@Schema(description = "备注", required = true, example = "啦啦啦啦~")
private String memo;
}

View File

@ -0,0 +1,18 @@
package co.yixiang.yshop.module.infra.controller.admin.redis.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
@Schema(description = "管理后台 - 单个 Redis Key Value Response VO")
@Data
@AllArgsConstructor
public class RedisKeyValueRespVO {
@Schema(description = "c5f6990767804a928f4bb96ca249febf", required = true, example = "String")
private String key;
@Schema(required = true, example = "String")
private String value;
}

View File

@ -0,0 +1,43 @@
package co.yixiang.yshop.module.infra.controller.admin.redis.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import java.util.List;
import java.util.Properties;
@Schema(description = "管理后台 - Redis 监控信息 Response VO")
@Data
@Builder
@AllArgsConstructor
public class RedisMonitorRespVO {
@Schema(description = "Redis info 指令结果,具体字段,查看 Redis 文档", required = true)
private Properties info;
@Schema(description = "Redis key 数量", required = true, example = "1024")
private Long dbSize;
@Schema(description = "CommandStat 数组", required = true)
private List<CommandStat> commandStats;
@Schema(description = "Redis 命令统计结果")
@Data
@Builder
@AllArgsConstructor
public static class CommandStat {
@Schema(description = "Redis 命令", required = true, example = "get")
private String command;
@Schema(description = "调用次数", required = true, example = "1024")
private Long calls;
@Schema(description = "消耗 CPU 秒数", required = true, example = "666")
private Long usec;
}
}

View File

@ -0,0 +1,97 @@
package co.yixiang.yshop.module.infra.controller.admin.test;
import co.yixiang.yshop.framework.common.pojo.CommonResult;
import co.yixiang.yshop.framework.common.pojo.PageResult;
import co.yixiang.yshop.framework.excel.core.util.ExcelUtils;
import co.yixiang.yshop.framework.operatelog.core.annotations.OperateLog;
import co.yixiang.yshop.module.infra.controller.admin.test.vo.*;
import co.yixiang.yshop.module.infra.convert.test.TestDemoConvert;
import co.yixiang.yshop.module.infra.dal.dataobject.test.TestDemoDO;
import co.yixiang.yshop.module.infra.service.test.TestDemoService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import static co.yixiang.yshop.framework.common.pojo.CommonResult.success;
import static co.yixiang.yshop.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
@Tag(name = "管理后台 - 字典类型")
@RestController
@RequestMapping("/infra/test-demo")
@Validated
public class TestDemoController {
@Resource
private TestDemoService testDemoService;
@PostMapping("/create")
@Operation(summary = "创建字典类型")
@PreAuthorize("@ss.hasPermission('infra:test-demo:create')")
public CommonResult<Long> createTestDemo(@Valid @RequestBody TestDemoCreateReqVO createReqVO) {
return success(testDemoService.createTestDemo(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新字典类型")
@PreAuthorize("@ss.hasPermission('infra:test-demo:update')")
public CommonResult<Boolean> updateTestDemo(@Valid @RequestBody TestDemoUpdateReqVO updateReqVO) {
testDemoService.updateTestDemo(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除字典类型")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('infra:test-demo:delete')")
public CommonResult<Boolean> deleteTestDemo(@RequestParam("id") Long id) {
testDemoService.deleteTestDemo(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得字典类型")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('infra:test-demo:query')")
public CommonResult<TestDemoRespVO> getTestDemo(@RequestParam("id") Long id) {
TestDemoDO testDemo = testDemoService.getTestDemo(id);
return success(TestDemoConvert.INSTANCE.convert(testDemo));
}
@GetMapping("/list")
@Operation(summary = "获得字典类型列表")
@Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
@PreAuthorize("@ss.hasPermission('infra:test-demo:query')")
public CommonResult<List<TestDemoRespVO>> getTestDemoList(@RequestParam("ids") Collection<Long> ids) {
List<TestDemoDO> list = testDemoService.getTestDemoList(ids);
return success(TestDemoConvert.INSTANCE.convertList(list));
}
@GetMapping("/page")
@Operation(summary = "获得字典类型分页")
@PreAuthorize("@ss.hasPermission('infra:test-demo:query')") public CommonResult<PageResult<TestDemoRespVO>> getTestDemoPage(@Valid TestDemoPageReqVO pageVO) {
PageResult<TestDemoDO> pageResult = testDemoService.getTestDemoPage(pageVO);
return success(TestDemoConvert.INSTANCE.convertPage(pageResult));
}
@GetMapping("/export-excel")
@Operation(summary = "导出字典类型 Excel")
@PreAuthorize("@ss.hasPermission('infra:test-demo:export')") @OperateLog(type = EXPORT)
public void exportTestDemoExcel(@Valid TestDemoExportReqVO exportReqVO,
HttpServletResponse response) throws IOException {
List<TestDemoDO> list = testDemoService.getTestDemoList(exportReqVO);
// 导出 Excel
List<TestDemoExcelVO> datas = TestDemoConvert.INSTANCE.convertList02(list);
ExcelUtils.write(response, "字典类型.xls", "数据", TestDemoExcelVO.class, datas);
}
}

View File

@ -0,0 +1,32 @@
package co.yixiang.yshop.module.infra.controller.admin.test.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import javax.validation.constraints.*;
/**
* 字典类型 Base VO提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class TestDemoBaseVO {
@Schema(description = "名字", required = true)
@NotNull(message = "名字不能为空")
private String name;
@Schema(description = "状态", required = true)
@NotNull(message = "状态不能为空")
private Integer status;
@Schema(description = "类型", required = true)
@NotNull(message = "类型不能为空")
private Integer type;
@Schema(description = "分类", required = true)
@NotNull(message = "分类不能为空")
private Integer category;
@Schema(description = "备注")
private String remark;
}

View File

@ -0,0 +1,38 @@
package co.yixiang.yshop.module.infra.controller.admin.test.vo;
import lombok.*;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.ExcelProperty;
/**
* 字典类型 Excel VO
*
* @author yshop
*/
@Data
public class TestDemoExcelVO {
@ExcelProperty("编号")
private Long id;
@ExcelProperty("名字")
private String name;
@ExcelProperty("状态")
private Integer status;
@ExcelProperty("类型")
private Integer type;
@ExcelProperty("分类")
private Integer category;
@ExcelProperty("备注")
private String remark;
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@ -0,0 +1,33 @@
package co.yixiang.yshop.module.infra.controller.admin.test.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.time.LocalDateTime;
import org.springframework.format.annotation.DateTimeFormat;
import static co.yixiang.yshop.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 字典类型 Excel 导出 Request VO,参数和 TestDemoPageReqVO 是一致的")
@Data
public class TestDemoExportReqVO {
@Schema(description = "名字")
private String name;
@Schema(description = "状态")
private Integer status;
@Schema(description = "类型")
private Integer type;
@Schema(description = "分类")
private Integer category;
@Schema(description = "备注")
private String remark;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@Schema(description = "创建时间")
private LocalDateTime[] createTime;
}

View File

@ -0,0 +1,19 @@
package co.yixiang.yshop.module.infra.controller.admin.test.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 字典类型 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class TestDemoRespVO extends TestDemoBaseVO {
@Schema(description = "编号", required = true)
private Long id;
@Schema(description = "创建时间", required = true)
private LocalDateTime createTime;
}

View File

@ -0,0 +1,16 @@
package co.yixiang.yshop.module.infra.controller.admin.test.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import javax.validation.constraints.*;
@Schema(description = "管理后台 - 字典类型更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class TestDemoUpdateReqVO extends TestDemoBaseVO {
@Schema(description = "编号", required = true)
@NotNull(message = "编号不能为空")
private Long id;
}

View File

@ -0,0 +1,44 @@
package co.yixiang.yshop.module.infra.controller.app.file;
import cn.hutool.core.io.IoUtil;
import co.yixiang.yshop.framework.common.pojo.CommonResult;
import co.yixiang.yshop.framework.operatelog.core.annotations.OperateLog;
import co.yixiang.yshop.module.infra.controller.app.file.vo.file.FileUploadReqVO;
import co.yixiang.yshop.module.infra.service.file.FileService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import static co.yixiang.yshop.framework.common.pojo.CommonResult.success;
@Tag(name = "用户 APP - 文件存储")
@RestController
@RequestMapping("/infra/file")
@Validated
@Slf4j
public class AppFileController {
@Resource
private FileService fileService;
@PostMapping("/upload")
@Operation(summary = "上传文件")
@OperateLog(logArgs = false) // 上传文件,没有记录操作日志的必要
public CommonResult<String> uploadFile(FileUploadReqVO uploadReqVO) throws Exception {
MultipartFile file = uploadReqVO.getFile();
String path = uploadReqVO.getPath();
return success(fileService.createFile(file.getOriginalFilename(), path, IoUtil.readBytes(file.getInputStream())));
}
}

View File

@ -0,0 +1,20 @@
package co.yixiang.yshop.module.infra.controller.app.file.vo.file;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
import javax.validation.constraints.NotNull;
@Schema(description = "app - 上传文件 Request VO")
@Data
public class FileUploadReqVO {
@Schema(description = "文件附件", required = true)
@NotNull(message = "文件附件不能为空")
private MultipartFile file;
@Schema(description = "文件附件", example = "yshopyuanma.png")
private String path;
}

View File

@ -0,0 +1,6 @@
/**
* 提供 RESTful API 给前端:
* 1. admin 包:提供给管理后台 yshop-ui-admin 前端项目
* 2. app 包:提供给用户 APP yshop-ui-app 前端项目,它的 Controller 和 VO 都要添加 App 前缀,用于和管理后台进行区分
*/
package co.yixiang.yshop.module.infra.controller;

View File

@ -0,0 +1,93 @@
package co.yixiang.yshop.module.infra.convert.codegen;
import co.yixiang.yshop.framework.common.pojo.PageResult;
import co.yixiang.yshop.module.infra.controller.admin.codegen.vo.CodegenDetailRespVO;
import co.yixiang.yshop.module.infra.controller.admin.codegen.vo.CodegenPreviewRespVO;
import co.yixiang.yshop.module.infra.controller.admin.codegen.vo.CodegenUpdateReqVO;
import co.yixiang.yshop.module.infra.controller.admin.codegen.vo.column.CodegenColumnRespVO;
import co.yixiang.yshop.module.infra.controller.admin.codegen.vo.table.CodegenTableRespVO;
import co.yixiang.yshop.module.infra.controller.admin.codegen.vo.table.DatabaseTableRespVO;
import co.yixiang.yshop.module.infra.dal.dataobject.codegen.CodegenColumnDO;
import co.yixiang.yshop.module.infra.dal.dataobject.codegen.CodegenTableDO;
import com.baomidou.mybatisplus.generator.config.po.TableField;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import org.apache.ibatis.type.JdbcType;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Mapper
public interface CodegenConvert {
CodegenConvert INSTANCE = Mappers.getMapper(CodegenConvert.class);
// ========== TableInfo 相关 ==========
@Mappings({
@Mapping(source = "name", target = "tableName"),
@Mapping(source = "comment", target = "tableComment"),
})
CodegenTableDO convert(TableInfo bean);
List<CodegenColumnDO> convertList(List<TableField> list);
@Mappings({
@Mapping(source = "name", target = "columnName"),
@Mapping(source = "metaInfo.jdbcType", target = "dataType", qualifiedByName = "getDataType"),
@Mapping(source = "comment", target = "columnComment"),
@Mapping(source = "metaInfo.nullable", target = "nullable"),
@Mapping(source = "keyFlag", target = "primaryKey"),
@Mapping(source = "keyIdentityFlag", target = "autoIncrement"),
@Mapping(source = "columnType.type", target = "javaType"),
@Mapping(source = "propertyName", target = "javaField"),
})
CodegenColumnDO convert(TableField bean);
@Named("getDataType")
default String getDataType(JdbcType jdbcType) {
return jdbcType.name();
}
// ========== CodegenTableDO 相关 ==========
// List<CodegenTableRespVO> convertList02(List<CodegenTableDO> list);
CodegenTableRespVO convert(CodegenTableDO bean);
PageResult<CodegenTableRespVO> convertPage(PageResult<CodegenTableDO> page);
// ========== CodegenTableDO 相关 ==========
List<CodegenColumnRespVO> convertList02(List<CodegenColumnDO> list);
CodegenTableDO convert(CodegenUpdateReqVO.Table bean);
List<CodegenColumnDO> convertList03(List<CodegenUpdateReqVO.Column> columns);
List<DatabaseTableRespVO> convertList04(List<TableInfo> list);
// ========== 其它 ==========
default CodegenDetailRespVO convert(CodegenTableDO table, List<CodegenColumnDO> columns) {
CodegenDetailRespVO respVO = new CodegenDetailRespVO();
respVO.setTable(convert(table));
respVO.setColumns(convertList02(columns));
return respVO;
}
default List<CodegenPreviewRespVO> convert(Map<String, String> codes) {
return codes.entrySet().stream().map(entry -> {
CodegenPreviewRespVO respVO = new CodegenPreviewRespVO();
respVO.setFilePath(entry.getKey());
respVO.setCode(entry.getValue());
return respVO;
}).collect(Collectors.toList());
}
}

View File

@ -0,0 +1,33 @@
package co.yixiang.yshop.module.infra.convert.config;
import co.yixiang.yshop.framework.common.pojo.PageResult;
import co.yixiang.yshop.module.infra.controller.admin.config.vo.ConfigCreateReqVO;
import co.yixiang.yshop.module.infra.controller.admin.config.vo.ConfigExcelVO;
import co.yixiang.yshop.module.infra.controller.admin.config.vo.ConfigRespVO;
import co.yixiang.yshop.module.infra.controller.admin.config.vo.ConfigUpdateReqVO;
import co.yixiang.yshop.module.infra.dal.dataobject.config.ConfigDO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface ConfigConvert {
ConfigConvert INSTANCE = Mappers.getMapper(ConfigConvert.class);
PageResult<ConfigRespVO> convertPage(PageResult<ConfigDO> page);
@Mapping(source = "configKey", target = "key")
ConfigRespVO convert(ConfigDO bean);
@Mapping(source = "key", target = "configKey")
ConfigDO convert(ConfigCreateReqVO bean);
ConfigDO convert(ConfigUpdateReqVO bean);
@Mapping(source = "configKey", target = "key")
List<ConfigExcelVO> convertList(List<ConfigDO> list);
}

View File

@ -0,0 +1,30 @@
package co.yixiang.yshop.module.infra.convert.db;
import java.util.*;
import co.yixiang.yshop.framework.common.pojo.PageResult;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import co.yixiang.yshop.module.infra.controller.admin.db.vo.*;
import co.yixiang.yshop.module.infra.dal.dataobject.db.DataSourceConfigDO;
/**
* 数据源配置 Convert
*
* @author yshop
*/
@Mapper
public interface DataSourceConfigConvert {
DataSourceConfigConvert INSTANCE = Mappers.getMapper(DataSourceConfigConvert.class);
DataSourceConfigDO convert(DataSourceConfigCreateReqVO bean);
DataSourceConfigDO convert(DataSourceConfigUpdateReqVO bean);
DataSourceConfigRespVO convert(DataSourceConfigDO bean);
List<DataSourceConfigRespVO> convertList(List<DataSourceConfigDO> list);
}

View File

@ -0,0 +1,36 @@
package co.yixiang.yshop.module.infra.convert.file;
import co.yixiang.yshop.framework.common.pojo.PageResult;
import co.yixiang.yshop.module.infra.controller.admin.file.vo.config.FileConfigCreateReqVO;
import co.yixiang.yshop.module.infra.controller.admin.file.vo.config.FileConfigRespVO;
import co.yixiang.yshop.module.infra.controller.admin.file.vo.config.FileConfigUpdateReqVO;
import co.yixiang.yshop.module.infra.dal.dataobject.file.FileConfigDO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* 文件配置 Convert
*
* @author yshop
*/
@Mapper
public interface FileConfigConvert {
FileConfigConvert INSTANCE = Mappers.getMapper(FileConfigConvert.class);
@Mapping(target = "config", ignore = true)
FileConfigDO convert(FileConfigCreateReqVO bean);
@Mapping(target = "config", ignore = true)
FileConfigDO convert(FileConfigUpdateReqVO bean);
FileConfigRespVO convert(FileConfigDO bean);
List<FileConfigRespVO> convertList(List<FileConfigDO> list);
PageResult<FileConfigRespVO> convertPage(PageResult<FileConfigDO> page);
}

View File

@ -0,0 +1,18 @@
package co.yixiang.yshop.module.infra.convert.file;
import co.yixiang.yshop.framework.common.pojo.PageResult;
import co.yixiang.yshop.module.infra.controller.admin.file.vo.file.FileRespVO;
import co.yixiang.yshop.module.infra.dal.dataobject.file.FileDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface FileConvert {
FileConvert INSTANCE = Mappers.getMapper(FileConvert.class);
FileRespVO convert(FileDO bean);
PageResult<FileRespVO> convertPage(PageResult<FileDO> page);
}

View File

@ -0,0 +1,36 @@
package co.yixiang.yshop.module.infra.convert.job;
import co.yixiang.yshop.framework.common.pojo.PageResult;
import co.yixiang.yshop.module.infra.controller.admin.job.vo.job.JobCreateReqVO;
import co.yixiang.yshop.module.infra.controller.admin.job.vo.job.JobExcelVO;
import co.yixiang.yshop.module.infra.controller.admin.job.vo.job.JobRespVO;
import co.yixiang.yshop.module.infra.controller.admin.job.vo.job.JobUpdateReqVO;
import co.yixiang.yshop.module.infra.dal.dataobject.job.JobDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* 定时任务 Convert
*
* @author yshop
*/
@Mapper
public interface JobConvert {
JobConvert INSTANCE = Mappers.getMapper(JobConvert.class);
JobDO convert(JobCreateReqVO bean);
JobDO convert(JobUpdateReqVO bean);
JobRespVO convert(JobDO bean);
List<JobRespVO> convertList(List<JobDO> list);
PageResult<JobRespVO> convertPage(PageResult<JobDO> page);
List<JobExcelVO> convertList02(List<JobDO> list);
}

View File

@ -0,0 +1,30 @@
package co.yixiang.yshop.module.infra.convert.job;
import co.yixiang.yshop.framework.common.pojo.PageResult;
import co.yixiang.yshop.module.infra.controller.admin.job.vo.log.JobLogExcelVO;
import co.yixiang.yshop.module.infra.controller.admin.job.vo.log.JobLogRespVO;
import co.yixiang.yshop.module.infra.dal.dataobject.job.JobLogDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* 定时任务日志 Convert
*
* @author yshop
*/
@Mapper
public interface JobLogConvert {
JobLogConvert INSTANCE = Mappers.getMapper(JobLogConvert.class);
JobLogRespVO convert(JobLogDO bean);
List<JobLogRespVO> convertList(List<JobLogDO> list);
PageResult<JobLogRespVO> convertPage(PageResult<JobLogDO> page);
List<JobLogExcelVO> convertList02(List<JobLogDO> list);
}

View File

@ -0,0 +1,33 @@
package co.yixiang.yshop.module.infra.convert.logger;
import co.yixiang.yshop.framework.common.pojo.PageResult;
import co.yixiang.yshop.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO;
import co.yixiang.yshop.module.infra.controller.admin.logger.vo.apiaccesslog.ApiAccessLogExcelVO;
import co.yixiang.yshop.module.infra.controller.admin.logger.vo.apiaccesslog.ApiAccessLogRespVO;
import co.yixiang.yshop.module.infra.dal.dataobject.logger.ApiAccessLogDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* API 访问日志 Convert
*
* @author yshop
*/
@Mapper
public interface ApiAccessLogConvert {
ApiAccessLogConvert INSTANCE = Mappers.getMapper(ApiAccessLogConvert.class);
ApiAccessLogRespVO convert(ApiAccessLogDO bean);
List<ApiAccessLogRespVO> convertList(List<ApiAccessLogDO> list);
PageResult<ApiAccessLogRespVO> convertPage(PageResult<ApiAccessLogDO> page);
List<ApiAccessLogExcelVO> convertList02(List<ApiAccessLogDO> list);
ApiAccessLogDO convert(ApiAccessLogCreateReqDTO bean);
}

View File

@ -0,0 +1,31 @@
package co.yixiang.yshop.module.infra.convert.logger;
import co.yixiang.yshop.framework.common.pojo.PageResult;
import co.yixiang.yshop.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO;
import co.yixiang.yshop.module.infra.controller.admin.logger.vo.apierrorlog.ApiErrorLogExcelVO;
import co.yixiang.yshop.module.infra.controller.admin.logger.vo.apierrorlog.ApiErrorLogRespVO;
import co.yixiang.yshop.module.infra.dal.dataobject.logger.ApiErrorLogDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* API 错误日志 Convert
*
* @author yshop
*/
@Mapper
public interface ApiErrorLogConvert {
ApiErrorLogConvert INSTANCE = Mappers.getMapper(ApiErrorLogConvert.class);
ApiErrorLogRespVO convert(ApiErrorLogDO bean);
PageResult<ApiErrorLogRespVO> convertPage(PageResult<ApiErrorLogDO> page);
List<ApiErrorLogExcelVO> convertList02(List<ApiErrorLogDO> list);
ApiErrorLogDO convert(ApiErrorLogCreateReqDTO bean);
}

View File

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

View File

@ -0,0 +1,34 @@
package co.yixiang.yshop.module.infra.convert.redis;
import cn.hutool.core.util.StrUtil;
import co.yixiang.yshop.framework.redis.core.RedisKeyDefine;
import co.yixiang.yshop.module.infra.controller.admin.redis.vo.RedisKeyDefineRespVO;
import co.yixiang.yshop.module.infra.controller.admin.redis.vo.RedisMonitorRespVO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
@Mapper
public interface RedisConvert {
RedisConvert INSTANCE = Mappers.getMapper(RedisConvert.class);
default RedisMonitorRespVO build(Properties info, Long dbSize, Properties commandStats) {
RedisMonitorRespVO respVO = RedisMonitorRespVO.builder().info(info).dbSize(dbSize)
.commandStats(new ArrayList<>(commandStats.size())).build();
commandStats.forEach((key, value) -> {
respVO.getCommandStats().add(RedisMonitorRespVO.CommandStat.builder()
.command(StrUtil.subAfter((String) key, "cmdstat_", false))
.calls(Long.valueOf(StrUtil.subBetween((String) value, "calls=", ",")))
.usec(Long.valueOf(StrUtil.subBetween((String) value, "usec=", ",")))
.build());
});
return respVO;
}
List<RedisKeyDefineRespVO> convertList(List<RedisKeyDefine> list);
}

View File

@ -0,0 +1,36 @@
package co.yixiang.yshop.module.infra.convert.test;
import co.yixiang.yshop.framework.common.pojo.PageResult;
import co.yixiang.yshop.module.infra.controller.admin.test.vo.TestDemoCreateReqVO;
import co.yixiang.yshop.module.infra.controller.admin.test.vo.TestDemoExcelVO;
import co.yixiang.yshop.module.infra.controller.admin.test.vo.TestDemoRespVO;
import co.yixiang.yshop.module.infra.controller.admin.test.vo.TestDemoUpdateReqVO;
import co.yixiang.yshop.module.infra.dal.dataobject.test.TestDemoDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* 字典类型 Convert
*
* @author yshop
*/
@Mapper
public interface TestDemoConvert {
TestDemoConvert INSTANCE = Mappers.getMapper(TestDemoConvert.class);
TestDemoDO convert(TestDemoCreateReqVO bean);
TestDemoDO convert(TestDemoUpdateReqVO bean);
TestDemoRespVO convert(TestDemoDO bean);
List<TestDemoRespVO> convertList(List<TestDemoDO> list);
PageResult<TestDemoRespVO> convertPage(PageResult<TestDemoDO> page);
List<TestDemoExcelVO> convertList02(List<TestDemoDO> list);
}

View File

@ -0,0 +1,142 @@
package co.yixiang.yshop.module.infra.dal.dataobject.codegen;
import co.yixiang.yshop.framework.mybatis.core.dataobject.BaseDO;
import co.yixiang.yshop.module.infra.enums.codegen.CodegenColumnHtmlTypeEnum;
import co.yixiang.yshop.module.infra.enums.codegen.CodegenColumnListConditionEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.generator.config.po.TableField;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 代码生成 column 字段定义
*
* @author yshop
*/
@TableName(value = "infra_codegen_column", autoResultMap = true)
@KeySequence("infra_codegen_column_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
public class CodegenColumnDO extends BaseDO {
/**
* ID 编号
*/
@TableId
private Long id;
/**
* 表编号
* <p>
* 关联 {@link CodegenTableDO#getId()}
*/
private Long tableId;
// ========== 表相关字段 ==========
/**
* 字段名
*
* 关联 {@link TableField#getName()}
*/
private String columnName;
/**
* 数据库字段类型
*
* 关联 {@link TableField.MetaInfo#getJdbcType()}
*/
private String dataType;
/**
* 字段描述
*
* 关联 {@link TableField#getComment()}
*/
private String columnComment;
/**
* 是否允许为空
*
* 关联 {@link TableField.MetaInfo#isNullable()}
*/
private Boolean nullable;
/**
* 是否主键
*
* 关联 {@link TableField#isKeyFlag()}
*/
private Boolean primaryKey;
/**
* 是否自增
*
* 关联 {@link TableField#isKeyIdentityFlag()}
*/
private Boolean autoIncrement;
/**
* 排序
*/
private Integer ordinalPosition;
// ========== Java 相关字段 ==========
/**
* Java 属性类型
*
* 例如说 String、Boolean 等等
*
* 关联 {@link TableField#getColumnType()}
*/
private String javaType;
/**
* Java 属性名
*
* 关联 {@link TableField#getPropertyName()}
*/
private String javaField;
/**
* 字典类型
* <p>
* 关联 DictTypeDO 的 type 属性
*/
private String dictType;
/**
* 数据示例,主要用于生成 Swagger 注解的 example 字段
*/
private String example;
// ========== CRUD 相关字段 ==========
/**
* 是否为 Create 创建操作的字段
*/
private Boolean createOperation;
/**
* 是否为 Update 更新操作的字段
*/
private Boolean updateOperation;
/**
* 是否为 List 查询操作的字段
*/
private Boolean listOperation;
/**
* List 查询操作的条件类型
* <p>
* 枚举 {@link CodegenColumnListConditionEnum}
*/
private String listOperationCondition;
/**
* 是否为 List 查询操作的返回字段
*/
private Boolean listOperationResult;
// ========== UI 相关字段 ==========
/**
* 显示类型
* <p>
* 枚举 {@link CodegenColumnHtmlTypeEnum}
*/
private String htmlType;
}

View File

@ -0,0 +1,119 @@
package co.yixiang.yshop.module.infra.dal.dataobject.codegen;
import co.yixiang.yshop.framework.mybatis.core.dataobject.BaseDO;
import co.yixiang.yshop.module.infra.dal.dataobject.db.DataSourceConfigDO;
import co.yixiang.yshop.module.infra.enums.codegen.CodegenFrontTypeEnum;
import co.yixiang.yshop.module.infra.enums.codegen.CodegenSceneEnum;
import co.yixiang.yshop.module.infra.enums.codegen.CodegenTemplateTypeEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 代码生成 table 表定义
*
* @author yshop
*/
@TableName(value = "infra_codegen_table", autoResultMap = true)
@KeySequence("infra_codegen_table_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
public class CodegenTableDO extends BaseDO {
/**
* ID 编号
*/
@TableId
private Long id;
/**
* 数据源编号
*
* 关联 {@link DataSourceConfigDO#getId()}
*/
private Long dataSourceConfigId;
/**
* 生成场景
*
* 枚举 {@link CodegenSceneEnum}
*/
private Integer scene;
// ========== 表相关字段 ==========
/**
* 表名称
*
* 关联 {@link TableInfo#getName()}
*/
private String tableName;
/**
* 表描述
*
* 关联 {@link TableInfo#getComment()}
*/
private String tableComment;
/**
* 备注
*/
private String remark;
// ========== 类相关字段 ==========
/**
* 模块名,即一级目录
*
* 例如说system、infra、tool 等等
*/
private String moduleName;
/**
* 业务名,即二级目录
*
* 例如说user、permission、dict 等等
*/
private String businessName;
/**
* 类名称(首字母大写)
*
* 例如说SysUser、SysMenu、SysDictData 等等
*/
private String className;
/**
* 类描述
*/
private String classComment;
/**
* 作者
*/
private String author;
// ========== 生成相关字段 ==========
/**
* 模板类型
*
* 枚举 {@link CodegenTemplateTypeEnum}
*/
private Integer templateType;
/**
* 代码生成的前端类型
*
* 枚举 {@link CodegenFrontTypeEnum}
*/
private Integer frontType;
// ========== 菜单相关字段 ==========
/**
* 父菜单编号
*
* 关联 MenuDO 的 id 属性
*/
private Long parentMenuId;
}

View File

@ -0,0 +1,64 @@
package co.yixiang.yshop.module.infra.dal.dataobject.config;
import co.yixiang.yshop.framework.mybatis.core.dataobject.BaseDO;
import co.yixiang.yshop.module.infra.enums.config.ConfigTypeEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* 参数配置表
*
* @author yshop
*/
@TableName("infra_config")
@KeySequence("infra_config_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ConfigDO extends BaseDO {
/**
* 参数主键
*/
@TableId
private Long id;
/**
* 参数分类
*/
private String category;
/**
* 参数名称
*/
private String name;
/**
* 参数键名
*
* 支持多 DB 类型时,无法直接使用 key + @TableField("config_key") 来实现转换,原因是 "config_key" AS key 而存在报错
*/
private String configKey;
/**
* 参数键值
*/
private String value;
/**
* 参数类型
*
* 枚举 {@link ConfigTypeEnum}
*/
private Integer type;
/**
* 是否可见
*
* 不可见的参数,一般是敏感参数,前端不可获取
*/
private Boolean visible;
/**
* 备注
*/
private String remark;
}

View File

@ -0,0 +1,48 @@
package co.yixiang.yshop.module.infra.dal.dataobject.db;
import co.yixiang.yshop.framework.mybatis.core.dataobject.BaseDO;
import co.yixiang.yshop.framework.mybatis.core.type.EncryptTypeHandler;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* 数据源配置
*
* @author yshop
*/
@TableName(value = "infra_data_source_config", autoResultMap = true)
@KeySequence("infra_data_source_config_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
public class DataSourceConfigDO extends BaseDO {
/**
* 主键编号 - Master 数据源
*/
public static final Long ID_MASTER = 0L;
/**
* 主键编号
*/
private Long id;
/**
* 连接名
*/
private String name;
/**
* 数据源连接
*/
private String url;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
@TableField(typeHandler = EncryptTypeHandler.class)
private String password;
}

View File

@ -0,0 +1,58 @@
package co.yixiang.yshop.module.infra.dal.dataobject.file;
import co.yixiang.yshop.framework.file.core.client.FileClientConfig;
import co.yixiang.yshop.framework.file.core.enums.FileStorageEnum;
import co.yixiang.yshop.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import lombok.*;
/**
* 文件配置表
*
* @author yshop
*/
@TableName(value = "infra_file_config", autoResultMap = true)
@KeySequence("infra_file_config_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class FileConfigDO extends BaseDO {
/**
* 配置编号,数据库自增
*/
private Long id;
/**
* 配置名
*/
private String name;
/**
* 存储器
*
* 枚举 {@link FileStorageEnum}
*/
private Integer storage;
/**
* 备注
*/
private String remark;
/**
* 是否为主配置
*
* 由于我们可以配置多个文件配置,默认情况下,使用主配置进行文件的上传
*/
private Boolean master;
/**
* 支付渠道配置
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private FileClientConfig config;
}

View File

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

View File

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

View File

@ -0,0 +1,74 @@
package co.yixiang.yshop.module.infra.dal.dataobject.job;
import co.yixiang.yshop.framework.mybatis.core.dataobject.BaseDO;
import co.yixiang.yshop.module.infra.enums.job.JobStatusEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
/**
* 定时任务 DO
*
* @author yshop
*/
@TableName("infra_job")
@KeySequence("infra_job_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class JobDO extends BaseDO {
/**
* 任务编号
*/
@TableId
private Long id;
/**
* 任务名称
*/
private String name;
/**
* 任务状态
*
* 枚举 {@link JobStatusEnum}
*/
private Integer status;
/**
* 处理器的名字
*/
private String handlerName;
/**
* 处理器的参数
*/
private String handlerParam;
/**
* CRON 表达式
*/
private String cronExpression;
// ========== 重试相关字段 ==========
/**
* 重试次数
* 如果不重试,则设置为 0
*/
private Integer retryCount;
/**
* 重试间隔,单位:毫秒
* 如果没有间隔,则设置为 0
*/
private Integer retryInterval;
// ========== 监控相关字段 ==========
/**
* 监控超时时间,单位:毫秒
* 为空时,表示不监控
*
* 注意,这里的超时的目的,不是进行任务的取消,而是告警任务的执行时间过长
*/
private Integer monitorTimeout;
}

View File

@ -0,0 +1,82 @@
package co.yixiang.yshop.module.infra.dal.dataobject.job;
import co.yixiang.yshop.framework.mybatis.core.dataobject.BaseDO;
import co.yixiang.yshop.framework.quartz.core.handler.JobHandler;
import co.yixiang.yshop.module.infra.enums.job.JobLogStatusEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDateTime;
/**
* 定时任务的执行日志
*
* @author yshop
*/
@TableName("infra_job_log")
@KeySequence("infra_job_log_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class JobLogDO extends BaseDO {
/**
* 日志编号
*/
private Long id;
/**
* 任务编号
*
* 关联 {@link JobDO#getId()}
*/
private Long jobId;
/**
* 处理器的名字
*
* 冗余字段 {@link JobDO#getHandlerName()}
*/
private String handlerName;
/**
* 处理器的参数
*
* 冗余字段 {@link JobDO#getHandlerParam()}
*/
private String handlerParam;
/**
* 第几次执行
*
* 用于区分是不是重试执行。如果是重试执行,则 index 大于 1
*/
private Integer executeIndex;
/**
* 开始执行时间
*/
private LocalDateTime beginTime;
/**
* 结束执行时间
*/
private LocalDateTime endTime;
/**
* 执行时长,单位:毫秒
*/
private Integer duration;
/**
* 状态
*
* 枚举 {@link JobLogStatusEnum}
*/
private Integer status;
/**
* 结果数据
*
* 成功时,使用 {@link JobHandler#execute(String)} 的结果
* 失败时,使用 {@link JobHandler#execute(String)} 的异常堆栈
*/
private String result;
}

View File

@ -0,0 +1,109 @@
package co.yixiang.yshop.module.infra.dal.dataobject.logger;
import co.yixiang.yshop.framework.common.enums.UserTypeEnum;
import co.yixiang.yshop.framework.common.pojo.CommonResult;
import co.yixiang.yshop.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDateTime;
/**
* API 访问日志
*
* @author yshop
*/
@TableName("infra_api_access_log")
@KeySequence(value = "infra_api_access_log_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ApiAccessLogDO extends BaseDO {
/**
* 编号
*/
@TableId
private Long id;
/**
* 链路追踪编号
*
* 一般来说通过链路追踪编号可以将访问日志错误日志链路追踪日志logger 打印日志等,结合在一起,从而进行排错。
*/
private String traceId;
/**
* 用户编号
*/
private Long userId;
/**
* 用户类型
*
* 枚举 {@link UserTypeEnum}
*/
private Integer userType;
/**
* 应用名
*
* 目前读取 `spring.application.name` 配置项
*/
private String applicationName;
// ========== 请求相关字段 ==========
/**
* 请求方法名
*/
private String requestMethod;
/**
* 访问地址
*/
private String requestUrl;
/**
* 请求参数
*
* query: Query String
* body: Quest Body
*/
private String requestParams;
/**
* 用户 IP
*/
private String userIp;
/**
* 浏览器 UA
*/
private String userAgent;
// ========== 执行相关字段 ==========
/**
* 开始请求时间
*/
private LocalDateTime beginTime;
/**
* 结束请求时间
*/
private LocalDateTime endTime;
/**
* 执行时长,单位:毫秒
*/
private Integer duration;
/**
* 结果码
*
* 目前使用的 {@link CommonResult#getCode()} 属性
*/
private Integer resultCode;
/**
* 结果提示
*
* 目前使用的 {@link CommonResult#getMsg()} 属性
*/
private String resultMsg;
}

View File

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

View File

@ -0,0 +1,50 @@
package co.yixiang.yshop.module.infra.dal.dataobject.test;
import co.yixiang.yshop.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
/**
* 字典类型 DO
*
* @author yshop
*/
@TableName("infra_test_demo")
@KeySequence("infra_test_demo_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class TestDemoDO extends BaseDO {
/**
* 编号
*/
@TableId
private Long id;
/**
* 名字
*/
private String name;
/**
* 状态
*/
private Integer status;
/**
* 类型
*/
private Integer type;
/**
* 分类
*/
private Integer category;
/**
* 备注
*/
private String remark;
}

View File

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

View File

@ -0,0 +1,32 @@
package co.yixiang.yshop.module.infra.dal.mysql.codegen;
import co.yixiang.yshop.framework.common.pojo.PageResult;
import co.yixiang.yshop.framework.mybatis.core.mapper.BaseMapperX;
import co.yixiang.yshop.framework.mybatis.core.query.LambdaQueryWrapperX;
import co.yixiang.yshop.module.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO;
import co.yixiang.yshop.module.infra.dal.dataobject.codegen.CodegenTableDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface CodegenTableMapper extends BaseMapperX<CodegenTableDO> {
default CodegenTableDO selectByTableNameAndDataSourceConfigId(String tableName, Long dataSourceConfigId) {
return selectOne(CodegenTableDO::getTableName, tableName,
CodegenTableDO::getDataSourceConfigId, dataSourceConfigId);
}
default PageResult<CodegenTableDO> selectPage(CodegenTablePageReqVO pageReqVO) {
return selectPage(pageReqVO, new LambdaQueryWrapperX<CodegenTableDO>()
.likeIfPresent(CodegenTableDO::getTableName, pageReqVO.getTableName())
.likeIfPresent(CodegenTableDO::getTableComment, pageReqVO.getTableComment())
.likeIfPresent(CodegenTableDO::getClassName, pageReqVO.getClassName())
.betweenIfPresent(CodegenTableDO::getCreateTime, pageReqVO.getCreateTime()));
}
default List<CodegenTableDO> selectListByDataSourceConfigId(Long dataSourceConfigId) {
return selectList(CodegenTableDO::getDataSourceConfigId, dataSourceConfigId);
}
}

View File

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

View File

@ -0,0 +1,14 @@
package co.yixiang.yshop.module.infra.dal.mysql.db;
import co.yixiang.yshop.framework.mybatis.core.mapper.BaseMapperX;
import co.yixiang.yshop.module.infra.dal.dataobject.db.DataSourceConfigDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 数据源配置 Mapper
*
* @author yshop
*/
@Mapper
public interface DataSourceConfigMapper extends BaseMapperX<DataSourceConfigDO> {
}

View File

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

View File

@ -0,0 +1,46 @@
package co.yixiang.yshop.module.infra.dal.mysql.file;
import cn.hutool.core.collection.CollUtil;
import co.yixiang.yshop.framework.file.core.client.db.DBFileContentFrameworkDAO;
import co.yixiang.yshop.module.infra.dal.dataobject.file.FileContentDO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
import java.util.List;
import java.util.Optional;
@Repository
public class FileContentDAOImpl implements DBFileContentFrameworkDAO {
@Resource
private FileContentMapper fileContentMapper;
@Override
public void insert(Long configId, String path, byte[] content) {
FileContentDO entity = new FileContentDO().setConfigId(configId)
.setPath(path).setContent(content);
fileContentMapper.insert(entity);
}
@Override
public void delete(Long configId, String path) {
fileContentMapper.delete(buildQuery(configId, path));
}
@Override
public byte[] selectContent(Long configId, String path) {
List<FileContentDO> list = fileContentMapper.selectList(
buildQuery(configId, path).select(FileContentDO::getContent).orderByDesc(FileContentDO::getId));
return Optional.ofNullable(CollUtil.getFirst(list))
.map(FileContentDO::getContent)
.orElse(null);
}
private LambdaQueryWrapper<FileContentDO> buildQuery(Long configId, String path) {
return new LambdaQueryWrapper<FileContentDO>()
.eq(FileContentDO::getConfigId, configId)
.eq(FileContentDO::getPath, path);
}
}

View File

@ -0,0 +1,9 @@
package co.yixiang.yshop.module.infra.dal.mysql.file;
import co.yixiang.yshop.module.infra.dal.dataobject.file.FileContentDO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface FileContentMapper extends BaseMapper<FileContentDO> {
}

View File

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

View File

@ -0,0 +1,43 @@
package co.yixiang.yshop.module.infra.dal.mysql.job;
import co.yixiang.yshop.framework.common.pojo.PageResult;
import co.yixiang.yshop.framework.mybatis.core.mapper.BaseMapperX;
import co.yixiang.yshop.framework.mybatis.core.query.LambdaQueryWrapperX;
import co.yixiang.yshop.module.infra.controller.admin.job.vo.log.JobLogExportReqVO;
import co.yixiang.yshop.module.infra.controller.admin.job.vo.log.JobLogPageReqVO;
import co.yixiang.yshop.module.infra.dal.dataobject.job.JobLogDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 任务日志 Mapper
*
* @author yshop
*/
@Mapper
public interface JobLogMapper extends BaseMapperX<JobLogDO> {
default PageResult<JobLogDO> selectPage(JobLogPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<JobLogDO>()
.eqIfPresent(JobLogDO::getJobId, reqVO.getJobId())
.likeIfPresent(JobLogDO::getHandlerName, reqVO.getHandlerName())
.geIfPresent(JobLogDO::getBeginTime, reqVO.getBeginTime())
.leIfPresent(JobLogDO::getEndTime, reqVO.getEndTime())
.eqIfPresent(JobLogDO::getStatus, reqVO.getStatus())
.orderByDesc(JobLogDO::getId) // ID 倒序
);
}
default List<JobLogDO> selectList(JobLogExportReqVO reqVO) {
return selectList(new LambdaQueryWrapperX<JobLogDO>()
.eqIfPresent(JobLogDO::getJobId, reqVO.getJobId())
.likeIfPresent(JobLogDO::getHandlerName, reqVO.getHandlerName())
.geIfPresent(JobLogDO::getBeginTime, reqVO.getBeginTime())
.leIfPresent(JobLogDO::getEndTime, reqVO.getEndTime())
.eqIfPresent(JobLogDO::getStatus, reqVO.getStatus())
.orderByDesc(JobLogDO::getId) // ID 倒序
);
}
}

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