多模块重构 11:代码生成器,支持多租户

This commit is contained in:
YunaiV 2022-02-03 00:44:07 +08:00
parent 24e083b3ae
commit 8e3d7663bb
27 changed files with 34 additions and 831 deletions

View File

@ -38,6 +38,10 @@
<groupId>cn.iocoder.boot</groupId> <groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-biz-dict</artifactId> <artifactId>yudao-spring-boot-starter-biz-dict</artifactId>
</dependency> </dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
</dependency>
<!-- Web 相关 --> <!-- Web 相关 -->
<dependency> <dependency>

View File

@ -1 +0,0 @@
package cn.iocoder.yudao.module.tool.controller.admin.test;

View File

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

View File

@ -1,14 +0,0 @@
package cn.iocoder.yudao.module.tool.controller.admin.test.vo;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
import javax.validation.constraints.*;
@ApiModel("管理后台 - 字典类型创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class TestDemoCreateReqVO extends TestDemoBaseVO {
}

View File

@ -1,38 +0,0 @@
package cn.iocoder.yudao.module.tool.controller.admin.test.vo;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@ApiModel(value = "管理后台 - 字典类型 Excel 导出 Request VO", description = "参数和 TestDemoPageReqVO 是一致的")
@Data
public class TestDemoExportReqVO {
@ApiModelProperty(value = "名字")
private String name;
@ApiModelProperty(value = "状态")
private Integer status;
@ApiModelProperty(value = "类型")
private Integer type;
@ApiModelProperty(value = "分类")
private Integer category;
@ApiModelProperty(value = "备注")
private String remark;
@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

@ -1,40 +0,0 @@
package cn.iocoder.yudao.module.tool.controller.admin.test.vo;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@ApiModel("管理后台 - 字典类型分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class TestDemoPageReqVO extends PageParam {
@ApiModelProperty(value = "名字")
private String name;
@ApiModelProperty(value = "状态")
private Integer status;
@ApiModelProperty(value = "类型")
private Integer type;
@ApiModelProperty(value = "分类")
private Integer category;
@ApiModelProperty(value = "备注")
private String remark;
@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

@ -1,19 +0,0 @@
package cn.iocoder.yudao.module.tool.controller.admin.test.vo;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
@ApiModel("管理后台 - 字典类型 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class TestDemoRespVO extends TestDemoBaseVO {
@ApiModelProperty(value = "编号", required = true)
private Long id;
@ApiModelProperty(value = "创建时间", required = true)
private Date createTime;
}

View File

@ -1,18 +0,0 @@
package cn.iocoder.yudao.module.tool.controller.admin.test.vo;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
import javax.validation.constraints.*;
@ApiModel("管理后台 - 字典类型更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class TestDemoUpdateReqVO extends TestDemoBaseVO {
@ApiModelProperty(value = "编号", required = true)
@NotNull(message = "编号不能为空")
private Long id;
}

View File

@ -1,95 +0,0 @@
package cn.iocoder.yudao.module.tool.controller.app.test;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import io.swagger.annotations.*;
import javax.validation.constraints.*;
import javax.validation.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
import cn.iocoder.yudao.module.tool.controller.app.test.vo.*;
import cn.iocoder.yudao.module.tool.dal.dataobject.test.TestDemoDO;
import cn.iocoder.yudao.module.tool.convert.test.TestDemoConvert;
import cn.iocoder.yudao.module.tool.service.test.TestDemoService;
@Api(tags = "用户 APP - 字典类型")
@RestController
@RequestMapping("/tool/test-demo")
@Validated
public class AppTestDemoController {
@Resource
private TestDemoService testDemoService;
@PostMapping("/create")
@ApiOperation("创建字典类型")
public CommonResult<Long> createTestDemo(@Valid @RequestBody AppTestDemoCreateReqVO createReqVO) {
return success(testDemoService.createTestDemo(createReqVO));
}
@PutMapping("/update")
@ApiOperation("更新字典类型")
public CommonResult<Boolean> updateTestDemo(@Valid @RequestBody AppTestDemoUpdateReqVO updateReqVO) {
testDemoService.updateTestDemo(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@ApiOperation("删除字典类型")
@ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
public CommonResult<Boolean> deleteTestDemo(@RequestParam("id") Long id) {
testDemoService.deleteTestDemo(id);
return success(true);
}
@GetMapping("/get")
@ApiOperation("获得字典类型")
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
@PreAuthorize("@ss.hasPermission('tool:test-demo:query')")
public CommonResult<AppTestDemoRespVO> getTestDemo(@RequestParam("id") Long id) {
TestDemoDO testDemo = testDemoService.getTestDemo(id);
return success(TestDemoConvert.INSTANCE.convert(testDemo));
}
@GetMapping("/list")
@ApiOperation("获得字典类型列表")
@ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = List.class)
public CommonResult<List<AppTestDemoRespVO>> getTestDemoList(@RequestParam("ids") Collection<Long> ids) {
List<TestDemoDO> list = testDemoService.getTestDemoList(ids);
return success(TestDemoConvert.INSTANCE.convertList(list));
}
@GetMapping("/page")
@ApiOperation("获得字典类型分页")
public CommonResult<PageResult<AppTestDemoRespVO>> getTestDemoPage(@Valid AppTestDemoPageReqVO pageVO) {
PageResult<TestDemoDO> pageResult = testDemoService.getTestDemoPage(pageVO);
return success(TestDemoConvert.INSTANCE.convertPage(pageResult));
}
@GetMapping("/export-excel")
@ApiOperation("导出字典类型 Excel")
@OperateLog(type = EXPORT)
public void exportTestDemoExcel(@Valid AppTestDemoExportReqVO exportReqVO,
HttpServletResponse response) throws IOException {
List<TestDemoDO> list = testDemoService.getTestDemoList(exportReqVO);
// 导出 Excel
List<AppTestDemoExcelVO> datas = TestDemoConvert.INSTANCE.convertList02(list);
ExcelUtils.write(response, "字典类型.xls", "数据", AppTestDemoExcelVO.class, datas);
}
}

View File

@ -1,34 +0,0 @@
package cn.iocoder.yudao.module.tool.controller.app.test.vo;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
import javax.validation.constraints.*;
/**
* 字典类型 Base VO提供给添加修改详细的子 VO 使用
* 如果子 VO 存在差异的字段请不要添加到这里影响 Swagger 文档生成
*/
@Data
public class AppTestDemoBaseVO {
@ApiModelProperty(value = "名字", required = true)
@NotNull(message = "名字不能为空")
private String name;
@ApiModelProperty(value = "状态", required = true)
@NotNull(message = "状态不能为空")
private Integer status;
@ApiModelProperty(value = "类型", required = true)
@NotNull(message = "类型不能为空")
private Integer type;
@ApiModelProperty(value = "分类", required = true)
@NotNull(message = "分类不能为空")
private Integer category;
@ApiModelProperty(value = "备注")
private String remark;
}

View File

@ -1,14 +0,0 @@
package cn.iocoder.yudao.module.tool.controller.app.test.vo;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
import javax.validation.constraints.*;
@ApiModel("用户 APP - 字典类型创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class AppTestDemoCreateReqVO extends AppTestDemoBaseVO {
}

View File

@ -1,38 +0,0 @@
package cn.iocoder.yudao.module.tool.controller.app.test.vo;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
import com.alibaba.excel.annotation.ExcelProperty;
/**
* 字典类型 Excel VO
*
* @author 芋道源码
*/
@Data
public class AppTestDemoExcelVO {
@ExcelProperty("编号")
private Long id;
@ExcelProperty("名字")
private String name;
@ExcelProperty("状态")
private Integer status;
@ExcelProperty("类型")
private Integer type;
@ExcelProperty("分类")
private Integer category;
@ExcelProperty("备注")
private String remark;
@ExcelProperty("创建时间")
private Date createTime;
}

View File

@ -1,38 +0,0 @@
package cn.iocoder.yudao.module.tool.controller.app.test.vo;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@ApiModel(value = "用户 APP - 字典类型 Excel 导出 Request VO", description = "参数和 TestDemoPageReqVO 是一致的")
@Data
public class AppTestDemoExportReqVO {
@ApiModelProperty(value = "名字")
private String name;
@ApiModelProperty(value = "状态")
private Integer status;
@ApiModelProperty(value = "类型")
private Integer type;
@ApiModelProperty(value = "分类")
private Integer category;
@ApiModelProperty(value = "备注")
private String remark;
@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

@ -1,40 +0,0 @@
package cn.iocoder.yudao.module.tool.controller.app.test.vo;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@ApiModel("用户 APP - 字典类型分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class AppTestDemoPageReqVO extends PageParam {
@ApiModelProperty(value = "名字")
private String name;
@ApiModelProperty(value = "状态")
private Integer status;
@ApiModelProperty(value = "类型")
private Integer type;
@ApiModelProperty(value = "分类")
private Integer category;
@ApiModelProperty(value = "备注")
private String remark;
@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

@ -1,19 +0,0 @@
package cn.iocoder.yudao.module.tool.controller.app.test.vo;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
@ApiModel("用户 APP - 字典类型 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class AppTestDemoRespVO extends AppTestDemoBaseVO {
@ApiModelProperty(value = "编号", required = true)
private Long id;
@ApiModelProperty(value = "创建时间", required = true)
private Date createTime;
}

View File

@ -1,18 +0,0 @@
package cn.iocoder.yudao.module.tool.controller.app.test.vo;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
import javax.validation.constraints.*;
@ApiModel("用户 APP - 字典类型更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class AppTestDemoUpdateReqVO extends AppTestDemoBaseVO {
@ApiModelProperty(value = "编号", required = true)
@NotNull(message = "编号不能为空")
private Long id;
}

View File

@ -1,34 +0,0 @@
package cn.iocoder.yudao.module.tool.convert.test;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import cn.iocoder.yudao.module.tool.controller.app.test.vo.*;
import cn.iocoder.yudao.module.tool.dal.dataobject.test.TestDemoDO;
/**
* 字典类型 Convert
*
* @author 芋道源码
*/
@Mapper
public interface TestDemoConvert {
TestDemoConvert INSTANCE = Mappers.getMapper(TestDemoConvert.class);
TestDemoDO convert(AppTestDemoCreateReqVO bean);
TestDemoDO convert(AppTestDemoUpdateReqVO bean);
AppTestDemoRespVO convert(TestDemoDO bean);
List<AppTestDemoRespVO> convertList(List<TestDemoDO> list);
PageResult<AppTestDemoRespVO> convertPage(PageResult<TestDemoDO> page);
List<AppTestDemoExcelVO> convertList02(List<TestDemoDO> list);
}

View File

@ -1,48 +0,0 @@
package cn.iocoder.yudao.module.tool.dal.dataobject.test;
import lombok.*;
import java.util.*;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* 字典类型 DO
*
* @author 芋道源码
*/
@TableName("tool_test_demo")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class TestDemoDO extends BaseDO {
/**
* 编号
*/
@TableId
private Long id;
/**
* 名字
*/
private String name;
/**
* 状态
*/
private Integer status;
/**
* 类型
*/
private Integer type;
/**
* 分类
*/
private Integer category;
/**
* 备注
*/
private String remark;
}

View File

@ -1,66 +0,0 @@
package cn.iocoder.yudao.module.tool.dal.mysql.test;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.tool.controller.app.test.vo.AppTestDemoExportReqVO;
import cn.iocoder.yudao.module.tool.controller.app.test.vo.AppTestDemoPageReqVO;
import cn.iocoder.yudao.module.tool.dal.dataobject.test.TestDemoDO;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.module.tool.controller.admin.test.vo.*;
/**
* 字典类型 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface TestDemoMapper extends BaseMapperX<TestDemoDO> {
default PageResult<TestDemoDO> selectPage(TestDemoPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<TestDemoDO>()
.likeIfPresent(TestDemoDO::getName, reqVO.getName())
.eqIfPresent(TestDemoDO::getStatus, reqVO.getStatus())
.eqIfPresent(TestDemoDO::getType, reqVO.getType())
.eqIfPresent(TestDemoDO::getCategory, reqVO.getCategory())
.eqIfPresent(TestDemoDO::getRemark, reqVO.getRemark())
.betweenIfPresent(TestDemoDO::getCreateTime, reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())
.orderByDesc(TestDemoDO::getId));
}
default List<TestDemoDO> selectList(TestDemoExportReqVO reqVO) {
return selectList(new LambdaQueryWrapperX<TestDemoDO>()
.likeIfPresent(TestDemoDO::getName, reqVO.getName())
.eqIfPresent(TestDemoDO::getStatus, reqVO.getStatus())
.eqIfPresent(TestDemoDO::getType, reqVO.getType())
.eqIfPresent(TestDemoDO::getCategory, reqVO.getCategory())
.eqIfPresent(TestDemoDO::getRemark, reqVO.getRemark())
.betweenIfPresent(TestDemoDO::getCreateTime, reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())
.orderByDesc(TestDemoDO::getId));
}
default PageResult<TestDemoDO> selectPage(AppTestDemoPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<TestDemoDO>()
.likeIfPresent(TestDemoDO::getName, reqVO.getName())
.eqIfPresent(TestDemoDO::getStatus, reqVO.getStatus())
.eqIfPresent(TestDemoDO::getType, reqVO.getType())
.eqIfPresent(TestDemoDO::getCategory, reqVO.getCategory())
.eqIfPresent(TestDemoDO::getRemark, reqVO.getRemark())
.betweenIfPresent(TestDemoDO::getCreateTime, reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())
.orderByDesc(TestDemoDO::getId));
}
default List<TestDemoDO> selectList(AppTestDemoExportReqVO reqVO) {
return selectList(new LambdaQueryWrapperX<TestDemoDO>()
.likeIfPresent(TestDemoDO::getName, reqVO.getName())
.eqIfPresent(TestDemoDO::getStatus, reqVO.getStatus())
.eqIfPresent(TestDemoDO::getType, reqVO.getType())
.eqIfPresent(TestDemoDO::getCategory, reqVO.getCategory())
.eqIfPresent(TestDemoDO::getRemark, reqVO.getRemark())
.betweenIfPresent(TestDemoDO::getCreateTime, reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())
.orderByDesc(TestDemoDO::getId));
}
}

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.tool.service.codegen.inner;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
import cn.iocoder.yudao.module.tool.convert.codegen.CodegenConvert; import cn.iocoder.yudao.module.tool.convert.codegen.CodegenConvert;
import cn.iocoder.yudao.module.tool.dal.dataobject.codegen.CodegenColumnDO; import cn.iocoder.yudao.module.tool.dal.dataobject.codegen.CodegenColumnDO;
import cn.iocoder.yudao.module.tool.dal.dataobject.codegen.CodegenTableDO; import cn.iocoder.yudao.module.tool.dal.dataobject.codegen.CodegenTableDO;
@ -55,7 +56,11 @@ public class CodegenBuilder {
.build(); .build();
/** /**
* {@link BaseDO} 的字段 * 多租户编号的字段名
*/
public static final String TENANT_ID_FIELD = "tenant_id";
/**
* {@link TenantBaseDO} 的字段
*/ */
public static final Set<String> BASE_DO_FIELDS = new HashSet<>(); public static final Set<String> BASE_DO_FIELDS = new HashSet<>();
/** /**
@ -91,7 +96,7 @@ public class CodegenBuilder {
.build(); .build();
static { static {
Arrays.stream(BaseDO.class.getDeclaredFields()).forEach(field -> BASE_DO_FIELDS.add(field.getName())); Arrays.stream(TenantBaseDO.class.getDeclaredFields()).forEach(field -> BASE_DO_FIELDS.add(field.getName()));
// 处理 OPERATION 相关的字段 // 处理 OPERATION 相关的字段
CREATE_OPERATION_EXCLUDE_COLUMN.addAll(BASE_DO_FIELDS); CREATE_OPERATION_EXCLUDE_COLUMN.addAll(BASE_DO_FIELDS);
UPDATE_OPERATION_EXCLUDE_COLUMN.addAll(BASE_DO_FIELDS); UPDATE_OPERATION_EXCLUDE_COLUMN.addAll(BASE_DO_FIELDS);

View File

@ -1,6 +1,8 @@
package cn.iocoder.yudao.module.tool.service.codegen.inner; package cn.iocoder.yudao.module.tool.service.codegen.inner;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.template.TemplateConfig; import cn.hutool.extra.template.TemplateConfig;
import cn.hutool.extra.template.TemplateEngine; import cn.hutool.extra.template.TemplateEngine;
@ -10,6 +12,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
import cn.iocoder.yudao.module.tool.enums.codegen.CodegenSceneEnum; import cn.iocoder.yudao.module.tool.enums.codegen.CodegenSceneEnum;
import cn.iocoder.yudao.module.tool.framework.codegen.config.CodegenProperties; import cn.iocoder.yudao.module.tool.framework.codegen.config.CodegenProperties;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
@ -121,7 +124,6 @@ public class CodegenEngine {
globalBindingMap.put("DictFormatClassName", DictFormat.class.getName()); globalBindingMap.put("DictFormatClassName", DictFormat.class.getName());
// DO 独有字段 // DO 独有字段
globalBindingMap.put("baseDOFields", CodegenBuilder.BASE_DO_FIELDS); globalBindingMap.put("baseDOFields", CodegenBuilder.BASE_DO_FIELDS);
globalBindingMap.put("BaseDOClassName", BaseDO.class.getName());
globalBindingMap.put("QueryWrapperClassName", LambdaQueryWrapperX.class.getName()); globalBindingMap.put("QueryWrapperClassName", LambdaQueryWrapperX.class.getName());
globalBindingMap.put("BaseMapperClassName", BaseMapperX.class.getName()); globalBindingMap.put("BaseMapperClassName", BaseMapperX.class.getName());
// Util 工具类 // Util 工具类
@ -141,6 +143,7 @@ public class CodegenEngine {
bindingMap.put("columns", columns); bindingMap.put("columns", columns);
bindingMap.put("primaryColumn", CollectionUtils.findFirst(columns, CodegenColumnDO::getPrimaryKey)); // 主键字段 bindingMap.put("primaryColumn", CollectionUtils.findFirst(columns, CodegenColumnDO::getPrimaryKey)); // 主键字段
bindingMap.put("sceneEnum", CodegenSceneEnum.valueOf(table.getScene())); bindingMap.put("sceneEnum", CodegenSceneEnum.valueOf(table.getScene()));
// className 相关 // className 相关
// 去掉指定前缀 TestDictType 转换成 DictType. 因为在 create 等方法后不需要带上 Test 前缀 // 去掉指定前缀 TestDictType 转换成 DictType. 因为在 create 等方法后不需要带上 Test 前缀
String simpleClassName = removePrefix(table.getClassName(), upperFirst(table.getModuleName())); String simpleClassName = removePrefix(table.getClassName(), upperFirst(table.getModuleName()));
@ -153,6 +156,15 @@ public class CodegenEngine {
// permission 前缀 // permission 前缀
bindingMap.put("permissionPrefix", table.getModuleName() + ":" + simpleClassNameStrikeCase); bindingMap.put("permissionPrefix", table.getModuleName() + ":" + simpleClassNameStrikeCase);
// 如果多租户则进行覆盖 DB 独有字段
if (CollectionUtils.findFirst(columns, column -> column.getColumnName().equals(CodegenBuilder.TENANT_ID_FIELD)) != null) {
globalBindingMap.put("BaseDOClassName", TenantBaseDO.class.getName());
globalBindingMap.put("BaseDOClassName_simple", TenantBaseDO.class.getSimpleName());
} else {
globalBindingMap.put("BaseDOClassName", BaseDO.class.getName());
globalBindingMap.put("BaseDOClassName_simple", BaseDO.class.getSimpleName());
}
// 执行生成 // 执行生成
final Map<String, String> result = Maps.newLinkedHashMapWithExpectedSize(TEMPLATES.size()); // 有序 final Map<String, String> result = Maps.newLinkedHashMapWithExpectedSize(TEMPLATES.size()); // 有序
TEMPLATES.forEach((vmPath, filePath) -> { TEMPLATES.forEach((vmPath, filePath) -> {

View File

@ -1,70 +0,0 @@
package cn.iocoder.yudao.module.tool.service.test;
import java.util.*;
import javax.validation.*;
import cn.iocoder.yudao.module.tool.controller.app.test.vo.*;
import cn.iocoder.yudao.module.tool.dal.dataobject.test.TestDemoDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
/**
* 字典类型 Service 接口
*
* @author 芋道源码
*/
public interface TestDemoService {
/**
* 创建字典类型
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createTestDemo(@Valid AppTestDemoCreateReqVO createReqVO);
/**
* 更新字典类型
*
* @param updateReqVO 更新信息
*/
void updateTestDemo(@Valid AppTestDemoUpdateReqVO updateReqVO);
/**
* 删除字典类型
*
* @param id 编号
*/
void deleteTestDemo(Long id);
/**
* 获得字典类型
*
* @param id 编号
* @return 字典类型
*/
TestDemoDO getTestDemo(Long id);
/**
* 获得字典类型列表
*
* @param ids 编号
* @return 字典类型列表
*/
List<TestDemoDO> getTestDemoList(Collection<Long> ids);
/**
* 获得字典类型分页
*
* @param pageReqVO 分页查询
* @return 字典类型分页
*/
PageResult<TestDemoDO> getTestDemoPage(AppTestDemoPageReqVO pageReqVO);
/**
* 获得字典类型列表, 用于 Excel 导出
*
* @param exportReqVO 查询条件
* @return 字典类型列表
*/
List<TestDemoDO> getTestDemoList(AppTestDemoExportReqVO exportReqVO);
}

View File

@ -1,82 +0,0 @@
package cn.iocoder.yudao.module.tool.service.test;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import java.util.*;
import cn.iocoder.yudao.module.tool.controller.app.test.vo.*;
import cn.iocoder.yudao.module.tool.dal.dataobject.test.TestDemoDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.tool.convert.test.TestDemoConvert;
import cn.iocoder.yudao.module.tool.dal.mysql.test.TestDemoMapper;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.tool.enums.ErrorCodeConstants.*;
/**
* 字典类型 Service 实现类
*
* @author 芋道源码
*/
@Service
@Validated
public class TestDemoServiceImpl implements TestDemoService {
@Resource
private TestDemoMapper testDemoMapper;
@Override
public Long createTestDemo(AppTestDemoCreateReqVO createReqVO) {
// 插入
TestDemoDO testDemo = TestDemoConvert.INSTANCE.convert(createReqVO);
testDemoMapper.insert(testDemo);
// 返回
return testDemo.getId();
}
@Override
public void updateTestDemo(AppTestDemoUpdateReqVO updateReqVO) {
// 校验存在
this.validateTestDemoExists(updateReqVO.getId());
// 更新
TestDemoDO updateObj = TestDemoConvert.INSTANCE.convert(updateReqVO);
testDemoMapper.updateById(updateObj);
}
@Override
public void deleteTestDemo(Long id) {
// 校验存在
this.validateTestDemoExists(id);
// 删除
testDemoMapper.deleteById(id);
}
private void validateTestDemoExists(Long id) {
if (testDemoMapper.selectById(id) == null) {
throw exception(TEST_DEMO_NOT_EXISTS);
}
}
@Override
public TestDemoDO getTestDemo(Long id) {
return testDemoMapper.selectById(id);
}
@Override
public List<TestDemoDO> getTestDemoList(Collection<Long> ids) {
return testDemoMapper.selectBatchIds(ids);
}
@Override
public PageResult<TestDemoDO> getTestDemoPage(AppTestDemoPageReqVO pageReqVO) {
return testDemoMapper.selectPage(pageReqVO);
}
@Override
public List<TestDemoDO> getTestDemoList(AppTestDemoExportReqVO exportReqVO) {
return testDemoMapper.selectList(exportReqVO);
}
}

View File

@ -17,7 +17,7 @@ import ${BaseDOClassName};
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class ${table.className}DO extends BaseDO { public class ${table.className}DO extends ${BaseDOClassName_simple} {
#foreach ($column in $columns) #foreach ($column in $columns)
#if (!${baseDOFields.contains(${column.javaField})})##排除 BaseDO 的字段 #if (!${baseDOFields.contains(${column.javaField})})##排除 BaseDO 的字段

View File

@ -1,39 +0,0 @@
package cn.iocoder.yudao.module.tool.test;
import cn.iocoder.yudao.framework.datasource.config.YudaoDataSourceAutoConfiguration;
import cn.iocoder.yudao.framework.mybatis.config.YudaoMybatisAutoConfiguration;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.jdbc.Sql;
/**
* 依赖内存 DB 的单元测试
*
* 注意Service 层同样适用对于 Service 层的单元测试我们针对自己模块的 Mapper 走的是 H2 内存数据库针对别的模块的 Service 走的是 Mock 方法
*
* @author 芋道源码
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbUnitTest.Application.class)
@ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件
@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) // 每个单元测试结束后清理 DB
public class BaseDbUnitTest {
@Import({
// DB 配置类
YudaoDataSourceAutoConfiguration.class, // 自己的 DB 配置类
DataSourceAutoConfiguration.class, // Spring DB 自动配置类
DataSourceTransactionManagerAutoConfiguration.class, // Spring 事务自动配置类
DruidDataSourceAutoConfigure.class, // Druid 自动配置类
// MyBatis 配置类
YudaoMybatisAutoConfiguration.class, // 自己的 MyBatis 配置类
MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类
})
public static class Application {
}
}

View File

@ -3,34 +3,16 @@
<!-- 操作工作栏 --> <!-- 操作工作栏 -->
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px"> <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="表名称" prop="tableName"> <el-form-item label="表名称" prop="tableName">
<el-input <el-input v-model="queryParams.tableName" placeholder="请输入表名称" clearable size="small"
v-model="queryParams.tableName" @keyup.enter.native="handleQuery"/>
placeholder="请输入表名称"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item> </el-form-item>
<el-form-item label="表描述" prop="tableComment"> <el-form-item label="表描述" prop="tableComment">
<el-input <el-input v-model="queryParams.tableComment" placeholder="请输入表描述" clearable size="small"
v-model="queryParams.tableComment" @keyup.enter.native="handleQuery"/>
placeholder="请输入表描述"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item> </el-form-item>
<el-form-item label="创建时间"> <el-form-item label="创建时间">
<el-date-picker <el-date-picker v-model="dateRange" size="small" style="width: 240px" value-format="yyyy-MM-dd" type="daterange"
v-model="dateRange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"/>
size="small"
style="width: 240px"
value-format="yyyy-MM-dd"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
@ -79,11 +61,11 @@
<!-- 预览界面 --> <!-- 预览界面 -->
<el-dialog :title="preview.title" :visible.sync="preview.open" width="90%" top="5vh" append-to-body> <el-dialog :title="preview.title" :visible.sync="preview.open" width="90%" top="5vh" append-to-body>
<el-row> <el-row>
<el-col :span="7"> <el-col :span="9">
<el-tree :data="preview.fileTree" :expand-on-click-node="false" default-expand-all highlight-current <el-tree :data="preview.fileTree" :expand-on-click-node="false" default-expand-all highlight-current
@node-click="handleNodeClick"/> @node-click="handleNodeClick"/>
</el-col> </el-col>
<el-col :span="17"> <el-col :span="15">
<el-tabs v-model="preview.activeName"> <el-tabs v-model="preview.activeName">
<el-tab-pane v-for="item in preview.data" :label="item.filePath.substring(item.filePath.lastIndexOf('/') + 1)" <el-tab-pane v-for="item in preview.data" :label="item.filePath.substring(item.filePath.lastIndexOf('/') + 1)"
:name="item.filePath" :key="item.filePath"> :name="item.filePath" :key="item.filePath">
@ -252,7 +234,6 @@ export default {
previewCodegen(row.id).then(response => { previewCodegen(row.id).then(response => {
this.preview.data = response.data; this.preview.data = response.data;
let files = this.handleFiles(response.data); let files = this.handleFiles(response.data);
// console.log(files)
this.preview.fileTree = this.handleTree(files, "id", "parentId", "children", this.preview.fileTree = this.handleTree(files, "id", "parentId", "children",
"/"); // "/" "/"); // "/"
// console.log(this.preview.fileTree) // console.log(this.preview.fileTree)

View File

@ -24,7 +24,7 @@
*【重构】大模块按照多 Maven Module 的方式拆分,提升可维护性,为后续重构 onemall 提供基础 *【重构】大模块按照多 Maven Module 的方式拆分,提升可维护性,为后续重构 onemall 提供基础
*【新增】Spring Security 支持读取多种用户类型,从不同的数据库表,从而实现单项目提供管理后台、用户 APP 的不同 RESTful API 接口 *【新增】Spring Security 支持读取多种用户类型,从不同的数据库表,从而实现单项目提供管理后台、用户 APP 的不同 RESTful API 接口
*【新增】代码生成器支持多 Maven Module 的方式生成代码,支持管理后台、用户 APP 两种场景的 RESTful API 的生成,支持 H2 测试数据库的生成 *【新增】代码生成器支持多 Maven Module 的方式生成代码,支持管理后台、用户 APP 两种场景的 RESTful API 的生成,支持 H2 SQL 脚本的生成
*【重构】将数据库文档调整到 tool 模块,更加明确 *【重构】将数据库文档调整到 tool 模块,更加明确
### 🐞 Bug Fixes ### 🐞 Bug Fixes