Merge branch 'master' into feat_userprofile

# Conflicts:
#	src/main/resources/application-dev.yaml
#	src/main/resources/application-local.yaml
pull/2/head
niudehua 2021-03-13 21:02:01 +08:00
commit e4bda22b73
46 changed files with 1633 additions and 1149 deletions

View File

@ -50,6 +50,7 @@
| --- | --- | --- | | --- | --- | --- |
| 🚀 | 配置管理 | 对系统动态配置常用参数,支持 SpringBoot 加载 | | 🚀 | 配置管理 | 对系统动态配置常用参数,支持 SpringBoot 加载 |
| | 定时任务 | 在线(添加、修改、删除)任务调度包含执行结果日志 | | | 定时任务 | 在线(添加、修改、删除)任务调度包含执行结果日志 |
| 🚀 | 文件服务 | 支持本地文件存储,同时支持兼容 Amazon S3 协议的云服务、开源组件 |
| 🚀 | API 日志 | 包括 RESTful API 访问日志、异常日志两部分,方便排查 API 相关的问题 | | 🚀 | API 日志 | 包括 RESTful API 访问日志、异常日志两部分,方便排查 API 相关的问题 |
| | MySQL 监控 | 监视当前系统数据库连接池状态可进行分析SQL找出系统性能瓶颈 | | | MySQL 监控 | 监视当前系统数据库连接池状态可进行分析SQL找出系统性能瓶颈 |
| | Redis 监控 |监控 Redis 数据库的使用情况,使用的 Redis Key 管理 | | | Redis 监控 |监控 Redis 数据库的使用情况,使用的 Redis Key 管理 |
@ -64,7 +65,6 @@
计划新增: 计划新增:
* 工作流 * 工作流
* 错误码 * 错误码
* 文件服务
### 研发工具 ### 研发工具

30
pom.xml
View File

@ -40,8 +40,8 @@
<skywalking.version>8.3.0</skywalking.version> <skywalking.version>8.3.0</skywalking.version>
<spring-boot-admin.version>2.3.1</spring-boot-admin.version> <spring-boot-admin.version>2.3.1</spring-boot-admin.version>
<!-- 工具类相关 --> <!-- 工具类相关 -->
<mapstruct.version>1.4.2.Final</mapstruct.version> <lombok.version>1.16.14</lombok.version>
<lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version> <mapstruct.version>1.4.1.Final</mapstruct.version>
<hutool.version>5.5.6</hutool.version> <hutool.version>5.5.6</hutool.version>
<easyexcel.verion>2.2.7</easyexcel.verion> <easyexcel.verion>2.2.7</easyexcel.verion>
<velocity.version>2.2</velocity.version> <velocity.version>2.2</velocity.version>
@ -227,26 +227,24 @@
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
<optional>true</optional> <version>${lombok.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.mapstruct</groupId> <groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher --> <artifactId>mapstruct</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher -->
<version>${mapstruct.version}</version> <version>${mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.mapstruct</groupId> <groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId> <artifactId>mapstruct-jdk8</artifactId>
<version>${mapstruct.version}</version> <version>${mapstruct.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>${lombok-mapstruct-binding.version}</version>
<optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
@ -325,6 +323,18 @@
<configuration> <configuration>
<source>${java.version}</source> <!-- or higher, depending on your project --> <source>${java.version}</source> <!-- or higher, depending on your project -->
<target>${java.version}</target> <!-- or higher, depending on your project --> <target>${java.version}</target> <!-- or higher, depending on your project -->
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
</annotationProcessorPaths>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>

View File

@ -0,0 +1,18 @@
import request from '@/utils/request'
// 删除文件
export function deleteFile(id) {
return request({
url: '/infra/file/delete?id=' + id,
method: 'delete'
})
}
// 获得文件分页
export function getFilePage(query) {
return request({
url: '/infra/file/page',
method: 'get',
params: query
})
}

View File

@ -0,0 +1,202 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="文件路径" prop="id">
<el-input v-model="queryParams.id" placeholder="请输入文件路径" clearable size="small" @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="文件类型" prop="type">
<el-select v-model="queryParams.type" placeholder="请选择文件类型" clearable size="small">
<el-option label="请选择字典生成" value="" />
</el-select>
</el-form-item>
<el-form-item label="创建时间">
<el-date-picker v-model="dateRangeCreateTime" size="small" style="width: 240px" value-format="yyyy-MM-dd"
type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"></el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="文件路径" align="center" prop="id" width="300" />
<el-table-column label="文件类型" align="center" prop="type" width="80" />
<el-table-column label="文件内容" align="center" prop="content">
<template slot-scope="scope">
<img v-if="scope.row.type === 'jpg' || scope.row.type === 'png' || scope.row.type === 'gif'"
width="200px" :src="getFileUrl + scope.row.id">
<i v-else></i>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['infra:file:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>
<el-upload ref="upload" :limit="1" accept=".jpg, .png, .gif" :auto-upload="false" drag
:headers="upload.headers" :action="upload.url" :data="upload.data" :disabled="upload.isUploading"
:on-change="handleFileChange"
:on-progress="handleFileUploadProgress"
:on-success="handleFileSuccess">
<i class="el-icon-upload"></i>
<div class="el-upload__text">
将文件拖到此处 <em>点击上传</em>
</div>
<div class="el-upload__tip" style="color:red" slot="tip">提示仅允许导入 jpgpnggif 格式文件</div>
</el-upload>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitFileForm"> </el-button>
<el-button @click="upload.open = false"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { deleteFile, getFilePage } from "@/api/infra/file";
import {getToken} from "@/utils/auth";
export default {
name: "File",
data() {
return {
getFileUrl: process.env.VUE_APP_BASE_API + '/api/infra/file/get/',
//
loading: true,
//
showSearch: true,
//
total: 0,
//
list: [],
//
title: "",
dateRangeCreateTime: [],
//
queryParams: {
pageNo: 1,
pageSize: 10,
id: null,
type: null,
},
//
upload: {
open: false, //
title: "", //
isUploading: false, //
url: process.env.VUE_APP_BASE_API + '/api/' + "/infra/file/upload", //
headers: { Authorization: "Bearer " + getToken() }, //
data: {} //
},
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
//
let params = {...this.queryParams};
this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');
//
getFilePage(params).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
content: undefined,
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.dateRangeCreateTime = [];
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.upload.open = true;
this.upload.title = "上传文件";
},
/** 处理上传的文件发生变化 */
handleFileChange(file, fileList) {
this.upload.data.path = file.name;
},
/** 处理文件上传中 */
handleFileUploadProgress(event, file, fileList) {
this.upload.isUploading = true; //
},
/** 发起文件上窜 */
submitFileForm() {
this.$refs.upload.submit();
},
/** 文件上传成功处理 */
handleFileSuccess(response, file, fileList) {
//
this.upload.open = false;
this.upload.isUploading = false;
this.$refs.upload.clearFiles();
//
this.msgSuccess("上传成功");
this.getList();
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$confirm('是否确认删除文件编号为"' + id + '"的数据项?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function() {
return deleteFile(id);
}).then(() => {
this.getList();
this.msgSuccess("删除成功");
})
},
}
};
</script>

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
package cn.iocoder.dashboard.framework.file.config; package cn.iocoder.dashboard.framework.file.config;
import cn.iocoder.dashboard.modules.system.controller.common.SysFileController; import cn.iocoder.dashboard.modules.infra.controller.file.InfFileController;
import lombok.Data; import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@ -13,7 +13,7 @@ import javax.validation.constraints.NotNull;
public class FileProperties { public class FileProperties {
/** /**
* {@link SysFileController#} * {@link InfFileController#}
*/ */
@NotNull(message = "基础文件路径不能为空") @NotNull(message = "基础文件路径不能为空")
private String basePath; private String basePath;

View File

@ -134,7 +134,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
// 静态资源,可匿名访问 // 静态资源,可匿名访问
.antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll() .antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll()
// 文件的获取接口,可匿名访问 // 文件的获取接口,可匿名访问
.antMatchers(webProperties.getApiPrefix() + "/system/file/get/**").anonymous() .antMatchers(webProperties.getApiPrefix() + "/infra/file/get/**").anonymous()
// Swagger 接口文档 // Swagger 接口文档
.antMatchers("/swagger-ui.html").anonymous() .antMatchers("/swagger-ui.html").anonymous()
.antMatchers("/swagger-resources/**").anonymous() .antMatchers("/swagger-resources/**").anonymous()

View File

@ -1,9 +1,13 @@
package cn.iocoder.dashboard.modules.system.controller.common; package cn.iocoder.dashboard.modules.infra.controller.file;
import cn.hutool.core.io.IoUtil; import cn.hutool.core.io.IoUtil;
import cn.iocoder.dashboard.common.pojo.CommonResult; import cn.iocoder.dashboard.common.pojo.CommonResult;
import cn.iocoder.dashboard.modules.system.dal.dataobject.common.SysFileDO; import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.modules.system.service.common.SysFileService; import cn.iocoder.dashboard.modules.infra.controller.file.vo.InfFilePageReqVO;
import cn.iocoder.dashboard.modules.infra.controller.file.vo.InfFileRespVO;
import cn.iocoder.dashboard.modules.infra.convert.file.InfFileConvert;
import cn.iocoder.dashboard.modules.infra.dal.dataobject.file.InfFileDO;
import cn.iocoder.dashboard.modules.infra.service.file.InfFileService;
import cn.iocoder.dashboard.util.servlet.ServletUtils; import cn.iocoder.dashboard.util.servlet.ServletUtils;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParam;
@ -11,40 +15,53 @@ import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException; import java.io.IOException;
import static cn.iocoder.dashboard.common.pojo.CommonResult.success; import static cn.iocoder.dashboard.common.pojo.CommonResult.success;
@Api(tags = "文件存储") @Api(tags = "文件存储")
@RestController @RestController
@RequestMapping("/system/file") @RequestMapping("/infra/file")
@Validated
@Slf4j @Slf4j
public class SysFileController { public class InfFileController {
@Resource @Resource
private SysFileService fileService; private InfFileService fileService;
@PostMapping("/upload")
@ApiOperation("上传文件") @ApiOperation("上传文件")
@ApiImplicitParams({ @ApiImplicitParams({
@ApiImplicitParam(name = "path", value = "文件附件", required = true, dataTypeClass = MultipartFile.class), @ApiImplicitParam(name = "file", value = "文件附件", required = true, dataTypeClass = MultipartFile.class),
@ApiImplicitParam(name = "path", value = "文件路径", required = true, example = "yudaoyuanma.png", dataTypeClass = Long.class) @ApiImplicitParam(name = "path", value = "文件路径", required = false, example = "yudaoyuanma.png", dataTypeClass = String.class)
}) })
@PostMapping("/upload")
public CommonResult<String> uploadFile(@RequestParam("file") MultipartFile file, public CommonResult<String> uploadFile(@RequestParam("file") MultipartFile file,
@RequestParam("path") String path) throws IOException { @RequestParam("path") String path) throws IOException {
return success(fileService.createFile(path, IoUtil.readBytes(file.getInputStream()))); return success(fileService.createFile(path, IoUtil.readBytes(file.getInputStream())));
} }
@DeleteMapping("/delete")
@ApiOperation("删除文件")
@ApiImplicitParam(name = "id", value = "编号", required = true)
@PreAuthorize("@ss.hasPermission('infra:file:delete')")
public CommonResult<Boolean> deleteFile(@RequestParam("id") String id) {
fileService.deleteFile(id);
return success(true);
}
@GetMapping("/get/{path}")
@ApiOperation("下载文件") @ApiOperation("下载文件")
@ApiImplicitParam(name = "path", value = "文件附件", required = true, dataTypeClass = MultipartFile.class) @ApiImplicitParam(name = "path", value = "文件附件", required = true, dataTypeClass = MultipartFile.class)
@GetMapping("/get/{path}")
public void getFile(HttpServletResponse response, @PathVariable("path") String path) throws IOException { public void getFile(HttpServletResponse response, @PathVariable("path") String path) throws IOException {
SysFileDO file = fileService.getFile(path); InfFileDO file = fileService.getFile(path);
if (file == null) { if (file == null) {
log.warn("[getFile][path({}) 文件不存在]", path); log.warn("[getFile][path({}) 文件不存在]", path);
response.setStatus(HttpStatus.NOT_FOUND.value()); response.setStatus(HttpStatus.NOT_FOUND.value());
@ -53,4 +70,12 @@ public class SysFileController {
ServletUtils.writeAttachment(response, path, file.getContent()); ServletUtils.writeAttachment(response, path, file.getContent());
} }
@GetMapping("/page")
@ApiOperation("获得文件分页")
@PreAuthorize("@ss.hasPermission('infra:file:query')")
public CommonResult<PageResult<InfFileRespVO>> getFilePage(@Valid InfFilePageReqVO pageVO) {
PageResult<InfFileDO> pageResult = fileService.getFilePage(pageVO);
return success(InfFileConvert.INSTANCE.convertPage(pageResult));
}
} }

View File

@ -0,0 +1,35 @@
package cn.iocoder.dashboard.modules.infra.controller.file.vo;
import cn.iocoder.dashboard.common.pojo.PageParam;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
import static cn.iocoder.dashboard.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@ApiModel("文件分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class InfFilePageReqVO extends PageParam {
@ApiModelProperty(value = "文件路径", example = "yudao", notes = "模糊匹配")
private String id;
@ApiModelProperty(value = "文件类型", example = "jpg", notes = "模糊匹配")
private String type;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@ApiModelProperty(value = "开始创建时间")
private Date beginCreateTime;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@ApiModelProperty(value = "结束创建时间")
private Date endCreateTime;
}

View File

@ -0,0 +1,22 @@
package cn.iocoder.dashboard.modules.infra.controller.file.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
@ApiModel(value = "文件 Response VO", description = "不返回 content 字段,太大")
@Data
public class InfFileRespVO {
@ApiModelProperty(value = "文件路径", required = true, example = "yudao.jpg")
private String id;
@ApiModelProperty(value = "文件类型", required = true, example = "jpg")
private String type;
@ApiModelProperty(value = "创建时间", required = true)
private Date createTime;
}

View File

@ -0,0 +1,18 @@
package cn.iocoder.dashboard.modules.infra.convert.file;
import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.modules.infra.controller.file.vo.InfFileRespVO;
import cn.iocoder.dashboard.modules.infra.dal.dataobject.file.InfFileDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface InfFileConvert {
InfFileConvert INSTANCE = Mappers.getMapper(InfFileConvert.class);
InfFileRespVO convert(InfFileDO bean);
PageResult<InfFileRespVO> convertPage(PageResult<InfFileDO> page);
}

View File

@ -0,0 +1,43 @@
package cn.iocoder.dashboard.modules.infra.dal.dataobject.file;
import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.io.InputStream;
/**
*
*
* @author
*/
@Data
@TableName("inf_file")
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class InfFileDO extends BaseDO {
/**
*
*/
@TableId(type = IdType.INPUT)
private String id;
/**
*
*
* {@link cn.hutool.core.io.FileTypeUtil#getType(InputStream)}
*/
@TableField(value = "`type`")
private String type;
/**
*
*/
private byte[] content;
}

View File

@ -0,0 +1,25 @@
package cn.iocoder.dashboard.modules.infra.dal.mysql.file;
import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.dashboard.framework.mybatis.core.query.QueryWrapperX;
import cn.iocoder.dashboard.modules.infra.controller.file.vo.InfFilePageReqVO;
import cn.iocoder.dashboard.modules.infra.dal.dataobject.file.InfFileDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface InfFileMapper extends BaseMapperX<InfFileDO> {
default Integer selectCountById(String id) {
return selectCount("id", id);
}
default PageResult<InfFileDO> selectPage(InfFilePageReqVO reqVO) {
return selectPage(reqVO, new QueryWrapperX<InfFileDO>()
.likeIfPresent("id", reqVO.getId())
.likeIfPresent("type", reqVO.getType())
.betweenIfPresent("create_time", reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())
.orderByDesc("create_time"));
}
}

View File

@ -27,4 +27,7 @@ public interface InfErrorCodeConstants {
ErrorCode API_ERROR_LOG_NOT_FOUND = new ErrorCode(1001002000, "API 错误日志不存在"); ErrorCode API_ERROR_LOG_NOT_FOUND = new ErrorCode(1001002000, "API 错误日志不存在");
ErrorCode API_ERROR_LOG_PROCESSED = new ErrorCode(1001002001, "API 错误日志已处理"); ErrorCode API_ERROR_LOG_PROCESSED = new ErrorCode(1001002001, "API 错误日志已处理");
// ========== 文件 1001003000 ==========
ErrorCode FILE_NOT_EXISTS = new ErrorCode(1001003000, "文件不存在");
} }

View File

@ -0,0 +1,46 @@
package cn.iocoder.dashboard.modules.infra.service.file;
import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.modules.infra.controller.file.vo.InfFilePageReqVO;
import cn.iocoder.dashboard.modules.infra.dal.dataobject.file.InfFileDO;
/**
* Service
*
* @author
*/
public interface InfFileService {
/**
* 访
*
* @param path
* @param content
* @return
*/
String createFile(String path, byte[] content);
/**
*
*
* @param id
*/
void deleteFile(String id);
/**
*
*
* @param path
* @return
*/
InfFileDO getFile(String path);
/**
*
*
* @param pageReqVO
* @return
*/
PageResult<InfFileDO> getFilePage(InfFilePageReqVO pageReqVO);
}

View File

@ -0,0 +1,72 @@
package cn.iocoder.dashboard.modules.infra.service.file.impl;
import cn.hutool.core.io.FileTypeUtil;
import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.framework.file.config.FileProperties;
import cn.iocoder.dashboard.modules.infra.controller.file.vo.InfFilePageReqVO;
import cn.iocoder.dashboard.modules.infra.dal.dataobject.file.InfFileDO;
import cn.iocoder.dashboard.modules.infra.dal.mysql.file.InfFileMapper;
import cn.iocoder.dashboard.modules.infra.service.file.InfFileService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import static cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants.FILE_NOT_EXISTS;
import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.FILE_PATH_EXISTS;
/**
* Service
*
* @author
*/
@Service
public class InfFileServiceImpl implements InfFileService {
@Resource
private InfFileMapper fileMapper;
@Resource
private FileProperties fileProperties;
@Override
public String createFile(String path, byte[] content) {
if (fileMapper.selectCountById(path) > 0) {
throw exception(FILE_PATH_EXISTS);
}
// 保存到数据库
InfFileDO file = new InfFileDO();
file.setId(path);
file.setType(FileTypeUtil.getType(new ByteArrayInputStream(content)));
file.setContent(content);
fileMapper.insert(file);
// 拼接路径返回
return fileProperties.getBasePath() + path;
}
@Override
public void deleteFile(String id) {
// 校验存在
this.validateFileExists(id);
// 更新
fileMapper.deleteById(id);
}
private void validateFileExists(String id) {
if (fileMapper.selectById(id) == null) {
throw exception(FILE_NOT_EXISTS);
}
}
@Override
public InfFileDO getFile(String path) {
return fileMapper.selectById(path);
}
@Override
public PageResult<InfFileDO> getFilePage(InfFilePageReqVO pageReqVO) {
return fileMapper.selectPage(pageReqVO);
}
}

View File

@ -21,8 +21,8 @@ public class SysCaptchaController {
@Resource @Resource
private SysCaptchaService captchaService; private SysCaptchaService captchaService;
@ApiOperation("生成图片验证码")
@GetMapping("/get-image") @GetMapping("/get-image")
@ApiOperation("生成图片验证码")
public CommonResult<SysCaptchaImageRespVO> getCaptchaImage() { public CommonResult<SysCaptchaImageRespVO> getCaptchaImage() {
return success(captchaService.getCaptchaImage()); return success(captchaService.getCaptchaImage());
} }

View File

@ -1,30 +0,0 @@
package cn.iocoder.dashboard.modules.system.dal.dataobject.common;
import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
*
*
* @author
*/
@Data
@TableName("sys_file")
@EqualsAndHashCode(callSuper = true)
public class SysFileDO extends BaseDO {
/**
*
*/
@TableId(type = IdType.INPUT)
private String id;
/**
*
*/
private byte[] content;
}

View File

@ -1,15 +0,0 @@
package cn.iocoder.dashboard.modules.system.dal.mysql.common;
import cn.iocoder.dashboard.modules.system.dal.dataobject.common.SysFileDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface SysFileMapper extends BaseMapper<SysFileDO> {
default Integer selectCountById(String id) {
return selectCount(new QueryWrapper<SysFileDO>().eq("id", id));
}
}

View File

@ -1,29 +0,0 @@
package cn.iocoder.dashboard.modules.system.service.common;
import cn.iocoder.dashboard.modules.system.dal.dataobject.common.SysFileDO;
/**
* Service
*
* @author
*/
public interface SysFileService {
/**
* 访
*
* @param path
* @param content
* @return
*/
String createFile(String path, byte[] content);
/**
*
*
* @param path
* @return
*/
SysFileDO getFile(String path);
}

View File

@ -1,47 +0,0 @@
package cn.iocoder.dashboard.modules.system.service.common.impl;
import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.dashboard.framework.file.config.FileProperties;
import cn.iocoder.dashboard.modules.system.dal.dataobject.common.SysFileDO;
import cn.iocoder.dashboard.modules.system.dal.mysql.common.SysFileMapper;
import cn.iocoder.dashboard.modules.system.service.common.SysFileService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.FILE_PATH_EXISTS;
/**
* Service
*
* @author
*/
@Service
public class SysFileServiceImpl implements SysFileService {
@Resource
private SysFileMapper fileMapper;
@Resource
private FileProperties fileProperties;
@Override
public String createFile(String path, byte[] content) {
if (fileMapper.selectCountById(path) > 0) {
throw ServiceExceptionUtil.exception(FILE_PATH_EXISTS);
}
// 保存到数据库
SysFileDO file = new SysFileDO();
file.setId(path);
file.setContent(content);
fileMapper.insert(file);
// 拼接路径返回
return fileProperties.getBasePath() + path;
}
@Override
public SysFileDO getFile(String path) {
return fileMapper.selectById(path);
}
}

View File

@ -9,6 +9,7 @@ import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/** /**
* column * column
@ -17,7 +18,7 @@ import lombok.EqualsAndHashCode;
*/ */
@TableName(value = "tool_codegen_column", autoResultMap = true) @TableName(value = "tool_codegen_column", autoResultMap = true)
@Data @Data
@Builder @Accessors(chain = true)
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class ToolCodegenColumnDO extends BaseDO { public class ToolCodegenColumnDO extends BaseDO {

View File

@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/** /**
* table * table
@ -15,7 +16,7 @@ import lombok.EqualsAndHashCode;
*/ */
@TableName(value = "tool_codegen_table", autoResultMap = true) @TableName(value = "tool_codegen_table", autoResultMap = true)
@Data @Data
@Builder @Accessors(chain = true)
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class ToolCodegenTableDO extends BaseDO { public class ToolCodegenTableDO extends BaseDO {

View File

@ -99,6 +99,7 @@ public class ToolCodegenBuilder {
.put(String.class.getSimpleName(), Sets.newHashSet("tinytext", "text", "mediumtext", "longtext", // 长文本 .put(String.class.getSimpleName(), Sets.newHashSet("tinytext", "text", "mediumtext", "longtext", // 长文本
"char", "varchar", "nvarchar", "varchar2")) // 短文本 "char", "varchar", "nvarchar", "varchar2")) // 短文本
.put(Date.class.getSimpleName(), Sets.newHashSet("datetime", "time", "date", "timestamp")) .put(Date.class.getSimpleName(), Sets.newHashSet("datetime", "time", "date", "timestamp"))
.put("byte[]", Sets.newHashSet("blob"))
.build(); .build();
static { static {

View File

@ -55,7 +55,7 @@ public class ${table.className}Controller {
@DeleteMapping("/delete") @DeleteMapping("/delete")
@ApiOperation("删除${table.classComment}") @ApiOperation("删除${table.classComment}")
@ApiImplicitParam(name = "id", value = "编号", required = true) @ApiImplicitParam(name = "id", value = "编号", required = true)
@PreAuthorize("@ss.hasPermission('${permissionPrefix}:delete')") @PreAuthorize("@ss.hasPermission('${permissionPrefix}:delete')")
public CommonResult<Boolean> delete${simpleClassName}(@RequestParam("id") ${primaryColumn.javaType} id) { public CommonResult<Boolean> delete${simpleClassName}(@RequestParam("id") ${primaryColumn.javaType} id) {
${classNameVar}Service.delete${simpleClassName}(id); ${classNameVar}Service.delete${simpleClassName}(id);
return success(true); return success(true);

View File

@ -16,6 +16,7 @@ import ${basePackage}.modules.${table.moduleName}.service.${table.businessName}.
import ${ServiceExceptionUtilClassName}; import ${ServiceExceptionUtilClassName};
import static cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil.exception;
import static ${basePackage}.modules.${table.moduleName}.enums.${simpleModuleName_upperFirst}ErrorCodeConstants.*; import static ${basePackage}.modules.${table.moduleName}.enums.${simpleModuleName_upperFirst}ErrorCodeConstants.*;
/** /**
@ -58,7 +59,7 @@ public class ${table.className}ServiceImpl implements ${table.className}Service
private void validate${simpleClassName}Exists(${primaryColumn.javaType} id) { private void validate${simpleClassName}Exists(${primaryColumn.javaType} id) {
if (${classNameVar}Mapper.selectById(id) == null) { if (${classNameVar}Mapper.selectById(id) == null) {
throw ServiceExceptionUtil.exception(${simpleClassName_underlineCase.toUpperCase()}_NOT_EXISTS); throw exception(${simpleClassName_underlineCase.toUpperCase()}_NOT_EXISTS);
} }
} }

View File

@ -1,13 +1,11 @@
package ${basePackage}.modules.${table.moduleName}.service.${table.businessName}; package ${basePackage}.modules.${table.moduleName}.service.${table.businessName};
import ${basePackage}.BaseSpringBootUnitTest;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.MockBean;
import javax.annotation.Resource; import javax.annotation.Resource;
import cn.iocoder.dashboard.BaseSpringBootUnitTest; import ${basePackage}.BaseDbUnitTest;
import ${basePackage}.modules.${table.moduleName}.service.${table.businessName}.impl.${table.className}ServiceImpl; import ${basePackage}.modules.${table.moduleName}.service.${table.businessName}.impl.${table.className}ServiceImpl;
import ${basePackage}.modules.${table.moduleName}.controller.${table.businessName}.vo.*; import ${basePackage}.modules.${table.moduleName}.controller.${table.businessName}.vo.*;
import ${basePackage}.modules.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO; import ${basePackage}.modules.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO;
@ -16,6 +14,7 @@ import ${basePackage}.util.object.ObjectUtils;
import ${PageResultClassName}; import ${PageResultClassName};
import javax.annotation.Resource; import javax.annotation.Resource;
import org.springframework.context.annotation.Import;
import java.util.*; import java.util.*;
import static cn.hutool.core.util.RandomUtil.*; import static cn.hutool.core.util.RandomUtil.*;
@ -64,7 +63,8 @@ import static org.mockito.Mockito.*;
* *
* @author ${table.author} * @author ${table.author}
*/ */
public class ${table.className}ServiceTest extends BaseSpringBootUnitTest { @Import(${table.className}ServiceImpl.class)
public class ${table.className}ServiceTest extends BaseDbUnitTest {
@Resource @Resource
private ${table.className}ServiceImpl ${classNameVar}Service; private ${table.className}ServiceImpl ${classNameVar}Service;
@ -78,7 +78,7 @@ public class ${table.className}ServiceTest extends BaseSpringBootUnitTest {
${table.className}CreateReqVO reqVO = randomPojo(${table.className}CreateReqVO.class); ${table.className}CreateReqVO reqVO = randomPojo(${table.className}CreateReqVO.class);
// 调用 // 调用
Long ${classNameVar}Id = ${classNameVar}Service.create${simpleClassName}(reqVO); ${primaryColumn.javaType} ${classNameVar}Id = ${classNameVar}Service.create${simpleClassName}(reqVO);
// 断言 // 断言
assertNotNull(${classNameVar}Id); assertNotNull(${classNameVar}Id);
// 校验记录的属性是否正确 // 校验记录的属性是否正确
@ -118,7 +118,7 @@ public class ${table.className}ServiceTest extends BaseSpringBootUnitTest {
${table.className}DO db${simpleClassName} = randomPojo(${table.className}DO.class); ${table.className}DO db${simpleClassName} = randomPojo(${table.className}DO.class);
${classNameVar}Mapper.insert(db${simpleClassName});// @Sql: 先插入出一条存在的数据 ${classNameVar}Mapper.insert(db${simpleClassName});// @Sql: 先插入出一条存在的数据
// 准备参数 // 准备参数
Long id = db${simpleClassName}.getId(); ${primaryColumn.javaType} id = db${simpleClassName}.getId();
// 调用 // 调用
${classNameVar}Service.delete${simpleClassName}(id); ${classNameVar}Service.delete${simpleClassName}(id);
@ -129,7 +129,7 @@ public class ${table.className}ServiceTest extends BaseSpringBootUnitTest {
@Test @Test
public void testDelete${simpleClassName}_notExists() { public void testDelete${simpleClassName}_notExists() {
// 准备参数 // 准备参数
Long id = randomLongId(); ${primaryColumn.javaType} id = random${primaryColumn.javaType}Id();
// 调用, 并断言异常 // 调用, 并断言异常
assertServiceException(() -> ${classNameVar}Service.delete${simpleClassName}(id), ${simpleClassName_underlineCase.toUpperCase()}_NOT_EXISTS); assertServiceException(() -> ${classNameVar}Service.delete${simpleClassName}(id), ${simpleClassName_underlineCase.toUpperCase()}_NOT_EXISTS);

View File

@ -4,7 +4,7 @@ INSERT INTO `sys_menu`(
`path`, `icon`, `component`, `status` `path`, `icon`, `component`, `status`
) )
VALUES ( VALUES (
'${table.classComment}管理', '${permissionPrefix}:query', 2, 0, ${table.parentMenuId}, '${table.classComment}管理', '', 2, 0, ${table.parentMenuId},
'${simpleClassName_strikeCase}', '', '${table.moduleName}/${classNameVar}/index', 0 '${simpleClassName_strikeCase}', '', '${table.moduleName}/${classNameVar}/index', 0
); );
@ -12,8 +12,8 @@ VALUES (
SELECT @parentId := LAST_INSERT_ID(); SELECT @parentId := LAST_INSERT_ID();
-- 按钮 SQL -- 按钮 SQL
#set ($functionNames = ['创建', '更新', '删除', '导出']) #set ($functionNames = ['查询', '创建', '更新', '删除', '导出'])
#set ($functionOps = ['create', 'update', 'delete', 'export']) #set ($functionOps = ['query', 'create', 'update', 'delete', 'export'])
#foreach ($functionName in $functionNames) #foreach ($functionName in $functionNames)
#set ($index = $foreach.count - 1) #set ($index = $foreach.count - 1)
INSERT INTO `sys_menu`( INSERT INTO `sys_menu`(
@ -21,7 +21,7 @@ INSERT INTO `sys_menu`(
`path`, `icon`, `component`, `status` `path`, `icon`, `component`, `status`
) )
VALUES ( VALUES (
'${table.tableComment}${functionName}', '${permissionPrefix}:${functionOps.get($index)}', 3, $foreach.count, @parentId, '${table.classComment}${functionName}', '${permissionPrefix}:${functionOps.get($index)}', 3, $foreach.count, @parentId,
'', '', '', 0 '', '', '', 0
); );
#end #end

View File

@ -68,7 +68,7 @@
#if ($column.javaType == "Date")## 时间类型 #if ($column.javaType == "Date")## 时间类型
<el-table-column label="${comment}" align="center" prop="${javaField}" width="180"> <el-table-column label="${comment}" align="center" prop="${javaField}" width="180">
<template slot-scope="scope"> <template slot-scope="scope">
<span>{{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}</span> <span>{{ parseTime(scope.row.${javaField}) }}</span>
</template> </template>
</el-table-column> </el-table-column>
#elseif("" != $column.dictType)## 数据字典 #elseif("" != $column.dictType)## 数据字典

View File

@ -15,9 +15,9 @@ import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.jdbc.Sql;
/** /**
* DB * DB + Redis
* *
* Service Service Mapper H2 Service Mock * {@link BaseDbUnitTest} Redis
* *
* @author * @author
*/ */

View File

@ -0,0 +1,32 @@
package cn.iocoder.dashboard;
import cn.iocoder.dashboard.config.RedisTestConfiguration;
import cn.iocoder.dashboard.framework.redis.config.RedisConfig;
import org.redisson.spring.starter.RedissonAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ActiveProfiles;
/**
* Redis
*
* {@link BaseDbUnitTest} DB Redis
*
* @author
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseRedisUnitTest.Application.class)
@ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件
public class BaseRedisUnitTest {
@Import({
// Redis 配置类
RedisTestConfiguration.class, // Redis 测试配置类,用于启动 RedisServer
RedisAutoConfiguration.class, // Spring Redis 自动配置类
RedisConfig.class, // 自己的 Redis 配置类
RedissonAutoConfiguration.class, // Redisson 自动高配置类
})
public static class Application {
}
}

View File

@ -1,32 +0,0 @@
package cn.iocoder.dashboard;
import org.junit.jupiter.api.AfterEach;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.jdbc.Sql;
import javax.annotation.Resource;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
@ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件
@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) // 每个单元测试结束后,清理 DB
@Deprecated
public class BaseSpringBootUnitTest {
@Resource
private StringRedisTemplate stringRedisTemplate;
/**
* Redis
*/
@AfterEach
public void cleanRedis() {
stringRedisTemplate.execute((RedisCallback<Object>) connection -> {
connection.flushDb();
return null;
});
}
}

View File

@ -1,19 +1,17 @@
package cn.iocoder.dashboard.config; package cn.iocoder.dashboard.config;
import com.github.fppt.jedismock.RedisServer; import com.github.fppt.jedismock.RedisServer;
import org.redisson.spring.starter.RedissonAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties; import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import java.io.IOException; import java.io.IOException;
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@Lazy(false) // 禁止延迟加载
@EnableConfigurationProperties(RedisProperties.class) @EnableConfigurationProperties(RedisProperties.class)
@AutoConfigureBefore({RedisAutoConfiguration.class, RedissonAutoConfiguration.class}) // 在 Redis 自动配置前,进行初始化
public class RedisTestConfiguration { public class RedisTestConfiguration {
/** /**

View File

@ -1,14 +1,14 @@
package cn.iocoder.dashboard.framework.quartz.core.scheduler; package cn.iocoder.dashboard.framework.quartz.core.scheduler;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.iocoder.dashboard.BaseSpringBootUnitTest; import cn.iocoder.dashboard.BaseDbUnitTest;
import cn.iocoder.dashboard.modules.system.job.auth.SysUserSessionTimeoutJob; import cn.iocoder.dashboard.modules.system.job.auth.SysUserSessionTimeoutJob;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.quartz.SchedulerException; import org.quartz.SchedulerException;
import javax.annotation.Resource; import javax.annotation.Resource;
class SchedulerManagerTest extends BaseSpringBootUnitTest { class SchedulerManagerTest extends BaseDbUnitTest {
@Resource @Resource
private SchedulerManager schedulerManager; private SchedulerManager schedulerManager;

View File

@ -0,0 +1,126 @@
package cn.iocoder.dashboard.modules.infra.service.file;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.iocoder.dashboard.BaseDbUnitTest;
import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.framework.file.config.FileProperties;
import cn.iocoder.dashboard.modules.infra.controller.file.vo.InfFilePageReqVO;
import cn.iocoder.dashboard.modules.infra.dal.dataobject.file.InfFileDO;
import cn.iocoder.dashboard.modules.infra.dal.mysql.file.InfFileMapper;
import cn.iocoder.dashboard.modules.infra.service.file.impl.InfFileServiceImpl;
import cn.iocoder.dashboard.util.object.ObjectUtils;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import static cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants.FILE_NOT_EXISTS;
import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.FILE_PATH_EXISTS;
import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException;
import static cn.iocoder.dashboard.util.RandomUtils.randomPojo;
import static cn.iocoder.dashboard.util.RandomUtils.randomString;
import static cn.iocoder.dashboard.util.date.DateUtils.buildTime;
import static org.junit.jupiter.api.Assertions.*;
@Import({InfFileServiceImpl.class, FileProperties.class})
public class InfFileServiceTest extends BaseDbUnitTest {
@Resource
private InfFileServiceImpl fileService;
@Resource
private FileProperties fileProperties;
@Resource
private InfFileMapper fileMapper;
@Test
public void testCreateFile_success() {
// 准备参数
String path = randomString();
byte[] content = ResourceUtil.readBytes("file/erweima.jpg");
// 调用
String url = fileService.createFile(path, content);
// 断言
assertEquals(fileProperties.getBasePath() + path, url);
// 校验数据
InfFileDO file = fileMapper.selectById(path);
assertEquals(path, file.getId());
assertEquals("jpg", file.getType());
assertArrayEquals(content, file.getContent());
}
@Test
public void testCreateFile_exists() {
// mock 数据
InfFileDO dbFile = randomPojo(InfFileDO.class);
fileMapper.insert(dbFile);
// 准备参数
String path = dbFile.getId(); // 模拟已存在
byte[] content = ResourceUtil.readBytes("file/erweima.jpg");
// 调用,并断言异常
assertServiceException(() -> fileService.createFile(path, content), FILE_PATH_EXISTS);
}
@Test
public void testDeleteFile_success() {
// mock 数据
InfFileDO dbFile = randomPojo(InfFileDO.class);
fileMapper.insert(dbFile);// @Sql: 先插入出一条存在的数据
// 准备参数
String id = dbFile.getId();
// 调用
fileService.deleteFile(id);
// 校验数据不存在了
assertNull(fileMapper.selectById(id));
}
@Test
public void testDeleteFile_notExists() {
// 准备参数
String id = randomString();
// 调用, 并断言异常
assertServiceException(() -> fileService.deleteFile(id), FILE_NOT_EXISTS);
}
@Test
public void testGetFilePage() {
// mock 数据
InfFileDO dbFile = randomPojo(InfFileDO.class, o -> { // 等会查询到
o.setId("yudao");
o.setType("jpg");
o.setCreateTime(buildTime(2021, 1, 15));
});
fileMapper.insert(dbFile);
// 测试 id 不匹配
fileMapper.insert(ObjectUtils.clone(dbFile, o -> o.setId("tudou")));
// 测试 type 不匹配
fileMapper.insert(ObjectUtils.clone(dbFile, o -> {
o.setId("yudao02");
o.setType("png");
}));
// 测试 createTime 不匹配
fileMapper.insert(ObjectUtils.clone(dbFile, o -> {
o.setId("yudao03");
o.setCreateTime(buildTime(2020, 1, 15));
}));
// 准备参数
InfFilePageReqVO reqVO = new InfFilePageReqVO();
reqVO.setId("yudao");
reqVO.setType("jp");
reqVO.setBeginCreateTime(buildTime(2021, 1, 10));
reqVO.setEndCreateTime(buildTime(2021, 1, 20));
// 调用
PageResult<InfFileDO> pageResult = fileService.getFilePage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbFile, pageResult.getList().get(0), "content");
}
}

View File

@ -2,7 +2,6 @@ package cn.iocoder.dashboard.modules.system.service.auth;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import cn.iocoder.dashboard.BaseDbAndRedisUnitTest; import cn.iocoder.dashboard.BaseDbAndRedisUnitTest;
import cn.iocoder.dashboard.framework.mybatis.core.query.QueryWrapperX;
import cn.iocoder.dashboard.framework.security.config.SecurityProperties; import cn.iocoder.dashboard.framework.security.config.SecurityProperties;
import cn.iocoder.dashboard.modules.system.dal.dataobject.auth.SysUserSessionDO; import cn.iocoder.dashboard.modules.system.dal.dataobject.auth.SysUserSessionDO;
import cn.iocoder.dashboard.modules.system.dal.mysql.auth.SysUserSessionMapper; import cn.iocoder.dashboard.modules.system.dal.mysql.auth.SysUserSessionMapper;
@ -32,24 +31,24 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
* @version 1.0 * @version 1.0
* @since <pre>3 8, 2021</pre> * @since <pre>3 8, 2021</pre>
*/ */
@Import( @Import(SysUserSessionServiceImpl.class)
SysUserSessionServiceImpl.class)
public class SysUserSessionServiceImplTest extends BaseDbAndRedisUnitTest { public class SysUserSessionServiceImplTest extends BaseDbAndRedisUnitTest {
@Resource @Resource
SysUserSessionServiceImpl sysUserSessionService; private SysUserSessionServiceImpl sysUserSessionService;
@Resource @Resource
SysUserSessionMapper sysUserSessionMapper; private SysUserSessionMapper sysUserSessionMapper;
@MockBean @MockBean
SecurityProperties securityProperties; private SecurityProperties securityProperties;
@MockBean @MockBean
SysDeptServiceImpl sysDeptService; private SysDeptServiceImpl sysDeptService;
@MockBean @MockBean
SysUserServiceImpl sysUserService; private SysUserServiceImpl sysUserService;
@MockBean @MockBean
SysLoginLogServiceImpl sysLoginLogService; private SysLoginLogServiceImpl sysLoginLogService;
@MockBean @MockBean
SysLoginUserRedisDAO sysLoginUserRedisDAO; private SysLoginUserRedisDAO sysLoginUserRedisDAO;
@Test @Test
public void testClearSessionTimeout_success() throws Exception { public void testClearSessionTimeout_success() throws Exception {
@ -75,4 +74,4 @@ public class SysUserSessionServiceImplTest extends BaseDbAndRedisUnitTest {
AssertUtils.assertPojoEquals(sessionDO, userSessionDOS.get(0), "updateTime"); AssertUtils.assertPojoEquals(sessionDO, userSessionDOS.get(0), "updateTime");
} }
} }

View File

@ -0,0 +1,66 @@
package cn.iocoder.dashboard.modules.system.service.common;
import cn.iocoder.dashboard.BaseRedisUnitTest;
import cn.iocoder.dashboard.framework.captcha.config.CaptchaProperties;
import cn.iocoder.dashboard.modules.system.controller.common.vo.SysCaptchaImageRespVO;
import cn.iocoder.dashboard.modules.system.dal.redis.common.SysCaptchaRedisDAO;
import cn.iocoder.dashboard.modules.system.service.common.impl.SysCaptchaServiceImpl;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import static cn.iocoder.dashboard.util.RandomUtils.randomString;
import static org.junit.jupiter.api.Assertions.*;
@Import({SysCaptchaServiceImpl.class, CaptchaProperties.class, SysCaptchaRedisDAO.class})
public class SysCaptchaServiceTest extends BaseRedisUnitTest {
@Resource
private SysCaptchaServiceImpl captchaService;
@Resource
private SysCaptchaRedisDAO captchaRedisDAO;
@Resource
private CaptchaProperties captchaProperties;
@Test
public void testGetCaptchaImage() {
// 调用
SysCaptchaImageRespVO respVO = captchaService.getCaptchaImage();
// 断言
assertNotNull(respVO.getUuid());
assertNotNull(respVO.getImg());
String captchaCode = captchaRedisDAO.get(respVO.getUuid());
assertNotNull(captchaCode);
}
@Test
public void testGetCaptchaCode() {
// 准备参数
String uuid = randomString();
String code = randomString();
// mock 数据
captchaRedisDAO.set(uuid, code, captchaProperties.getTimeout());
// 调用
String resultCode = captchaService.getCaptchaCode(uuid);
// 断言
assertEquals(code, resultCode);
}
@Test
public void testDeleteCaptchaCode() {
// 准备参数
String uuid = randomString();
String code = randomString();
// mock 数据
captchaRedisDAO.set(uuid, code, captchaProperties.getTimeout());
// 调用
captchaService.deleteCaptchaCode(uuid);
// 断言
assertNull(captchaRedisDAO.get(uuid));
}
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.dashboard.modules.tool.dal.mysql.codegen; package cn.iocoder.dashboard.modules.tool.dal.mysql.codegen;
import cn.iocoder.dashboard.BaseSpringBootUnitTest; import cn.iocoder.dashboard.BaseDbUnitTest;
import cn.iocoder.dashboard.modules.tool.dal.dataobject.codegen.ToolSchemaColumnDO; import cn.iocoder.dashboard.modules.tool.dal.dataobject.codegen.ToolSchemaColumnDO;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -9,7 +9,7 @@ import java.util.List;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
public class ToolInformationSchemaColumnMapperTest extends BaseSpringBootUnitTest { public class ToolInformationSchemaColumnMapperTest extends BaseDbUnitTest {
@Resource @Resource
private ToolSchemaColumnMapper toolInformationSchemaColumnMapper; private ToolSchemaColumnMapper toolInformationSchemaColumnMapper;

View File

@ -1,6 +1,6 @@
package cn.iocoder.dashboard.modules.tool.dal.mysql.codegen; package cn.iocoder.dashboard.modules.tool.dal.mysql.codegen;
import cn.iocoder.dashboard.BaseSpringBootUnitTest; import cn.iocoder.dashboard.BaseDbUnitTest;
import cn.iocoder.dashboard.modules.tool.dal.dataobject.codegen.ToolSchemaTableDO; import cn.iocoder.dashboard.modules.tool.dal.dataobject.codegen.ToolSchemaTableDO;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -9,7 +9,7 @@ import java.util.List;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
class ToolInformationSchemaTableMapperTest extends BaseSpringBootUnitTest { class ToolInformationSchemaTableMapperTest extends BaseDbUnitTest {
@Resource @Resource
private ToolSchemaTableMapper toolInformationSchemaTableMapper; private ToolSchemaTableMapper toolInformationSchemaTableMapper;

View File

@ -1,18 +1,17 @@
package cn.iocoder.dashboard.modules.tool.service.codegen.impl; package cn.iocoder.dashboard.modules.tool.service.codegen.impl;
import cn.iocoder.dashboard.BaseSpringBootUnitTest; import cn.iocoder.dashboard.BaseDbUnitTest;
import cn.iocoder.dashboard.modules.tool.dal.dataobject.codegen.ToolCodegenColumnDO; import cn.iocoder.dashboard.modules.tool.dal.dataobject.codegen.ToolCodegenColumnDO;
import cn.iocoder.dashboard.modules.tool.dal.dataobject.codegen.ToolCodegenTableDO; import cn.iocoder.dashboard.modules.tool.dal.dataobject.codegen.ToolCodegenTableDO;
import cn.iocoder.dashboard.modules.tool.dal.mysql.codegen.ToolCodegenColumnMapper; import cn.iocoder.dashboard.modules.tool.dal.mysql.codegen.ToolCodegenColumnMapper;
import cn.iocoder.dashboard.modules.tool.dal.mysql.codegen.ToolCodegenTableMapper; import cn.iocoder.dashboard.modules.tool.dal.mysql.codegen.ToolCodegenTableMapper;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
public class ToolCodegenEngineTest extends BaseSpringBootUnitTest { public class ToolCodegenEngineTest extends BaseDbUnitTest {
@Resource @Resource
private ToolCodegenTableMapper codegenTableMapper; private ToolCodegenTableMapper codegenTableMapper;

View File

@ -1,9 +1,9 @@
package cn.iocoder.dashboard.modules.tool.service.codegen.impl; package cn.iocoder.dashboard.modules.tool.service.codegen.impl;
import cn.iocoder.dashboard.BaseSpringBootUnitTest; import cn.iocoder.dashboard.BaseDbUnitTest;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
public class ToolCodegenSQLParserTest extends BaseSpringBootUnitTest { public class ToolCodegenSQLParserTest extends BaseDbUnitTest {
@Test @Test
public void testParse() { public void testParse() {

View File

@ -1,12 +1,11 @@
package cn.iocoder.dashboard.modules.tool.service.codegen.impl; package cn.iocoder.dashboard.modules.tool.service.codegen.impl;
import cn.iocoder.dashboard.BaseSpringBootUnitTest; import cn.iocoder.dashboard.BaseDbUnitTest;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource; import javax.annotation.Resource;
class ToolCodegenServiceImplTest extends BaseSpringBootUnitTest { class ToolCodegenServiceImplTest extends BaseDbUnitTest {
@Resource @Resource
private ToolCodegenServiceImpl toolCodegenService; private ToolCodegenServiceImpl toolCodegenService;

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -1,5 +1,6 @@
-- inf 开头的 DB -- inf 开头的 DB
DELETE FROM "inf_config"; DELETE FROM "inf_config";
DELETE FROM "inf_file";
-- sys 开头的 DB -- sys 开头的 DB
DELETE FROM "sys_dept"; DELETE FROM "sys_dept";

View File

@ -17,6 +17,18 @@ CREATE TABLE IF NOT EXISTS "inf_config" (
PRIMARY KEY ("id") PRIMARY KEY ("id")
) COMMENT '参数配置表'; ) COMMENT '参数配置表';
CREATE TABLE IF NOT EXISTS "inf_file" (
"id" varchar(188) NOT NULL,
"type" varchar(63) DEFAULT NULL,
"content" blob NOT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '文件表';
-- sys 开头的 DB -- sys 开头的 DB
CREATE TABLE IF NOT EXISTS "sys_dept" ( CREATE TABLE IF NOT EXISTS "sys_dept" (
@ -101,7 +113,7 @@ CREATE TABLE IF NOT EXISTS "sys_menu" (
PRIMARY KEY ("id") PRIMARY KEY ("id")
) COMMENT '菜单权限表'; ) COMMENT '菜单权限表';
CREATE TABLE "sys_dict_type" ( CREATE TABLE IF NOT EXISTS "sys_dict_type" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"name" varchar(100) NOT NULL DEFAULT '', "name" varchar(100) NOT NULL DEFAULT '',
"type" varchar(100) NOT NULL DEFAULT '', "type" varchar(100) NOT NULL DEFAULT '',
@ -115,7 +127,7 @@ CREATE TABLE "sys_dict_type" (
PRIMARY KEY ("id") PRIMARY KEY ("id")
) COMMENT '字典类型表'; ) COMMENT '字典类型表';
CREATE TABLE `sys_user_session` ( CREATE TABLE IF NOT EXISTS `sys_user_session` (
`id` varchar(32) NOT NULL, `id` varchar(32) NOT NULL,
`user_id` bigint DEFAULT NULL, `user_id` bigint DEFAULT NULL,
`username` varchar(50) NOT NULL DEFAULT '', `username` varchar(50) NOT NULL DEFAULT '',
@ -179,7 +191,7 @@ CREATE TABLE IF NOT EXISTS `sys_login_log` (
) COMMENT ='系统访问记录'; ) COMMENT ='系统访问记录';
CREATE TABLE `sys_operate_log` ( CREATE TABLE IF NOT EXISTS `sys_operate_log` (
`id` bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY, `id` bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY,
`trace_id` varchar(64) NOT NULL DEFAULT '', `trace_id` varchar(64) NOT NULL DEFAULT '',
`user_id` bigint(20) NOT NULL, `user_id` bigint(20) NOT NULL,
@ -207,7 +219,7 @@ CREATE TABLE `sys_operate_log` (
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
) COMMENT ='操作日志记录'; ) COMMENT ='操作日志记录';
create table "sys_user" ( create table IF NOT EXISTS "sys_user" (
"id" bigint not null GENERATED BY DEFAULT AS IDENTITY, "id" bigint not null GENERATED BY DEFAULT AS IDENTITY,
"username" varchar(30) not null, "username" varchar(30) not null,
"password" varchar(100) not null default '', "password" varchar(100) not null default '',