766 lines
16 KiB
Markdown
766 lines
16 KiB
Markdown
# 微信小程序:语音信息打卡 API 接口文档
|
||
|
||
本文档详细描述了WxCheck项目中Login控制器和Check控制器的所有API接口,包括接口功能、请求参数、响应格式以及调用示例。
|
||
|
||
## 基础信息
|
||
|
||
- **API基础路径**:`https://wx-xcx-check.blv-oa.com:4433/api/[controller]/[action]`
|
||
- **请求方式**:POST
|
||
|
||
## 统一响应格式
|
||
|
||
所有接口返回以下统一格式的JSON响应:
|
||
|
||
```json
|
||
{
|
||
"success": true/false,
|
||
"message": "操作结果描述",
|
||
"data": {...}, // 可选,返回的具体数据
|
||
"error": "错误信息" // 可选,错误时返回
|
||
}
|
||
```
|
||
|
||
## 1. Login控制器接口
|
||
|
||
### 1.1 用户注册接口
|
||
|
||
#### 接口描述
|
||
用户信息更新功能,根据传入的UserKey更新用户的UserName、WeChatName和PhoneNumber信息。
|
||
|
||
#### 接口路径
|
||
`/api/Login/Register`
|
||
|
||
#### 请求参数
|
||
|
||
| 参数名 | 类型 | 必填 | 描述 |
|
||
|--------|------|------|------|
|
||
| UserName | string | 否 | 用户名(可以为空) |
|
||
| UserKey | string | 是 | 用户唯一标识键 |
|
||
| WeChatName | string | 否 | 微信名称 |
|
||
| PhoneNumber | string | 否 | 电话号码 |
|
||
| AvatarUrl | string | 否 | 头像地址 |
|
||
|
||
#### 请求示例
|
||
|
||
```json
|
||
{
|
||
"UserName": "张三",
|
||
"UserKey": "openid_from_wechat",
|
||
"WeChatName": "张三的微信",
|
||
"PhoneNumber": "13800138000",
|
||
"AvatarUrl": "https://example.com/avatar.jpg"
|
||
}
|
||
```
|
||
|
||
#### 响应示例
|
||
|
||
**成功响应:**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"Id": 1,
|
||
"UserName": "张三",
|
||
"UserKey": "openid_from_wechat",
|
||
"WeChatName": "张三的微信",
|
||
"PhoneNumber": "13800138000",
|
||
"AvatarUrl": "https://example.com/avatar.jpg",
|
||
"FirstLoginTime": "2023-10-31T10:00:00",
|
||
"IsDisabled": false,
|
||
"CreateTime": "2023-10-31T10:00:00",
|
||
"UpdateTime": "2023-10-31T15:30:00",
|
||
"Token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||
}
|
||
}
|
||
```
|
||
|
||
**失败响应:**
|
||
|
||
```json
|
||
{
|
||
"success": false,
|
||
"message": "用户不存在"
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
"success": false,
|
||
"message": "更新用户信息失败",
|
||
"error": "数据库连接错误"
|
||
}
|
||
```
|
||
|
||
### 1.2 用户登录接口
|
||
|
||
#### 接口描述
|
||
用户登录功能,将微信小程序code转换为OpenID并验证用户身份。如果用户不存在,则自动创建新用户记录。无论用户是否存在,都返回完整的用户信息和Token。
|
||
|
||
#### 接口路径
|
||
`/api/Login/Login`
|
||
|
||
#### 请求参数
|
||
|
||
| 参数名 | 类型 | 必填 | 描述 |
|
||
|--------|------|------|------|
|
||
| Code | string | 是 | 微信小程序登录凭证code,用于获取OpenID |
|
||
|
||
#### 请求示例
|
||
|
||
```json
|
||
{
|
||
"Code": "wx_login_code_here"
|
||
}
|
||
```
|
||
|
||
#### 响应示例
|
||
|
||
**成功响应(已存在用户):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"Id": 1,
|
||
"UserName": "张三",
|
||
"UserKey": "openid_from_wechat",
|
||
"WeChatName": "张三的微信",
|
||
"PhoneNumber": "13800138000",
|
||
"AvatarUrl": "https://example.com/avatar.jpg",
|
||
"FirstLoginTime": "2023-10-31T10:00:00",
|
||
"IsDisabled": false,
|
||
"CreateTime": "2023-10-31T10:00:00",
|
||
"UpdateTime": "2023-10-31T10:00:00",
|
||
"Token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||
}
|
||
}
|
||
```
|
||
|
||
**成功响应(新用户自动注册):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"Id": 2,
|
||
"UserName": "",
|
||
"UserKey": "new_openid_from_wechat",
|
||
"WeChatName": "",
|
||
"PhoneNumber": "",
|
||
"AvatarUrl": "",
|
||
"FirstLoginTime": "2023-10-31T16:00:00",
|
||
"IsDisabled": false,
|
||
"CreateTime": "2023-10-31T16:00:00",
|
||
"UpdateTime": "2023-10-31T16:00:00",
|
||
"Token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||
}
|
||
}
|
||
```
|
||
|
||
**失败响应:**
|
||
```json
|
||
{
|
||
"success": false,
|
||
"message": "用户已被禁用"
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
"success": false,
|
||
"message": "获取微信OpenID失败",
|
||
"error": "微信API调用失败"
|
||
}
|
||
```
|
||
|
||
## 2. Check控制器接口
|
||
|
||
### 2.1 检查地址接口
|
||
|
||
#### 接口描述
|
||
根据会话记录的GUID查询经纬度信息,并转换为详细地址更新到数据库中。
|
||
|
||
#### 接口路径
|
||
`/api/Check/CheckAddress`
|
||
|
||
#### 请求参数
|
||
|
||
| 参数名 | 类型 | 必填 | 描述 |
|
||
|--------|------|------|------|
|
||
| Guid | string | 是 | 会话唯一标识 |
|
||
|
||
#### 请求示例
|
||
|
||
```json
|
||
{
|
||
"Guid": "会话唯一标识"
|
||
}
|
||
```
|
||
|
||
#### 响应示例
|
||
|
||
**成功响应:**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "地址更新成功",
|
||
"address": "北京市海淀区中关村南大街5号"
|
||
}
|
||
```
|
||
|
||
**失败响应:**
|
||
```json
|
||
{
|
||
"success": false,
|
||
"message": "记录不存在或已被删除"
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
"success": false,
|
||
"message": "更新失败",
|
||
"error": "数据库操作错误"
|
||
}
|
||
```
|
||
|
||
### 2.2 添加会话记录接口
|
||
|
||
#### 接口描述
|
||
添加新的会话记录到系统中。
|
||
|
||
#### 接口路径
|
||
`/api/Check/AddConversation`
|
||
|
||
#### 请求参数
|
||
|
||
| 参数名 | 类型 | 必填 | 描述 |
|
||
|--------|------|------|------|
|
||
| UserKey | string | 是 | 用户唯一标识键 |
|
||
| ConversationContent | string | 是 | 会话内容 |
|
||
| SendMethod | string | 是 | 发送方式 |
|
||
| UserLocation | string | 否 | 用户定位信息(经纬度格式:"纬度,经度") |
|
||
| MessageType | int | 否 | 1:公有消息,2:私有消息 |
|
||
| Guid | string | 否 | 会话唯一标识(不提供则系统自动生成) |
|
||
|
||
#### 请求示例
|
||
|
||
```json
|
||
{
|
||
"UserKey": "user_123456",
|
||
"ConversationContent": "这是一条测试消息",
|
||
"SendMethod": "文本",
|
||
"UserLocation": "39.9087,116.3975",
|
||
"MessageType": 1
|
||
}
|
||
```
|
||
|
||
#### 响应示例
|
||
|
||
**成功响应:**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "收到!",
|
||
"conversationGuid": "会话唯一标识",
|
||
"receivedTime": "2023-10-31 10:05:00"
|
||
}
|
||
```
|
||
|
||
**失败响应:**
|
||
```json
|
||
{
|
||
"success": false,
|
||
"message": "发送失败",
|
||
"error": "数据库操作错误"
|
||
}
|
||
```
|
||
|
||
### 2.3 查询会话记录接口
|
||
|
||
#### 接口描述
|
||
根据用户唯一标识键查询该用户的所有会话记录。
|
||
|
||
#### 接口路径
|
||
`https://wx-xcx-check.blv-oa.com/api/Check/GetConversations`
|
||
|
||
#### 请求参数
|
||
|
||
| 参数名 | 类型 | 必填 | 描述 |
|
||
|--------|------|------|------|
|
||
| UserKey | string | 是 | 用户唯一标识键 |
|
||
| MessageType | int | 否 | 0:不判断消息类型(默认),1:只返回公有消息,2:只返回私有消息 |
|
||
|
||
#### 请求示例
|
||
|
||
```json
|
||
{
|
||
"UserKey": "user_123456",
|
||
"MessageType": 0
|
||
}
|
||
```
|
||
|
||
#### 响应示例
|
||
|
||
**成功响应:**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": [
|
||
{
|
||
"Id": 1,
|
||
"Guid": "会话唯一标识",
|
||
"UserKey": "user_123456",
|
||
"ConversationContent": "这是一条测试消息",
|
||
"SendMethod": "文本",
|
||
"UserLocation": "北京市海淀区",
|
||
"Latitude": "39.9087",
|
||
"Longitude": "116.3975",
|
||
"RecordTime": "2023-10-31T10:05:00",
|
||
"RecordTimeUTCStamp": 1698732300000,
|
||
"IsDeleted": false,
|
||
"CreateTime": "2023-10-31T10:05:00",
|
||
"MessageType": 1
|
||
},
|
||
{
|
||
"Id": 2,
|
||
"Guid": "会话唯一标识",
|
||
"UserKey": "user_123456",
|
||
"ConversationContent": "这是第二条测试消息",
|
||
"SendMethod": "图片",
|
||
"UserLocation": "北京市朝阳区",
|
||
"Latitude": "39.9180",
|
||
"Longitude": "116.4272",
|
||
"RecordTime": "2023-10-31T10:06:00",
|
||
"RecordTimeUTCStamp": 1698732360000,
|
||
"IsDeleted": false,
|
||
"CreateTime": "2023-10-31T10:06:00",
|
||
"MessageType": 2
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**失败响应:**
|
||
```json
|
||
{
|
||
"success": false,
|
||
"message": "查询失败",
|
||
"error": "数据库连接错误"
|
||
}
|
||
```
|
||
|
||
### 2.4 分页查询会话记录接口
|
||
|
||
#### 接口描述
|
||
分页查询用户的会话记录,每页默认10条,按时间戳从**最新到最旧**排序,支持根据消息类型进行过滤。
|
||
|
||
#### 接口路径
|
||
`https://wx-xcx-check.blv-oa.com:4433/api/Check/GetConversationsByPage`
|
||
|
||
#### 请求参数
|
||
|
||
| 参数名 | 类型 | 必填 | 描述 |
|
||
|--------|------|------|------|
|
||
| UserKey | string | 是 | 用户唯一标识键 |
|
||
| Page | int | 否 | 页码,默认为1,小于1时自动设为1 |
|
||
| PageSize | int | 否 | 每页数量,默认为10,最大100,小于1时自动设为10 |
|
||
| MessageType | int | 否 | 0:不判断消息类型(默认),1:只返回公有消息,2:只返回私有消息 |
|
||
|
||
#### 请求示例
|
||
|
||
```json
|
||
{
|
||
"UserKey": "user_123456",
|
||
"Page": 1,
|
||
"PageSize": 10,
|
||
"MessageType": 0
|
||
}
|
||
```
|
||
|
||
#### 响应示例
|
||
|
||
**成功响应:**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"conversations": [
|
||
{
|
||
"Id": 1,
|
||
"Guid": "会话唯一标识",
|
||
"UserKey": "user_123456",
|
||
"ConversationContent": "这是一条测试消息",
|
||
"SendMethod": "文本",
|
||
"UserLocation": "北京市海淀区",
|
||
"Latitude": "39.9087",
|
||
"Longitude": "116.3975",
|
||
"RecordTime": "2023-10-31T10:00:00",
|
||
"RecordTimeUTCStamp": 1698727200000,
|
||
"IsDeleted": false,
|
||
"CreateTime": "2023-10-31T10:00:00",
|
||
"MessageType": 1
|
||
},
|
||
{
|
||
"Id": 2,
|
||
"Guid": "会话唯一标识",
|
||
"UserKey": "user_123456",
|
||
"ConversationContent": "这是一条私有消息",
|
||
"SendMethod": "文本",
|
||
"UserLocation": "北京市朝阳区",
|
||
"Latitude": "39.9180",
|
||
"Longitude": "116.4272",
|
||
"RecordTime": "2023-10-31T09:55:00",
|
||
"RecordTimeUTCStamp": 1698724500000,
|
||
"IsDeleted": false,
|
||
"CreateTime": "2023-10-31T09:55:00",
|
||
"MessageType": 2
|
||
}
|
||
],
|
||
"totalCount": 25,
|
||
"page": 1,
|
||
"pageSize": 10,
|
||
"totalPages": 3
|
||
}
|
||
}
|
||
```
|
||
|
||
**失败响应:**
|
||
```json
|
||
{
|
||
"success": false,
|
||
"message": "查询失败",
|
||
"error": "数据库操作错误"
|
||
}
|
||
```
|
||
|
||
### 2.5 根据GUID查询会话记录接口
|
||
|
||
#### 接口描述
|
||
根据会话唯一标识(GUID)查询会话记录详情,不考虑IsDeleted状态。
|
||
|
||
#### 接口路径
|
||
`/api/Check/GetConversationByGuid`
|
||
|
||
#### 请求参数
|
||
|
||
| 参数名 | 类型 | 必填 | 描述 |
|
||
|--------|------|------|------|
|
||
| Guid | string | 是 | 会话唯一标识 |
|
||
|
||
#### 请求示例
|
||
|
||
```json
|
||
{
|
||
"Guid": "会话唯一标识"
|
||
}
|
||
```
|
||
|
||
#### 响应示例
|
||
|
||
**成功响应:**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "查询成功",
|
||
"data": {
|
||
"Id": 1,
|
||
"Guid": "会话唯一标识",
|
||
"UserKey": "user_123456",
|
||
"ConversationContent": "这是一条测试消息",
|
||
"SendMethod": "文本",
|
||
"UserLocation": "北京市海淀区",
|
||
"Latitude": "39.9087",
|
||
"Longitude": "116.3975",
|
||
"RecordTime": "2023-10-31T10:05:00",
|
||
"RecordTimeUTCStamp": 1698732300000,
|
||
"IsDeleted": false,
|
||
"CreateTime": "2023-10-31T10:05:00",
|
||
"MessageType": 1
|
||
}
|
||
}
|
||
```
|
||
|
||
**失败响应:**
|
||
```json
|
||
{
|
||
"success": false,
|
||
"message": "未找到该记录"
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
"success": false,
|
||
"message": "查询失败",
|
||
"error": "数据库操作错误"
|
||
}
|
||
```
|
||
|
||
### 2.6 更新会话记录接口
|
||
|
||
#### 接口描述
|
||
更新指定GUID的会话记录,需要验证UserKey的权限。每次更新时,RecordTime会自动更新为当前时间。
|
||
|
||
#### 接口路径
|
||
`/api/Check/UpdateConversation`
|
||
|
||
#### 请求参数
|
||
|
||
| 参数名 | 类型 | 必填 | 描述 |
|
||
|--------|------|------|------|
|
||
| Guid | string | 是 | 会话唯一标识 |
|
||
| UserKey | string | 是 | 用户唯一标识键(用于权限验证) |
|
||
| ConversationContent | string | 是 | 新的会话内容 |
|
||
| SendMethod | string | 是 | 新的发送方式 |
|
||
| UserLocation | string | 否 | 新的用户定位信息 |
|
||
| MessageType | int | 否 | 1:公有消息,2:私有消息 |
|
||
|
||
#### 请求示例
|
||
|
||
```json
|
||
{
|
||
"Guid": "会话唯一标识",
|
||
"UserKey": "user_123456",
|
||
"ConversationContent": "更新后的会话内容",
|
||
"SendMethod": "文本",
|
||
"UserLocation": "北京市西城区",
|
||
"MessageType": 1
|
||
}
|
||
```
|
||
|
||
#### 响应示例
|
||
|
||
**成功响应:**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "更新成功",
|
||
"receivedTime": "2023-10-31 10:10:00"
|
||
}
|
||
```
|
||
|
||
**失败响应:**
|
||
```json
|
||
{
|
||
"success": false,
|
||
"message": "记录不存在或无权限修改"
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
"success": false,
|
||
"message": "更新失败",
|
||
"error": "数据库操作错误"
|
||
}
|
||
```
|
||
|
||
### 2.7 删除会话记录接口
|
||
|
||
#### 接口描述
|
||
软删除会话记录(将IsDeleted标记为1),需要验证UserKey的权限。
|
||
|
||
#### 接口路径
|
||
`/api/Check/DeleteConversation`
|
||
|
||
#### 请求参数
|
||
|
||
| 参数名 | 类型 | 必填 | 描述 |
|
||
|--------|------|------|------|
|
||
| Guid | string | 是 | 会话唯一标识 |
|
||
| UserKey | string | 是 | 用户唯一标识键(用于权限验证) |
|
||
|
||
#### 请求示例
|
||
|
||
```json
|
||
{
|
||
"Guid": "会话唯一标识",
|
||
"UserKey": "user_123456"
|
||
}
|
||
```
|
||
|
||
#### 响应示例
|
||
|
||
**成功响应:**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "删除成功"
|
||
}
|
||
```
|
||
|
||
**失败响应:**
|
||
```json
|
||
{
|
||
"success": false,
|
||
"message": "记录不存在或已被删除"
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
"success": false,
|
||
"message": "删除失败",
|
||
"error": "数据库操作错误"
|
||
}
|
||
```
|
||
|
||
### 2.8 从Redis Stream读取消息接口
|
||
|
||
#### 接口描述
|
||
从Redis Stream读取新的会话消息。
|
||
|
||
#### 接口路径
|
||
`/api/Check/ReadMessageFromRedis`
|
||
|
||
#### 请求参数
|
||
|
||
| 参数名 | 类型 | 必填 | 描述 |
|
||
|--------|------|------|------|
|
||
| GroupName | string | 否 | 消费组名称,默认:xcx_group |
|
||
| ConsumerName | string | 否 | 消费者名称,默认:consumer_时间戳 |
|
||
| Count | int | 否 | 读取消息数量,默认:1 |
|
||
|
||
#### 请求示例
|
||
|
||
```json
|
||
{
|
||
"GroupName": "xcx_group",
|
||
"ConsumerName": "consumer_1",
|
||
"Count": 5
|
||
}
|
||
```
|
||
|
||
#### 响应示例
|
||
|
||
**成功响应:**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "成功读取消息",
|
||
"data": [
|
||
{
|
||
"MessageId": "1635739200000-0",
|
||
"Id": "1",
|
||
"Guid": "会话唯一标识",
|
||
"UserKey": "user_123456",
|
||
"ConversationContent": "测试消息",
|
||
"SendMethod": "文本",
|
||
"UserLocation": "北京市海淀区",
|
||
"Latitude": "39.9087",
|
||
"Longitude": "116.3975",
|
||
"RecordTime": "2023-10-31T10:00:00",
|
||
"RecordTimeUTCStamp": "1698727200000",
|
||
"IsDeleted": "false",
|
||
"CreateTime": "2023-10-31T10:00:00",
|
||
"MessageType": "1",
|
||
"UserName": "张三",
|
||
"WeChatName": "张三的微信",
|
||
"PhoneNumber": "13800138000",
|
||
"AvatarUrl": "https://example.com/avatar.jpg"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**失败响应:**
|
||
```json
|
||
{
|
||
"success": false,
|
||
"message": "读取消息失败",
|
||
"error": "Redis操作错误"
|
||
}
|
||
```
|
||
|
||
## 3. 接口调用说明
|
||
|
||
### 3.1 HTTP客户端调用示例(JavaScript)
|
||
|
||
```javascript
|
||
// 封装API请求函数
|
||
async function callApi(endpoint, data) {
|
||
try {
|
||
const response = await fetch(`http://your-api-domain/api${endpoint}`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify(data)
|
||
});
|
||
|
||
const result = await response.json();
|
||
return result;
|
||
} catch (error) {
|
||
console.error('API调用失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 用户信息更新示例
|
||
async function updateUser() {
|
||
const result = await callApi('/Login/Register', {
|
||
UserName: '张三',
|
||
UserKey: 'openid_from_wechat',
|
||
WeChatName: '张三的微信',
|
||
PhoneNumber: '13800138000'
|
||
});
|
||
|
||
if (result.success) {
|
||
console.log('用户信息更新成功');
|
||
} else {
|
||
console.log('用户信息更新失败:', result.message);
|
||
}
|
||
}
|
||
|
||
// 用户登录示例
|
||
async function loginUser() {
|
||
const result = await callApi('/Login/Login', {
|
||
Code: 'wx_login_code_here'
|
||
});
|
||
|
||
if (result.success) {
|
||
console.log('登录成功,用户信息:', result.data);
|
||
// 保存用户信息和Token到本地存储
|
||
localStorage.setItem('userInfo', JSON.stringify(result.data));
|
||
localStorage.setItem('token', result.data.Token);
|
||
} else {
|
||
console.log('登录失败:', result.message);
|
||
}
|
||
}
|
||
|
||
// 添加会话示例
|
||
async function addConversation() {
|
||
const result = await callApi('/Check/AddConversation', {
|
||
UserKey: 'user_123456',
|
||
ConversationContent: '测试会话内容',
|
||
SendMethod: '文本',
|
||
UserLocation: '北京',
|
||
MessageType: 0
|
||
});
|
||
|
||
if (result.success) {
|
||
console.log('会话添加成功');
|
||
} else {
|
||
console.log('会话添加失败:', result.message);
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3.2 常见错误处理
|
||
|
||
1. **数据库连接错误**:检查数据库服务是否正常运行,连接字符串是否正确
|
||
2. **权限验证失败**:确保提供的UserKey与操作资源匹配
|
||
3. **记录不存在**:在更新或删除前确认记录ID和UserKey的正确性
|
||
4. **网络错误**:检查API服务是否正常运行,网络连接是否稳定
|
||
|
||
## 4. 安全注意事项
|
||
|
||
1. **参数验证**:所有接口都应在前端进行基本的数据格式验证
|
||
2. **UserKey保护**:UserKey作为用户身份标识,应妥善保护,避免泄露
|
||
3. **错误信息处理**:生产环境中应避免返回详细的错误信息,防止信息泄露
|
||
4. **请求频率限制**:建议在生产环境中对API接口实施请求频率限制,防止滥用
|
||
|
||
## 5. 接口维护信息
|
||
|
||
- **最后更新时间**:2023-11-01
|
||
- **维护人员**:系统管理员
|
||
- **版本号**:v1.2.0 |