# 后端

本章节讲述如何在搭好的后端脚手架上,简单地开发增删改查功能。

# 生成代码

脚手架资源下载“后端代码生成器”。用编辑器打开代码生成器项目。代码生成器可以为我们根据数据库表结构,预初始化好“实体类”、“持久层”、“业务逻辑层”、“Controller层”,能有效提高我们的效率。

  • 打开Generator.java
  • 修改一下代码
String projectPath = "填写项目根目录路径";    // 例:F:\代码库\code-deep-Git\hhh-megrez-bridge

gc.setAuthor("作者名称");

dsc.setUrl("数据库地址"); // 例:jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai&characterEncoding=UTF-8&useSSL=false

dsc.setDriverName("数据库驱动");    // 例:com.mysql.cj.jdbc.Driver
dsc.setUsername("数据库账号");
dsc.setPassword("数据库密码");

pc.setParent("com.hhh.cloud.framework.项目名");    // 例:假设项目叫“监管系统(supervise-os)”,项目名可以叫“supervise”,则填写“com.hhh.cloud.framework.supervise”
  • 运行Generator.java,根据提示依次填写生成的代码块所属模块名、数据表名。
  • 查看项目,会发现项目中多了一个模块,模块下有controller(Controller层)、service(业务逻辑层)、mapper(java持久层)、entity(实体类),里面分别有相关的代码。
  • 再查看/src/main/resources/mapper,会发现同样生成一个模块,并生成了***Mapper.xml,这也是持久层,是mybatis的数据库操作脚本,复杂的自定义sql脚本都要写在这里。

# Controller开发

# 出参入参

和前端脚手架的业务交互数据,有固定的出入参规范。

  • 要求交互使用http的“application/json”请求,入参出参自动转为json格式。
  • 入参使用JavaBean接收,使用@RequestBody注解获取,如:
@RequestBody CdCredential credential
  • 出参使用“com.hhh.cloud.framework.util.result.Result”和“com.hhh.cloud.framework.util.result.TableResult”。

# 入参校验

  • 使用javabean接收入参,要校验的bean必须加注解@Valid。
public Result monitorData(@RequestBody @Valid MonitorClientData clientData) {}
  • 接收入参的bean中,增加校验注解。

@NotBlank(message = "发送端编号不能为空")
@Size(max = 32,message = "发送端编号超过最大长度限制")
private String clientCode;

  • 常用的校验注解
注解 类型 说明
@NotNull 任何类型 属性不能为null
@NotEmpty 集合 集合不能为null,且size大于0
@NotBlanck 字符串、字符 字符类不能为null,且去掉空格之后长度大于0
@AssertTrue Boolean、boolean 布尔属性必须是true
@Min 数字类型(原子和包装) 限定数字的最小值(整型)
@Max 同@Min 限定数字的最大值(整型)
@DecimalMin 同@Min 限定数字的最小值(字符串,可以是小数)
@DecimalMax 同@Min 限定数字的最大值(字符串,可以是小数)
@Range 数字类型(原子和包装) 限定数字范围(长整型)
@Length 字符串 限定字符串长度
@Size 集合 限定集合大小
@Past 时间、日期 必须是一个过去的时间或日期
@Future 时期、时间 必须是一个未来的时间或日期
@Email 字符串 必须是一个邮箱格式
@Pattern 字符串、字符 正则匹配字符串

# CRUD接口

  • 打开模块的Controller,把类上的“@Controller”改为“@RestController”。前后端分离不需要视图层,所以就不需要用“@Controller”了。
  • CRUD接口分为:新增、列表查询、详情查询、编辑、删除。具体代码参考:
  
    /**
     * 新增
     */ 
    @PostMapping("add")
    public Result<Boolean> addCredential(@RequestBody CdCredential credential) {
        if(cdCredentialService.count(new LambdaQueryWrapper<CdCredential>().eq(CdCredential::getName, credential.getName())) > 0) {
            return new Result<>(CodeEnum.BUSINESS_ERROR.get(), false, "证书名称已存在");
        }
        credential.setId(CommonUtil.GUID());
        UserSet.create(UserContextHolder.get().getUserJwt(), credential);
        cdCredentialService.save(credential);
        return new Result<>(CodeEnum.SUCCESS.get(), true, "");
    }
    
    /**
     * 详情查询
     */ 
    @PostMapping("get")
    public Result<CdCredential> getCredential(@RequestBody CdCredential credential) {
        return new Result<>(CodeEnum.SUCCESS.get(), cdCredentialService.getById(credential.getId()), "");
    }

    /**
     * 列表查询
     */ 
    @PostMapping("list")
    public TableResult<List<CdCredential>> listCredential(@RequestBody CdCredentialSearch search) {
        IPage<CdCredential> page = cdCredentialService.listCredential(search);
        return new TableResult<>(CodeEnum.SUCCESS.get(), page.getRecords(), page.getTotal(), "");
    }
    
    /**
     * 编辑
     */ 
    @PostMapping("edit")
    public Result<Boolean> editCredential(@RequestBody CdCredential credential) {
        UserSet.update(UserContextHolder.get().getUserJwt(), credential);
        cdCredentialService.updateById(credential);
        return new Result<>(CodeEnum.SUCCESS.get(), true, "");
    }
    
    /**
     * 删除
     */ 
    @PostMapping("del")
    public Result<Boolean> delCredential(@RequestBody CdCredential credential) {
        cdCredentialService.removeById(credential.getId());
        return new Result<>(CodeEnum.SUCCESS.get(), true, "");
    }

# service层开发

使用代码生成器,service层会继承“ServiceImpl”类。该类实现了单表的增删改查方法,如保存数据,直接调用save()即可,不需要开发者动手实现。单表查询也可以使用QueryWrapper进行,有条件更新数据的,也可以使用UpdateWrapper。具体可以阅读mybatis-plus官方文档 (opens new window)

# 出参入参

出入参必须准确。

应该严格要求入参,只接收对业务逻辑有用的入参,达到方法可以复用。

出参准确,尽量不要直接返回Result或TableResult。封装返回给前端的逻辑应该是Controller应该做的事。

# 调用持久层

ServiceImpl默认引用对应的实体类Mapper,如果service层需要使用mapper,可以直接使用baseMapper调用方法。

    baseMapper.selectCount(new LambdaQueryWrapper<CdCredential>().eq(CdCredential::getName, "123"));

# bean引用

非必要尽量不要引用其他业务的service层,避免循环依赖。如果需要查询其他实体类的数据,可以直接引用Mapper。

    @Autowired
    private CdProjectPropertyFileModelMapper cdProjectPropertyFileModelMapper;

# 列表查询

列表查询相对复发,需要编写,例:

    @Override
    public IPage<CdCredential> listCredential(CdCredentialSearch search) {
        LambdaQueryWrapper<CdCredential> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StringUtils.isNotBlank(search.getName()), CdCredential::getName, QuerySet.likeParam(search.getName()));
        queryWrapper.like(StringUtils.isNotBlank(search.getUsername()), CdCredential::getUsername, QuerySet.likeParam(search.getUsername()));
        return page(new Page<>(search.getPage(), search.getLimit()), queryWrapper);
    }

# 持久层开发

# 自定义sql

这部分可以参考mybatis文档 (opens new window)

复杂的查询:

  • 在Mapper.java中添加接口

public interface CdEnvProjectMapper extends BaseMapper<CdEnvProject> {

    public Page<CdEnvProjectSearch> list(Page<CdEnvProjectSearch> resultPage, @Param("map") CdEnvProjectSearch search);
}

  • 在对应的Mapper.xml中增加脚本

<select id="list" resultType="com.hhh.cloud.framework.code.env.model.CdEnvProjectSearch">
    SELECT
        cep.id,
        cep.env_id,
        cep.project_id,
        cep.unit_id,
        cep.unit_name,
        cep.management_id,
        cep.management_name,
        cep.creator_id,
        cep.creator_name,
        cep.create_time,
        cep.remark,
        cep.disable,
        cp.project_code,
        cp.project_name
    FROM cd_env_project cep
    LEFT JOIN cd_project cp ON cep.project_id=cp.id WHERE cep.env_id=#{map.envId}
    <if test="map.groupId != null and map.groupId != ''">
        and cp.group_id=#{map.groupId}
    </if>
    <if test="map.projectName != null and map.projectName != ''">
        and cp.project_name=#{map.projectName}
    </if>
    AND cep.invalid=0
    ORDER BY cep.create_time DESC
</select>

# 逻辑删除

脚手架中的配置文件已经有配置逻辑删除

#逻辑删除配置
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0

打开有逻辑删除的实体类,在逻辑删除字段上增加注解“@TableLogic”。

    /**
     * 逻辑删除(0未删除,1已删除)
     */
    @TableLogic
    private Integer invalid;

如果自定义sql,需要在sql上自己加上逻辑删除条件。


AND invalid=0

# 列表查询

  • 在Mapper.java中添加接口

public interface CdEnvProjectMapper extends BaseMapper<CdEnvProject> {

    public Page<CdEnvProjectSearch> list(Page<CdEnvProjectSearch> resultPage, @Param("map") CdEnvProjectSearch search);
}

  • 在对应的Mapper.xml中增加脚本

<select id="list" resultType="com.hhh.cloud.framework.code.env.model.CdEnvProjectSearch">
    SELECT
        cep.id,
        cep.env_id,
        cep.project_id,
        cep.unit_id,
        cep.unit_name,
        cep.management_id,
        cep.management_name,
        cep.creator_id,
        cep.creator_name,
        cep.create_time,
        cep.remark,
        cep.disable,
        cp.project_code,
        cp.project_name
    FROM cd_env_project cep
    LEFT JOIN cd_project cp ON cep.project_id=cp.id WHERE cep.env_id=#{map.envId}
    <if test="map.groupId != null and map.groupId != ''">
        and cp.group_id=#{map.groupId}
    </if>
    <if test="map.projectName != null and map.projectName != ''">
        and cp.project_name=#{map.projectName}
    </if>
    AND cep.invalid=0
    ORDER BY cep.create_time DESC
</select>

# 权限处理

可以使用工具类中的“ClassFilterUtil”设置权限参数

/**
 * 继承参数数据过滤工具类
 * @param params 过滤条件
 */
public static void getClssParams(Map<String,Object> params, String requestURI);

/**
 * QueryWrapper封装过滤权限
 * @param queryWrapper 查询条件
 * @param params 入参(包含已经使用getClssParams返回的入参)
 */
public static void initClassQuery(QueryWrapper queryWrapper, Map<String,Object> params);

自定义sql上,增加以下查询条件:


    <sql id="classFilter">
        <if test="unitId != null and unitId != '' ">
            And D.unit_id = #{unitId}
        </if>
        <choose>
            <when test="ALL != null"></when>
            <when test="UNIT_MORE != null">
                <if test="UNIT_MORE != null">
                    And D.unit_id in
                    <foreach collection="UNIT_MORE" item="unitId" index="index" open="(" close=")" separator=",">
                        #{unitId}
                    </foreach>
                </if>
            </when>
            <when test="GROUP != null">
                <if test="GROUP != null">
                    And D.unit_id in
                    <foreach collection="GROUP" item="unitId" index="index" open="(" close=")" separator=",">
                        #{unitId}
                    </foreach>
                </if>
            </when>
            <when test="DEPT_MORE != null">
            <if test="DEPT_MORE != null">
                And D.management_id in
                <foreach collection="DEPT_MORE" item="managementId" index="index" open="(" close=")" separator=",">
                    #{managementId}
                </foreach>
            </if>
        </when>
            <when test="DEPT_SUB != null">
                <if test="DEPT_SUB != null">
                    And D.management_id in
                    <foreach collection="DEPT_SUB" item="managementId" index="index" open="(" close=")" separator=",">
                        #{managementId}
                    </foreach>
                </if>
            </when>
            <when test="DEPT != null">
                <if test="DEPT != null">
                    And D.management_id in
                    <foreach collection="DEPT" item="managementId" index="index" open="(" close=")" separator=",">
                        #{managementId}
                    </foreach>
                </if>
            </when>
            <when test="PERSON != null">
                <if test="PERSON != null">
                    And D.creator_id =
                    <foreach collection="PERSON" item="creatorId" index="index" >
                        #{creatorId}
                    </foreach>
                </if>
            </when>
        </choose>
    </sql>