508 lines
13 KiB
Markdown
508 lines
13 KiB
Markdown
|
|
# API 文档 - 删除操作
|
|||
|
|
|
|||
|
|
## 元数据
|
|||
|
|
- 需求编号:003-05
|
|||
|
|
- 创建时间:2026-02-06
|
|||
|
|
- 创建人:AI Assistant
|
|||
|
|
- 状态:已完成
|
|||
|
|
- 版本:v1.0.0
|
|||
|
|
|
|||
|
|
## API 概述
|
|||
|
|
本文档描述了 Salesforce Metadata API 删除操作相关的 REST API 接口,包括元数据删除、批量删除和删除历史查询功能。
|
|||
|
|
|
|||
|
|
### 核心功能
|
|||
|
|
1. **元数据删除**:根据元数据类型和完整名称删除单个或多个元数据
|
|||
|
|
2. **批量删除**:支持批量删除不同类型的元数据,自动分批处理(每批 10 个)
|
|||
|
|
3. **删除历史查询**:查询删除操作的历史记录
|
|||
|
|
|
|||
|
|
### 技术栈
|
|||
|
|
- Spring Boot 2.7.x
|
|||
|
|
- Spring Security(权限控制)
|
|||
|
|
- MyBatis Plus(数据持久化)
|
|||
|
|
- Salesforce Metadata API
|
|||
|
|
|
|||
|
|
### 基础路径
|
|||
|
|
```
|
|||
|
|
/salesforce/metadata/delete
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 接口列表
|
|||
|
|
|
|||
|
|
### 接口 1:删除元数据
|
|||
|
|
|
|||
|
|
#### 功能描述
|
|||
|
|
根据元数据类型和完整名称列表删除元数据。支持一次删除多个同类型的元数据(最多 10 个,超过会自动分批处理)。
|
|||
|
|
|
|||
|
|
#### 请求方式
|
|||
|
|
`POST`
|
|||
|
|
|
|||
|
|
#### 请求路径
|
|||
|
|
```
|
|||
|
|
/salesforce/metadata/delete/{type}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 权限要求
|
|||
|
|
- 权限标识:`salesforce:metadata:delete`
|
|||
|
|
- 使用 `@PreAuthorize("@ss.hasPermi('salesforce:metadata:delete')")` 控制
|
|||
|
|
|
|||
|
|
#### 请求参数
|
|||
|
|
|
|||
|
|
##### 路径参数
|
|||
|
|
| 参数名 | 类型 | 必填 | 说明 | 示例 |
|
|||
|
|
|--------|------|------|------|------|
|
|||
|
|
| type | String | 是 | 元数据类型 | `CustomObject`、`ApexClass`、`ApexTrigger` |
|
|||
|
|
|
|||
|
|
##### 请求体参数
|
|||
|
|
| 参数名 | 类型 | 必填 | 说明 | 示例 |
|
|||
|
|
|--------|------|------|------|------|
|
|||
|
|
| fullNames | String[] | 是 | 元数据完整名称列表 | `["MyCustomObject__c", "AnotherObject__c"]` |
|
|||
|
|
|
|||
|
|
#### 请求示例
|
|||
|
|
```json
|
|||
|
|
POST /salesforce/metadata/delete/CustomObject
|
|||
|
|
Content-Type: application/json
|
|||
|
|
|
|||
|
|
["MyCustomObject__c", "AnotherObject__c"]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 响应参数
|
|||
|
|
|
|||
|
|
##### 成功响应(HTTP 200)
|
|||
|
|
| 参数名 | 类型 | 说明 | 示例 |
|
|||
|
|
|--------|------|------|------|
|
|||
|
|
| code | Integer | 状态码,200 表示成功 | `200` |
|
|||
|
|
| msg | String | 提示信息 | `"操作成功"` |
|
|||
|
|
| data | Array | 删除结果列表 | 见下方示例 |
|
|||
|
|
|
|||
|
|
##### 删除结果对象(DeleteResultVo)
|
|||
|
|
| 参数名 | 类型 | 说明 | 示例 |
|
|||
|
|
|--------|------|------|------|
|
|||
|
|
| fullName | String | 元数据完整名称 | `"MyCustomObject__c"` |
|
|||
|
|
| success | Boolean | 是否删除成功 | `true` |
|
|||
|
|
| errors | Array | 错误消息数组(失败时) | `["Entity is not found"]` |
|
|||
|
|
|
|||
|
|
#### 成功响应示例
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"code": 200,
|
|||
|
|
"msg": "操作成功",
|
|||
|
|
"data": [
|
|||
|
|
{
|
|||
|
|
"fullName": "MyCustomObject__c",
|
|||
|
|
"success": true,
|
|||
|
|
"errors": null
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"fullName": "AnotherObject__c",
|
|||
|
|
"success": false,
|
|||
|
|
"errors": ["Entity is not found"]
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 失败响应示例
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"code": 500,
|
|||
|
|
"msg": "Salesforce 连接失败: Connection refused"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 接口 2:批量删除元数据
|
|||
|
|
|
|||
|
|
#### 功能描述
|
|||
|
|
批量删除不同类型的元数据。支持一次提交多个删除请求,每个请求包含元数据类型和完整名称列表。系统会自动对每个请求进行分批处理(每批 10 个)。
|
|||
|
|
|
|||
|
|
#### 请求方式
|
|||
|
|
`POST`
|
|||
|
|
|
|||
|
|
#### 请求路径
|
|||
|
|
```
|
|||
|
|
/salesforce/metadata/delete/batch
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 权限要求
|
|||
|
|
- 权限标识:`salesforce:metadata:delete`
|
|||
|
|
- 使用 `@PreAuthorize("@ss.hasPermi('salesforce:metadata:delete')")` 控制
|
|||
|
|
|
|||
|
|
#### 请求参数
|
|||
|
|
|
|||
|
|
##### 请求体参数(DeleteRequest 数组)
|
|||
|
|
| 参数名 | 类型 | 必填 | 说明 | 示例 |
|
|||
|
|
|--------|------|------|------|------|
|
|||
|
|
| type | String | 是 | 元数据类型 | `CustomObject`、`ApexClass` |
|
|||
|
|
| fullNames | String[] | 是 | 元数据完整名称列表 | `["Object1__c", "Object2__c"]` |
|
|||
|
|
|
|||
|
|
#### 请求示例
|
|||
|
|
```json
|
|||
|
|
POST /salesforce/metadata/delete/batch
|
|||
|
|
Content-Type: application/json
|
|||
|
|
|
|||
|
|
[
|
|||
|
|
{
|
|||
|
|
"type": "CustomObject",
|
|||
|
|
"fullNames": ["MyCustomObject__c", "AnotherObject__c"]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"type": "ApexClass",
|
|||
|
|
"fullNames": ["MyApexClass", "AnotherClass"]
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 响应参数
|
|||
|
|
|
|||
|
|
##### 成功响应(HTTP 200)
|
|||
|
|
| 参数名 | 类型 | 说明 | 示例 |
|
|||
|
|
|--------|------|------|------|
|
|||
|
|
| code | Integer | 状态码,200 表示成功 | `200` |
|
|||
|
|
| msg | String | 提示信息 | `"批量删除完成,成功 3 个,失败 1 个"` |
|
|||
|
|
| data | Object | 批量删除结果 | 见下方示例 |
|
|||
|
|
|
|||
|
|
##### 批量删除结果对象
|
|||
|
|
| 参数名 | 类型 | 说明 | 示例 |
|
|||
|
|
|--------|------|------|------|
|
|||
|
|
| totalCount | Integer | 总删除数量 | `4` |
|
|||
|
|
| successCount | Integer | 成功数量 | `3` |
|
|||
|
|
| failureCount | Integer | 失败数量 | `1` |
|
|||
|
|
| results | Array | 详细删除结果列表 | DeleteResultVo 数组 |
|
|||
|
|
|
|||
|
|
#### 成功响应示例
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"code": 200,
|
|||
|
|
"msg": "批量删除完成,成功 3 个,失败 1 个",
|
|||
|
|
"data": {
|
|||
|
|
"totalCount": 4,
|
|||
|
|
"successCount": 3,
|
|||
|
|
"failureCount": 1,
|
|||
|
|
"results": [
|
|||
|
|
{
|
|||
|
|
"fullName": "MyCustomObject__c",
|
|||
|
|
"success": true,
|
|||
|
|
"errors": null
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"fullName": "AnotherObject__c",
|
|||
|
|
"success": false,
|
|||
|
|
"errors": ["Entity is not found"]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"fullName": "MyApexClass",
|
|||
|
|
"success": true,
|
|||
|
|
"errors": null
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"fullName": "AnotherClass",
|
|||
|
|
"success": true,
|
|||
|
|
"errors": null
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 失败响应示例
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"code": 500,
|
|||
|
|
"msg": "批量删除失败: 参数校验失败"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 接口 3:获取删除历史
|
|||
|
|
|
|||
|
|
#### 功能描述
|
|||
|
|
查询元数据删除操作的历史记录,支持限制返回数量。
|
|||
|
|
|
|||
|
|
#### 请求方式
|
|||
|
|
`GET`
|
|||
|
|
|
|||
|
|
#### 请求路径
|
|||
|
|
```
|
|||
|
|
/salesforce/metadata/delete/history
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 权限要求
|
|||
|
|
- 权限标识:`salesforce:metadata:delete:query`
|
|||
|
|
- 使用 `@PreAuthorize("@ss.hasPermi('salesforce:metadata:delete:query')")` 控制
|
|||
|
|
|
|||
|
|
#### 请求参数
|
|||
|
|
|
|||
|
|
##### 查询参数
|
|||
|
|
| 参数名 | 类型 | 必填 | 说明 | 默认值 | 范围 |
|
|||
|
|
|--------|------|------|------|--------|------|
|
|||
|
|
| limit | Integer | 否 | 返回记录数量限制 | `10` | 1-100 |
|
|||
|
|
|
|||
|
|
#### 请求示例
|
|||
|
|
```
|
|||
|
|
GET /salesforce/metadata/delete/history?limit=20
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 响应参数
|
|||
|
|
|
|||
|
|
##### 成功响应(HTTP 200)
|
|||
|
|
| 参数名 | 类型 | 说明 | 示例 |
|
|||
|
|
|--------|------|------|------|
|
|||
|
|
| code | Integer | 状态码,200 表示成功 | `200` |
|
|||
|
|
| msg | String | 提示信息 | `"操作成功"` |
|
|||
|
|
| data | Array | 删除历史记录列表 | 见下方示例 |
|
|||
|
|
|
|||
|
|
##### 删除历史记录对象(DataiMetadataDelete)
|
|||
|
|
| 参数名 | 类型 | 说明 | 示例 |
|
|||
|
|
|--------|------|------|------|
|
|||
|
|
| id | Long | 记录 ID | `1` |
|
|||
|
|
| metadataType | String | 元数据类型 | `CustomObject` |
|
|||
|
|
| fullName | String | 元数据完整名称 | `MyCustomObject__c` |
|
|||
|
|
| success | Integer | 是否成功(0-否,1-是) | `1` |
|
|||
|
|
| errorMessage | String | 错误消息(失败时) | `null` |
|
|||
|
|
| deleteTime | Date | 删除时间 | `2026-02-06 10:30:00` |
|
|||
|
|
| userId | Long | 操作用户 ID | `1` |
|
|||
|
|
| createBy | String | 创建者 | `admin` |
|
|||
|
|
| createTime | Date | 创建时间 | `2026-02-06 10:30:00` |
|
|||
|
|
|
|||
|
|
#### 成功响应示例
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"code": 200,
|
|||
|
|
"msg": "操作成功",
|
|||
|
|
"data": [
|
|||
|
|
{
|
|||
|
|
"id": 1,
|
|||
|
|
"metadataType": "CustomObject",
|
|||
|
|
"fullName": "MyCustomObject__c",
|
|||
|
|
"success": 1,
|
|||
|
|
"errorMessage": null,
|
|||
|
|
"deleteTime": "2026-02-06 10:30:00",
|
|||
|
|
"userId": 1,
|
|||
|
|
"createBy": "admin",
|
|||
|
|
"createTime": "2026-02-06 10:30:00"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"id": 2,
|
|||
|
|
"metadataType": "ApexClass",
|
|||
|
|
"fullName": "MyApexClass",
|
|||
|
|
"success": 0,
|
|||
|
|
"errorMessage": "Entity is not found",
|
|||
|
|
"deleteTime": "2026-02-06 10:35:00",
|
|||
|
|
"userId": 1,
|
|||
|
|
"createBy": "admin",
|
|||
|
|
"createTime": "2026-02-06 10:35:00"
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 失败响应示例
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"code": 500,
|
|||
|
|
"msg": "获取删除历史失败"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 错误码
|
|||
|
|
|
|||
|
|
### 系统错误码
|
|||
|
|
| 错误码 | 说明 | 场景 |
|
|||
|
|
|--------|------|------|
|
|||
|
|
| 200 | 操作成功 | 请求处理成功 |
|
|||
|
|
| 500 | 操作失败 | 服务器内部错误 |
|
|||
|
|
| 401 | 未授权 | 用户未登录或 Token 过期 |
|
|||
|
|
| 403 | 禁止访问 | 用户没有权限 |
|
|||
|
|
| 404 | 资源不存在 | 请求的资源不存在 |
|
|||
|
|
|
|||
|
|
### 业务错误码
|
|||
|
|
| 错误码 | 说明 | 场景 | 解决方案 |
|
|||
|
|
|--------|------|------|----------|
|
|||
|
|
| DELETE_001 | Salesforce 连接失败 | 连接 Salesforce 时发生异常 | 检查网络连接和 Salesforce 配置 |
|
|||
|
|
| DELETE_002 | 删除元数据失败 | 调用 Salesforce API 删除失败 | 检查元数据是否存在,查看错误详情 |
|
|||
|
|
| DELETE_003 | 批量删除失败 | 批量删除过程中发生错误 | 检查请求参数,查看具体错误信息 |
|
|||
|
|
| DELETE_004 | 参数校验失败 | 请求参数不符合要求 | 检查参数是否为空,格式是否正确 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 数据模型
|
|||
|
|
|
|||
|
|
### DeleteRequest(删除请求)
|
|||
|
|
```java
|
|||
|
|
@Data
|
|||
|
|
@Schema(description = "删除请求")
|
|||
|
|
public class DeleteRequest implements Serializable {
|
|||
|
|
private static final long serialVersionUID = 1L;
|
|||
|
|
|
|||
|
|
@NotBlank(message = "元数据类型不能为空")
|
|||
|
|
@Schema(description = "元数据类型", required = true, example = "CustomObject")
|
|||
|
|
private String type;
|
|||
|
|
|
|||
|
|
@NotEmpty(message = "元数据名称列表不能为空")
|
|||
|
|
@Schema(description = "元数据完整名称列表", required = true, example = "[\"MyCustomObject__c\", \"AnotherObject__c\"]")
|
|||
|
|
private String[] fullNames;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### DeleteResultVo(删除结果)
|
|||
|
|
```java
|
|||
|
|
@Data
|
|||
|
|
@Schema(description = "删除结果")
|
|||
|
|
public class DeleteResultVo implements Serializable {
|
|||
|
|
private static final long serialVersionUID = 1L;
|
|||
|
|
|
|||
|
|
@Schema(description = "元数据完整名称", example = "MyCustomObject__c")
|
|||
|
|
private String fullName;
|
|||
|
|
|
|||
|
|
@Schema(description = "是否删除成功", example = "true")
|
|||
|
|
private boolean success;
|
|||
|
|
|
|||
|
|
@Schema(description = "错误消息数组", example = "[\"Entity is not found\"]")
|
|||
|
|
private String[] errors;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### DataiMetadataDelete(删除历史实体)
|
|||
|
|
```java
|
|||
|
|
@Data
|
|||
|
|
public class DataiMetadataDelete implements Serializable {
|
|||
|
|
private static final long serialVersionUID = 1L;
|
|||
|
|
|
|||
|
|
private Long id;
|
|||
|
|
private String metadataType;
|
|||
|
|
private String fullName;
|
|||
|
|
private Integer success;
|
|||
|
|
private String errorMessage;
|
|||
|
|
private Date deleteTime;
|
|||
|
|
private Long userId;
|
|||
|
|
private String createBy;
|
|||
|
|
private Date createTime;
|
|||
|
|
private String updateBy;
|
|||
|
|
private Date updateTime;
|
|||
|
|
private Integer delFlag;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 调用示例
|
|||
|
|
|
|||
|
|
### JavaScript 调用示例
|
|||
|
|
|
|||
|
|
#### 单个删除
|
|||
|
|
```javascript
|
|||
|
|
const response = await fetch('/salesforce/metadata/delete/CustomObject', {
|
|||
|
|
method: 'POST',
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
'Authorization': 'Bearer ' + token
|
|||
|
|
},
|
|||
|
|
body: JSON.stringify(['MyCustomObject__c'])
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const result = await response.json();
|
|||
|
|
console.log(result);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 批量删除
|
|||
|
|
```javascript
|
|||
|
|
const response = await fetch('/salesforce/metadata/delete/batch', {
|
|||
|
|
method: 'POST',
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
'Authorization': 'Bearer ' + token
|
|||
|
|
},
|
|||
|
|
body: JSON.stringify([
|
|||
|
|
{ type: 'CustomObject', fullNames: ['Object1__c', 'Object2__c'] },
|
|||
|
|
{ type: 'ApexClass', fullNames: ['Class1', 'Class2'] }
|
|||
|
|
])
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const result = await response.json();
|
|||
|
|
console.log(result);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 查询历史
|
|||
|
|
```javascript
|
|||
|
|
const response = await fetch('/salesforce/metadata/delete/history?limit=10', {
|
|||
|
|
method: 'GET',
|
|||
|
|
headers: {
|
|||
|
|
'Authorization': 'Bearer ' + token
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const result = await response.json();
|
|||
|
|
console.log(result);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Java 调用示例
|
|||
|
|
|
|||
|
|
#### 单个删除
|
|||
|
|
```java
|
|||
|
|
@RestController
|
|||
|
|
public class DeleteExample {
|
|||
|
|
|
|||
|
|
@Autowired
|
|||
|
|
private RestTemplate restTemplate;
|
|||
|
|
|
|||
|
|
public void deleteMetadata() {
|
|||
|
|
String url = "http://localhost:8080/salesforce/metadata/delete/CustomObject";
|
|||
|
|
|
|||
|
|
HttpHeaders headers = new HttpHeaders();
|
|||
|
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
|||
|
|
headers.setBearerAuth(token);
|
|||
|
|
|
|||
|
|
String[] fullNames = {"MyCustomObject__c"};
|
|||
|
|
HttpEntity<String[]> entity = new HttpEntity<>(fullNames, headers);
|
|||
|
|
|
|||
|
|
ResponseEntity<AjaxResult> response = restTemplate.exchange(
|
|||
|
|
url, HttpMethod.POST, entity, AjaxResult.class);
|
|||
|
|
|
|||
|
|
System.out.println(response.getBody());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 注意事项
|
|||
|
|
|
|||
|
|
### 1. 权限控制
|
|||
|
|
- 所有接口都需要有效的 JWT Token
|
|||
|
|
- 删除操作需要 `salesforce:metadata:delete` 权限
|
|||
|
|
- 历史查询需要 `salesforce:metadata:delete:query` 权限
|
|||
|
|
|
|||
|
|
### 2. 分批处理
|
|||
|
|
- Salesforce Metadata API 每次最多删除 10 个元数据
|
|||
|
|
- 系统会自动对超过 10 个的请求进行分批处理
|
|||
|
|
- 分批处理是串行执行,单个失败不影响其他批次
|
|||
|
|
|
|||
|
|
### 3. 异步日志
|
|||
|
|
- 删除操作的历史记录是异步写入数据库的
|
|||
|
|
- 可能会有短暂的延迟(通常 < 1 秒)
|
|||
|
|
- 不影响主业务流程的响应时间
|
|||
|
|
|
|||
|
|
### 4. 错误处理
|
|||
|
|
- 建议客户端对所有可能的错误码进行处理
|
|||
|
|
- 对于批量删除,建议检查每个结果的 success 字段
|
|||
|
|
- 对于失败的删除,可以根据 errors 字段进行针对性处理
|
|||
|
|
|
|||
|
|
### 5. 性能考虑
|
|||
|
|
- 批量删除时,如果数量很大,可能需要较长时间
|
|||
|
|
- 建议客户端设置合理的超时时间(如 60 秒)
|
|||
|
|
- 对于超大量删除,建议分批调用接口
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 相关文档
|
|||
|
|
- [需求文档](../requirements/sub/2026-01-28-003-05-删除操作.md)
|
|||
|
|
- [设计文档](../design/2026-02-03-003-05-删除操作-设计.md)
|
|||
|
|
- [决策记录](../decisions/2026-02-03-003-05-ADR-删除操作技术选型.md)
|
|||
|
|
- [变更日志](../changelog/2026-02-06-003-05-changelog.md)
|
|||
|
|
- [复盘文档](../retros/2026-02-06-003-05-retro.md)
|
|||
|
|
- [会话记录](../sessions/2026-02-03-003-05-session.md)
|