# 开发实现

::: details 目录
[[toc]]
:::

## 代码文档
* 为保持代码与文档一致，所有代码的类，函数，属性都必需写入相应的注释。注释以javadoc规范为准。
* 代码文档将使用dokka工具自动化生成。

![开发 dev](./img/dev_001.jpg)

## 实体类定义规范
* 代码文档注释。使用/** */加注释，不可以使用 //。类对象的注释必需添加作者。
* Swagger注解
    * 类使用ApiModel注解，description为描述内容。
    * 属性使用ApiModelProperty注解，value为描述内容。日期型默认的输出格式与我们要求的数据格式不同，通过加example指写其输出内容。
      例如：@ApiModelProperty(value = "创建时间", example = "2019-03-01 10:10:10")
      
![开发 dev](./img/dev_003.jpg)

![开发 dev](./img/dev_002.jpg)
      
* Json注解（FastJson）
    * 首字母大小不同，不需要加注解。数据字典定义的首字母大写，实体类属性名的首字母小写（按java命名规范），由应用程序统一控制输出的json属性首字母大写；
    * 属性名与Json输出名称不一致，要加注解@JSONField。如建筑名称
    * 不希望生成到json的属性加serialize = false，@JSONField(name = "SpeedSensor",serialize = false )
* 所有属性默认为空。框架在更新时默认不更新为空的字段
* 在数据中心中默认所有实体类都有lastUpdate与createTime属性
* lastUpdate与createTime只从数据库读取。 @Column注解的insertable=false， updateable=false
* 数据库映射注解（框架使用该注解对数据库进行读写操作）
    * @Table注释标注该实体类对应的数据库表，如果数据库表有模式，则name=“模式名.表名”。
    * 属性对应数据库表的主键，使用@Id标注释。
    * 属性要写入数据库或从数据库读取需加@Column注解，name属性为数据库表对象的字段。
    * 插入时不插入数据库的字段@Column注解的insertable=false。
    * 更新时不更新数据库的字段@Column注解的updateable=false。
* 实体类可以通过文档中数据库表介绍，根据表自动生成，达到 设计即完成的操作。

## 控制类规范

::: details 查看代码
<<< @/docs/dev/lib/coding/src/PropertyController.kt
:::

<SCodeGit repos="server/data-center" src="/datacenter/src/main/kotlin/cn/sagacloud/server/datacenter/controllers/PropertyController.kt"/>

标准的通用接口，增删改查统计

### 控制类注解说明 
![开发 dev](./img/dev_005.jpg)

* 类使用/** */加注释，不可以使用 //。类对象的注释必需添加作者。
* @Api 在接口文档 swagger 中显示每个对象控制类说明， 并写明当前控制类的作用，代码中书写 @Api 上面代码示例，swagger 显示如下：

![开发 dev](./img/dev_004.jpg)

* @RestController 和 @RequestMapping 是spring 提供的控制类注解，不理解的可以参看 是spring boot注解介绍

![开发 dev](./img/dev_006.jpg)

* 接口函数和类注释一样使用/** */加注释，在注解中一定要写明函数入参的作用，返回的结果
* 接口注解 @ApiOperation 为配合 swagger 显示接口的描述作用在，@ApiOperation 注解里面有 notes 后面可以书写项目的接口说明以及注意事项
* 接口注解 @PostMapping 设备接口的请求方式为 post 并映射接口名， 接口使用 @GetMapping 还是 @PostMapping 根据需求 使用 @PostMapping

### 控制类接口
#### 创建接口
![开发 dev](./img/dev_006.jpg)

* SCreateRequest 创建请求体基类，所有对象统一创建请求体结构

![开发 dev](./img/dev_008.jpg)

* SCreateResponse 应答体基类，统一返回数据结构，创建接口创建成功会把创建的对象返回

![开发 dev](./img/dev_009.jpg)

#### 删除接口
![开发 dev](./img/dev_010.jpg)

* idList: ArrayList\<Property\> 请求体对象列表，只需要填写对象 id 即可或填写对象主键
* SBaseResponse 标准的应答基类，在不需要附带参数的时候使用

![开发 dev](./img/dev_011.jpg)

#### 修改接口
![开发 dev](./img/dev_012.jpg)

* SUpdateRequest 修改接口请求基类，统一修改对象的请求体 ，修改对象属性可以指定属性来修改，在 projection 指定修改的属性，置空

![开发 dev](./img/dev_013.jpg)

修改接口支持级联修改，例如修改设备信息，同时修改设备所在的业务空间信息，修改业务空间对应的竖井信息，修改设备对那个的部件的部件信息，支持 n 级修改

#### 查询
![开发 dev](./img/dev_014.jpg)

* SQueryRequest 请求体基类，所有的标准接口都是用相同的基类，分页、去重 、分组、排序、级联查询 、操作符关系符查询，指定返回属性查询，设置请求返回最大数
::: details 查看代码
<<< @/docs/dev/lib/coding/src/SQueryRequest.kt
:::

* SQueryResponse 查询应答基类 返回数据包括 请求页码 请求条数，符合条件的总数量，数据列表 继承自 SBaseResponse 返回成功标志

![开发 dev](./img/dev_015.jpg)

### 统计查询

![开发 dev](./img/dev_016.jpg)

* SCountRequest 统计请求基类，在 filters 中写查询条件

![开发 dev](./img/dev_017.jpg)

* SCountResponse 统计应答基类，只返回统计的数量以及应答装填

![开发 dev](./img/dev_018.jpg)

::: warning 注意 
控制类中只写基本的接口函数定义，不允许有逻辑代码，控制类只做接口定义以及说明
:::



## service 规范
![开发 dev](./img/dev_007.jpg)
* 类SObjectService封装了标准的增删改查统计操作，标准操作可以直接使用该类,可以做为接口的标准接口的通用服务类,对于只有标准操作的对象，只需要写好实体类操作类及相关注释即可
* 这里入参使用Property::class.java是因为泛型在编译时使用，运行时泛型信息将被擦除
### 扩展标准接口 service 中代码逻辑
* SObjectService 提供的标准增删改查统计，都可以在执行前或执行后做相应大逻辑操作,定义一个 BuildingService 继承自 SObjectService 然后在 BuildingService 重写对应的函数例如：

![开发 dev](./img/dev_019.jpg)

* 标准接口都会提供对应的 Before 和 Success 的函数可以重写，作相应的逻辑处理

![开发 dev](./img/dev_020.jpg)
* 自定义方法，然后调用 SObjectService 提供的 select insert replace update delete 
```metadata json
fun equipInfo(building: Building){
        // 插入数据
        insert(building)
        // 替换，有则替换，无则创建
        replace(building)
        // 更新
        update(building)
        // 删除
        delete(building)
        // 查询
        select(SFilter.eq("id","123456")).entity()
    } // Fun
```
#### 扩展接口使用
* insert 插入，直接传入插入的数据会返回 Boolean 布尔值 false 或 true，传入的对象一定是 BuildingService 注解一致的
```metadata json
        // 插入数据
        val insert = insert(building)
```
* replace 替换和插入一样返回布尔值，是否成功传入对象也相同
* update 同样会返回布尔值，传入的对象一样，也可以输入条件更新，或只更新某一个或某几个属性
```metadata json
        // 更新
        update(building)
        // 根据条件更新
        update(building,null, SFilter.eq("projectId","Pj1101060001"))
        // 指定更新属性
        update(building, arrayListOf("name"), SFilter.eq("projectId","Pj1101060001"))
```
* delete 删除 可以指定条件传出，也可以直接删除某一个对象

```metadata json
        // 删除
        delete(building)
        //指定条件删除
        delete(SFilter.eq("equipId","Eq110106000145984513212132"))
        // 多条件删除
        delete(SFilter.eq("name","大厦"),SFilter.eq("type",1))
```

* select 查询可以多条件组合，利用下面要介绍的表达式、逻辑表达式、函数。
```metadata json
        // 查询 单条件,可以在 BuildingService类外调用 如下
        BuildingService.select(SFilter.eq("id","123456")).exec()
        //  entity 代表返回单条数据  exec 返回一个列表
        // 多条件查询
        select(SFilter.eq("name","123456"),SFilter.eq("projectId","Pj1101060001")).exec()
``` 
 


### 关系表达式
| 关系 | 后台语法 |
|:--:|:--:|:--:|
| 等于 | SFilter.eq(“属性名”, 值) |
| 不等于 | SFilter.ne(“属性名”, 值) |
| 大于 | SFilter.gt(“属性名”, 值) |
| 大于等于 | SFilter.gte(“属性名”, 值) |
| 小于 | SFilter.lt(“属性名”, 值) |
| 小于等于 | SFilter.lte(“属性名”, 值) |
| 包含 | SFilter.contain(“属性名”, 值) |
| 前缀 | SFilter.startwith(“属性名”, 值) |
| 后缀 | SFilter.endwith(“属性名”, 值) |
| 为空 | SFilter.isnull(“属性名”) |
| 在列表内 | SFilter.inList(“属性名”, arrayList(值1, 值2, ...)) |

* 使用示例：查询 id 为 123456 的 对象
```metadata json
BuildingService.select(SFilter.eq("id","123456")).exec()
```

### 逻辑表达式
| 逻辑 | 后台语法 |
|:--:|:--:|
| 非 | SFilter.not(表达式) |
| 与 | SFilter.and(表达式1, 表达式2) |
| 或 | SFilter.or(表达式1, 表达式2) |

* 示例：查询 type 类型为 1 或 2 的对象
```metadata json
BuildingService.select(SFilter.or(SFilter.eq("type","1"),SFilter.eq("type","2"))).exec()
```

### 函数
| 函数 | 说明 |
|:--:|:--:|
| SLeft(属性名, 长度) | 字符串左截取 |
| SLength(属性名) | 取字符串长度 |
| SLower(属性名) | 字符串转换为小写 |
| SLPad(属性名, 长度, 填充字符) | 左填充字符串 |
| SMid(属性名, 开始位置, 长度) | 从指定位置截取批暄长度字符串 |
| SReverse(属性名) | 字符串逆序 |
| SRight(属性名, 长度) | 字符串右截取 |
| SRPad(属性名, 长度, 填充字符) | 右填充字符串 |
| SSubstring(属性名, 开始位置, 长度) | 从指定位置截取批暄长度字符串 |
| SUpper(属性名) | 字符串转换为大写 |

示例 ：查询名称长度等于4个字符
```
BuildingService.select(SFilter.eq(SLength("name"),4)).exec()
```
#### 自定义函数 

标准化 实体类、接口类 ，减少开发工作量，以及开发代码量