生成库表
前端页面的配置,也就是视图功能,我们先放一放,来看看生成库表和后端代码。
关闭实体配置界面,回到实体列表,勾选“文件夹”实体,点击“生成库表”,并确定。
系统提示成功后,使用数据库客户端图形化工具查看,如下:
库表创建完成了,库表的名称和注释、字段的名称、编码、类型、注释都是完整的,来源于数据模型的配置。
注意:配置中的是否必填,平台在生成库表环节特意去除了数据库层面的不能为空的处理,改由应用层的前端与后端去做逻辑验证。
生成代码
在实体列表页面,勾选“文件夹”实体,点击“生成代码”按钮,提示成功后,到idea的项目根目录下,找到output目录,生成的代码会按照前面的配置,结合代码模板,生成各层的代码,如下图所示:
注意上图中,包路径出现了两次edoc,实际上含义不同,第一个edoc是应用的编码,第二个edoc是模块的编码,如果要避免这种情况,也简单,模块可以命名为main或core等其他不重复的名字,这里我就不调整了。
复制后端代码
复制输出的代码,到我们新建的模块对应包下,如下图所示:
查看后端代码
以下完全是基于配置和代码模板一次性生成的各层代码,无任何手工调整的地方,包括注释,均为自动生成。
实体类
package tech.abc.edoc.edoc.entity;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 tech.abc.platform.common.base.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;/*** 文件夹 实体类** @author wqliu* @date 2024-01-29**/
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@TableName("ed_folder")
public class Folder extends BaseEntity {/*** 上级*/@TableField("parent_id")private String parentId;/*** 名称*/@TableField("name")private String name;/********非库表存储属性*****/
}
mapper类及xml
package tech.abc.edoc.edoc.mapper;import tech.abc.edoc.edoc.entity.Folder;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;/*** 文件夹 Mapper 接口** @author wqliu* @date 2024-01-29*/
public interface FolderMapper extends BaseMapper<Folder> {}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="tech.abc.edoc.edoc.mapper.FolderMapper"></mapper>
保留了MybatisPlus(或者说Mybatis)的xml和mapper接口类,如需要使用sql语句扩展功能,如多表关联查询,在当前文件上追加即可,非常方便。
服务接口
package tech.abc.edoc.edoc.service;import tech.abc.edoc.edoc.entity.Folder;
import tech.abc.platform.common.base.BaseService;
import java.util.List;
import java.util.Map;/*** 文件夹 服务接口类** @author wqliu* @date 2024-01-29*/
public interface FolderService extends BaseService<Folder> {/*** 获取标识与名称的Map集合** @param idList 标识列表* @return 集合*/Map<String,String> getNameMap(List<String> idList);
}
公用操作如增删改查,都放到了父接口中BaseService,并内置了一个获取标识与名称的Map集合,用于处理列表页面转换显示数据时产生的经典1+N问题。
服务实现类
package tech.abc.edoc.edoc.service.impl;import tech.abc.edoc.edoc.entity.Folder;
import tech.abc.edoc.edoc.mapper.FolderMapper;
import tech.abc.edoc.edoc.service.FolderService;
import tech.abc.platform.common.base.BaseServiceImpl;
import org.springframework.stereotype.Service;
import tech.abc.platform.common.exception.CommonException;
import tech.abc.platform.common.exception.CustomException;
import java.math.BigDecimal;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
import java.util.Map;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
/**
* 文件夹 服务实现类
*
* @author wqliu
* @date 2024-01-29
*/
@Service
@Slf4j
public class FolderServiceImpl extends BaseServiceImpl<FolderMapper, Folder> implements FolderService {@Overridepublic Folder init() {Folder entity=new Folder();// 预先分配标识entity.setId(IdWorker.getIdStr());//默认值处理return entity;}@Overridepublic void beforeAdd(Folder entity) {//唯一性验证//验证 名称 同节点下唯一if (StringUtils.isNotBlank(entity.getName())) {long countName = this.lambdaQuery().eq(Folder::getName, entity.getName()).eq(Folder::getParentId, entity.getParentId()).count();if (countName > 0) {throw new CustomException(CommonException.PROPERTY_EXIST_IN_SAME_NODE,"【名称】");}}}@Overridepublic void beforeModify(Folder entity) {//唯一性验证//验证 名称 同节点下唯一if (StringUtils.isNotBlank(entity.getName())) {long countName = this.lambdaQuery().eq(Folder::getName, entity.getName()).eq(Folder::getParentId, entity.getParentId()).ne(Folder::getId, entity.getId()).count();if (countName > 0) {throw new CustomException(CommonException.PROPERTY_EXIST_IN_SAME_NODE,"【名称】");}}}@Overridepublic Map<String, String> getNameMap(List<String> idList) {Map<String, String> result = new HashMap<>(5);if (CollectionUtils.isNotEmpty(idList)) {List<Folder> list = this.lambdaQuery().in(Folder::getId, idList).list();if (CollectionUtils.isNotEmpty(list)) {list.stream().forEach(x -> {result.put(x.getId(), x.getName());});}}return result;}@Overrideprotected void copyPropertyHandle(Folder entity, String... value) {// 主属性后附加“副本”用于区分entity.setName (entity.getName() + " 副本");}}
模型属性配置时的唯一性配置,平台处理时,会自动在新增和修改操作前生成验证代码;默认值配置,会在初始化方法init中体现。
控制器
package tech.abc.edoc.edoc.controller;import org.springframework.web.bind.annotation.RestController;
import tech.abc.platform.common.base.BaseController;
import org.springframework.web.bind.annotation.RequestMapping;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import tech.abc.platform.common.annotation.SystemLog;
import tech.abc.platform.common.constant.TreeDefaultConstant;
import tech.abc.platform.common.query.QueryGenerator;
import tech.abc.platform.common.utils.ResultUtil;
import tech.abc.platform.common.vo.PageInfo;
import tech.abc.platform.common.vo.Result;
import tech.abc.platform.common.vo.SortInfo;
import tech.abc.edoc.edoc.entity.Folder;
import tech.abc.edoc.edoc.service.FolderService;
import tech.abc.edoc.edoc.vo.FolderVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;import tech.abc.platform.common.utils.TreeUtil;
import tech.abc.platform.common.vo.TreeVO;/**
* 文件夹 前端控制器类
*
* @author wqliu
* @date 2024-01-29
*/
@RestController
@RequestMapping("/edoc/folder")
@Slf4j
public class FolderController extends BaseController {@Autowiredprivate FolderService folderService;//region 基本操作/*** 初始化*/@GetMapping("/init")public ResponseEntity<Result> init() {Folder entity=folderService.init();FolderVO vo = convert2VO(entity);return ResultUtil.success(vo);}/*** 新增*/@PostMapping("/")@SystemLog(value = "文件夹-新增")@PreAuthorize("hasPermission(null,'edoc:folder:add')")public ResponseEntity<Result> add(@Validated @RequestBody FolderVO vo) {Folder entity=convert2Entity(vo);folderService.add(entity);FolderVO newVO = convert2VO(entity);return ResultUtil.success(newVO);}/*** 修改*/@PutMapping("/")@SystemLog(value = "文件夹-修改")@PreAuthorize("hasPermission(null,'edoc:folder:modify')")public ResponseEntity<Result> modify(@Validated @RequestBody FolderVO vo) {Folder entity=convert2Entity(vo);folderService.modify(entity);FolderVO newVO = convert2VO(entity);return ResultUtil.success(newVO);}/*** 删除数据,单条数据标识,或多条数据标识用逗号间隔拼成的字符串*/@DeleteMapping("/{id}")@SystemLog(value = "文件夹-删除")@PreAuthorize("hasPermission(null,'edoc:folder:remove')")public ResponseEntity<Result> remove(@PathVariable("id") String id) {folderService.remove(id);return ResultUtil.success();}/*** 分页*/@GetMapping("/page")@SystemLog(value = "文件夹-分页")@PreAuthorize("hasPermission(null,'edoc:folder:query')")public ResponseEntity<Result> page(FolderVO queryVO, PageInfo pageInfo, SortInfo sortInfo) {//构造分页对象IPage<Folder> page = new Page<Folder>(pageInfo.getPageNum(), pageInfo.getPageSize());// 当勾选查询所有复选框时,查询所有数据if (queryVO.getIgnoreParent() != null && queryVO.getIgnoreParent()) {queryVO.setParentId(null);}//构造查询条件QueryWrapper<Folder> queryWrapper = QueryGenerator.generateQueryWrapper(Folder.class,queryVO,sortInfo);//查询数据folderService.page(page, queryWrapper);//转换voIPage<FolderVO> pageVO = mapperFacade.map(page, IPage.class);List<FolderVO> folderVOList=convert2VO(page.getRecords());pageVO.setRecords(folderVOList);return ResultUtil.success(pageVO);}/*** 列表*/@GetMapping("/list")@SystemLog(value = "文件夹-列表")@PreAuthorize("hasPermission(null,'edoc:folder:query')")public ResponseEntity<Result> list(FolderVO queryVO, SortInfo sortInfo) {//构造查询条件QueryWrapper<Folder> queryWrapper = QueryGenerator.generateQueryWrapper(Folder.class, queryVO,sortInfo);List<Folder> list= folderService.list(queryWrapper);//转换voList<FolderVO> folderVOList=convert2VO(list);return ResultUtil.success(folderVOList);}/*** 获取单条数据*/@GetMapping("/{id}")@SystemLog(value = "文件夹-详情")@PreAuthorize("hasPermission(null,'edoc:folder:view')")public ResponseEntity<Result> get(@PathVariable("id") String id) {Folder entity = folderService.query(id);FolderVO vo = convert2VO(entity);return ResultUtil.success(vo);}/*** 复制新增数据,单条数据标识,或多条数据标识用逗号间隔拼成的字符串*/@PostMapping("/{id}")@SystemLog(value = "文件夹-复制新增")@PreAuthorize("hasPermission(null,'edoc:folder:addByCopy')")public ResponseEntity<Result> addByCopy(@PathVariable("id") String id) {folderService.addByCopy(id);return ResultUtil.success();}//endregion//region 扩展操作//endregion//region 树操作/*** 获取树数据** @return*/@GetMapping("/tree")@PreAuthorize("hasPermission(null,'edoc:folder:query')")public ResponseEntity<Result> tree() {QueryWrapper<Folder> queryWrapper = new QueryWrapper<>();List<Folder> list = folderService.list(queryWrapper);// 转化成树结构数据List<TreeVO> treeList = list.stream().map(e -> convert2TreeVO(e)).collect(Collectors.toList());List<TreeVO> tree = TreeUtil.buildTree(treeList, TreeDefaultConstant.DEFAULT_TREE_ROOT_PARENT_ID);return ResultUtil.success(tree);}/*** 转换为树视图对象*/private TreeVO convert2TreeVO(Folder entity) {TreeVO tree = new TreeVO();tree.setId(entity.getId());tree.setParentId(entity.getParentId());tree.setLabel(entity.getName());return tree;}//endregion//region 辅助操作/*** 将单条实体转换为视图对象** @param entity 实体* @return {@link EntityVO} 视图对象*/protected FolderVO convert2VO(Folder entity){FolderVO vo=mapperFacade.map(entity,FolderVO.class);return vo;}/*** 将实体列表转换为视图对象列表** @param entityList 实体列表* @return {@link List}<{@link EntityVO}> 视图对象列表*/protected List<FolderVO> convert2VO(List<Folder> entityList) {List<FolderVO> voList = new ArrayList<>(entityList.size());entityList.stream().forEach(x -> {FolderVO vo = convert2VO(x);voList.add(vo);});return voList;}private Folder convert2Entity(FolderVO vo){Folder entity=mapperFacade.map(vo,Folder.class);return entity;}//endregion}
控制器类作为与前端交互的最后一个层,不能像服务层那样,通过泛型基类来提取公共代码复用,平台生成全量的增删改查基本操作,根据实际业务需求进行删除,比如某些实体只能删除不能修改。
除了基本的初始化、新增、删除、修改外,平台内置了分页操作、列表操作、复制新增操作以及实体类与视图对象类的转换操作,且转换时数据字典类型与实体关联类可自动化转换。如果该实体是自关联,同时还会附加树形数据的处理方法。
同时考虑到该类的方法比较多,使用region注释的方式进行了分组。
视图对象类
package tech.abc.edoc.edoc.vo;import tech.abc.platform.common.base.BaseVO;
import java.time.LocalDateTime;
import javax.validation.constraints.NotBlank;
import java.math.BigDecimal;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;/**
* 文件夹 视图对象类
*
* @author wqliu
* @date 2024-01-29
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class FolderVO extends BaseVO {/*** 上级*/private String parentId;/*** 名称*/@NotBlank(message = "【名称】不能为空")private String name;/********非库表存储属性*****//********字典类*****//********实体类、用户单选、组织机构单选*****//********范围查询*****//********自定义扩展*****//*** 忽略上级*/private Boolean ignoreParent;/********子对象*****/}
这里平台自动附加了一个忽略上级的属性,是对于树形结构的实体,默认查询是只查询选中节点下的数据,有时候不确定在哪个节点下,总不能逐个点击去查找,而是提供了这么一个额外属性,来忽略选中节点,查询所有数据。
编译代码
使用maven,clean并install该模块。
一次性编译成功,无需任何改动。
开发平台资料
平台名称:一二三应用开发平台
平台简介:企业级通用低代码应用开发平台,免费全开源可商用
设计资料:csdn专栏
开源地址:Gitee
开源协议:MIT
应用系统资料
应用名称:一二三文档管理系统
应用简介: 企事业单位一站式文档管理系统,让组织内文档管理有序,协作高效、安全可控
设计文档:csdn专栏
开源地址:Gitee
开源协议:MIT
如果您在阅读本文时获得了帮助或受到了启发,希望您能够喜欢并收藏这篇文章,为它点赞~
请在评论区与我分享您的想法和心得,一起交流学习,不断进步,遇见更加优秀的自己!