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

30
pom.xml
View File

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

View File

@ -122,10 +122,10 @@
<el-table-column label="状态" align="center">
<template slot-scope="scope">
<el-switch
v-model="scope.row.status"
:active-value="0"
:inactive-value="1"
@change="handleStatusChange(scope.row)"
v-model="scope.row.status"
:active-value="0"
:inactive-value="1"
@change="handleStatusChange(scope.row)"
></el-switch>
</template>
</el-table-column>
@ -142,34 +142,41 @@
>
<template slot-scope="scope">
<el-button
size="mini"
size="large"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['system:user:edit']"
v-hasPermi="['system:role:edit']"
>修改</el-button>
<el-button
v-if="scope.row.id !== 1"
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['system:user:remove']"
>删除</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-key"
@click="handleResetPwd(scope.row)"
v-hasPermi="['system:user:resetPwd']"
>重置</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-circle-check"
@click="handleRole(scope.row)"
v-hasPermi="['system:permission:assign-user-role']"
>分配角色</el-button>
<el-dropdown @command="(command) => handleCommand(command, scope.$index, scope.row)">
<span class="el-dropdown-link">
更多操作<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item
command="handleDelete"
v-if="scope.row.id !== 1"
size="mini"
type="text"
icon="el-icon-delete"
v-hasPermi="['system:user:remove']"
>删除</el-dropdown-item>
<el-dropdown-item
command="handleResetPwd"
size="mini"
type="text"
icon="el-icon-key"
v-hasPermi="['system:user:resetPwd']"
>重置</el-dropdown-item>
<el-dropdown-item
command="handleRole"
size="mini"
type="text"
icon="el-icon-circle-check"
v-hasPermi="['system:permission:assign-user-role']"
>分配角色</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
</el-table-column>
</el-table>
@ -452,6 +459,25 @@ export default {
});
},
methods: {
//
handleCommand(command, index, row) {
switch (command) {
case 'handleUpdate':
this.handleUpdate(row);//
break;
case 'handleDelete':
this.handleDelete(row);//
break;
case 'handleResetPwd':
this.handleResetPwd(row);
break;
case 'handleRole':
this.handleRole(row);
break;
default:
break;
}
},
/** 查询用户列表 */
getList() {
this.loading = true;
@ -713,3 +739,13 @@ export default {
}
};
</script>
<style>
.el-dropdown-link {
cursor: pointer;
color: #1890ff;
margin-left: 5px;
}
.el-icon-arrow-down {
font-size: 14px;
}
</style>

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
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 org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
@ -13,7 +13,7 @@ import javax.validation.constraints.NotNull;
public class FileProperties {
/**
* {@link SysFileController#}
* {@link InfFileController#}
*/
@NotNull(message = "基础文件路径不能为空")
private String basePath;

View File

@ -134,7 +134,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
// 静态资源,可匿名访问
.antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll()
// 文件的获取接口,可匿名访问
.antMatchers(webProperties.getApiPrefix() + "/system/file/get/**").anonymous()
.antMatchers(webProperties.getApiPrefix() + "/infra/file/get/**").anonymous()
// Swagger 接口文档
.antMatchers("/swagger-ui.html").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.iocoder.dashboard.common.pojo.CommonResult;
import cn.iocoder.dashboard.modules.system.dal.dataobject.common.SysFileDO;
import cn.iocoder.dashboard.modules.system.service.common.SysFileService;
import cn.iocoder.dashboard.common.pojo.PageResult;
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 io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
@ -11,40 +15,53 @@ import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import static cn.iocoder.dashboard.common.pojo.CommonResult.success;
@Api(tags = "文件存储")
@RestController
@RequestMapping("/system/file")
@RequestMapping("/infra/file")
@Validated
@Slf4j
public class SysFileController {
public class InfFileController {
@Resource
private SysFileService fileService;
private InfFileService fileService;
@PostMapping("/upload")
@ApiOperation("上传文件")
@ApiImplicitParams({
@ApiImplicitParam(name = "path", value = "文件附件", required = true, dataTypeClass = MultipartFile.class),
@ApiImplicitParam(name = "path", value = "文件路径", required = true, example = "yudaoyuanma.png", dataTypeClass = Long.class)
@ApiImplicitParam(name = "file", value = "文件附件", required = true, dataTypeClass = MultipartFile.class),
@ApiImplicitParam(name = "path", value = "文件路径", required = false, example = "yudaoyuanma.png", dataTypeClass = String.class)
})
@PostMapping("/upload")
public CommonResult<String> uploadFile(@RequestParam("file") MultipartFile file,
@RequestParam("path") String path) throws IOException {
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("下载文件")
@ApiImplicitParam(name = "path", value = "文件附件", required = true, dataTypeClass = MultipartFile.class)
@GetMapping("/get/{path}")
public void getFile(HttpServletResponse response, @PathVariable("path") String path) throws IOException {
SysFileDO file = fileService.getFile(path);
InfFileDO file = fileService.getFile(path);
if (file == null) {
log.warn("[getFile][path({}) 文件不存在]", path);
response.setStatus(HttpStatus.NOT_FOUND.value());
@ -53,4 +70,12 @@ public class SysFileController {
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_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
private SysCaptchaService captchaService;
@ApiOperation("生成图片验证码")
@GetMapping("/get-image")
@ApiOperation("生成图片验证码")
public CommonResult<SysCaptchaImageRespVO> 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.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* column
@ -17,7 +18,7 @@ import lombok.EqualsAndHashCode;
*/
@TableName(value = "tool_codegen_column", autoResultMap = true)
@Data
@Builder
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
public class ToolCodegenColumnDO extends BaseDO {

View File

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

View File

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

View File

@ -55,7 +55,7 @@ public class ${table.className}Controller {
@DeleteMapping("/delete")
@ApiOperation("删除${table.classComment}")
@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) {
${classNameVar}Service.delete${simpleClassName}(id);
return success(true);

View File

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

View File

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

View File

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

View File

@ -15,9 +15,9 @@ import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.jdbc.Sql;
/**
* DB
* DB + Redis
*
* Service Service Mapper H2 Service Mock
* {@link BaseDbUnitTest} Redis
*
* @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;
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.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import java.io.IOException;
@Configuration(proxyBeanMethods = false)
@Lazy(false) // 禁止延迟加载
@EnableConfigurationProperties(RedisProperties.class)
@AutoConfigureBefore({RedisAutoConfiguration.class, RedissonAutoConfiguration.class}) // 在 Redis 自动配置前,进行初始化
public class RedisTestConfiguration {
/**

View File

@ -1,14 +1,14 @@
package cn.iocoder.dashboard.framework.quartz.core.scheduler;
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 org.junit.jupiter.api.Test;
import org.quartz.SchedulerException;
import javax.annotation.Resource;
class SchedulerManagerTest extends BaseSpringBootUnitTest {
class SchedulerManagerTest extends BaseDbUnitTest {
@Resource
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.iocoder.dashboard.BaseDbAndRedisUnitTest;
import cn.iocoder.dashboard.framework.mybatis.core.query.QueryWrapperX;
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.mysql.auth.SysUserSessionMapper;
@ -32,24 +31,24 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
* @version 1.0
* @since <pre>3 8, 2021</pre>
*/
@Import(
SysUserSessionServiceImpl.class)
@Import(SysUserSessionServiceImpl.class)
public class SysUserSessionServiceImplTest extends BaseDbAndRedisUnitTest {
@Resource
SysUserSessionServiceImpl sysUserSessionService;
private SysUserSessionServiceImpl sysUserSessionService;
@Resource
SysUserSessionMapper sysUserSessionMapper;
private SysUserSessionMapper sysUserSessionMapper;
@MockBean
SecurityProperties securityProperties;
private SecurityProperties securityProperties;
@MockBean
SysDeptServiceImpl sysDeptService;
private SysDeptServiceImpl sysDeptService;
@MockBean
SysUserServiceImpl sysUserService;
private SysUserServiceImpl sysUserService;
@MockBean
SysLoginLogServiceImpl sysLoginLogService;
private SysLoginLogServiceImpl sysLoginLogService;
@MockBean
SysLoginUserRedisDAO sysLoginUserRedisDAO;
private SysLoginUserRedisDAO sysLoginUserRedisDAO;
@Test
public void testClearSessionTimeout_success() throws Exception {

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

View File

@ -1,6 +1,6 @@
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 org.junit.jupiter.api.Test;
@ -9,7 +9,7 @@ import java.util.List;
import static org.junit.jupiter.api.Assertions.assertTrue;
class ToolInformationSchemaTableMapperTest extends BaseSpringBootUnitTest {
class ToolInformationSchemaTableMapperTest extends BaseDbUnitTest {
@Resource
private ToolSchemaTableMapper toolInformationSchemaTableMapper;

View File

@ -1,18 +1,17 @@
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.ToolCodegenTableDO;
import cn.iocoder.dashboard.modules.tool.dal.mysql.codegen.ToolCodegenColumnMapper;
import cn.iocoder.dashboard.modules.tool.dal.mysql.codegen.ToolCodegenTableMapper;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
public class ToolCodegenEngineTest extends BaseSpringBootUnitTest {
public class ToolCodegenEngineTest extends BaseDbUnitTest {
@Resource
private ToolCodegenTableMapper codegenTableMapper;

View File

@ -1,9 +1,9 @@
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;
public class ToolCodegenSQLParserTest extends BaseSpringBootUnitTest {
public class ToolCodegenSQLParserTest extends BaseDbUnitTest {
@Test
public void testParse() {

View File

@ -1,12 +1,11 @@
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.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
class ToolCodegenServiceImplTest extends BaseSpringBootUnitTest {
class ToolCodegenServiceImplTest extends BaseDbUnitTest {
@Resource
private ToolCodegenServiceImpl toolCodegenService;

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

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

View File

@ -17,6 +17,18 @@ CREATE TABLE IF NOT EXISTS "inf_config" (
PRIMARY KEY ("id")
) 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
CREATE TABLE IF NOT EXISTS "sys_dept" (
@ -101,7 +113,7 @@ CREATE TABLE IF NOT EXISTS "sys_menu" (
PRIMARY KEY ("id")
) COMMENT '菜单权限表';
CREATE TABLE "sys_dict_type" (
CREATE TABLE IF NOT EXISTS "sys_dict_type" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"name" varchar(100) NOT NULL DEFAULT '',
"type" varchar(100) NOT NULL DEFAULT '',
@ -115,7 +127,7 @@ CREATE TABLE "sys_dict_type" (
PRIMARY KEY ("id")
) COMMENT '字典类型表';
CREATE TABLE `sys_user_session` (
CREATE TABLE IF NOT EXISTS `sys_user_session` (
`id` varchar(32) NOT NULL,
`user_id` bigint DEFAULT NULL,
`username` varchar(50) NOT NULL DEFAULT '',
@ -179,7 +191,7 @@ CREATE TABLE IF NOT EXISTS `sys_login_log` (
) COMMENT ='系统访问记录';
CREATE TABLE `sys_operate_log` (
CREATE TABLE IF NOT EXISTS `sys_operate_log` (
`id` bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY,
`trace_id` varchar(64) NOT NULL DEFAULT '',
`user_id` bigint(20) NOT NULL,
@ -207,7 +219,7 @@ CREATE TABLE `sys_operate_log` (
PRIMARY KEY (`id`)
) COMMENT ='操作日志记录';
create table "sys_user" (
create table IF NOT EXISTS "sys_user" (
"id" bigint not null GENERATED BY DEFAULT AS IDENTITY,
"username" varchar(30) not null,
"password" varchar(100) not null default '',