first commit

This commit is contained in:
2022-03-17 15:59:24 +08:00
commit 2b0debb847
592 changed files with 73946 additions and 0 deletions

21
Makefile Normal file
View File

@@ -0,0 +1,21 @@
build_amd:
@echo '┌ start build amd64'
@bash ./script/build/multi-service.sh
@echo '└ end build amd64'
build_arm:
@echo '┌ start build arm64'
@bash ./script/build/multi-service.sh arm64
@echo '└ end build arm64'
# 编译二进制 amd64
quick_build_amd:
@echo '┌ start quick build amd64'
@bash ./script/build/quick_build.sh
@echo '└ end quick build amd64'
# 编译二进制 amr64
quick_build_arm:
@echo '┌ start quick build arm64'
@bash ./script/build/quick_build.sh arm64
@echo '└ end quick build arm64'

22
README.md Normal file
View File

@@ -0,0 +1,22 @@
## 配置文件
```toml
# Env 三种模式
Env = "debug" # 测试环境
Env = "release" # 线上环境
Env = "benchmark" # 压测环境
```
## 部署环境
环境需要增加以下环境变量
GOLANG_PROTOBUF_REGISTRATION_CONFLICT=warn
## 快速部署
`make build_xxx` xxx 可选为 amd 或者 arm
如果要选择平台则自行修改`dtalk/script/build/util.sh`中的`initOS()`编译设置
`make build` 包括编译二进制, 配置文件并打包
`make quick_build` 仅编译二进制

57
doc/auth.md Normal file
View File

@@ -0,0 +1,57 @@
# 鉴权服务
#### 应用注册
URL: /auth/sign-in
`post`
**请求参数:**
| **参数** | **名字** | **类型** | **约束** | **说明** |
| -------------- | ------------ | -------- | -------- | -------- |
| Content-Type | body中的数据类型 | string | true | 值为multipart/form-data |
| configFile | 配置文件 | file | true | 包含第三方应用鉴权服务接口的相关信息 |
| appId | 应用ID | text | true | 应用ID位于请求头 |
**返回参数:**
| **参数** | **名字** | **类型** | **说明** |
| ----------- | ------------ | -------- | ---------- |
| appId | appId | string | appId |
| key | key | string | 授权第三方应用使用本鉴权服务 |
| createTime | 创建时间 | int | app注册时间 |
| updateTime | 更新时间 | int | 信息更新时间 |
```json
{
"result": 0,
"message": "",
"data": {
"appId": "dtalk",
"key": "key",
"createTime": 1625552582748,
"updateTime": 1625552582748
}
}
```
#### 鉴权
**请求参数:**
| **参数** | **名字** | **类型** | **约束** | **说明** |
| ---------- | -------------- | -------- | -------- | ------------------------------------------------------------ |
| appId | 应用的唯一标识 | string | true | 应用的唯一标识 |
| token | token | string | true | 第三方鉴权所要的token |
| digest | 消息摘要 | string | true | digest = SHA256 ( appId + token + createTime + key )key为注册时所返回的key |
| createTime | 请求创建时间 | int | true | 请求创建时的时间戳 |
**返回参数:**
| **参数** | **名字** | **类型** | **说明** |
| -------- | -------- | -------- | -------- |
| uid | 用户ID | string | 用户ID |

406
doc/backend.md Normal file
View File

@@ -0,0 +1,406 @@
# 后台服务
#### 检查更新
URL: /app/version/check
`post`
**请求参数:**
| **参数** | **名字** | **类型** | **约束** | **说明** |
| -------------- | ------------ | -------- | -------- | -------- |
| versionCode | 当前版本 | int | true | 版本code |
**返回参数:**
| **参数** | **名字** | **类型** | **说明** |
| ----------- | ------------ | -------- | ---------- |
| id | 最新的版本 | int | 版本编号 |
| platform | 平台 | string | chat33pro |
| status | 线上状态 | int | 0历史1线上版本 |
| deviceType | 终端类型 | string | Android/IOS |
| versionName | 版本名 | string | 3.6.8.10 |
| versionCode | 版本code | int | 36810 |
| url | 下载地址 | string | |
| force | 是否强制更新 | bool | false非强制true强制 |
| description | 描述信息 | string array | |
| opeUser | 操作者 | string | |
| updateTime | 更新时间 | int | |
| createTime | 创建时间 | int | |
| size | 包大小 | int | 单位byte |
| md5 | 包的md5 | string | |
```json
{
"result": 0,
"message": "",
"data": {
"id": 8,
"platform": "Chat33Pro",
"status": 1,
"deviceType": "IOS",
"versionName": "1.0.4",
"versionCode": 10400,
"url": "https://xxx",
"force": true,
"description": [
"qqq",
"ww"
],
"opeUser": "root",
"md5": "12345",
"size": 123,
"updateTime": 1621408163504,
"createTime": 1621396088880
}
}
```
#### 创建版本
URL: /backend/version/create
`post`
**请求参数:**
| 参数 | 名字 | 类型 | 约束 | 说明 |
| ----------- | ------------ | ------------ | ---- | ------------------------- |
| platform | 平台 | string | true | chat33pro |
| description | 描述信息 | string array | true | |
| force | 是否强制更新 | bool | true | false非强制true强制 |
| url | 下载地址 | string | true | |
| versionCode | 版本code | int | true | 36810 |
| versionName | 版本名 | string | true | 3.6.8.10 |
| deviceType | 终端类型 | string | true | Android/IOS |
| Authorization | 授权 | string | true | 用于传递token |
| size | 包大小 | int | 单位byte |
| md5 | 包的md5 | string | |
**返回参数:**
| 参数 | 名字 | 类型 | 说明 |
| ----------- | ------------ | ------------ | ------------------------- |
| version | 版本信息 | object | 创建的版本的全部信息 |
| id | 版本编号 | int | |
| platform | 平台 | string | chat33pro |
| status | 线上状态 | int | 0历史1线上版本 |
| deviceType | 终端类型 | string | Android/IOS |
| versionName | 版本名 | string | 3.6.8.10 |
| versionCode | 版本code | int | 36810 |
| url | 下载地址 | string | |
| force | 是否强制更新 | bool | false非强制true强制 |
| description | 描述信息 | string array | |
| opeUser | 操作者 | string | |
| updateTime | 更新时间 | int | |
| createTime | 创建时间 | int | |
| size | 包大小 | int | 单位byte |
| md5 | 包的md5 | string | |
```json
{
"result": 0,
"message": "",
"data": {
"version": {
"id": 3,
"platform": "Chat33Pro",
"status": 0,
"deviceType": "Android",
"versionName": "1.0.1",
"versionCode": 10000,
"url": "https://xxx",
"force": true,
"description": [
"qqq",
"ww"
],
"opeUser": "root",
"md5": "12345",
"size": 123,
"updateTime": 1621394358387,
"createTime": 1621394358387
}
}
}
```
#### 更新版本
URL: /backend/version/update
`put`
**请求参数:**
| 参数 | 名字 | 类型 | 约束 | 说明 |
| ----------- | ------------ | ------------ | ---- | ------------------------- |
| description | 描述信息 | string array | true | |
| force | 是否强制更新 | bool | true | false非强制true强制 |
| url | 下载地址 | string | true | |
| versionCode | 版本code | int | true | 36810 |
| versionName | 版本名 | string | true | 3.6.8.10 |
| id | 版本编号 | int | true | |
| Authorization | 授权 | string | true | 用于传递token |
| size | 包大小 | int | 单位byte |
| md5 | 包的md5 | string | |
**返回参数:**
| 参数 | 名字 | 类型 | 说明 |
| ----------- | ------------ | ------------ | ------------------------- |
| version | 版本信息 | object | 修改后的版本的全部信息 |
| id | 版本编号 | int | |
| platform | 平台 | string | chat33pro |
| status | 线上状态 | int | 0历史1线上版本 |
| deviceType | 终端类型 | string | Android/IOS |
| versionName | 版本名 | string | 3.6.8.10 |
| versionCode | 版本code | int | 36810 |
| url | 下载地址 | string | |
| force | 是否强制更新 | bool | false非强制true强制 |
| description | 描述信息 | string array | |
| opeUser | 操作者 | string | |
| updateTime | 更新时间 | int | |
| createTime | 创建时间 | int | |
| size | 包大小 | int | 单位byte |
| md5 | 包的md5 | string | |
```json
{
"result": 0,
"message": "",
"data": {
"version": {
"id": 3,
"platform": "Chat33Pro",
"status": 0,
"deviceType": "Android",
"versionName": "1.0.0",
"versionCode": 10000,
"url": "https://xxx",
"force": false,
"description": [
"xx",
"yy"
],
"opeUser": "root",
"md5": "234567",
"size": 2345,
"updateTime": 1621395843469,
"createTime": 1621394358387
}
}
}
```
#### 修改版本线上状态
URL: /backend/version/change-status
`put`
**请求参数:**
| 参数 | 名字 | 类型 | 约束 | 说明 |
| ------ | -------- | ------ | ---- | -------------------- |
| id | 版本编号 | int | true | 要修改的版本编号 |
| Authorization | 授权 | string | true | 用于传递token |
**返回参数:**
```json
{
"result": 0,
"message": "",
"data": null
}
```
#### 获取全部版本信息
URL: /backend/version/list
`get`
**请求参数:**
| 参数 | 名字 | 类型 | 约束 | 说明 |
| ---------- | -------- | ------ | ----- | ------------ |
| page | 页码 | int | false | 从0开始不填默认是0 |
| platform | 平台 | string | false | 要筛选的平台 |
| deviceType | 终端类型 | string | false | 要筛选的终端 |
| Authorization | 授权 | string | true | 用于传递token |
**返回参数:**
| 参数 | 名字 | 类型 | 说明 |
| ------------- | -------------- | ------------ | -------------------------- |
| totalElements | 所有的记录条数 | int | |
| totalPages | 总页数 | int | |
| versionList | 版本列表 | object array | 所查询的全部版本的全部信息 |
| id | 版本编号 | int | |
| platform | 平台 | string | chat33pro |
| status | 线上状态 | int | 0历史1线上版本 |
| deviceType | 终端类型 | string | Android/IOS |
| versionName | 版本名 | string | 3.6.8.10 |
| versionCode | 版本code | int | 36810 |
| url | 下载地址 | string | |
| force | 是否强制更新 | bool | false非强制true强制 |
| description | 描述信息 | string array | |
| opeUser | 操作者 | string | |
| updateTime | 更新时间 | int | |
| createTime | 创建时间 | int | |
| size | 包大小 | int | 单位byte |
| md5 | 包的md5 | string | |
```json
{
"result": 0,
"message": "",
"data": {
"totalElements": 5,
"totalPages": 1,
"versionList": [
{
"id": 12,
"platform": "Chat33Pro",
"status": 0,
"deviceType": "IOS",
"versionName": "1.0.0",
"versionCode": 10000,
"url": "https://xxx",
"force": false,
"description": [
"qqq",
"ww"
],
"opeUser": "root",
"md5": "12345",
"size": 123,
"updateTime": 1621396134321,
"createTime": 1621396134321
},
{
"id": 11,
"platform": "Chat33Pro",
"status": 1,
"deviceType": "IOS",
"versionName": "1.0.1",
"versionCode": 10100,
"url": "https://xxx",
"force": true,
"description": [
"qqq",
"ww"
],
"opeUser": "root",
"md5": "12345",
"size": 123,
"updateTime": 1621396320416,
"createTime": 1621396123534
},
{
"id": 10,
"platform": "Chat33Pro",
"status": 0,
"deviceType": "IOS",
"versionName": "1.0.2",
"versionCode": 10200,
"url": "https://xxx",
"force": true,
"description": [
"qqq",
"ww"
],
"opeUser": "root",
"md5": "12345",
"size": 123,
"updateTime": 1621396117028,
"createTime": 1621396117028
},
{
"id": 9,
"platform": "Chat33Pro",
"status": 0,
"deviceType": "IOS",
"versionName": "1.0.3",
"versionCode": 10300,
"url": "https://xxx",
"force": false,
"description": [
"qqq",
"ww"
],
"opeUser": "root",
"md5": "12345",
"size": 123,
"updateTime": 1621396108579,
"createTime": 1621396108579
},
{
"id": 8,
"platform": "Chat33Pro",
"status": 0,
"deviceType": "IOS",
"versionName": "1.0.4",
"versionCode": 10400,
"url": "https://xxx",
"force": true,
"description": [
"qqq",
"ww"
],
"opeUser": "root",
"md5": "12345",
"size": 123,
"updateTime": 1621396320416,
"createTime": 1621396088880
}
]
}
}
```
#### 获取token
URL: /backend/user/login
`get`
**请求参数:**
| 参数 | 名字 | 类型 | 约束 | 说明 |
| -------- | ------ | ------ | ---- | ------------ |
| userName | 用户名 | string | true | 暂时为“root” |
| password | 口令 | string | true | 暂时为“root” |
**返回参数:**
| 参数 | 名字 | 类型 | 说明 |
| ----- | ----- | ------ | ---- |
| userInfo | 用户信息 | object |包含用户名和token |
| userName | 用户名 | string | |
| token | token | string | |
```json
{
"result": 0,
"message": "",
"data": {
"userInfo": {
"userName": "root",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InJvb3QiLCJleHAiOjE2MjA5ODcyMDEsImlzcyI6IkJvYiJ9.w_NoSezjjJLRJMjiU4jiMYozdYvL6NPwv2xuCMepws4"
}
}
}
```

516
doc/backupApi.md Normal file
View File

@@ -0,0 +1,516 @@
# 找回服务
### 手机绑定查询
URL: /backup/phone-query
`get`|`post`
**请求参数:**
| 参数名 | 必选 | 类型 | 说明 |
| ------ | ---- | ------ | ------ |
| area | true | string | 区号 |
| phone | true | string | 手机号 |
**返回参数:**
```json
{
"result": 0,
"message": "操作成功",
"data": {
"exists":false
}
}
```
### 邮箱绑定查询
URL: /backup/email-query
`get`|`post`
**请求参数:**
| 参数名 | 必选 | 类型 | 说明 |
| ------ | ---- | ------ | ---- |
| email | true | string | 邮箱 |
**返回参数:**
```json
{
"result": 0,
"message": "操作成功",
"data": {
"exists":false
}
}
```
### 发送手机验证码
URL: /backup/v2/phone-send
`post`
**请求参数:**
| 参数名 | 必选 | 类型 | 说明 |
| ------ | ----- | ------ | ------ |
| area | false | string | 区号 |
| phone | true | string | 手机号 |
| codeType | true | string | 验证码类型quick->快速登录bind->绑定export->导出通讯录 |
**返回参数:**
```json
{
"result": 0,
"message": "操作成功",
"data": {
}
}
```
### 发送邮箱验证码
URL: /backup/v2/email-send
`post`
**请求参数:**
| 参数名 | 必选 | 类型 | 说明 |
| ------ | ---- | ------ | ---- |
| email | true | string | 邮箱 |
| codeType | true | string | 验证码类型quick->快速登录bind->绑定export->导出通讯录 |
**返回参数:**
```json
{
"result": 0,
"message": "操作成功",
"data": {
}
}
```
### 绑定手机
URL: /backup/v2/phone-binding
`post`|`auth`
type: bind
**请求参数:**
| 参数名 | 必选 | 类型 | 说明 |
| -------- | ----- | ------ | ---------- |
| area | false | string | 区号 |
| phone | true | string | 手机号 |
| code | true | string | 验证码 |
| mnemonic | true | string | 加密助记词 |
**返回参数:**
```json
{
"result": 0,
"message": "操作成功",
"data": {
}
}
```
### 绑定邮箱
URL: /backup/v2/email-binding
`post`|`auth`
type: bind
**请求参数:**
| 参数名 | 必选 | 类型 | 说明 |
| -------- | ---- | ------ | ---------- |
| email | true | string | 手机号 |
| code | true | string | 验证码 |
| mnemonic | true | string | 加密助记词 |
**返回参数:**
```json
{
"result": 0,
"message": "操作成功",
"data": {
}
}
```
### 手机找回私钥
URL: /backup/v2/phone-retrieve
`post`
type: quick
**请求参数:**
| 参数名 | 必选 | 类型 | 说明 |
| ------ | ---- | ------ | ------ |
| area | false | string | 区号 |
| phone | true | string | 手机号 |
| code | true | string | 验证码 |
**返回参数:**
| 参数名 | 必选 | 类型 | 说明 |
| ----------- | ---- | ------ | ---------- |
| address | true | string | 用户地址 |
| area | true | string | 区号 |
| phone | true | string | 手机号 |
| email | true | string | 邮箱 |
| mnemonic | true | string | 加密助记词 |
| update_time | true | string | 更新时间 |
```json
{
"result": 0,
"message": "操作成功",
"data": {
"address": "0xffffffff",
"area":"",
"phone":"",
"email":"",
"mnemonic":"",
"update_time":""
}
}
```
### 邮箱找回私钥
URL: /backup/v2/email-retrieve
`post`
type: quick
**请求参数:**
| 参数名 | 必选 | 类型 | 说明 |
| ------ | ---- | ------ | ------ |
| email | true | string | 邮箱 |
| code | true | string | 验证码 |
**返回参数:**
| 参数名 | 必选 | 类型 | 说明 |
| ----------- | ---- | ------ | ---------- |
| address | true | string | 用户地址 |
| area | true | string | 区号 |
| phone | true | string | 手机号 |
| email | true | string | 邮箱 |
| mnemonic | true | string | 加密助记词 |
| update_time | true | string | 更新时间 |
```json
{
"result": 0,
"message": "操作成功",
"data": {
"address": "0xffffffff",
"area":"",
"phone":"",
"email":"",
"mnemonic":"",
"update_time":""
}
}
```
### 手机导出通讯录验证
URL: /backup/v2/phone-export
`post`
type: export
**请求参数:**
| 参数名 | 必选 | 类型 | 说明 |
| ------ | ---- | ------ | ------ |
| area | false | string | 区号 |
| phone | true | string | 手机号 |
| code | true | string | 验证码 |
| address | true | string | 地址 |
**返回参数:**
ERR:-4010 账号与绑定手机不一致
```json
{
"result": 0,
"message": "操作成功",
"data": {
}
}
```
### 邮箱导出通讯录验证
URL: /backup/v2/email-export
`post`
type: export
**请求参数:**
| 参数名 | 必选 | 类型 | 说明 |
| ------ | ---- | ------ | ------ |
| email | true | string | 邮箱 |
| code | true | string | 验证码 |
| address | true | string | 地址 |
**返回参数:**
ERR:-4011 账号与绑定邮箱不一致
```json
{
"result": 0,
"message": "操作成功",
"data": {
}
}
```
### 修改助记词(修改密聊密码)
URL: /backup/edit-mnemonic
`post`|`auth`
**请求参数:**
| 参数名 | 必选 | 类型 | 说明 |
| -------- | ---- | ------ | ---------- |
| mnemonic | true | string | 加密助记词 |
**返回参数:**
```json
{
"result": 0,
"message": "操作成功",
"data": {
}
}
```
### 发送手机验证码
URL: /backup/phone-send弃用
`post`
**请求参数:**
| 参数名 | 必选 | 类型 | 说明 |
| ------ | ----- | ------ | ------ |
| area | false | string | 区号 |
| phone | true | string | 手机号 |
**返回参数:**
```json
{
"result": 0,
"message": "操作成功",
"data": {
}
}
```
### 发送邮箱验证码
URL: /backup/email-send弃用
`post`
**请求参数:**
| 参数名 | 必选 | 类型 | 说明 |
| ------ | ---- | ------ | ---- |
| email | true | string | 邮箱 |
**返回参数:**
```json
{
"result": 0,
"message": "操作成功",
"data": {
}
}
```
### 绑定手机
URL: /backup/phone-binding弃用
`post`|`auth`
**请求参数:**
| 参数名 | 必选 | 类型 | 说明 |
| -------- | ----- | ------ | ---------- |
| area | false | string | 区号 |
| phone | true | string | 手机号 |
| code | true | string | 验证码 |
| mnemonic | true | string | 加密助记词 |
**返回参数:**
```json
{
"result": 0,
"message": "操作成功",
"data": {
}
}
```
### 绑定邮箱
URL: /backup/email-binding弃用
`post`|`auth`
**请求参数:**
| 参数名 | 必选 | 类型 | 说明 |
| -------- | ---- | ------ | ---------- |
| email | true | string | 手机号 |
| code | true | string | 验证码 |
| mnemonic | true | string | 加密助记词 |
**返回参数:**
```json
{
"result": 0,
"message": "操作成功",
"data": {
}
}
```
### 手机找回私钥
URL: /backup/phone-retrieve弃用
`post`
**请求参数:**
| 参数名 | 必选 | 类型 | 说明 |
| ------ | ---- | ------ | ------ |
| area | true | string | 区号 |
| phone | true | string | 手机号 |
| code | true | string | 验证码 |
**返回参数:**
| 参数名 | 必选 | 类型 | 说明 |
| ----------- | ---- | ------ | ---------- |
| address | true | string | 用户地址 |
| area | true | string | 区号 |
| phone | true | string | 手机号 |
| email | true | string | 邮箱 |
| mnemonic | true | string | 加密助记词 |
| update_time | true | string | 更新时间 |
```json
{
"result": 0,
"message": "操作成功",
"data": {
"address": "0xffffffff",
"area":"",
"phone":"",
"email":"",
"mnemonic":"",
"update_time":""
}
}
```
### 邮箱找回私钥
URL: /backup/email-retrieve弃用
`post`
**请求参数:**
| 参数名 | 必选 | 类型 | 说明 |
| ------ | ---- | ------ | ------ |
| email | true | string | 邮箱 |
| code | true | string | 验证码 |
**返回参数:**
| 参数名 | 必选 | 类型 | 说明 |
| ----------- | ---- | ------ | ---------- |
| address | true | string | 用户地址 |
| area | true | string | 区号 |
| phone | true | string | 手机号 |
| email | true | string | 邮箱 |
| mnemonic | true | string | 加密助记词 |
| update_time | true | string | 更新时间 |
```json
{
"result": 0,
"message": "操作成功",
"data": {
"address": "0xffffffff",
"area":"",
"phone":"",
"email":"",
"mnemonic":"",
"update_time":""
}
}
```
### bty和btc地址映射第一阶段
URL: /backup/transform/addressEnrolment
`post`
**请求参数:**
| 参数名 | 必选 | 类型 | 说明 |
| ------ | ---- | ------ | ------ |
| btcAddress | true | string | |
| ethAddress | true | string | |
**返回参数:**
| 参数名 | 必选 | 类型 | 说明 |
| ----------- | ---- | ------ | ---------- |
```json
{
"result": 0,
"message": "操作成功",
"data": {
}
}
```

42
doc/discoveryApi.md Normal file
View File

@@ -0,0 +1,42 @@
# 发现服务
#### 获取聊天服务列表
URL: /disc/nodes
`post`
**请求参数:**无
**返回参数:**
| 参数名 | 必填 | 类型 | 说明 |
| ------- | ---- | -------- | -------------- |
| servers | true | []object | 聊天服务器列表 |
| name | true | string | 名称 |
| address | true | string | 地址 |
| nodes | true | []object | 合约节点列表 |
| name | true | string | 名称 |
| address | true | string | 地址 |
```json
{
"result": 0,
"message": "操作成功",
"data": {
"servers": [
{
"name": "默认服务器",
"address":"127.0.0.1:8080"
},{
"name": "默认服务器2",
"address":"chat.33.cn"
}
],
"nodes": [
{
"name": "合约节点1",
"address":"127.0.0.1:8080"
}
]
}
}
```

95
doc/gateway.md Normal file
View File

@@ -0,0 +1,95 @@
#### 获取模块启用状态
URL: /app/modules/all
`post`
**请求参数:**
**返回参数:**
| **参数** | **名字** | **类型** | **说明** |
| ----------- | ------------ | -------- | ---------- |
| name | 模块名称 | string | 钱包模块wallet企业模块oa红包模块redpacket |
| isEnabled | 是否启用 | bool | |
| endPoints | 模块访问地址 | string | |
```json
{
"result": 0,
"message": "",
"data": [
{
"name": "wallet",
"isEnabled": true,
"endPoints": [
"https://172.16.101.126:8083"
]
},
{
"name": "oa",
"isEnabled": true,
"endPoints": [
"http://127.0.0.1"
]
}
]
}
```
#### 消息撤回
URL: /app/record/revoke
`post`
**请求参数:**
| **参数** | **名字** | **类型** | **约束** | **说明** |
| ----------- | ------------ | -------- | ---------- |---------- |
| type | 类型 | int | 必填 | 0->撤回私聊消息1->撤回群聊消息 |
| logId | 消息id | int64 | 必填| |
```json
{
"type": 0,
"logId": 0
}
```
**返回参数:**
```json
{
"result": 0,
"message": "",
"data": {}
}
```
#### 关注消息
URL: /app/record/focus
`post` `auth`
**请求参数:**
| **参数** | **名字** | **类型** | **约束** | **说明** |
| ----------- | ------------ | -------- | ---------- |---------- |
| type | 类型 | int | 必填 | 0->关注私聊消息1->关注群聊消息 |
| logId | 消息id | int64 | 必填| |
```json
{
"type": 0,
"logId": 0
}
```
**返回参数:**
```json
{
"result": 0,
"message": "",
"data": {}
}
```

758
doc/group.md Normal file
View File

@@ -0,0 +1,758 @@
# 以最新swagger为准
[http://172.16.101.107:8888/group/swagger/index.html](http://172.16.101.107:8888/group/swagger/index.html)
## 群服务
测试地址 172.16.101.107:8888/group/app
## 已完成接口
| 路由 | 说明 |
| ------------------------------------- | ---------- |
| `POST` URL: /app/create-group | 创建群 |
| `POST` URL:/app /invite-group-members | 邀请新群友 |
| `POST` URL: /app/group-info | 群信息 |
| `POST` URL: /app/group-list | 群列表 |
| `POST` URL: /app/group-member-list | 群成员列表 |
| `POST`URL:/app/group-member-info | 群成员信息 |
| `POST` URL:/app/group-remove | 踢群成员 |
| `POST` URL:/app/group-exit | 退出群 |
| `POST` URL:/app/group-disband | 解散群 |
### 创建群+
`POST` URL: /app/create-group
**Herder**
`FZM-SIGNATURE` = token
**请求参数:**
| 参数名 | 必选 | 类型 | 说明 |
| --------- | ----- | -------- | ---------- |
| name | false | string | 群名称 |
| avatar | false | string | 群头像 url |
| introduce | false | string | 群简介 |
| memberIds | false | []string | 新群员 id |
**请求参数示例**
```json
{
"name": "test-group-1",
"avatar": "",
"memberIds": [
"member-1", "member-2"
]
}
```
**返回参数(data)**
| 参数名 | 类型 | 说明 |
| ---------- | ------------ | ------------------------------------------------------------ |
| id | int | 群id |
| markId | string | 群短 id(暂时没用, 后面可以供搜索加群使用) |
| name | string | 群名称 |
| avatar | string | 群头像 url |
| introduce | string | 群简介 |
| owner | MemberInfo | 群主信息 |
| members | []MemberInfo | 群成员信息list |
| memberNum | int | 群总人数 |
| maximum | int | 群成员人数上限 |
| status | int | 群状态0=正常 1=封禁 2=解散 |
| createTime | int | 群创建时间 |
| joinType | int | 加群方式0=允许任何方式加群1=群成员邀请加群2=群主和管理员邀请加群 |
| muteType | int | 禁言, 0=所有人可以发言, 1=群主和管理员可以发言 |
**MemberInfo 参数类型**
| 参数名 | 类型 | 说明 |
| ---------- | ------ | ------------------------------------------ |
| memberId | string | 用户id |
| memberName | string | 用户群昵称 |
| memberType | int | 用户角色2=群主1=管理员0=群员3=退群 |
**返回参数示例:**
```json
{
"result": 0,
"message": "",
"data": {
"id": 127043701116506112,
"markId": "00351854",
"name": "test-group-1",
"avatar": "",
"introduce": "",
"owner": {
"memberId": "1FKxgaEh5fuSm7a35BfUnKYAmradowpiTR",
"memberName": "",
"memberType": 2
},
"members": [
{
"memberId": "member-1",
"memberName": "",
"memberType": 0
},
{
"memberId": "member-2",
"memberName": "",
"memberType": 0
}
],
"memberNum": 3,
"maximum": 200,
"status": 0,
"createTime": 1621230378707,
"joinType": 0,
"muteType": 0
}
}
```
### 邀请新群友+
`POST` URL: /app/invite-group-members
**Herder**
`FZM-SIGNATURE` = token
**请求参数:**
| 参数名 | 必选 | 类型 | 说明 |
| ------------ | ---- | -------- | ---------- |
| id | true | int64 | 群ID |
| newMemberIds | true | []string | 被邀请人ID |
**请求参数示例**
```json
{
"id": 127043701116506112,
"newMemberIds": [
"member-5",
"member-6"
]
}
```
**返回参数(data)**
| 参数名 | 类型 | 说明 |
| ---------- | ------------ | ----------------- |
| id | int | 群id |
| memberNum | int | 群总人数 |
| inviter | MemberInfo | 邀请人信息 |
| newMembers | []MemberInfo | 被邀请人信息 list |
**MemberInfo 参数类型**
| 参数名 | 类型 | 说明 |
| ---------- | ------ | ------------------------------------------ |
| memberId | string | 用户id |
| memberName | string | 用户群昵称 |
| memberType | int | 用户角色2=群主1=管理员0=群员3=退群 |
**返回参数示例:**
```json
{
"result": 0,
"message": "",
"data": {
"id": 125290793882619904,
"memberNum": 5,
"inviter": {
"memberId": "chenhongyu",
"memberName": "",
"memberType": 0
},
"newMembers": [
{
"memberId": "member-4",
"memberName": "",
"memberType": 2
}
]
}
}
```
### 群信息+
`POST` URL: /app/group-info
**Herder**
`FZM-SIGNATURE` = token
**请求参数:**
| 参数名 | 必选 | 类型 | 说明 |
| ------ | ---- | ---- | ---- |
| id | true | int | 群ID |
**请求参数示例**
```json
{
"id":127082931377147904
}
```
**返回参数(data)**
| 参数名 | 类型 | 说明 |
| ---------- | ------------ | ------------------------------------------------------------ |
| id | int | 群id |
| markId | string | 群短 id(暂时没用, 后面可以供搜索加群使用) |
| name | string | 群名称 |
| avatar | string | 群头像 url |
| introduce | string | 群简介 |
| owner | MemberInfo | 群主信息 |
| person | MemberInfo | 本人信息 |
| members | []MemberInfo | 所有群成员信息list |
| memberNum | int | 群总人数 |
| maximum | int | 群成员人数上限 |
| status | int | 群状态0=正常 1=封禁 2=解散 |
| createTime | int | 群创建时间 |
| joinType | int | 加群方式0=允许任何方式加群1=群成员邀请加群2=群主和管理员邀请加群 |
| muteType | int | 禁言, 0=所有人可以发言, 1=群主和管理员可以发言 |
**MemberInfo 参数类型**
| 参数名 | 类型 | 说明 |
| ---------- | ------ | ------------------------------------------ |
| memberId | string | 用户id |
| memberName | string | 用户群昵称 |
| memberType | int | 用户角色0=群主1=管理员2=群员3=退群 |
**返回参数示例:**
```json
{
"result": 0,
"message": "",
"data": {
"id": 127043701116506112,
"markId": "",
"name": "",
"avatar": "",
"introduce": "",
"owner": {
"memberId": "1FKxgaEh5fuSm7a35BfUnKYAmradowpiTR",
"memberName": "",
"memberType": 2
},
"members": [
{
"memberId": "1FKxgaEh5fuSm7a35BfUnKYAmradowpiTR",
"memberName": "",
"memberType": 2
},
{
"memberId": "member-1",
"memberName": "",
"memberType": 0
},
{
"memberId": "member-2",
"memberName": "",
"memberType": 0
},
{
"memberId": "member-5",
"memberName": "",
"memberType": 0
},
{
"memberId": "member-6",
"memberName": "",
"memberType": 0
}
],
"memberNum": 5,
"maximum": 200,
"status": 0,
"createTime": 1621230378707,
"joinType": 0,
"muteType": 0
}
}
```
### 群列表+
`GET` | `POST` URL: /app/group-list
**Herder**
`FZM-SIGNATURE` = token
**请求参数:**
| 参数名 | 必选 | 类型 | 说明 |
| ------ | ---- | ---- | ---- |
| | | | |
**请求参数示例**
```json
```
**返回参数(data)**
| 参数名 | 类型 | 说明 |
| ------ | ----------- | ----------- |
| groups | []GroupInfo | 群信息 list |
**GroupInfo**
| 参数名 | 类型 | 说明 |
| ---------- | ---------- | ------------------------------------------------------------ |
| id | int | 群id |
| markId | string | 群短 id(暂时没用, 后面可以供搜索加群使用) |
| name | string | 群名称 |
| avatar | string | 群头像 url |
| introduce | string | 群简介 |
| owner | MemberInfo | 群主信息 |
| memberNum | int | 群总人数 |
| maximum | int | 群成员人数上限 |
| status | int | 群状态0=正常 1=封禁 2=解散 |
| createTime | int | 群创建时间 |
| joinType | int | 加群方式0=允许任何方式加群1=群成员邀请加群2=群主和管理员邀请加群 |
| muteType | int | 禁言, 0=所有人可以发言, 1=群主和管理员可以发言 |
**返回参数示例:**
```json
{
"result": 0,
"message": "",
"data": {
"groups": [
{
"id": 127043701116506112,
"markId": "00351854",
"name": "test-group-1",
"avatar": "",
"introduce": "",
"owner": {
"memberId": "1FKxgaEh5fuSm7a35BfUnKYAmradowpiTR",
"memberName": "",
"memberType": 2
},
"memberNum": 5,
"maximum": 200,
"status": 0,
"createTime": 1621230378707,
"joinType": 0,
"muteType": 0
},
{
"id": 127067012814868480,
"markId": "62025607",
"name": "test-group-1",
"avatar": "",
"introduce": "",
"owner": {
"memberId": "1FKxgaEh5fuSm7a35BfUnKYAmradowpiTR",
"memberName": "",
"memberType": 2
},
"memberNum": 5,
"maximum": 200,
"status": 0,
"createTime": 1621235936650,
"joinType": 0,
"muteType": 0
},
{
"id": 127082931377147904,
"markId": "88951481",
"name": "test-group-1",
"avatar": "",
"introduce": "",
"owner": {
"memberId": "1FKxgaEh5fuSm7a35BfUnKYAmradowpiTR",
"memberName": "",
"memberType": 2
},
"memberNum": 11,
"maximum": 200,
"status": 0,
"createTime": 1621239731935,
"joinType": 0,
"muteType": 0
},
{
"id": 127100214044528640,
"markId": "43833969",
"name": "test-group-1",
"avatar": "",
"introduce": "",
"owner": {
"memberId": "1FKxgaEh5fuSm7a35BfUnKYAmradowpiTR",
"memberName": "",
"memberType": 2
},
"memberNum": 3,
"maximum": 200,
"status": 0,
"createTime": 1621243852439,
"joinType": 0,
"muteType": 0
},
{
"id": 127485592593240064,
"markId": "00865442",
"name": "test-group-1",
"avatar": "",
"introduce": "",
"owner": {
"memberId": "1FKxgaEh5fuSm7a35BfUnKYAmradowpiTR",
"memberName": "",
"memberType": 2
},
"memberNum": 9,
"maximum": 200,
"status": 0,
"createTime": 1621335733857,
"joinType": 0,
"muteType": 0
}
]
}
}
```
### 群成员列表+
`POST` URL: /app/group-member-list
**Herder**
`FZM-SIGNATURE` = token
**请求参数:**
| 参数名 | 必选 | 类型 | 说明 |
| ------ | ---- | ----- | ---- |
| id | true | int64 | 群ID |
**请求参数示例**
```json
{
"id": 127082931377147904
}
```
**返回参数(data)**
| 参数名 | 类型 | 说明 |
| ------- | ------------ | --------------- |
| id | int | 群id |
| members | []MemberInfo | 全部群成员 list |
**MemberInfo 参数类型**
| 参数名 | 类型 | 说明 |
| ---------- | ------ | ------------------------------------------ |
| memberId | string | 用户id |
| memberName | string | 用户群昵称 |
| memberType | int | 用户角色0=群主1=管理员2=群员3=退群 |
**返回参数示例:**
```json
{
"result": 0,
"message": "",
"data": {
"id": 127043701116506112,
"members": [
{
"memberId": "1FKxgaEh5fuSm7a35BfUnKYAmradowpiTR",
"memberName": "",
"memberType": 2
},
{
"memberId": "member-1",
"memberName": "",
"memberType": 0
},
{
"memberId": "member-2",
"memberName": "",
"memberType": 0
},
{
"memberId": "member-5",
"memberName": "",
"memberType": 0
},
{
"memberId": "member-6",
"memberName": "",
"memberType": 0
}
]
}
}
```
### 群成员信息+
URL: /app/group-member-info
`GET`
**Herder**
`FZM-SIGNATURE` = token
**请求参数:**
| 参数名 | 必选 | 类型 | 说明 |
| -------- | ---- | ------ | --------- |
| id | true | int | 群ID |
| memberId | true | string | 群成员 ID |
**请求参数示例**
```json
{
"id": 125290793882619904,
"memberId": "123"
}
```
**返回参数(data)**
| 参数名 | 类型 | 说明 |
| ---------- | ---------- | ------ |
| id | int | 群id |
| newMembers | MemberInfo | 群成员 |
**MemberInfo 参数类型**
| 参数名 | 类型 | 说明 |
| ---------- | ------ | ------------------------------------------ |
| memberId | string | 用户id |
| memberName | string | 用户群昵称 |
| memberType | int | 用户角色0=群主1=管理员2=群员3=退群 |
**返回参数示例:**
```json
{
"result": 0,
"message": "",
"data": {
"memberId": "member-1",
"memberName": "",
"memberType": 0
}
}
```
### 踢人+
`POST` URL: /app/group-remove
### 退群+
`POST` URL:/app/group-exit
### 解散群
URL: /disband-group
`PUT`
### 更新群头像
URL: /update-group-avatar
`PUT`
### 更新群名称
URL: /update-group-name
`PUT`
### 更新个人群昵称(自己改自己的, 群主和管理员改所有人)
URL: /update-group-member-name
`PUT`
### 更新群简介
URL: /update-group-introduce
`PUT`
### 更新加群权限设置
URL: /update-group-join-type
`PUT`
### 更新群成员类型
URL: /update-group-member-type
`PUT`
### 转移群给群成员
URL: /update-group-owner
`PUT`
### 更新群状态
URL: /update-group-status
`PUT`
### 更新群成员上限
URL: /update-group-maximum
`PUT`

60
doc/ossApi.md Normal file
View File

@@ -0,0 +1,60 @@
# 对象存储服务
### 获取token
URL: /oss/get-token
`get`|`post`
**请求参数:**
**返回参数:**
```json
{
"result": 0,
"message": "操作成功",
"data": {
"RequestId": "E7A855E3-0A3B-479A-BF22-416A2D5D27B1",
"Credentials": {
"AccessKeySecret": "BxbYXWGjKrVT4qSzi6ZoLtD1qjZLAfNaULreCMb7EsYm",
"Expiration": "2021-02-24T07:48:21Z",
"AccessKeyId": "STS.NUrf45xSfTX7EkJxoo3G7C9Cm",
"SecurityToken": "CAIS8wF1q6Ft5B2yfSjIr5bHLY6BlYxH45rcR037nG86P8gbrPzojzz2IHhNfXlqBeket/g1n2hY5/oelqN1TIVATEiBZNNotm32E/M0Jdivgde8yJBZor/HcDHhJnyW9cvWZPqDP7G5U/yxalfCuzZuyL/hD1uLVECkNpv74vwOLK5gPG+CYCFBGc1dKyZ7tcYeLgGxD/u2NQPwiWeiZygB+CgE0DkhtfTvkp3Ht0OP1wOhk9V4/dqhfsKWCOB3J4p6XtuP2+h7S7HMyiY46WIRrP4v3PYYoG+e4ovGWwkAv0mcUezT6NhmKEpwfrAq7DDiSR5ZYpcagAEcYDFBCF3QY2aQ8Jcs9L4utLb7Z6JdPvyG/zF7l7P1Br0MX0ad6sgWEGWTvTuxemRtmKSWxMEivRzx+6aT515Y7OQdFyX1uLrR1YOq9R2tjysh2d54wu09qr7BhPavADSFHj8fCrZugoul6vHtoslRgL+KSpuI3buhZ08VDxh5rg=="
},
"AssumedRoleUser": {
"AssumedRoleId": "301182410142308944:normal-app",
"Arn": "acs:ram::1264888835193631:role/normal-app/normal-app"
}
}
}
```
### 获取华为云token
URL: /oss/get-huaweiyun-token
`get`|`post`
**请求参数:**
**返回参数:**
```json
{
"result": 0,
"message": "操作成功",
"data": {
"RequestId": "",
"Credentials": {
"AccessKeySecret": "BxbYXWGjKrVT4qSzi6ZoLtD1qjZLAfNaULreCMb7EsYm",
"Expiration": "2021-02-24T07:48:21Z",
"AccessKeyId": "NUrf45xSfTX7EkJxoo3G7C9Cm",
"SecurityToken": "CAIS8wF1q6Ft5B2yfSjIr5bHLY6BlYxH45rcR037nG86P8gbrPzojzz2IHhNfXlqBeket/g1n2hY5/oelqN1TIVATEiBZNNotm32E/M0Jdivgde8yJBZor/HcDHhJnyW9cvWZPqDP7G5U/yxalfCuzZuyL/hD1uLVECkNpv74vwOLK5gPG+CYCFBGc1dKyZ7tcYeLgGxD/u2NQPwiWeiZygB+CgE0DkhtfTvkp3Ht0OP1wOhk9V4/dqhfsKWCOB3J4p6XtuP2+h7S7HMyiY46WIRrP4v3PYYoG+e4ovGWwkAv0mcUezT6NhmKEpwfrAq7DDiSR5ZYpcagAEcYDFBCF3QY2aQ8Jcs9L4utLb7Z6JdPvyG/zF7l7P1Br0MX0ad6sgWEGWTvTuxemRtmKSWxMEivRzx+6aT515Y7OQdFyX1uLrR1YOq9R2tjysh2d54wu09qr7BhPavADSFHj8fCrZugoul6vHtoslRgL+KSpuI3buhZ08VDxh5rg=="
},
"AssumedRoleUser": {
"AssumedRoleId": "",
"Arn": ""
}
}
}
```

248
doc/proto.md Normal file
View File

@@ -0,0 +1,248 @@
# 应用方消息子协议
**协议格式:**
| 参数名 | 必选 | 类型 | 说明 |
| :----- | :--- | :--- | :--- |
|eventType | true | int32 | 事件类型 |
|body | true | byte[] | 消息体 |
## 事件类型
| 类型 | 说明 |
| :----- | :--- |
| 0 | 普通消息 |
| 1 | 消息回复 |
| 2 | 通知信令 |
## 普通消息
**body描述encode:proto序列化**
| 参数名 | 必选 | 类型 | 说明 |
| :----- | :--- | :--- | :--- |
|channelType | true | int32 | 消息通道 |
|logId | true | int64 | 消息id |
|msgId | true | string | 客户端序列 |
|from | true | string | 发送者 |
|target | true | string | 接收者 |
|msgType | true | int32 | 消息类型 |
|msg | true | binary | 消息体 |
|datetime | true | uint64 | 事件戳ms |
|source | true | Source | 来源,详见如下 |
Source结构
```
message Source {
int32 channelType=1;
SourceUser from=2;
SourceUser target=3;
}
message SourceUser {
string id=1;
string name=2;
}
```
### 消息通道
| 类型 | 说明 |
| :----- | :--- |
| 0 | 单聊 |
| 1 | 群聊 |
### 消息类型
| 类型 | 说明 |
| :----- | :--- |
| 0 | 系统消息 |
| 1 | 文本消息 |
| 2 | 音频消息 |
| 3 | 图片消息 |
| 4 | 视频消息 |
| 5 | 文件消息 |
| 6 | 卡片消息 |
| 7 | 通知消息 |
| 8 | 合并转发 |
注意msg 必须可以反序列化为msgType相对应的结构体
具体协议参考 `pkg/proto/api.proto`
```
message TextMsg {
string content = 1;
repeated string mentions = 2;
}
message AudioMsg {
string mediaUrl = 1;
int32 time = 2;
}
message ImageMsg {
string mediaUrl = 1;
int32 height = 2;
int32 width = 3;
}
message VideoMsg {
string mediaUrl = 1;
int32 time = 2;
int32 height = 3;
int32 width = 4;
}
message FileMsg {
string mediaUrl = 1;
string name = 2;
string md5 = 3;
int64 size = 4;
}
message CardMsg {
string bank = 1;
string name = 2;
string account = 3;
}
message NoticeMsg {
AlertType type = 1;
bytes body = 2;
}
message ForwardMsg {
repeated ForwardItem items = 1;
}
message ForwardItem {
string avatar=1;
string name=2;
int32 msgType=3;
bytes msg=4;
uint64 datetime=5;
}
```
#### 通知消息
##### 通知类型
| 类型 | 说明 |
| :----- | :--- |
| 0 | 修改群名 |
| 1 | 加群 |
| 2 | 退群 |
| 3 | 踢群 |
| 4 | 删群 |
| 5 | 群禁言模式更改 |
| 6 | 更改禁言名单 |
注意body必须可以反序列化为AlertType相对应的结构体
具体协议参考 `pkg/proto/api.proto`
```
message AlertUpdateGroupName {
int64 group = 1;
string operator = 2;
string name = 3;
}
message AlertSignInGroup {
int64 group = 1;
string inviter = 2;
repeated string members = 3;
}
message AlertSignOutGroup {
int64 group = 1;
string operator = 2;
}
message AlertKickOutGroup {
int64 group = 1;
string operator = 2;
repeated string members = 3;
}
message AlertDeleteGroup {
int64 group = 1;
string operator = 2;
}
message AlertUpdateGroupMuted {
int64 group = 1;
string operator = 2;
MuteType type = 3;
}
message AlertUpdateGroupMemberMutedTime {
int64 group = 1;
string operator = 2;
repeated string members = 3;
}
```
### Receive_Reply协议格式
| 参数名 | 必选 | 类型 | 说明 |
| :----- | :--- | :--- | :--- |
|eventType | true | int32 | 事件类型 |
|body | true | byte[] | 消息体 |
当eventType为0时body必须可以被反序列化为CommonMsgAck
```
message CommonMsgAck {
int64 logId = 2;
uint64 datetime = 8;
}
```
## 通知信令
**body描述encode:proto序列化**
| 参数名 | 必选 | 类型 | 说明 |
| :----- | :--- | :--- | :--- |
|action | true | ActionType | 信令类型 |
|body | true | binary | 消息体 |
```
//alert msg define
message NotifyMsg {
ActionType action = 1;
bytes body = 2;
}
```
### 信令类型
| 类型 | 说明 |
| :----- | :--- |
| 0 | 送达 |
| 1 | 加群 |
| 2 | 退群 |
| 3 | 删群 |
| 20 | 更新加群权限 |
| 21 | 更新群加好友权限 |
| 22 | 更新群禁言类型 |
| 23 | 更新群成员 |
| 24 | 更新禁言列表 |
| 25 | 更新群名 |
| 26 | 更新群头像 |
注意body必须可反序列化为ActionType对应的结构体。
具体参考 `pkg/proto/api.proto`
```
message ActionReceived {
repeated uint64 logs = 1;
}
message ActionSignInGroup {
repeated string uid = 1;
int64 group = 2;
uint64 time = 3;
}
message ActionSignOutGroup {
repeated string uid = 1;
int64 group = 2;
uint64 time = 3;
}
message ActionDeleteGroup {
int64 group = 1;
uint64 time = 2;
}
```

33
doc/recordApi.md Normal file
View File

@@ -0,0 +1,33 @@
# 找回服务
### 发送消息
URL: /record/push2
`post`
**Request Headers**
- Content-Type: multipart/form-data
**Body:**`form-data`
| 参数名 | 必选 | 类型 | 说明 |
| ------ | ---- | ------ | ------ |
| name | true | string | 区号 |
| filename | true | string | 手机号 |
**请求示例**
```
请求头:
Content-Type=multipart/form-data
请求参数:
name="message"
filename="message"
```
**返回参数:**
```json
{
"result": 0,
"message": "操作成功",
"data": {
}
}
```

11
doc/root.md Normal file
View File

@@ -0,0 +1,11 @@
# 总览
## 约定
- 数据类型`datetime`:时间戳,毫秒(ms)
- 所有 `http` 接口请求都是 `POST` 方法, 数据请求和返回结果 都是 `JSON` 格式
- Header: 接口里需要的
- `FZM-SIGNATURE`(地址):格式[<signature>#<message>#<publicKey>]; signature格式[<datetime>*<random string>]
- `FZM-DEVICE`(设备类型):枚举 iOS、Android、PC
- `FZM-UUID`设备mac
- `FZM-DEVICE-NAME`(设备名称)
- `FZM-VERSION` (应用版本号)
- 返回结果的 `result``0` 代表成功,`data` 里面是具体数据,`result` 不为 `0` 代表失败,`message` 为失败信息

32
gateway/api/READMD.md Normal file
View File

@@ -0,0 +1,32 @@
# Gateway
业务网关层往往又被称作业务聚合层,该层由若干个业务服务构成,负责接收通用网关转发过来的流量。核心作用如下:
### 1. 路由管理
业务网关层的服务负责自己服务的路由表维护。
### 2. 参数校验
业务网关层的服务负责执行与客户端约定的参数校验,验证通过之后再组装成后端微服务需要的数据结构请求到后端。
### 3. 权限校验
各个业务网关层的服务通过底层的用户服务调用来实现权限校验,对于哪些路由需要权限校验,哪些路由不需要,完全由业务网关层的服务自行维护。
### 4. 接口聚合
业务网关层的服务可能需要调用多个后端的微服务来组合实现一个接口,根据自身需求来对下层返回的数据进行聚合和处理。
### 5. 协议转换
业务网关层的服务接收转发过来的HTTP请求并转换为内部的一个或者多个GRPC微服务调用来实现接口逻辑。
### 6. 数据转换
业务网关层的服务的输入和输出数据结构必须是表示层需要的因此它所负责的数据结构和后端GRPC微服务的数据结构会不一样。业务网关层的服务需要负责数据结构的转换和封装处理。
> gateway 里不应该有复杂业务逻辑
>
文档路径 : http://172.16.101.107:8888/swagger/index.html

View File

@@ -0,0 +1,45 @@
版本号`major.minor.patch`具体规则如下:
- major主版本号如有重大版本重构则该字段递增通常各主版本间接口不兼容。
- minor次版本号各次版本号间接口保持兼容如有接口新增或优化则该字段递增。
- patch补丁号如有功能改善或缺陷修复则该字段递增。
## version 0.1.0 2022_01_20
**Feature**
- 增加 `group` 相关 api 接口
## version 0.0.8
**Feature**
- 更新gateway下服务修改signal和noticemsg名称 2021_12_07_17_52_18
## version 0.0.7
**配置文件新增**
所有 `[xxxRPCClient]` 增加 `RegAddrs = "127.0.0.1:2379"` 字段
**Feature**
- 更新 etcdv3.5.0 v0.0.7 2021.11.03
## version 0.0
**Feature**
- 模块开启接口 @v0.0.2
- 撤回消息 @v0.0.4
- 改用 imparse 中的 proto @v0.0.5 2021.10.14
- 新增撤回消息时限配置文件 v0.0.6 2021.10.22
## example x.x.x @yy.mm.dd
**Feature**
**Bug Fixes**
**Improvement**
**Breaking Change**

48
gateway/api/v1/Makefile Normal file
View File

@@ -0,0 +1,48 @@
# golang1.9 or latest
# 1. make help
# 2. make dep
# 3. make build
# ...
VERSION := $(shell echo $(shell cat gateway.go | grep "projectVersion =" | cut -d '=' -f2))
APP_NAME := gateway
BUILD_DIR := build
APP := ${BUILD_DIR}/${APP_NAME}_v${VERSION}
PKG_NAME := ${APP_NAME}_v${VERSION}
PKG := ${PKG_NAME}.tar.gz
main_path = "main"
go_version = $(shell go version | awk '{ print $3 }')
build_time = $(shell date "+%Y-%m-%d %H:%M:%S %Z")
git_commit = $(shell git rev-parse --short=10 HEAD)
flags := -ldflags "-X '${main_path}.goVersion=${go_version}' \
-X '${main_path}.buildTime=${build_time}' \
-X '${main_path}.gitCommit=${git_commit}' \
-X 'google.golang.org/protobuf/reflect/protoregistry.conflictPolicy=warn'"
.PHONY: clean build pkg
swag:
@echo '┌ start gen swag'
@swag init -g internal/handler/routes.go
@echo '└ end gen swag'
clean: ## Remove previous build
@rm -rf ${BUILD_DIR}
@go clean
build: swag #checkgofmt ## Build the binary file
GOOS=linux GOARCH=amd64 GO111MODULE=on GOPROXY=https://goproxy.cn,direct GOSUMDB="sum.golang.google.cn" go build -v $(flags) -o $(APP)
pkg: build ## Package
mkdir -p ${PKG_NAME}/bin
mkdir -p ${PKG_NAME}/etc
cp ${APP} ${PKG_NAME}/bin/
cp etc/* ${PKG_NAME}/etc/
tar zvcf ${PKG} ${PKG_NAME}
rm -rf ${PKG_NAME}
REMOTE_BIN_PATH := /opt/dtalk/srv/app/bin
upload: build
rsync -r $(APP) 107:$(REMOTE_BIN_PATH)
ssh 107 "cd $(REMOTE_BIN_PATH) && chmod 777 $(PKG_NAME) && ln -sf $(PKG_NAME) $(APP_NAME) && supervisorctl restart $(APP_NAME)"

2330
gateway/api/v1/docs/docs.go Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,63 @@
env = "debug"
[server]
addr = "0.0.0.0:19000"
[Trace]
ServiceName = "gateway"
Gen128Bit = true
[Trace.Sampler]
Type = "const"
Param = 1.0
[Trace.Reporter]
LogSpans = true
LocalAgentHostPort = "127.0.0.1:6831"
[AnswerRPCClient]
RegAddrs = "127.0.0.1:2379"
Schema = "dtalk"
SrvName = "answer"
Dial = "1s"
Timeout = "1s"
[StoreRPCClient]
RegAddrs = "127.0.0.1:2379"
Schema = "dtalk"
SrvName = "store"
Dial = "1s"
Timeout = "1s"
[GroupRPCClient]
RegAddrs = "127.0.0.1:2379"
Schema = "dtalk"
SrvName = "group"
Dial = "1s"
Timeout = "1s"
[[modules]]
Name = "wallet"
IsEnabled = true
EndPoints = ["http://172.16.101.87:8901","http://172.16.101.107:8083"] # changeme (1. 红包服务 http 服务地址, 2. 钱包服务 http 服务地址)
[[modules]]
Name = "redPacket"
IsEnabled = true
EndPoints = ["http://172.16.101.87:8901","http://172.16.101.107:8083"] # changeme (1. 红包服务 http 服务地址, 2. 钱包服务 http 服务地址)
[[modules]]
Name = "oa"
IsEnabled = true
EndPoints = ["http://127.0.0.1:20000"] # changeme (oa 服务地址)
[[modules]]
Name = "shop"
IsEnabled = true
EndPoints = ["http://146.56.218.121:12009"] # changeme (链上购服务地址)
[[modules]]
Name="live"
IsEnabled=true
EndPoints=[""]
[Revoke]
Expire = "86400h" #ten years (撤回消息有效时间)

106
gateway/api/v1/gateway.go Normal file
View File

@@ -0,0 +1,106 @@
package main
import (
"context"
"flag"
"fmt"
"os"
"os/signal"
"syscall"
"time"
"github.com/opentracing/opentracing-go"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/config"
http "gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/handler"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/im-pkg/trace"
)
const srvName = "gateway"
var (
// projectVersion 项目版本
projectVersion = "0.1.2"
// goVersion go版本
goVersion = ""
// gitCommit git提交commit id
gitCommit = ""
// buildTime 编译时间
buildTime = ""
isShowVersion = flag.Bool("v", false, "show project version")
)
// showVersion 显示项目版本信息
func showVersion(isShow bool) {
if isShow {
fmt.Printf("Project: %s\n", srvName)
fmt.Printf(" Version: %s\n", projectVersion)
fmt.Printf(" Go Version: %s\n", goVersion)
fmt.Printf(" Git Commit: %s\n", gitCommit)
fmt.Printf(" Build Time: %s\n", buildTime)
os.Exit(0)
}
}
// @Title 聊天网关
// @Version 0.1
// @Description
// @SecurityDefinitions.ApiKey ApiKeyAuth
// @In header
// @Name Authorization
// @BasePath /
func main() {
flag.Parse()
showVersion(*isShowVersion)
if err := config.Init(); err != nil {
panic(err)
}
//log init
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout}).With().Str("service", srvName).Logger()
zerolog.SetGlobalLevel(zerolog.InfoLevel)
log.Info().
Interface("Modules", config.Conf.Modules).
Interface("Server", config.Conf.Server).
Msg("config info")
//trace init
tracer, tracerCloser := trace.Init(srvName, config.Conf.Trace)
//不然后续不会有Jaeger实例
opentracing.SetGlobalTracer(tracer)
// service init
ctx := svc.NewServiceContext(*config.Conf)
httpSrv := http.Init(ctx)
// init signal
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
s := <-c
log.Info().Str("signal", s.String()).Msg("service get a signal")
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := httpSrv.Shutdown(ctx); err != nil {
log.Error().Err(err).Msg("server shutdown")
}
if err := tracerCloser.Close(); err != nil {
log.Error().Err(err).Msg("tracer close failed")
}
time.Sleep(time.Second * 2)
log.Info().Str("name", srvName).Msg("server exit")
return
case syscall.SIGHUP:
// TODO reload
default:
return
}
}
}

View File

@@ -0,0 +1,106 @@
package config
import (
"flag"
"github.com/BurntSushi/toml"
"github.com/uber/jaeger-client-go"
traceConfig "github.com/uber/jaeger-client-go/config"
xtime "gitlab.33.cn/chat/dtalk/pkg/time"
"time"
)
var (
confPath string
Conf *Config
)
func init() {
flag.StringVar(&confPath, "conf", "gateway.toml", "default config path.")
}
// Init init config.
func Init() (err error) {
Conf = Default()
_, err = toml.DecodeFile(confPath, &Conf)
return
}
func Default() *Config {
return &Config{
Env: "debug",
Server: &HttpServer{
Addr: "0.0.0.0:18002",
},
Trace: traceConfig.Configuration{
ServiceName: "gateway",
Gen128Bit: true,
Sampler: &traceConfig.SamplerConfig{
Type: jaeger.SamplerTypeConst,
Param: 1,
},
Reporter: &traceConfig.ReporterConfig{
LogSpans: true,
LocalAgentHostPort: "127.0.0.1:6831",
},
},
Revoke: &Revoke{
Expire: xtime.Duration(time.Hour * 24),
},
AnswerRPCClient: &RPCClient{
RegAddrs: "127.0.0.1:2379",
Schema: "dtalk",
SrvName: "answer",
Dial: xtime.Duration(time.Second),
Timeout: xtime.Duration(time.Second),
},
StoreRPCClient: &RPCClient{
RegAddrs: "127.0.0.1:2379",
Schema: "dtalk",
SrvName: "store",
Dial: xtime.Duration(time.Second),
Timeout: xtime.Duration(time.Second),
},
GroupRPCClient: &RPCClient{
RegAddrs: "127.0.0.1:2379",
Schema: "dtalk",
SrvName: "group",
Dial: xtime.Duration(time.Second),
Timeout: xtime.Duration(time.Second),
},
}
}
type Config struct {
Env string
Server *HttpServer
Trace traceConfig.Configuration
Modules []Module
Revoke *Revoke
AnswerRPCClient *RPCClient
StoreRPCClient *RPCClient
GroupRPCClient *RPCClient
}
type HttpServer struct {
Addr string
}
// RPCClient is RPC client config.
type RPCClient struct {
RegAddrs string // etcd addrs, seperate by ','
Schema string
SrvName string // call
Dial xtime.Duration
Timeout xtime.Duration
}
type Module struct {
Name string `json:"name"` // enums: wallet、oa、redpacket
IsEnabled bool `json:"isEnabled"`
EndPoints []string `json:"endPoints"`
}
type Revoke struct {
Expire xtime.Duration
}

View File

@@ -0,0 +1,27 @@
package account
import (
"github.com/gin-gonic/gin"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/model"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/pkg/api"
)
// AddressLogin
// @Summary 用户登录
// @Description 内部接口,comet层使用
// @Author dld@33.cn
// @Tags account 账户模块
// @Accept json
// @Produce json
// @Param FZM-SIGNATURE header string true "MOCK"
// @Success 200 {object} model.GeneralResponse{data=model.AddressLoginResp}
// @Router /user/login [post]
func AddressLogin(ctx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
c.Set(api.ReqResult, &model.AddressLoginResp{
Address: c.MustGet(api.Address).(string),
})
c.Set(api.ReqError, nil)
}
}

View File

@@ -0,0 +1,45 @@
package group
import (
"github.com/gin-gonic/gin"
logic "gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/logic/group"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
"gitlab.33.cn/chat/dtalk/pkg/api"
xerror "gitlab.33.cn/chat/dtalk/pkg/error"
"gitlab.33.cn/utils/go-kit/convert"
)
// ChangeOwnerHandler
// @Summary 转让群
// @Author chy@33.cn
// @Tags group 群动作
// @Param FZM-SIGNATURE header string true "MOCK"
// @Param data body types.ChangeOwnerReq false "body"
// @Success 200 {object} types.GeneralResp{data=types.ChangeOwnerResp}
// @Router /group/app/change-owner [post]
func ChangeOwnerHandler(ctx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
req := &types.ChangeOwnerReq{}
err := c.ShouldBind(req)
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage(err.Error()))
return
}
if req.Id == 0 && req.IdStr == "" {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError))
return
}
if req.IdStr != "" {
req.Id = convert.ToInt64(req.IdStr)
}
l := logic.NewGroupLogic(c, ctx)
res, err := l.ChangeOwner(req)
c.Set(api.ReqResult, res)
c.Set(api.ReqError, err)
}
}

View File

@@ -0,0 +1,35 @@
package group
import (
"github.com/gin-gonic/gin"
logic "gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/logic/group"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
"gitlab.33.cn/chat/dtalk/pkg/api"
xerror "gitlab.33.cn/chat/dtalk/pkg/error"
)
// CreateGroupHandler
// @Summary 创建群
// @Author chy@33.cn
// @Tags group 群动作
// @Param FZM-SIGNATURE header string true "MOCK"
// @Param data body types.CreateGroupReq false "body"
// @Success 200 {object} types.GeneralResp{data=types.CreateGroupResp}
// @Router /group/app/create-group [post]
func CreateGroupHandler(ctx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
req := &types.CreateGroupReq{}
err := c.ShouldBind(req)
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage(err.Error()))
return
}
l := logic.NewGroupLogic(c, ctx)
res, err := l.CreateGroup(req)
c.Set(api.ReqResult, res)
c.Set(api.ReqError, err)
}
}

View File

@@ -0,0 +1,45 @@
package group
import (
"github.com/gin-gonic/gin"
logic "gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/logic/group"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
"gitlab.33.cn/chat/dtalk/pkg/api"
xerror "gitlab.33.cn/chat/dtalk/pkg/error"
"gitlab.33.cn/utils/go-kit/convert"
)
// GetGroupInfoHandler
// @Summary 查询群信息
// @Author chy@33.cn
// @Tags group 群信息
// @Param FZM-SIGNATURE header string true "MOCK"
// @Param data body types.GetGroupInfoReq false "body"
// @Success 200 {object} types.GeneralResp{data=types.GetGroupInfoResp}
// @Router /group/app/group-info [post]
func GetGroupInfoHandler(ctx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
req := &types.GetGroupInfoReq{}
err := c.ShouldBind(req)
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage(err.Error()))
return
}
if req.Id == 0 && req.IdStr == "" {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError))
return
}
if req.IdStr != "" {
req.Id = convert.ToInt64(req.IdStr)
}
l := logic.NewGroupLogic(c, ctx)
res, err := l.GetPriGroupInfo(req)
c.Set(api.ReqResult, res)
c.Set(api.ReqError, err)
}
}

View File

@@ -0,0 +1,29 @@
package group
import (
"github.com/gin-gonic/gin"
logic "gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/logic/group"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
"gitlab.33.cn/chat/dtalk/pkg/api"
)
// GetGroupListHandler
// @Summary 查询群列表
// @Author chy@33.cn
// @Tags group 群信息
// @Param FZM-SIGNATURE header string true "MOCK"
// @Param data body types.GetGroupListReq false "body"
// @Success 200 {object} types.GeneralResp{data=types.GetGroupListResp}
// @Router /app/group-list [post]
func GetGroupListHandler(ctx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
req := &types.GetGroupListReq{}
l := logic.NewGroupLogic(c, ctx)
res, err := l.GetGroupList(req)
c.Set(api.ReqResult, res)
c.Set(api.ReqError, err)
}
}

View File

@@ -0,0 +1,45 @@
package group
import (
"github.com/gin-gonic/gin"
logic "gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/logic/group"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
"gitlab.33.cn/chat/dtalk/pkg/api"
xerror "gitlab.33.cn/chat/dtalk/pkg/error"
"gitlab.33.cn/utils/go-kit/convert"
)
// GetGroupMemberInfoHandler
// @Summary 查询群成员信息
// @Author chy@33.cn
// @Tags group 群成员信息
// @Param FZM-SIGNATURE header string true "MOCK"
// @Param data body types.GetGroupMemberInfoReq false "body"
// @Success 200 {object} types.GeneralResp{data=types.GetGroupMemberInfoResp}
// @Router /group/app/group-member-info [post]
func GetGroupMemberInfoHandler(ctx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
req := &types.GetGroupMemberInfoReq{}
err := c.ShouldBind(req)
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage(err.Error()))
return
}
if req.Id == 0 && req.IdStr == "" {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError))
return
}
if req.IdStr != "" {
req.Id = convert.ToInt64(req.IdStr)
}
l := logic.NewGroupLogic(c, ctx)
res, err := l.GetGroupMemberInfo(req)
c.Set(api.ReqResult, res)
c.Set(api.ReqError, err)
}
}

View File

@@ -0,0 +1,45 @@
package group
import (
"github.com/gin-gonic/gin"
logic "gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/logic/group"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
"gitlab.33.cn/chat/dtalk/pkg/api"
xerror "gitlab.33.cn/chat/dtalk/pkg/error"
"gitlab.33.cn/utils/go-kit/convert"
)
// GetGroupMemberListHandler
// @Summary 查询群成员列表
// @Author chy@33.cn
// @Tags group 群成员信息
// @Param FZM-SIGNATURE header string true "MOCK"
// @Param data body types.GetGroupMemberListReq false "body"
// @Success 200 {object} types.GeneralResp{data=types.GetGroupMemberListResp}
// @Router /group/app/group-member-list [post]
func GetGroupMemberListHandler(ctx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
req := &types.GetGroupMemberListReq{}
err := c.ShouldBind(req)
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage(err.Error()))
return
}
if req.Id == 0 && req.IdStr == "" {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError))
return
}
if req.IdStr != "" {
req.Id = convert.ToInt64(req.IdStr)
}
l := logic.NewGroupLogic(c, ctx)
res, err := l.GetGroupMemberList(req)
c.Set(api.ReqResult, res)
c.Set(api.ReqError, err)
}
}

View File

@@ -0,0 +1,45 @@
package group
import (
"github.com/gin-gonic/gin"
logic "gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/logic/group"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
"gitlab.33.cn/chat/dtalk/pkg/api"
xerror "gitlab.33.cn/chat/dtalk/pkg/error"
"gitlab.33.cn/utils/go-kit/convert"
)
// GetMuteListHandler
// @Summary 查询群内被禁言成员名单
// @Author chy@33.cn
// @Tags group 禁言
// @Param FZM-SIGNATURE header string true "MOCK"
// @Param data body types.GetMuteListReq false "body"
// @Success 200 {object} types.GeneralResp{data=types.GetMuteListResp}
// @Router /app/mute-list [post]
func GetMuteListHandler(ctx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
req := &types.GetMuteListReq{}
err := c.ShouldBind(req)
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage(err.Error()))
return
}
if req.Id == 0 && req.IdStr == "" {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError))
return
}
if req.IdStr != "" {
req.Id = convert.ToInt64(req.IdStr)
}
l := logic.NewGroupLogic(c, ctx)
res, err := l.GetMuteList(req)
c.Set(api.ReqResult, res)
c.Set(api.ReqError, err)
}
}

View File

@@ -0,0 +1,45 @@
package group
import (
"github.com/gin-gonic/gin"
logic "gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/logic/group"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
"gitlab.33.cn/chat/dtalk/pkg/api"
xerror "gitlab.33.cn/chat/dtalk/pkg/error"
"gitlab.33.cn/utils/go-kit/convert"
)
// GetGroupPubInfoHandler
// @Summary 查询群公开信息
// @Author chy@33.cn
// @Tags group 群信息
// @Param FZM-SIGNATURE header string true "MOCK"
// @Param data body types.GetGroupPubInfoReq false "body"
// @Success 200 {object} types.GeneralResp{data=types.GetGroupPubInfoResp}
// @Router /group/app/group-pub-info [post]
func GetGroupPubInfoHandler(ctx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
req := &types.GetGroupPubInfoReq{}
err := c.ShouldBind(req)
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage(err.Error()))
return
}
if req.Id == 0 && req.IdStr == "" {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError))
return
}
if req.IdStr != "" {
req.Id = convert.ToInt64(req.IdStr)
}
l := logic.NewGroupLogic(c, ctx)
res, err := l.GetPubGroupInfo(req)
c.Set(api.ReqResult, res)
c.Set(api.ReqError, err)
}
}

View File

@@ -0,0 +1,45 @@
package group
import (
"github.com/gin-gonic/gin"
logic "gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/logic/group"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
"gitlab.33.cn/chat/dtalk/pkg/api"
xerror "gitlab.33.cn/chat/dtalk/pkg/error"
"gitlab.33.cn/utils/go-kit/convert"
)
// GroupDisbandHandler
// @Summary 解散群
// @Author chy@33.cn
// @Tags group 群动作
// @Param FZM-SIGNATURE header string true "MOCK"
// @Param data body types.GroupDisbandReq false "body"
// @Success 200 {object} types.GeneralResp{data=types.GroupDisbandResp}
// @Router /group/app/group-disband [post]
func GroupDisbandHandler(ctx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
req := &types.GroupDisbandReq{}
err := c.ShouldBind(req)
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError))
return
}
if req.Id == 0 && req.IdStr == "" {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError))
return
}
if req.IdStr != "" {
req.Id = convert.ToInt64(req.IdStr)
}
l := logic.NewGroupLogic(c, ctx)
res, err := l.GroupDisband(req)
c.Set(api.ReqResult, res)
c.Set(api.ReqError, err)
}
}

View File

@@ -0,0 +1,45 @@
package group
import (
"github.com/gin-gonic/gin"
logic "gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/logic/group"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
"gitlab.33.cn/chat/dtalk/pkg/api"
xerror "gitlab.33.cn/chat/dtalk/pkg/error"
"gitlab.33.cn/utils/go-kit/convert"
)
// GroupExitHandler
// @Summary 退群
// @Author chy@33.cn
// @Tags group 群动作
// @Param FZM-SIGNATURE header string true "MOCK"
// @Param data body types.GroupExitReq false "body"
// @Success 200 {object} types.GeneralResp{data=types.GroupExitResp}
// @Router /group/app/group-exit [post]
func GroupExitHandler(ctx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
req := &types.GroupExitReq{}
err := c.ShouldBind(req)
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError))
return
}
if req.Id == 0 && req.IdStr == "" {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError))
return
}
if req.IdStr != "" {
req.Id = convert.ToInt64(req.IdStr)
}
l := logic.NewGroupLogic(c, ctx)
res, err := l.GroupExit(req)
c.Set(api.ReqResult, res)
c.Set(api.ReqError, err)
}
}

View File

@@ -0,0 +1,45 @@
package group
import (
"github.com/gin-gonic/gin"
logic "gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/logic/group"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
"gitlab.33.cn/chat/dtalk/pkg/api"
xerror "gitlab.33.cn/chat/dtalk/pkg/error"
"gitlab.33.cn/utils/go-kit/convert"
)
// GroupRemoveHandler
// @Summary 踢人
// @Author chy@33.cn
// @Tags group 群动作
// @Param FZM-SIGNATURE header string true "MOCK"
// @Param data body types.GroupRemoveReq false "body"
// @Success 200 {object} types.GeneralResp{data=types.GroupRemoveResp}
// @Router /group/app/group-remove [post]
func GroupRemoveHandler(ctx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
req := &types.GroupRemoveReq{}
err := c.ShouldBind(req)
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError))
return
}
if req.Id == 0 && req.IdStr == "" {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError))
return
}
if req.IdStr != "" {
req.Id = convert.ToInt64(req.IdStr)
}
l := logic.NewGroupLogic(c, ctx)
res, err := l.GroupRemove(req)
c.Set(api.ReqResult, res)
c.Set(api.ReqError, err)
}
}

View File

@@ -0,0 +1,45 @@
package group
import (
"github.com/gin-gonic/gin"
logic "gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/logic/group"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
"gitlab.33.cn/chat/dtalk/pkg/api"
xerror "gitlab.33.cn/chat/dtalk/pkg/error"
"gitlab.33.cn/utils/go-kit/convert"
)
// InviteGroupMembersHandler
// @Summary 邀请新群员
// @Author chy@33.cn
// @Tags group 群动作
// @Param FZM-SIGNATURE header string true "MOCK"
// @Param data body types.InviteGroupMembersReq false "body"
// @Success 200 {object} types.GeneralResp{data=types.InviteGroupMembersResp}
// @Router /group/app/invite-group-members [post]
func InviteGroupMembersHandler(ctx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
req := &types.InviteGroupMembersReq{}
err := c.ShouldBind(req)
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage(err.Error()))
return
}
if req.Id == 0 && req.IdStr == "" {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError))
return
}
if req.IdStr != "" {
req.Id = convert.ToInt64(req.IdStr)
}
l := logic.NewGroupLogic(c, ctx)
res, err := l.InviteGroupMembers(req)
c.Set(api.ReqResult, res)
c.Set(api.ReqError, err)
}
}

View File

@@ -0,0 +1,45 @@
package group
import (
"github.com/gin-gonic/gin"
logic "gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/logic/group"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
"gitlab.33.cn/chat/dtalk/pkg/api"
xerror "gitlab.33.cn/chat/dtalk/pkg/error"
"gitlab.33.cn/utils/go-kit/convert"
)
// JoinGroupHandler
// @Summary 直接进群
// @Author chy@33.cn
// @Tags group 群动作
// @Param FZM-SIGNATURE header string true "MOCK"
// @Param data body types.JoinGroupReq false "body"
// @Success 200 {object} types.GeneralResp{data=types.JoinGroupResp}
// @Router /group/app/join-group [post]
func JoinGroupHandler(ctx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
req := &types.JoinGroupReq{}
err := c.ShouldBind(req)
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage(err.Error()))
return
}
if req.Id == 0 && req.IdStr == "" {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError))
return
}
if req.IdStr != "" {
req.Id = convert.ToInt64(req.IdStr)
}
l := logic.NewGroupLogic(c, ctx)
res, err := l.JoinGroup(req)
c.Set(api.ReqResult, res)
c.Set(api.ReqError, err)
}
}

View File

@@ -0,0 +1,45 @@
package group
import (
"github.com/gin-gonic/gin"
logic "gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/logic/group"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
"gitlab.33.cn/chat/dtalk/pkg/api"
xerror "gitlab.33.cn/chat/dtalk/pkg/error"
"gitlab.33.cn/utils/go-kit/convert"
)
// SetAdminHandler
// @Summary 设置管理员
// @Author chy@33.cn
// @Tags group 群成员信息
// @Param FZM-SIGNATURE header string true "MOCK"
// @Param data body types.SetAdminReq false "body"
// @Success 200 {object} types.GeneralResp{data=types.SetAdminResp}
// @Router /group/app/member/type [post]
func SetAdminHandler(ctx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
req := &types.SetAdminReq{}
err := c.ShouldBind(req)
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage(err.Error()))
return
}
if req.Id == 0 && req.IdStr == "" {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError))
return
}
if req.IdStr != "" {
req.Id = convert.ToInt64(req.IdStr)
}
l := logic.NewGroupLogic(c, ctx)
res, err := l.SetAdmin(req)
c.Set(api.ReqResult, res)
c.Set(api.ReqError, err)
}
}

View File

@@ -0,0 +1,45 @@
package group
import (
"github.com/gin-gonic/gin"
logic "gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/logic/group"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
"gitlab.33.cn/chat/dtalk/pkg/api"
xerror "gitlab.33.cn/chat/dtalk/pkg/error"
"gitlab.33.cn/utils/go-kit/convert"
)
// UpdateGroupAvatarHandler
// @Summary 更新群头像
// @Author chy@33.cn
// @Tags group 群信息
// @Param FZM-SIGNATURE header string true "MOCK"
// @Param data body types.UpdateGroupAvatarReq false "body"
// @Success 200 {object} types.GeneralResp{data=types.UpdateGroupAvatarResp}
// @Router /group/app/avatar [post]
func UpdateGroupAvatarHandler(ctx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
req := &types.UpdateGroupAvatarReq{}
err := c.ShouldBind(req)
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage(err.Error()))
return
}
if req.Id == 0 && req.IdStr == "" {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError))
return
}
if req.IdStr != "" {
req.Id = convert.ToInt64(req.IdStr)
}
l := logic.NewGroupLogic(c, ctx)
res, err := l.UpdateGroupAvatar(req)
c.Set(api.ReqResult, res)
c.Set(api.ReqError, err)
}
}

View File

@@ -0,0 +1,45 @@
package group
import (
"github.com/gin-gonic/gin"
logic "gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/logic/group"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
"gitlab.33.cn/chat/dtalk/pkg/api"
xerror "gitlab.33.cn/chat/dtalk/pkg/error"
"gitlab.33.cn/utils/go-kit/convert"
)
// UpdateGroupFriendTypeHandler
// @Summary 更新群内加好友设置
// @Author chy@33.cn
// @Tags group 群信息
// @Param FZM-SIGNATURE header string true "MOCK"
// @Param data body types.UpdateGroupFriendTypeReq false "body"
// @Success 200 {object} types.GeneralResp{data=types.UpdateGroupFriendTypeResp}
// @Router /group/app/friendType [post]
func UpdateGroupFriendTypeHandler(ctx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
req := &types.UpdateGroupFriendTypeReq{}
err := c.ShouldBind(req)
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage(err.Error()))
return
}
if req.Id == 0 && req.IdStr == "" {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError))
return
}
if req.IdStr != "" {
req.Id = convert.ToInt64(req.IdStr)
}
l := logic.NewGroupLogic(c, ctx)
res, err := l.UpdateGroupFriendType(req)
c.Set(api.ReqResult, res)
c.Set(api.ReqError, err)
}
}

View File

@@ -0,0 +1,45 @@
package group
import (
"github.com/gin-gonic/gin"
logic "gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/logic/group"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
"gitlab.33.cn/chat/dtalk/pkg/api"
xerror "gitlab.33.cn/chat/dtalk/pkg/error"
"gitlab.33.cn/utils/go-kit/convert"
)
// UpdateGroupJoinTypeHandler
// @Summary 更新群内加好友设置
// @Author chy@33.cn
// @Tags group 群信息
// @Param FZM-SIGNATURE header string true "MOCK"
// @Param data body types.UpdateGroupJoinTypeReq false "body"
// @Success 200 {object} types.GeneralResp{data=types.UpdateGroupJoinTypeResp}
// @Router /group/app/joinType [post]
func UpdateGroupJoinTypeHandler(ctx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
req := &types.UpdateGroupJoinTypeReq{}
err := c.ShouldBind(req)
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage(err.Error()))
return
}
if req.Id == 0 && req.IdStr == "" {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError))
return
}
if req.IdStr != "" {
req.Id = convert.ToInt64(req.IdStr)
}
l := logic.NewGroupLogic(c, ctx)
res, err := l.UpdateGroupJoinType(req)
c.Set(api.ReqResult, res)
c.Set(api.ReqError, err)
}
}

View File

@@ -0,0 +1,45 @@
package group
import (
"github.com/gin-gonic/gin"
logic "gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/logic/group"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
"gitlab.33.cn/chat/dtalk/pkg/api"
xerror "gitlab.33.cn/chat/dtalk/pkg/error"
"gitlab.33.cn/utils/go-kit/convert"
)
// UpdateGroupMemberMuteTimeHandler
// @Summary 更新群成员禁言时间
// @Author chy@33.cn
// @Tags group 禁言
// @Param FZM-SIGNATURE header string true "MOCK"
// @Param data body types.UpdateGroupMemberMuteTimeReq false "body"
// @Success 200 {object} types.GeneralResp{data=types.UpdateGroupMemberMuteTimeResp}
// @Router /group/app/member/muteTime [post]
func UpdateGroupMemberMuteTimeHandler(ctx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
req := &types.UpdateGroupMemberMuteTimeReq{}
err := c.ShouldBind(req)
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage(err.Error()))
return
}
if req.Id == 0 && req.IdStr == "" {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError))
return
}
if req.IdStr != "" {
req.Id = convert.ToInt64(req.IdStr)
}
l := logic.NewGroupLogic(c, ctx)
res, err := l.UpdateGroupMemberMuteTime(req)
c.Set(api.ReqResult, res)
c.Set(api.ReqError, err)
}
}

View File

@@ -0,0 +1,45 @@
package group
import (
"github.com/gin-gonic/gin"
logic "gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/logic/group"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
"gitlab.33.cn/chat/dtalk/pkg/api"
xerror "gitlab.33.cn/chat/dtalk/pkg/error"
"gitlab.33.cn/utils/go-kit/convert"
)
// UpdateGroupMemberNameHandler
// @Summary 更新群成员昵称
// @Author chy@33.cn
// @Tags group 群成员信息
// @Param FZM-SIGNATURE header string true "MOCK"
// @Param data body types.UpdateGroupMemberNameReq false "body"
// @Success 200 {object} types.GeneralResp{data=types.UpdateGroupMemberNameResp}
// @Router /group/app/member/name [post]
func UpdateGroupMemberNameHandler(ctx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
req := &types.UpdateGroupMemberNameReq{}
err := c.ShouldBind(req)
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage(err.Error()))
return
}
if req.Id == 0 && req.IdStr == "" {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError))
return
}
if req.IdStr != "" {
req.Id = convert.ToInt64(req.IdStr)
}
l := logic.NewGroupLogic(c, ctx)
res, err := l.UpdateGroupMemberName(req)
c.Set(api.ReqResult, res)
c.Set(api.ReqError, err)
}
}

View File

@@ -0,0 +1,45 @@
package group
import (
"github.com/gin-gonic/gin"
logic "gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/logic/group"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
"gitlab.33.cn/chat/dtalk/pkg/api"
xerror "gitlab.33.cn/chat/dtalk/pkg/error"
"gitlab.33.cn/utils/go-kit/convert"
)
// UpdateGroupMuteTypeHandler
// @Summary 更新群内加好友设置
// @Author chy@33.cn
// @Tags group 群信息
// @Param FZM-SIGNATURE header string true "MOCK"
// @Param data body types.UpdateGroupMuteTypeReq false "body"
// @Success 200 {object} types.GeneralResp{data=types.UpdateGroupMuteTypeResp}
// @Router /group/app/muteType [post]
func UpdateGroupMuteTypeHandler(ctx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
req := &types.UpdateGroupMuteTypeReq{}
err := c.ShouldBind(req)
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage(err.Error()))
return
}
if req.Id == 0 && req.IdStr == "" {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError))
return
}
if req.IdStr != "" {
req.Id = convert.ToInt64(req.IdStr)
}
l := logic.NewGroupLogic(c, ctx)
res, err := l.UpdateGroupMuteType(req)
c.Set(api.ReqResult, res)
c.Set(api.ReqError, err)
}
}

View File

@@ -0,0 +1,45 @@
package group
import (
"github.com/gin-gonic/gin"
logic "gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/logic/group"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
"gitlab.33.cn/chat/dtalk/pkg/api"
xerror "gitlab.33.cn/chat/dtalk/pkg/error"
"gitlab.33.cn/utils/go-kit/convert"
)
// UpdateGroupNameHandler
// @Summary 更新群名称
// @Author chy@33.cn
// @Tags group 群信息
// @Param FZM-SIGNATURE header string true "MOCK"
// @Param data body types.UpdateGroupNameReq false "body"
// @Success 200 {object} types.GeneralResp{data=types.UpdateGroupNameResp}
// @Router /group/app/name [post]
func UpdateGroupNameHandler(ctx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
req := &types.UpdateGroupNameReq{}
err := c.ShouldBind(req)
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage(err.Error()))
return
}
if req.Id == 0 && req.IdStr == "" {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError))
return
}
if req.IdStr != "" {
req.Id = convert.ToInt64(req.IdStr)
}
l := logic.NewGroupLogic(c, ctx)
res, err := l.UpdateGroupName(req)
c.Set(api.ReqResult, res)
c.Set(api.ReqError, err)
}
}

View File

@@ -0,0 +1,34 @@
package modules
import (
"github.com/gin-gonic/gin"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/model"
"gitlab.33.cn/chat/dtalk/pkg/api"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
)
// GetModulesHandler
// @Summary 获取模块启用状态
// @Description
// @Author dld@33.cn
// @Tags startup 初始化模块
// @Accept json
// @Produce json
// @Success 200 {object} model.GeneralResponse{data=[]model.GetModuleResp}
// @Router /app/modules/all [post]
func GetModulesHandler(ctx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
modules := ctx.Config().Modules
var ret = make([]model.GetModuleResp, len(modules))
for i, v := range modules {
ret[i] = model.GetModuleResp{
Name: v.Name,
IsEnabled: v.IsEnabled,
EndPoints: v.EndPoints,
}
}
c.Set(api.ReqResult, ret)
c.Set(api.ReqError, nil)
}
}

View File

@@ -0,0 +1,48 @@
package record
import (
"github.com/gin-gonic/gin"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/logic/record"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/model"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/pkg/api"
xerror "gitlab.33.cn/chat/dtalk/pkg/error"
)
//@Summary 关注消息
//@Description
//@Author dld@33.cn
//@Tags record 消息模块
//@Accept json
//@Produce json
//@Param FZM-SIGNATURE header string true "MOCK"
//@Param data body model.FocusMsgReq true "body"
//@Success 200 {object} model.GeneralResponse{}
//@Router /app/record/focus [post]
func FocusHandler(ctx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
req := &model.FocusMsgReq{}
if err := c.ShouldBind(req); err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage(err.Error()))
return
}
var err error
operator := c.MustGet(api.Address).(string)
l := record.NewLogic(c.Request.Context(), ctx)
switch req.Type {
case model.Private:
err = l.FocusPersonal(operator, req.LogId)
case model.Group:
err = l.FocusGroup(operator, req.LogId)
default:
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage("undefined type"))
return
}
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.CodeInnerError).SetExtMessage(err.Error()))
return
}
c.Set(api.ReqResult, nil)
c.Set(api.ReqError, err)
}
}

View File

@@ -0,0 +1,142 @@
package record
import (
"github.com/gin-gonic/gin"
"github.com/golang/protobuf/proto"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/logic/record"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/pkg/api"
xerror "gitlab.33.cn/chat/dtalk/pkg/error"
comet "gitlab.33.cn/chat/im/api/comet/grpc"
"io/ioutil"
)
// PushToUid
// @Summary 推送消息
// @Description comet.Proto由接口组装客户端只需传入comet.Proto的body部分
// @Author dld@33.cn
// @Tags record 消息模块
// @Accept mpfd
// @Produce json
// @Param FZM-SIGNATURE header string true "MOCK"
// @Param message body string true "消息协议序列化"
// @Success 200 {object} model.GeneralResponse{}
// @Router /record/push [post]
func PushToUid(ctx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
form, err := c.MultipartForm()
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage("MultipartForm"+err.Error()))
return
}
files := form.File["message"]
if len(files) < 1 {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage("file len less than 1"))
return
}
//file, err := c.FormFile("")
file := files[0]
f, err := file.Open()
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage("Open File"+err.Error()))
return
}
defer f.Close()
body, err := ioutil.ReadAll(f)
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage("ReadAll"+err.Error()))
return
}
if len(body) == 0 {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage("error message length 0"))
return
}
p := comet.Proto{
Ver: 1,
Op: int32(comet.Op_SendMsg),
Seq: 0,
Ack: 0,
Body: body,
}
data, err := proto.Marshal(&p)
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.SendMsgFailed).SetExtMessage(err.Error()))
return
}
uid := c.MustGet(api.Address).(string)
l := record.NewLogic(c.Request.Context(), ctx)
mid, createTime, err := l.Push("", uid, data)
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.SendMsgFailed).SetExtMessage(err.Error()))
return
}
ret := map[string]interface{}{
"logId": mid,
"datetime": createTime,
}
c.Set(api.ReqResult, ret)
c.Set(api.ReqError, nil)
}
}
// PushToUid2
// @Summary 推送消息2
// @Description comet.Proto由客户端传入
// @Author dld@33.cn
// @Tags record 消息模块
// @Accept mpfd
// @Produce json
// @Param FZM-SIGNATURE header string true "MOCK"
// @Param message body string true "消息协议序列化"
// @Success 200 {object} model.GeneralResponse{}
// @Router /record/push2 [post]
func PushToUid2(ctx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
form, err := c.MultipartForm()
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage("MultipartForm"+err.Error()))
return
}
files := form.File["message"]
if len(files) < 1 {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage("file len less than 1"))
return
}
//file, err := c.FormFile("")
file := files[0]
f, err := file.Open()
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage("Open File"+err.Error()))
return
}
defer f.Close()
body, err := ioutil.ReadAll(f)
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage("ReadAll"+err.Error()))
return
}
if len(body) == 0 {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage("error message length 0"))
return
}
uid := c.MustGet(api.Address).(string)
l := record.NewLogic(c.Request.Context(), ctx)
mid, createTime, err := l.Push("", uid, body)
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.SendMsgFailed).SetExtMessage(err.Error()))
return
}
ret := map[string]interface{}{
"logId": mid,
"datetime": createTime,
}
c.Set(api.ReqResult, ret)
c.Set(api.ReqError, nil)
}
}

View File

@@ -0,0 +1,46 @@
package record
import (
"github.com/gin-gonic/gin"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/logic/record"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/model"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/pkg/api"
xerror "gitlab.33.cn/chat/dtalk/pkg/error"
)
// GetPriRecords
// @Summary 获得聊天记录
// @Author chy@33.cn
// @Tags record 消息模块
// @Accept json
// @Produce json
// @Param FZM-SIGNATURE header string true "MOCK"
// @Param data body model.GetPriRecordsReq false "body"
// @Success 200 {object} model.GeneralResponse{data=model.GetPriRecordsResp}
// @Router /app/pri-chat-record [post]
func GetPriRecords(ctx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
req := &model.GetPriRecordsReq{}
if err := c.ShouldBind(req); err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage(err.Error()))
return
}
userId, ok := c.Get(api.Address)
if !ok {
c.Set(api.ReqError, xerror.NewError(xerror.SignatureInvalid))
return
}
req.FromId = userId.(string)
if req.Mid == "" {
req.Mid = "999999999999999999"
}
l := record.NewLogic(c.Request.Context(), ctx)
data, err := l.GetPriRecord(req)
c.Set(api.ReqResult, data)
c.Set(api.ReqError, err)
}
}

View File

@@ -0,0 +1,49 @@
package record
import (
"github.com/gin-gonic/gin"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/logic/record"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/model"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/pkg/api"
xerror "gitlab.33.cn/chat/dtalk/pkg/error"
)
// RevokeHandler
// @Summary 撤回消息
// @Description
// @Author dld@33.cn
// @Tags record 消息模块
// @Accept json
// @Produce json
// @Param FZM-SIGNATURE header string true "MOCK"
// @Param data body model.RevokeMsgReq true "body"
// @Success 200 {object} model.GeneralResponse{}
// @Router /app/record/revoke [post]
func RevokeHandler(ctx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
req := &model.RevokeMsgReq{}
if err := c.ShouldBind(req); err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage(err.Error()))
return
}
var err error
operator := c.MustGet(api.Address).(string)
l := record.NewLogic(c.Request.Context(), ctx)
switch req.Type {
case model.Private:
err = l.RevokePersonal(operator, req.Mid)
case model.Group:
err = l.RevokeGroup(operator, req.Mid)
default:
c.Set(api.ReqError, xerror.NewError(xerror.ParamsError).SetExtMessage("undefined type"))
return
}
if err != nil {
c.Set(api.ReqError, xerror.NewError(xerror.CodeInnerError).SetExtMessage(err.Error()))
return
}
c.Set(api.ReqResult, nil)
c.Set(api.ReqError, err)
}
}

View File

@@ -0,0 +1,152 @@
package handler
import (
"net/http"
"github.com/gin-gonic/gin"
zlog "github.com/rs/zerolog/log"
ginSwagger "github.com/swaggo/gin-swagger"
"github.com/swaggo/gin-swagger/swaggerFiles"
_ "gitlab.33.cn/chat/dtalk/gateway/api/v1/docs"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/handler/account"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/handler/group"
modules "gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/handler/modules"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/handler/record"
test "gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/handler/test"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/pkg/api"
"gitlab.33.cn/chat/dtalk/pkg/api/logger"
"gitlab.33.cn/chat/dtalk/pkg/api/trace"
xlog "gitlab.33.cn/chat/dtalk/pkg/logger"
otrace "gitlab.33.cn/chat/im-pkg/trace"
)
var (
serverCtx *svc.ServiceContext
log = zlog.Logger
)
func Init(ctx *svc.ServiceContext) *http.Server {
serverCtx = ctx
addr := serverCtx.Config().Server.Addr
engine := Default()
SetupEngine(engine)
SetupGroupRoutes(engine)
SetupResourceRoutes(engine)
srv := &http.Server{
Addr: addr,
Handler: engine,
}
go func() {
// service connections
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Error().Err(err).Msg("engineInner.Start() err")
panic(err)
}
}()
return srv
}
// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *gin.Engine {
router := gin.New()
// LoggerWithFormatter middleware will write the logs to gin.DefaultWriter
// By default gin.DefaultWriter = os.Stdout
router.Use(gin.LoggerWithFormatter(api.Chat33GinLogFormatter))
router.Use(gin.Recovery())
return router
}
// @title 即时通讯系统后端接口
// @version 1.0
// @description
// @termsOfService
// @contact.name
// @contact.url
// @contact.email
// @schemes https
// @host localhost:8080
// @BasePath /api/v1
func SetupEngine(e *gin.Engine) *gin.Engine {
root := e.Group("/", otrace.OpenTracingServerMiddleWare(), api.RespMiddleWare())
root.POST("/test", test.GetHelloWord(serverCtx))
userRoute := root.Group("/user")
//获取服务器列表
userRoute.Use(api.AuthMiddleWare())
{
userRoute.POST("/login", account.AddressLogin(serverCtx))
}
app := root.Group("/app")
{
modulesRoute := app.Group("/modules")
//获取模块启用状态
modulesRoute.POST("/all", modules.GetModulesHandler(serverCtx))
recordRoute := app.Group("/record")
{
recordRoute.POST("/revoke", api.AuthMiddleWare(), record.RevokeHandler(serverCtx))
recordRoute.POST("/focus", api.AuthMiddleWare(), record.FocusHandler(serverCtx))
}
}
recordRoute := root.Group("/record")
recordRoute.Use(api.AuthMiddleWare())
{
recordRoute.POST("/push", record.PushToUid(serverCtx))
recordRoute.POST("/push2", record.PushToUid2(serverCtx))
}
store := root.Group("/store/app", api.RespMiddleWare())
store.Use(api.AuthMiddleWare())
{
store.POST("/pri-chat-record", record.GetPriRecords(serverCtx))
}
return e
}
func SetupGroupRoutes(e *gin.Engine) *gin.Engine {
logMiddleware := logger.NewMiddleware(xlog.New(serverCtx.Config().Env, "group"))
root := e.Group("/group/app", api.RespMiddleWare())
root.Use(api.AuthMiddleWare(), trace.TraceMiddleware(), logMiddleware.Handle())
{
root.POST("/create-group", group.CreateGroupHandler(serverCtx))
root.POST("/invite-group-members", group.InviteGroupMembersHandler(serverCtx))
root.POST("/group-exit", group.GroupExitHandler(serverCtx))
root.POST("/group-disband", group.GroupDisbandHandler(serverCtx))
root.POST("/group-remove", group.GroupRemoveHandler(serverCtx))
root.POST("/change-owner", group.ChangeOwnerHandler(serverCtx))
root.POST("/join-group", group.JoinGroupHandler(serverCtx))
root.POST("/name", group.UpdateGroupNameHandler(serverCtx))
root.POST("/avatar", group.UpdateGroupAvatarHandler(serverCtx))
root.POST("/joinType", group.UpdateGroupJoinTypeHandler(serverCtx))
root.POST("/friendType", group.UpdateGroupFriendTypeHandler(serverCtx))
root.POST("/muteType", group.UpdateGroupMuteTypeHandler(serverCtx))
root.POST("/member/name", group.UpdateGroupMemberNameHandler(serverCtx))
root.POST("/member/type", group.SetAdminHandler(serverCtx))
root.POST("/member/muteTime", group.UpdateGroupMemberMuteTimeHandler(serverCtx))
root.POST("/group-info", group.GetGroupInfoHandler(serverCtx))
root.POST("/group-pub-info", group.GetGroupPubInfoHandler(serverCtx))
root.POST("/group-list", group.GetGroupListHandler(serverCtx))
root.POST("/group-member-list", group.GetGroupMemberListHandler(serverCtx))
root.POST("/group-member-info", group.GetGroupMemberInfoHandler(serverCtx))
root.POST("/mute-list", group.GetMuteListHandler(serverCtx))
}
return e
}
func SetupResourceRoutes(e *gin.Engine) *gin.Engine {
// swagger 文档接口
if serverCtx.Config().Env == "debug" {
// todo : 单独开一个 swagger 服务
e.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
}
return e
}

View File

@@ -0,0 +1,17 @@
package handler
import (
"github.com/gin-gonic/gin"
logic "gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/logic/test"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/pkg/api"
)
func GetHelloWord(ctx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
l := logic.NewTestLogic(c.Request.Context(), ctx)
resp, err := l.GetHelloWorld()
c.Set(api.ReqResult, resp)
c.Set(api.ReqError, err)
}
}

View File

@@ -0,0 +1,24 @@
package logic
import (
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
pb "gitlab.33.cn/chat/dtalk/service/group/api"
"gitlab.33.cn/utils/go-kit/convert"
)
func (l *GroupLogic) ChangeOwner(req *types.ChangeOwnerReq) (*types.ChangeOwnerResp, error) {
groupId := convert.ToInt64(req.Id)
personId := l.getOpe()
memberId := req.MemberId
_, err := l.svcCtx.GroupClient.ChangeOwner(l.ctx, &pb.ChangeOwnerReq{
GroupId: groupId,
PersonId: personId,
MemberId: memberId,
})
if err != nil {
return nil, err
}
return &types.ChangeOwnerResp{}, nil
}

View File

@@ -0,0 +1,51 @@
package logic
import (
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
pb "gitlab.33.cn/chat/dtalk/service/group/api"
)
func (l *GroupLogic) CreateGroup(req *types.CreateGroupReq) (*types.CreateGroupResp, error) {
name := req.Name
addr := l.getOpe()
owner := &pb.GroupMemberInfo{
Id: addr,
Name: "",
}
members := make([]*pb.GroupMemberInfo, 0, len(req.MemberIds))
for _, memberId := range req.MemberIds {
members = append(members, &pb.GroupMemberInfo{
Id: memberId,
Name: "",
})
}
resp1, err := l.svcCtx.GroupClient.CreateGroup(l.ctx, &pb.CreateGroupReq{
Name: name,
GroupType: pb.GroupType_GROUP_TYPE_NORMAL,
Owner: owner,
Members: members,
})
if err != nil {
return nil, err
}
groupId := resp1.GroupId
resp2, err := l.svcCtx.GroupClient.GetPriGroupInfo(l.ctx, &pb.GetPriGroupInfoReq{
GroupId: groupId,
PersonId: addr,
DisplayNum: int32(1 + len(members)),
})
if err != nil {
return nil, err
}
Group := NewTypesGroupInfo(resp2.Group)
Members := NewTypesGroupMemberInfos(resp2.Group.Members)
return &types.CreateGroupResp{
GroupInfo: Group,
Members: Members,
}, nil
}

View File

@@ -0,0 +1,29 @@
package logic
import (
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
pb "gitlab.33.cn/chat/dtalk/service/group/api"
)
func (l *GroupLogic) GetPriGroupInfo(req *types.GetGroupInfoReq) (*types.GetGroupInfoResp, error) {
if req.DisPlayNum == 0 {
req.DisPlayNum = 10
}
resp, err := l.svcCtx.GroupClient.GetPriGroupInfo(l.ctx, &pb.GetPriGroupInfoReq{
GroupId: req.Id,
PersonId: l.getOpe(),
DisplayNum: int32(req.DisPlayNum),
})
if err != nil {
return nil, err
}
Group := NewTypesGroupInfo(resp.Group)
Members := NewTypesGroupMemberInfos(resp.Group.Members)
return &types.GetGroupInfoResp{
GroupInfo: Group,
Members: Members,
}, nil
}

View File

@@ -0,0 +1,23 @@
package logic
import (
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
pb "gitlab.33.cn/chat/dtalk/service/group/api"
)
func (l *GroupLogic) GetGroupList(req *types.GetGroupListReq) (*types.GetGroupListResp, error) {
personId := l.getOpe()
resp, err := l.svcCtx.GroupClient.GetGroupList(l.ctx, &pb.GetGroupListReq{
PersonId: personId,
})
if err != nil {
return nil, err
}
Groups := NewTypesGroupInfos(resp.Groups)
return &types.GetGroupListResp{
Groups: Groups,
}, nil
}

View File

@@ -0,0 +1,26 @@
package logic
import (
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
pb "gitlab.33.cn/chat/dtalk/service/group/api"
)
func (l *GroupLogic) GetGroupMemberInfo(req *types.GetGroupMemberInfoReq) (*types.GetGroupMemberInfoResp, error) {
groupId := req.Id
memberId := req.MemberId
resp, err := l.svcCtx.GroupClient.GetGroupMemberInfo(l.ctx, &pb.GetGroupMemberInfoReq{
GroupId: groupId,
PersonId: l.getOpe(),
MemberId: memberId,
})
if err != nil {
return nil, err
}
Member := NewTypesGroupMemberInfo(resp.Member)
res := &types.GetGroupMemberInfoResp{}
res.GroupMember = Member
return res, nil
}

View File

@@ -0,0 +1,27 @@
package logic
import (
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
pb "gitlab.33.cn/chat/dtalk/service/group/api"
"gitlab.33.cn/utils/go-kit/convert"
)
func (l *GroupLogic) GetGroupMemberList(req *types.GetGroupMemberListReq) (*types.GetGroupMemberListResp, error) {
groupId := req.Id
resp, err := l.svcCtx.GroupClient.GetGroupMemberList(l.ctx, &pb.GetGroupMemberListReq{
GroupId: groupId,
PersonId: l.getOpe(),
})
if err != nil {
return nil, err
}
Members := NewTypesGroupMemberInfos(resp.Members)
return &types.GetGroupMemberListResp{
Id: groupId,
IdStr: convert.ToString(groupId),
Members: Members,
}, nil
}

View File

@@ -0,0 +1,19 @@
package logic
import (
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
pb "gitlab.33.cn/chat/dtalk/service/group/api"
)
func (l *GroupLogic) GetMuteList(req *types.GetMuteListReq) (*types.GetMuteListResp, error) {
resp, err := l.svcCtx.GroupClient.GetMuteList(l.ctx, &pb.GetMuteListReq{
GroupId: req.Id,
PersonId: l.getOpe(),
})
if err != nil {
return nil, err
}
Members := NewTypesGroupMemberInfos(resp.Members)
return &types.GetMuteListResp{Members: Members}, nil
}

View File

@@ -0,0 +1,21 @@
package logic
import (
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
pb "gitlab.33.cn/chat/dtalk/service/group/api"
)
func (l *GroupLogic) GetPubGroupInfo(req *types.GetGroupPubInfoReq) (*types.GetGroupPubInfoResp, error) {
resp, err := l.svcCtx.GroupClient.GetPubGroupInfo(l.ctx, &pb.GetPubGroupInfoReq{
GroupId: req.Id,
PersonId: l.getOpe(),
})
if err != nil {
return nil, err
}
res := &types.GetGroupPubInfoResp{}
Group := NewTypesGroupInfo(resp.Group)
res.GroupInfo = Group
return res, nil
}

View File

@@ -0,0 +1,18 @@
package logic
import (
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
pb "gitlab.33.cn/chat/dtalk/service/group/api"
)
func (l *GroupLogic) GroupDisband(req *types.GroupDisbandReq) (*types.GroupDisbandResp, error) {
_, err := l.svcCtx.GroupClient.GroupDisband(l.ctx, &pb.GroupDisbandReq{
GroupId: req.Id,
PersonId: l.getOpe(),
})
if err != nil {
return nil, err
}
return &types.GroupDisbandResp{}, nil
}

View File

@@ -0,0 +1,18 @@
package logic
import (
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
pb "gitlab.33.cn/chat/dtalk/service/group/api"
)
func (l *GroupLogic) GroupExit(req *types.GroupExitReq) (*types.GroupExitResp, error) {
_, err := l.svcCtx.GroupClient.GroupExit(l.ctx, &pb.GroupExitReq{
GroupId: req.Id,
PersonId: l.getOpe(),
})
if err != nil {
return nil, err
}
return &types.GroupExitResp{}, nil
}

View File

@@ -0,0 +1,22 @@
package logic
import (
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
pb "gitlab.33.cn/chat/dtalk/service/group/api"
)
func (l *GroupLogic) GroupRemove(req *types.GroupRemoveReq) (*types.GroupRemoveResp, error) {
resp, err := l.svcCtx.GroupClient.GroupRemove(l.ctx, &pb.GroupRemoveReq{
GroupId: req.Id,
PersonId: l.getOpe(),
MemberIds: req.MemberIds,
})
if err != nil {
return nil, err
}
return &types.GroupRemoveResp{
MemberNum: resp.MemberNum,
MemberIds: resp.MemberIds,
}, nil
}

View File

@@ -0,0 +1,25 @@
package logic
import (
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
pb "gitlab.33.cn/chat/dtalk/service/group/api"
"gitlab.33.cn/utils/go-kit/convert"
)
func (l *GroupLogic) InviteGroupMembers(req *types.InviteGroupMembersReq) (*types.InviteGroupMembersResp, error) {
groupId := req.Id
_, err := l.svcCtx.GroupClient.InviteGroupMembers(l.ctx, &pb.InviteGroupMembersReq{
GroupId: groupId,
InviterId: l.getOpe(),
MemberIds: req.NewMemberIds,
})
if err != nil {
return nil, err
}
return &types.InviteGroupMembersResp{
Id: groupId,
IdStr: convert.ToString(groupId),
}, nil
}

View File

@@ -0,0 +1,25 @@
package logic
import (
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
pb "gitlab.33.cn/chat/dtalk/service/group/api"
"gitlab.33.cn/utils/go-kit/convert"
)
func (l *GroupLogic) JoinGroup(req *types.JoinGroupReq) (*types.JoinGroupResp, error) {
groupId := req.Id
_, err := l.svcCtx.GroupClient.InviteGroupMembers(l.ctx, &pb.InviteGroupMembersReq{
GroupId: groupId,
InviterId: req.InviterId,
MemberIds: []string{l.getOpe()},
})
if err != nil {
return nil, err
}
return &types.JoinGroupResp{
Id: groupId,
IdStr: convert.ToString(groupId),
}, nil
}

View File

@@ -0,0 +1,96 @@
package logic
import (
"context"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
"gitlab.33.cn/chat/dtalk/pkg/api"
pb "gitlab.33.cn/chat/dtalk/service/group/api"
"gitlab.33.cn/utils/go-kit/convert"
)
type GroupLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGroupLogic(ctx context.Context, svcCtx *svc.ServiceContext) GroupLogic {
return GroupLogic{
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GroupLogic) getOpe() string {
return api.NewAddrWithContext(l.ctx)
}
func NewTypesGroupInfo(do *pb.GroupBizInfo) *types.GroupInfo {
if do == nil {
return &types.GroupInfo{}
}
res := &types.GroupInfo{
Id: do.Id,
IdStr: convert.ToString(do.Id),
MarkId: do.MarkId,
Name: do.Name,
PublicName: do.PubName,
Avatar: do.Avatar,
Introduce: do.Introduce,
Owner: nil,
Person: nil,
MemberNum: do.MemberNum,
Maximum: do.MemberMaximum,
Status: int32(do.Status),
CreateTime: do.CreateTime,
JoinType: int32(do.JoinType),
MuteType: int32(do.MuteType),
FriendType: int32(do.FriendType),
MuteNum: do.MuteNum,
AdminNum: do.AdminNum,
AESKey: do.AESKey,
GroupType: int32(do.GetType()),
}
if do.Owner != nil {
res.Owner = NewTypesGroupMemberInfo(do.Owner)
}
if do.Person != nil {
res.Person = NewTypesGroupMemberInfo(do.Person)
}
return res
}
func NewTypesGroupInfos(dos []*pb.GroupBizInfo) []*types.GroupInfo {
dtos := make([]*types.GroupInfo, 0, len(dos))
for _, do := range dos {
dtos = append(dtos, NewTypesGroupInfo(do))
}
return dtos
}
func NewTypesGroupMemberInfo(do *pb.GroupMemberBizInfo) *types.GroupMember {
if do == nil {
return &types.GroupMember{}
}
return &types.GroupMember{
MemberId: do.Id,
MemberName: do.Name,
MemberType: int32(do.Type),
MemberMuteTime: do.MuteTime,
}
}
func NewTypesGroupMemberInfos(dos []*pb.GroupMemberBizInfo) []*types.GroupMember {
dtos := make([]*types.GroupMember, 0, len(dos))
for _, do := range dos {
dtos = append(dtos, NewTypesGroupMemberInfo(do))
}
return dtos
}

View File

@@ -0,0 +1,25 @@
package logic
import (
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
xerror "gitlab.33.cn/chat/dtalk/pkg/error"
pb "gitlab.33.cn/chat/dtalk/service/group/api"
)
func (l *GroupLogic) SetAdmin(req *types.SetAdminReq) (*types.SetAdminResp, error) {
if _, ok := pb.GroupMemberType_name[req.MemberType]; !ok {
return nil, xerror.NewError(xerror.ParamsError)
}
_, err := l.svcCtx.GroupClient.SetAdmin(l.ctx, &pb.SetAdminReq{
GroupId: req.Id,
PersonId: l.getOpe(),
MemberId: req.MemberId,
GroupMemberType: pb.GroupMemberType(req.MemberType),
})
if err != nil {
return nil, err
}
return &types.SetAdminResp{}, nil
}

View File

@@ -0,0 +1,19 @@
package logic
import (
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
pb "gitlab.33.cn/chat/dtalk/service/group/api"
)
func (l *GroupLogic) UpdateGroupAvatar(req *types.UpdateGroupAvatarReq) (*types.UpdateGroupAvatarResp, error) {
_, err := l.svcCtx.GroupClient.UpdateGroupAvatar(l.ctx, &pb.UpdateGroupAvatarReq{
GroupId: req.Id,
PersonId: l.getOpe(),
Avatar: req.Avatar,
})
if err != nil {
return nil, err
}
return &types.UpdateGroupAvatarResp{}, nil
}

View File

@@ -0,0 +1,19 @@
package logic
import (
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
pb "gitlab.33.cn/chat/dtalk/service/group/api"
)
func (l *GroupLogic) UpdateGroupFriendType(req *types.UpdateGroupFriendTypeReq) (*types.UpdateGroupFriendTypeResp, error) {
_, err := l.svcCtx.GroupClient.UpdateGroupFriendType(l.ctx, &pb.UpdateGroupFriendTypeReq{
GroupId: req.Id,
PersonId: l.getOpe(),
GroupFriendType: pb.GroupFriendType(req.FriendType),
})
if err != nil {
return nil, err
}
return &types.UpdateGroupFriendTypeResp{}, nil
}

View File

@@ -0,0 +1,19 @@
package logic
import (
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
pb "gitlab.33.cn/chat/dtalk/service/group/api"
)
func (l *GroupLogic) UpdateGroupJoinType(req *types.UpdateGroupJoinTypeReq) (*types.UpdateGroupJoinTypeResp, error) {
_, err := l.svcCtx.GroupClient.UpdateGroupJoinType(l.ctx, &pb.UpdateGroupJoinTypeReq{
GroupId: req.Id,
PersonId: l.getOpe(),
GroupJoinType: pb.GroupJoinType(req.JoinType),
})
if err != nil {
return nil, err
}
return &types.UpdateGroupJoinTypeResp{}, nil
}

View File

@@ -0,0 +1,22 @@
package logic
import (
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
pb "gitlab.33.cn/chat/dtalk/service/group/api"
)
func (l *GroupLogic) UpdateGroupMemberMuteTime(req *types.UpdateGroupMemberMuteTimeReq) (*types.UpdateGroupMemberMuteTimeResp, error) {
resp, err := l.svcCtx.GroupClient.UpdateGroupMemberMuteTime(l.ctx, &pb.UpdateGroupMemberMuteTimeReq{
GroupId: req.Id,
PersonId: l.getOpe(),
MemberIds: req.MemberIds,
MuteTime: req.MuteTime,
})
if err != nil {
return nil, err
}
Members := NewTypesGroupMemberInfos(resp.Members)
return &types.UpdateGroupMemberMuteTimeResp{Members: Members}, nil
}

View File

@@ -0,0 +1,19 @@
package logic
import (
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
pb "gitlab.33.cn/chat/dtalk/service/group/api"
)
func (l *GroupLogic) UpdateGroupMemberName(req *types.UpdateGroupMemberNameReq) (*types.UpdateGroupMemberNameResp, error) {
_, err := l.svcCtx.GroupClient.UpdateGroupMemberName(l.ctx, &pb.UpdateGroupMemberNameReq{
GroupId: req.Id,
PersonId: l.getOpe(),
MemberName: req.MemberName,
})
if err != nil {
return nil, err
}
return &types.UpdateGroupMemberNameResp{}, nil
}

View File

@@ -0,0 +1,19 @@
package logic
import (
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
pb "gitlab.33.cn/chat/dtalk/service/group/api"
)
func (l *GroupLogic) UpdateGroupMuteType(req *types.UpdateGroupMuteTypeReq) (*types.UpdateGroupMuteTypeResp, error) {
_, err := l.svcCtx.GroupClient.UpdateGroupMuteType(l.ctx, &pb.UpdateGroupMuteTypeReq{
GroupId: req.Id,
PersonId: l.getOpe(),
GroupMuteType: pb.GroupMuteType(req.MuteType),
})
if err != nil {
return nil, err
}
return &types.UpdateGroupMuteTypeResp{}, nil
}

View File

@@ -0,0 +1,20 @@
package logic
import (
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
pb "gitlab.33.cn/chat/dtalk/service/group/api"
)
func (l *GroupLogic) UpdateGroupName(req *types.UpdateGroupNameReq) (*types.UpdateGroupNameResp, error) {
_, err := l.svcCtx.GroupClient.UpdateGroupName(l.ctx, &pb.UpdateGroupNameReq{
GroupId: req.Id,
PersonId: l.getOpe(),
Name: req.Name,
PublicName: req.PublicName,
})
if err != nil {
return nil, err
}
return &types.UpdateGroupNameResp{}, nil
}

View File

@@ -0,0 +1,87 @@
package record
import (
"time"
xproto "github.com/golang/protobuf/proto"
"github.com/pkg/errors"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/model"
"gitlab.33.cn/chat/dtalk/pkg/util"
"gitlab.33.cn/chat/dtalk/service/group/model/biz"
"gitlab.33.cn/chat/imparse/proto"
)
func (l *Logic) FocusPersonal(Operator string, logId int64) error {
//查找消息
rd, err := l.svcCtx.StoreClient.GetRecord(l.ctx, proto.Channel_ToUser, logId)
if err != nil {
return err
}
target := rd.ReceiverId
sender := rd.SenderId
if Operator == sender || Operator != target {
return model.ErrPermission
}
now := uint64(util.TimeNowUnixNano() / int64(time.Millisecond))
num, err := l.svcCtx.StoreClient.AddRecordFocus(l.ctx, Operator, logId, now)
if err != nil {
return err
}
action := &proto.SignalFocusMessage{
Mid: logId,
Uid: Operator,
CurrentNum: num,
Time: now,
}
body, err := xproto.Marshal(action)
if err != nil {
return errors.WithMessagef(err, "proto.Marshal, action=%+v", action)
}
err = l.svcCtx.AnswerClient.UniCastSignal(l.ctx, proto.SignalType_FocusMessage, sender, body)
if err != nil {
return err
}
return l.svcCtx.AnswerClient.UniCastSignal(l.ctx, proto.SignalType_FocusMessage, target, body)
}
func (l *Logic) FocusGroup(Operator string, logId int64) error {
//查找消息
rd, err := l.svcCtx.StoreClient.GetRecord(l.ctx, proto.Channel_ToGroup, logId)
if err != nil {
return err
}
target := rd.ReceiverId
sender := rd.SenderId
if Operator == sender {
return model.ErrPermission
}
//群成员判断
memOpt, err := l.svcCtx.GroupClient.GetMember(l.ctx, util.ToInt64(target), Operator)
if err != nil || memOpt == nil {
return err
}
switch memOpt.GroupMemberType {
case biz.GroupMemberTypeOwner:
case biz.GroupMemberTypeAdmin:
case biz.GroupMemberTypeNormal:
default:
return model.ErrPermission
}
now := uint64(util.TimeNowUnixNano() / int64(time.Millisecond))
num, err := l.svcCtx.StoreClient.AddRecordFocus(l.ctx, Operator, logId, now)
if err != nil {
return err
}
action := &proto.SignalFocusMessage{
Mid: logId,
Uid: Operator,
CurrentNum: num,
Time: now,
}
body, err := xproto.Marshal(action)
if err != nil {
return errors.WithMessagef(err, "proto.Marshal, action=%+v", action)
}
return l.svcCtx.AnswerClient.GroupCastSignal(l.ctx, proto.SignalType_FocusMessage, target, body)
}

View File

@@ -0,0 +1,5 @@
package record
func (l *Logic) Push(key, from string, body []byte) (int64, uint64, error) {
return l.svcCtx.AnswerClient.PushCommonMsg(l.ctx, key, from, body)
}

View File

@@ -0,0 +1,47 @@
package record
import (
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/model"
xerror "gitlab.33.cn/chat/dtalk/pkg/error"
"gitlab.33.cn/chat/dtalk/pkg/util"
store "gitlab.33.cn/chat/dtalk/service/record/store/api"
storeModel "gitlab.33.cn/chat/dtalk/service/record/store/model"
"gitlab.33.cn/chat/imparse/proto"
)
func (l *Logic) GetPriRecord(req *model.GetPriRecordsReq) (*model.GetPriRecordsResp, error) {
resp, err := l.svcCtx.StoreClient.GetRecordsAfterMid(l.ctx, &store.GetRecordsAfterMidReq{
Tp: proto.Channel_ToUser,
Mid: util.ToInt64(req.Mid),
From: req.FromId,
Target: req.TargetId,
Count: req.RecordCount,
})
if err != nil {
return nil, xerror.NewError(xerror.QueryFailed).SetExtMessage(err.Error())
}
res := &model.GetPriRecordsResp{
RecordCount: len(resp.Records),
Records: toChatRecord(resp.Records),
}
return res, nil
}
// toChatRecord []MsgContent -> []Record , content json -> proto
func toChatRecord(msgs []*store.GetRecordReply) []*model.Record {
Result := make([]*model.Record, len(msgs))
for i, msg := range msgs {
Result[i] = &model.Record{
Mid: msg.Mid,
Seq: msg.Seq,
FromId: msg.SenderId,
TargetId: msg.ReceiverId,
MsgType: int32(msg.MsgType),
Content: storeModel.JsonUnmarshal(msg.MsgType, []byte(msg.Content)),
CreateTime: msg.CreateTime,
}
}
return Result
}

View File

@@ -0,0 +1,101 @@
package record
import (
"context"
"gitlab.33.cn/chat/dtalk/service/group/model/biz"
"time"
xproto "github.com/golang/protobuf/proto"
"github.com/pkg/errors"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/model"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/pkg/util"
"gitlab.33.cn/chat/imparse/proto"
)
type Logic struct {
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewLogic(ctx context.Context, svcCtx *svc.ServiceContext) Logic {
return Logic{
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *Logic) RevokePersonal(Operator string, mid int64) error {
//查找消息
rd, err := l.svcCtx.StoreClient.GetRecord(l.ctx, proto.Channel_ToUser, mid)
if err != nil {
return err
}
target := rd.ReceiverId
if rd.SenderId != Operator || time.Since(util.UnixToTime(int64(rd.CreateTime))) > time.Duration(l.svcCtx.Config().Revoke.Expire) {
return model.ErrPermission
}
action := &proto.SignalRevoke{
Mid: mid,
Operator: Operator,
Self: rd.SenderId == Operator,
}
body, err := xproto.Marshal(action)
if err != nil {
return errors.WithMessagef(err, "proto.Marshal, action=%+v", action)
}
err = l.svcCtx.AnswerClient.UniCastSignal(l.ctx, proto.SignalType_Revoke, target, body)
if err != nil {
return err
}
if err := l.svcCtx.StoreClient.DelRecord(l.ctx, proto.Channel_ToUser, mid); err != nil {
return err
}
return l.svcCtx.AnswerClient.UniCastSignal(l.ctx, proto.SignalType_Revoke, Operator, body)
}
func (l *Logic) RevokeGroup(Operator string, mid int64) error {
//查找消息
rd, err := l.svcCtx.StoreClient.GetRecord(l.ctx, proto.Channel_ToGroup, mid)
if err != nil {
return err
}
target := rd.ReceiverId
if rd.SenderId == Operator && time.Since(util.UnixToTime(int64(rd.CreateTime))) > time.Duration(l.svcCtx.Config().Revoke.Expire) {
return model.ErrPermission
}
if rd.SenderId != Operator {
//执行者
memOpt, err := l.svcCtx.GroupClient.GetMember(l.ctx, util.ToInt64(target), Operator)
if err != nil || memOpt == nil {
return err
}
//消息所有者
memOwn, err := l.svcCtx.GroupClient.GetMember(l.ctx, util.ToInt64(target), rd.SenderId)
if err != nil || memOwn == nil {
return err
}
switch memOpt.GroupMemberType {
case biz.GroupMemberTypeOwner:
case biz.GroupMemberTypeAdmin:
if memOwn.GroupMemberType == biz.GroupMemberTypeOwner {
return model.ErrPermission
}
default:
return model.ErrPermission
}
}
action := &proto.SignalRevoke{
Mid: mid,
Operator: Operator,
Self: rd.SenderId == Operator,
}
body, err := xproto.Marshal(action)
if err != nil {
return errors.WithMessagef(err, "proto.Marshal, action=%+v", action)
}
if err := l.svcCtx.StoreClient.DelRecord(l.ctx, proto.Channel_ToGroup, mid); err != nil {
return err
}
return l.svcCtx.AnswerClient.GroupCastSignal(l.ctx, proto.SignalType_Revoke, target, body)
}

View File

@@ -0,0 +1,24 @@
package logic
import (
"context"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/svc"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/types"
)
type TestLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewTestLogic(ctx context.Context, svcCtx *svc.ServiceContext) TestLogic {
return TestLogic{
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *TestLogic) GetHelloWorld() (*types.Hello, error) {
return &types.Hello{Content: "hello world"}, nil
}

View File

@@ -0,0 +1,7 @@
package model
// 用户地址登录请求结果
type AddressLoginResp struct {
// 用户地址
Address string `json:"address" example:"123"`
}

View File

@@ -0,0 +1,6 @@
package model
const (
Private = 0
Group = 1
)

View File

@@ -0,0 +1,7 @@
package model
import "errors"
var (
ErrPermission = errors.New("权限不足")
)

View File

@@ -0,0 +1,7 @@
package model
type GeneralResponse struct {
Result int `json:"result"`
Message string `json:"message"`
Data interface{} `json:"data"`
}

View File

@@ -0,0 +1,7 @@
package model
type GetModuleResp struct {
Name string `json:"name" enums:"wallet,oa,redpacket"`
IsEnabled bool `json:"isEnabled"`
EndPoints []string `json:"endPoints"`
}

View File

@@ -0,0 +1,50 @@
package model
type RevokeMsgReq struct {
Type int `json:"type" enums:"0,1"`
Mid int64 `json:"logId" binding:"required"`
}
type FocusMsgReq struct {
Type int `json:"type" enums:"0,1"`
LogId int64 `json:"logId" binding:"required"`
}
type Record struct {
// log id
Mid string `json:"logId"`
// msg id (uuid)
Seq string `json:"msgId"`
// 发送者 id
FromId string `json:"fromId"`
// 接收者 id
TargetId string `json:"targetId"`
// 消息类型
MsgType int32 `json:"msgType"`
// 消息内容
Content interface{} `json:"content"`
// 消息发送时间
CreateTime uint64 `json:"createTime"`
}
type GetPriRecordsReq struct {
// 发送者 ID
FromId string `json:"-"`
// 发送者 ID
//FromId string `json:"fromId" binding:"required"`
// 接受者 ID
TargetId string `json:"targetId" binding:"required"`
// 消息数量
RecordCount int64 `json:"count" binding:"required,min=1,max=100"`
// 消息 ID
Mid string `json:"logId"`
}
type GetPriRecordsResp struct {
// 聊天记录数量
RecordCount int `json:"record_count"`
// 聊天记录
Records []*Record `json:"records"`
}

View File

@@ -0,0 +1,48 @@
package svc
import (
xerror "gitlab.33.cn/chat/dtalk/pkg/error"
"gitlab.33.cn/chat/dtalk/pkg/interceptor/trace"
group "gitlab.33.cn/chat/dtalk/service/group/api"
store "gitlab.33.cn/chat/dtalk/service/record/store/api"
"google.golang.org/grpc"
"sync"
"time"
"gitlab.33.cn/chat/dtalk/gateway/api/v1/internal/config"
answer "gitlab.33.cn/chat/dtalk/service/record/answer/api"
)
// ServiceContext 服务上下文
type ServiceContext struct {
m sync.RWMutex
c config.Config
AnswerClient *answer.Client
StoreClient *store.Client
GroupClient *group.Client
}
func NewServiceContext(c config.Config) *ServiceContext {
sc := &ServiceContext{
c: c,
AnswerClient: answer.New(c.AnswerRPCClient.RegAddrs, c.AnswerRPCClient.Schema, c.AnswerRPCClient.SrvName, time.Duration(c.AnswerRPCClient.Dial)),
StoreClient: store.New(c.StoreRPCClient.RegAddrs, c.StoreRPCClient.Schema, c.StoreRPCClient.SrvName, time.Duration(c.StoreRPCClient.Dial)),
GroupClient: group.New(c.GroupRPCClient.RegAddrs,
c.GroupRPCClient.Schema,
c.GroupRPCClient.SrvName,
time.Duration(c.GroupRPCClient.Dial),
grpc.WithChainUnaryInterceptor(xerror.ErrClientInterceptor, trace.UnaryClientInterceptor),
),
}
return sc
}
// Config 获取全局配置
func (sc *ServiceContext) Config() config.Config {
sc.m.RLock()
defer sc.m.RUnlock()
return sc.c
}

View File

@@ -0,0 +1,431 @@
package types
type GeneralResp struct {
Result int `json:"result"`
Message int `json:"message"`
Data interface{} `json:"data"`
}
type GroupMember struct {
// 用户 ID
MemberId string `json:"memberId" form:"memberId"`
// 用户群昵称
MemberName string `json:"memberName" form:"memberName"`
// 用户角色2=群主1=管理员0=群员10=退群
MemberType int32 `json:"memberType" form:"memberType"`
// 该用户被禁言结束的时间 9223372036854775807=永久禁言
MemberMuteTime int64 `json:"memberMuteTime"`
}
type GroupInfo struct {
// 群 ID
Id int64 `json:"id" form:"id"`
IdStr string `json:"idStr"`
// 群显示的 ID
MarkId string `json:"markId" form:"markId"`
// 群名称 加密的
Name string `json:"name" form:"name"`
// 公开的群名称 不加密的
PublicName string `json:"publicName"`
// 头像 url
Avatar string `json:"avatar" form:"avatar"`
Introduce string `json:"introduce" form:"introduce"`
// 群主 信息
Owner *GroupMember `json:"owner" form:"owner"`
// 本人在群内的信息
Person *GroupMember `json:"person" form:"person"`
// 群人数
MemberNum int32 `json:"memberNum" form:"memberNum"`
// 群人数上限
Maximum int32 `json:"maximum" form:"maximum"`
// 群状态0=正常 1=封禁 2=解散
Status int32 `json:"status" form:"status"`
// 群创建时间
CreateTime int64 `json:"createTime" form:"createTime"`
// 加群方式0=无需审批默认1=禁止加群,群主和管理员邀请加群, 2=普通人邀请需要审批,群主和管理员直接加群
JoinType int32 `json:"joinType" form:"joinType"`
// 禁言, 0=全员可发言, 1=全员禁言(除群主和管理员)
MuteType int32 `json:"muteType" form:"muteType"`
// 加好友限制, 0=群内可加好友1=群内禁止加好友
FriendType int32 `json:"friendType"`
// 群内当前被禁言的人数
MuteNum int32 `json:"muteNum"`
// 群内管理员数量
AdminNum int32 `json:"adminNum"`
//
AESKey string `json:"key"`
// 群类型 (0: 普通群, 1: 全员群, 2: 部门群)
GroupType int32 `json:"groupType"`
}
type CreateGroupReq struct {
Name string `json:"name" form:"name"`
Avatar string `json:"avatar" form:"avatar"`
Introduce string `json:"introduce" form:"introduce"`
MemberIds []string `json:"memberIds" form:"memberIds"`
}
type CreateGroupResp struct {
*GroupInfo
// 群成员
Members []*GroupMember `json:"members" form:"members"`
}
type InviteGroupMembersReq struct {
Id int64 `json:"id" form:"id"`
// 如果同时填了 idStr, 则优先选择 idStr
IdStr string `json:"idStr"`
Inviter GroupMember `json:"-"`
NewMembers []GroupMember `json:"-"`
NewMemberIds []string `json:"newMemberIds" form:"newMemberIds" binding:"required"`
}
type InviteGroupMembersResp struct {
Id int64 `json:"id" form:"id" example:"123821199217135616"`
IdStr string `json:"idStr"`
MemberNum int32 `json:"memberNum" form:"memberNum" example:"5"`
//Inviter GroupMember `json:"inviter" form:"inviter"`
//NewMembers []GroupMember `json:"newMembers" form:"newMembers"`
}
type GetGroupInfoReq struct {
Id int64 `json:"id" uri:"id"`
// 如果同时填了 idStr, 则优先选择 idStr
IdStr string `json:"idStr"`
PersonId string `json:"-"`
DisPlayNum int64 `json:"-"`
}
type GetGroupInfoResp struct {
*GroupInfo
Members []*GroupMember `json:"members" form:"members"`
}
type GetGroupListReq struct {
PersonId string `json:"-"`
}
type GetGroupListResp struct {
Groups []*GroupInfo `json:"groups"`
}
type GetGroupMemberListReq struct {
Id int64 `json:"id" uri:"id"`
// 如果同时填了 idStr, 则优先选择 idStr
IdStr string `json:"idStr"`
PersonId string `json:"-"`
}
type GetGroupMemberListResp struct {
Id int64 `json:"id"`
// 如果同时填了 idStr, 则优先选择 idStr
IdStr string `json:"idStr"`
Members []*GroupMember `json:"members"`
}
type GetGroupMemberInfoReq struct {
Id int64 `json:"id" uri:"id"`
// 如果同时填了 idStr, 则优先选择 idStr
IdStr string `json:"idStr"`
MemberId string `json:"memberId" uri:"memberId" binding:"required"`
PersonId string `json:"-"`
}
type GetGroupMemberInfoResp struct {
*GroupMember
}
type GroupExitReq struct {
Id int64 `json:"id"`
// 如果同时填了 idStr, 则优先选择 idStr
IdStr string `json:"idStr"`
PersonId string `json:"-"`
}
type GroupExitResp struct {
}
type GroupRemoveReq struct {
Id int64 `json:"id"`
// 如果同时填了 idStr, 则优先选择 idStr
IdStr string `json:"idStr"`
MemberIds []string `json:"memberIds" binding:"required"`
PersonId string `json:"-"`
}
type GroupRemoveResp struct {
// 群人数
MemberNum int32 `json:"memberNum" form:"memberNum"`
// 成功被踢的成员列表
MemberIds []string `json:"memberIds"`
}
type GroupDisbandReq struct {
Id int64 `json:"id"`
// 如果同时填了 idStr, 则优先选择 idStr
IdStr string `json:"idStr"`
PersonId string `json:"-"`
}
type GroupDisbandResp struct {
}
type UpdateGroupNameReq struct {
Id int64 `json:"id"`
// 如果同时填了 idStr, 则优先选择 idStr
IdStr string `json:"idStr"`
PersonId string `json:"-"`
Name string `json:"name"`
PublicName string `json:"publicName"`
}
type UpdateGroupNameResp struct {
}
type UpdateGroupAvatarReq struct {
Id int64 `json:"id"`
// 如果同时填了 idStr, 则优先选择 idStr
IdStr string `json:"idStr"`
PersonId string `json:"-"`
Avatar string `json:"avatar"`
}
type UpdateGroupAvatarResp struct {
}
type UpdateGroupMemberNameReq struct {
Id int64 `json:"id"`
// 如果同时填了 idStr, 则优先选择 idStr
IdStr string `json:"idStr"`
PersonId string `json:"-"`
MemberName string `json:"memberName"`
}
type UpdateGroupMemberNameResp struct {
}
type UpdateGroupJoinTypeReq struct {
// 群 ID
Id int64 `json:"id"`
// 如果同时填了 idStr, 则优先选择 idStr
IdStr string `json:"idStr"`
PersonId string `json:"-"`
// 加群方式0=无需审批默认1=禁止加群,群主和管理员邀请加群, 2=普通人邀请需要审批,群主和管理员直接加群
JoinType int32 `json:"joinType" binding:"oneof=0 1 2"`
}
type UpdateGroupJoinTypeResp struct {
}
type UpdateGroupFriendTypeReq struct {
// 群 ID
Id int64 `json:"id"`
// 如果同时填了 idStr, 则优先选择 idStr
IdStr string `json:"idStr"`
PersonId string `json:"-"`
// 加好友限制, 0=群内可加好友1=群内禁止加好友
FriendType int32 `json:"friendType" binding:"oneof=0 1"`
}
type UpdateGroupFriendTypeResp struct {
}
type ChangeOwnerReq struct {
// 群 ID
Id int64 `json:"id"`
// 如果同时填了 idStr, 则优先选择 idStr
IdStr string `json:"idStr"`
// 被转让为群主的群成员 ID
MemberId string `json:"memberId" binding:"required"`
PersonId string `json:"-"`
}
type ChangeOwnerResp struct {
}
// JoinGroupReq 扫二维码加群
type JoinGroupReq struct {
Id int64 `json:"id"`
IdStr string `json:"idStr"`
InviterId string `json:"inviterId"`
PersonId string `json:"-"`
}
type JoinGroupResp struct {
Id int64 `json:"id"`
IdStr string `json:"idStr"`
}
type SetAdminReq struct {
// 群 ID
Id int64 `json:"id"`
// 如果同时填了 idStr, 则优先选择 idStr
IdStr string `json:"idStr"`
// 被设置的群成员 ID
MemberId string `json:"memberId" binding:"required"`
PersonId string `json:"-"`
// 用户角色 0=群员, 1=管理员
MemberType int32 `json:"memberType" binding:"oneof=0 1"`
}
type SetAdminResp struct {
}
type UpdateGroupMuteTypeReq struct {
// 群 ID
Id int64 `json:"id"`
// 如果同时填了 idStr, 则优先选择 idStr
IdStr string `json:"idStr"`
// 禁言, 0=全员可发言, 1=全员禁言(除群主和管理员)
MuteType int32 `json:"muteType" binding:"oneof=0 1"`
PersonId string `json:"-"`
}
type UpdateGroupMuteTypeResp struct {
}
type UpdateGroupMemberMuteTimeReq struct {
// 群 ID
Id int64 `json:"id"`
// 如果同时填了 idStr, 则优先选择 idStr
IdStr string `json:"idStr"`
// 被禁言的群员 ID
MemberIds []string `json:"memberIds" binding:"required"`
// 禁言持续时间, 传9223372036854775807=永久禁言, 0=解除禁言
MuteTime int64 `json:"muteTime"`
PersonId string `json:"-"`
}
type UpdateGroupMemberMuteTimeResp struct {
Members []*GroupMember `json:"members"`
}
type GetMuteListReq struct {
// 群 ID
Id int64 `json:"id"`
// 如果同时填了 idStr, 则优先选择 idStr
IdStr string `json:"idStr"`
PersonId string `json:"-"`
}
type GetMuteListResp struct {
Members []*GroupMember `json:"members"`
}
type GetGroupPubInfoReq struct {
// 群 ID
Id int64 `json:"id"`
// 如果同时填了 idStr, 则优先选择 idStr
IdStr string `json:"idStr"`
PersonId string `json:"-"`
}
type GetGroupPubInfoResp struct {
*GroupInfo
}
type GetGroupInfoByConditionReq struct {
// 查询方法 0:groupMarkId, 1:groupId
Tp int32 `json:"tp" binding:"oneof=0 1"`
Query string `json:"query" binding:"required"`
PersonId string `json:"-"`
}
type GetGroupInfoByConditionResp struct {
Groups []*GroupInfo `json:"groups"`
}
// ---群审批------------------
// GroupApplyInfo 群审批信息
type GroupApplyInfo struct {
// 审批 ID
ApplyId string `json:"applyId,omitempty"`
// 群 ID
GroupId string `json:"id,omitempty"`
// 邀请人 ID, 空表示是自己主动申请的
InviterId string `json:"inviterId,omitempty"`
// 申请加入人 ID
MemberId string `json:"memberId,omitempty"`
// 申请备注
ApplyNote string `json:"applyNote,omitempty"`
// 审批人 ID
OperatorId string `json:"operatorId,omitempty"`
// 审批情况 0=待审批, 1=审批通过, 2=审批不通过, 10=审批忽略
ApplyStatus int32 `json:"applyStatus,omitempty"`
// 拒绝原因
RejectReason string `json:"rejectReason,omitempty"`
// 创建时间 ms
CreateTime int64 `json:"createTime,omitempty"`
// 修改时间 ms
UpdateTime int64 `json:"updateTime,omitempty"`
}
// CreateGroupApplyReq 创建群审批请求
type CreateGroupApplyReq struct {
// 群 ID
Id string `json:"id,omitempty" binding:"required"`
// 申请备注
ApplyNote string `json:"applyNote,omitempty"`
PersonId string `json:"-"`
}
// CreateGroupApplyResp 创建群审批响应
type CreateGroupApplyResp struct {
}
// GetGroupApplyByIdReq 查询群审批请求
type GetGroupApplyByIdReq struct {
// 审批 ID
ApplyId string `json:"applyId" binding:"required"`
// 群 ID
Id string `json:"id" binding:"required"`
PersonId string `json:"-"`
}
// GetGroupApplysReq 查询群审批列表请求
type GetGroupApplysReq struct {
// 群 ID
Id string `json:"id" binding:"required"`
// 每页记录数
Count int32 `json:"count" binding:"required"`
// 当前审批记录数量
Offset int32 `json:"offset"`
PersonId string `json:"-"`
}
// GetGroupApplysResp 查询群审批响应
type GetGroupApplysResp struct {
GroupApplys []*GroupApplyInfo `json:"applys"`
}
// AcceptGroupApplyReq 接受加群审批请求
type AcceptGroupApplyReq struct {
// 审批 ID
ApplyId string `json:"applyId" binding:"required"`
// 群 ID
Id string `json:"id" binding:"required"`
PersonId string `json:"-"`
}
type AcceptGroupApplyResp struct {
}
// RejectGroupApplyReq 拒绝加群审批请求
type RejectGroupApplyReq struct {
// 审批 ID
ApplyId string `json:"applyId" binding:"required"`
// 群 ID
Id string `json:"id" binding:"required"`
// 拒绝原因
RejectReason string `json:"rejectReason"`
PersonId string `json:"-"`
}
type RejectGroupApplyResp struct {
}

View File

@@ -0,0 +1,6 @@
// Code generated by goctl. DO NOT EDIT.
package types
type Hello struct {
Content string `json:"content"`
}

16
gateway/api/v1/version.go Normal file
View File

@@ -0,0 +1,16 @@
package main
//var (
// // The full version string
// Version = "0.0.7"
//
// // GitCommit is set with --ldflags "-X main.gitCommit=$(git rev-parse --short=8 HEAD)"
// GitCommit string
//)
//
//func GetVersion() string {
// if GitCommit != "" {
// return Version + "-" + GitCommit
// }
// return Version
//}

64
go.mod Normal file
View File

@@ -0,0 +1,64 @@
module gitlab.33.cn/chat/dtalk
go 1.15
require (
github.com/33cn/chain33 v1.65.3
github.com/BurntSushi/toml v0.3.1
github.com/Shopify/sarama v1.19.0
github.com/Terry-Mao/goim v0.0.0-20210523140626-e742c99ad76e
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
github.com/aliyun/alibaba-cloud-sdk-go v1.61.948
github.com/aliyun/aliyun-oss-go-sdk v2.1.8+incompatible
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect
github.com/bsm/sarama-cluster v2.1.15+incompatible
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/gammazero/workerpool v1.1.2
github.com/gin-gonic/gin v1.6.3
github.com/go-sql-driver/mysql v1.5.0
github.com/gogo/protobuf v1.3.2
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
github.com/golang/protobuf v1.5.2
github.com/gomodule/redigo v2.0.0+incompatible
github.com/google/uuid v1.3.0
github.com/haltingstate/secp256k1-go v0.0.0-20151224084235-572209b26df6
github.com/huaweicloud/huaweicloud-sdk-go-obs v3.21.1+incompatible
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.0.35-rc
github.com/inconshreveable/log15 v0.0.0-20201112154412-8562bdadbbac
github.com/jinzhu/gorm v1.9.16
github.com/mailru/easyjson v0.7.7 // indirect
github.com/minio/minio-go/v7 v7.0.12
github.com/oofpgDLD/u-push v0.0.2
github.com/opentracing/opentracing-go v1.2.0
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.11.0
github.com/rs/xid v1.3.0
github.com/rs/zerolog v1.21.0
github.com/spf13/cobra v1.0.0
github.com/stretchr/testify v1.7.0
github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14
github.com/swaggo/gin-swagger v1.1.0
github.com/swaggo/swag v1.7.8
github.com/tencentyun/tls-sig-api-v2-golang v1.2.0
github.com/uber/jaeger-client-go v2.28.0+incompatible
github.com/ugorji/go v1.1.13 // indirect
gitlab.33.cn/chat/im v1.0.10
//gitlab.33.cn/chat/imparse v0.0.0-00010101000000-000000000000
//gitlab.33.cn/chat/im-pkg v0.0.0-00010101000000-000000000000
gitlab.33.cn/chat/im-pkg v0.0.1
gitlab.33.cn/chat/imparse v1.0.6
gitlab.33.cn/utils/go-kit v1.0.7
go.etcd.io/etcd/api/v3 v3.5.0
go.etcd.io/etcd/client/v3 v3.5.0
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
golang.org/x/net v0.0.0-20211020060615-d418f374d309 // indirect
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 // indirect
google.golang.org/grpc v1.40.0
google.golang.org/protobuf v1.27.1
gopkg.in/Shopify/sarama.v1 v1.19.0
gopkg.in/go-playground/validator.v8 v8.18.2
gopkg.in/yaml.v2 v2.4.0
)
//replace gitlab.33.cn/chat/imparse => ../imparse
//replace gitlab.33.cn/chat/im-pkg => ../im-pkg

2398
go.sum Normal file

File diff suppressed because it is too large Load Diff

85
pkg/address/address.go Normal file
View File

@@ -0,0 +1,85 @@
package address
import (
"bytes"
"encoding/hex"
"errors"
"gitlab.33.cn/chat/dtalk/pkg/address/base58"
"gitlab.33.cn/chat/dtalk/pkg/address/crypto"
)
//Address 地址
type Address struct {
Version byte
Hash160 [20]byte // For a stealth address: it's HASH160
Checksum []byte // Unused for a stealth address
Pubkey []byte // Unused for a stealth address
Enc58str string
}
//SetBytes 设置地址的bytes
func (a *Address) SetBytes(b []byte) {
copy(a.Hash160[:], b)
}
func (a *Address) String() string {
if a.Enc58str == "" {
var ad [25]byte
ad[0] = a.Version
copy(ad[1:21], a.Hash160[:])
if a.Checksum == nil {
sh := crypto.Sha2Sum(ad[0:21])
a.Checksum = make([]byte, 4)
copy(a.Checksum, sh[:4])
}
copy(ad[21:25], a.Checksum[:])
a.Enc58str = base58.Encode(ad[:])
}
return a.Enc58str
}
//NormalVer 普通地址的版本号
const NormalVer byte = 0
func PublicKeyToAddress(version byte, in []byte) string {
a := new(Address)
a.Pubkey = make([]byte, len(in))
copy(a.Pubkey[:], in[:])
a.Version = version
a.SetBytes(crypto.Rimp160(in))
return a.String()
}
//CheckAddress 检查地址
func CheckAddress(ver byte, addr string) (e error) {
dec := base58.Decode(addr)
if dec == nil {
e = errors.New("Cannot decode b58 string '" + addr + "'")
return
}
if len(dec) < 25 {
e = errors.New("Address too short " + hex.EncodeToString(dec))
return
}
//version 的错误优先
if dec[0] != ver {
e = ErrCheckVersion
return
}
//需要兼容以前的错误(以前的错误,是一种特殊的情况)
if len(dec) == 25 {
sh := crypto.Sha2Sum(dec[0:21])
if !bytes.Equal(sh[:4], dec[21:25]) {
e = ErrCheckChecksum
return
}
}
var cksum [4]byte
copy(cksum[:], dec[len(dec)-4:])
//新的错误: 这个错误用一种新的错误标记
if crypto.Checksum(dec[:len(dec)-4]) != cksum {
e = ErrAddressChecksum
}
return e
}

View File

@@ -0,0 +1,30 @@
package address
import (
"encoding/hex"
"testing"
)
func Test_Encoding(t *testing.T) {
in, err := hex.DecodeString("02cec0b297406fc5298e9fe829c9f6f96fa176a1bd1a7c55fa0d345a6f49b09d25")
if err != nil {
t.Error(err)
return
}
addr := PublicKeyToAddress(NormalVer, in)
t.Log(addr)
if err := CheckAddress(NormalVer, addr); err != nil {
t.Error(err)
return
}
t.Log("check success")
}
func Test_CheckAddress(t *testing.T) {
addr := "1JoFzozbxvst22c2K7MBYwQGjCaMZbC5Qm"
if err := CheckAddress(NormalVer, addr); err != nil {
t.Error(err)
return
}
t.Log("check success")
}

View File

@@ -0,0 +1,50 @@
// Copyright (c) 2015 The btcsuite developers
// Copyright (c) 2015 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
// AUTOGENERATED by genalphabet.go; do not edit.
package base58
const (
// alphabet is the modified base58 alphabet used by Bitcoin.
alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
alphabetIdx0 = '1'
)
var b58 = [256]byte{
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 0, 1, 2, 3, 4, 5, 6,
7, 8, 255, 255, 255, 255, 255, 255,
255, 9, 10, 11, 12, 13, 14, 15,
16, 255, 17, 18, 19, 20, 21, 255,
22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 255, 255, 255, 255, 255,
255, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 255, 44, 45, 46,
47, 48, 49, 50, 51, 52, 53, 54,
55, 56, 57, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
}

View File

@@ -0,0 +1,118 @@
// Copyright (c) 2013-2015 The btcsuite developers
// Copyright (c) 2015-2019 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package base58
//go:generate go run genalphabet.go
// Decode decodes a modified base58 string to a byte slice.
func Decode(input string) []byte {
if len(input) == 0 {
return []byte("")
}
// The max possible output size is when a base58 encoding consists of
// nothing but the alphabet character at index 0 which would result in the
// same number of bytes as the number of input chars.
output := make([]byte, len(input))
// Encode to base256 in reverse order to avoid extra calculations to
// determine the final output size in favor of just keeping track while
// iterating.
var index int
for _, r := range input {
// Invalid base58 character.
val := uint32(b58[r])
if val == 255 {
return []byte("")
}
// Multiply each byte in the output by 58 and encode to base256 while
// propagating the carry.
for i, b := range output[:index] {
val += uint32(b) * 58
output[i] = byte(val)
val >>= 8
}
for ; val > 0; val >>= 8 {
output[index] = byte(val)
index++
}
}
// Account for the leading zeros in the input. They are appended since the
// encoding is happening in reverse order.
for _, r := range input {
if r != alphabetIdx0 {
break
}
output[index] = 0
index++
}
// Truncate the output buffer to the actual number of decoded bytes and
// reverse it since it was calculated in reverse order.
output = output[:index:index]
for i := 0; i < index/2; i++ {
output[i], output[index-1-i] = output[index-1-i], output[i]
}
return output
}
// Encode encodes a byte slice to a modified base58 string.
func Encode(input []byte) string {
// Since the conversion is from base256 to base58, the max possible number
// of bytes of output per input byte is log_58(256) ~= 1.37. Thus, the max
// total output size is ceil(len(input) * 137/100). Rather than worrying
// about the ceiling, just add one even if it isn't needed since the final
// output is truncated to the right size at the end.
output := make([]byte, (len(input)*137/100)+1)
// Encode to base58 in reverse order to avoid extra calculations to
// determine the final output size in favor of just keeping track while
// iterating.
var index int
for _, r := range input {
// Multiply each byte in the output by 256 and encode to base58 while
// propagating the carry.
val := uint32(r)
for i, b := range output[:index] {
val += uint32(b) << 8
output[i] = byte(val % 58)
val /= 58
}
for ; val > 0; val /= 58 {
output[index] = byte(val % 58)
index++
}
}
// Replace the calculated remainders with their corresponding base58 digit.
for i, b := range output[:index] {
output[i] = alphabet[b]
}
// Account for the leading zeros in the input. They are appended since the
// encoding is happening in reverse order.
for _, r := range input {
if r != 0 {
break
}
output[index] = alphabetIdx0
index++
}
// Truncate the output buffer to the actual number of encoded bytes and
// reverse it since it was calculated in reverse order.
output = output[:index:index]
for i := 0; i < index/2; i++ {
output[i], output[index-1-i] = output[index-1-i], output[i]
}
return string(output)
}

View File

@@ -0,0 +1,44 @@
package crypto
import (
"crypto/sha256"
"golang.org/x/crypto/ripemd160"
)
func Checksum(input []byte) (cksum [4]byte) {
h := sha256.Sum256(input)
h2 := sha256.Sum256(h[:])
copy(cksum[:], h2[:4])
return
}
// Sha2Sum Returns hash: SHA256( SHA256( data ) )
// Where possible, using ShaHash() should be a bit faster
func Sha2Sum(b []byte) []byte {
tmp := sha256.Sum256(b)
tmp = sha256.Sum256(tmp[:])
return tmp[:]
}
// Rimp160 Returns hash: RIMP160( SHA256( data ) )
// Where possible, using RimpHash() should be a bit faster
func Rimp160(b []byte) []byte {
out := make([]byte, 20)
rimpHash(b, out[:])
return out[:]
}
func rimpHash(in []byte, out []byte) {
sha := sha256.New()
_, err := sha.Write(in)
if err != nil {
return
}
rim := ripemd160.New()
_, err = rim.Write(sha.Sum(nil)[:])
if err != nil {
return
}
copy(out, rim.Sum(nil))
}

12
pkg/address/error.go Normal file
View File

@@ -0,0 +1,12 @@
package address
import "errors"
// ErrCheckVersion :
var ErrCheckVersion = errors.New("check version error")
//ErrCheckChecksum :
var ErrCheckChecksum = errors.New("Address Checksum error")
//ErrAddressChecksum :
var ErrAddressChecksum = errors.New("address checksum error")

83
pkg/api/api.go Normal file
View File

@@ -0,0 +1,83 @@
package api
import (
"fmt"
"net/http"
"reflect"
"strconv"
"time"
"github.com/gin-gonic/gin"
"gopkg.in/go-playground/validator.v8"
)
// 处理跨域请求,支持options访问
func Cors() gin.HandlerFunc {
return func(c *gin.Context) {
method := c.Request.Method
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Headers", "*") //Content-Type,AccessToken,X-CSRF-Token,Authorization,Token,FZM-APP-ID
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, PATCH, DELETE")
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
c.Header("Access-Control-Allow-Credentials", "true")
// 放行所有OPTIONS方法因为有的模板是要请求两次的
if method == "OPTIONS" {
c.AbortWithStatus(http.StatusNoContent)
}
// 处理请求
c.Next()
}
}
// defaultLogFormatter is the default log format function Logger middleware uses.
var Chat33GinLogFormatter = func(param gin.LogFormatterParams) string {
var statusColor, methodColor, resetColor string
if param.IsOutputColor() {
statusColor = param.StatusCodeColor()
methodColor = param.MethodColor()
resetColor = param.ResetColor()
}
if param.Latency > time.Minute {
// Truncate in a golang < 1.8 safe way
param.Latency = param.Latency - param.Latency%time.Second
}
return fmt.Sprintf("[GIN] %v |%s %3d %s| %13v | %15s |%s %-7s %s %s\n%s",
param.TimeStamp.Format("2006/01/02 - 15:04:05"),
statusColor, param.StatusCode, resetColor,
param.Latency,
param.ClientIP,
methodColor, param.Method, resetColor,
param.Path,
//param.Keys[DeviceType], param.Keys[Version], param.Keys[AppId], param.Keys[UserId], param.Keys[Uuid],
param.ErrorMessage,
)
}
func CheckNumber(
v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value,
field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string,
) bool {
val := field.Interface()
switch val.(type) {
case int:
return true
case string:
if val.(string) == "" {
return true
}
_, err := strconv.ParseInt(val.(string), 10, 64)
if err != nil {
return false
}
return true
case int64:
return true
default:
//utility_log.Error("func ToInt error unknow type")
return false
}
}

33
pkg/api/const.go Normal file
View File

@@ -0,0 +1,33 @@
package api
import (
"context"
"time"
)
const (
RespMiddleWareDisabled = "RespMiddleWareDisabled"
ReqError = "error"
ReqResult = "result"
)
const (
Address = "address"
Signature = "signature"
DeviceName = "deviceName"
DeviceType = "deviceType"
Uuid = "uuid"
Version = "version"
)
const HeaderTimeOut = 120 * time.Second
func NewAddrWithContext(ctx context.Context) string {
addr, ok := ctx.Value(Address).(string)
if !ok {
addr = ""
}
return addr
}

View File

@@ -0,0 +1,142 @@
package logger
import (
"bytes"
"io"
"io/ioutil"
"net/http"
"net/http/httputil"
"time"
"gitlab.33.cn/chat/dtalk/pkg/logger"
"gitlab.33.cn/chat/dtalk/pkg/api"
"github.com/gin-gonic/gin"
"github.com/rs/zerolog"
xerror "gitlab.33.cn/chat/dtalk/pkg/error"
)
type Middleware struct {
log zerolog.Logger
}
func NewMiddleware(log zerolog.Logger) *Middleware {
return &Middleware{
log: log,
}
}
func (m *Middleware) Handle() gin.HandlerFunc {
return func(ctx *gin.Context) {
body := dumpRequest(ctx.Request)
start := time.Now()
ctx.Next()
tlog := logger.NewLogWithCtx(ctx, m.log)
latency := time.Since(start)
if latency > time.Minute {
latency = latency - latency%time.Second
}
reqURI := ctx.Request.RequestURI
if reqURI == "" {
reqURI = ctx.Request.URL.RequestURI()
}
tlog.Info().
Str("clientIP", ctx.ClientIP()).
Str("method", ctx.Request.Method).
Str("Path", reqURI).
Dur("span", latency).
Str("|body", body).
Int("status", ctx.Writer.Status()).
Msg("http req")
err, ok := ctx.Get(api.ReqError)
if ok {
code, msg := parseErr(err)
if code != 0 {
tlog.Error().
Int("code", code).
Str("msg", msg).
Msg("http err")
}
}
}
}
func ParseErr(err interface{}) (code int, msg string) {
return parseErr(err)
}
func parseErr(err interface{}) (code int, msg string) {
if err != nil {
switch ty := err.(type) {
case *xerror.Error:
code = ty.Code()
msg = ty.Error()
case error:
code = xerror.CodeInnerError
msg = err.(error).Error()
default:
e := xerror.NewError(xerror.CodeInnerError)
code = e.Code()
msg = e.Error()
}
return
}
code = xerror.CodeOK
return
}
func DupReadCloser(reader io.ReadCloser) (io.ReadCloser, io.ReadCloser) {
var buf bytes.Buffer
tee := io.TeeReader(reader, &buf)
return ioutil.NopCloser(tee), ioutil.NopCloser(&buf)
}
// dumpRequest 格式化请求样式
func dumpRequest(req *http.Request) string {
var dup io.ReadCloser
var err error
//req.Body, dup = iox.DupReadCloser(req.Body)
req.Body, dup = DupReadCloser(req.Body)
var b bytes.Buffer
//reqURI := req.RequestURI
//if reqURI == "" {
// reqURI = req.URL.RequestURI()
//}
//fmt.Fprintf(&b, "%s - %s - HTTP/%d.%d - OperaotrId:%s - ReqBody:", req.Method,
// reqURI, req.ProtoMajor, req.ProtoMinor, operatorId)
chunked := len(req.TransferEncoding) > 0 && req.TransferEncoding[0] == "chunked"
if req.Body != nil {
var n int64
var dest io.Writer = &b
if chunked {
dest = httputil.NewChunkedWriter(dest)
}
n, err = io.Copy(dest, req.Body)
if chunked {
dest.(io.Closer).Close()
}
if n > 0 {
//io.WriteString(&b, "\n")
}
}
req.Body = dup
if err != nil {
return err.Error()
}
return b.String()
}

192
pkg/api/midware.go Normal file
View File

@@ -0,0 +1,192 @@
package api
import (
"crypto/sha256"
"encoding/base64"
"fmt"
"net/http"
"reflect"
"strconv"
"strings"
"github.com/gin-gonic/gin"
zlog "github.com/rs/zerolog/log"
"gitlab.33.cn/chat/dtalk/pkg/address"
xerror "gitlab.33.cn/chat/dtalk/pkg/error"
"gitlab.33.cn/chat/dtalk/pkg/util"
)
var log = zlog.Logger
func composeHttpResp(code int, msg string, data interface{}) interface{} {
type HttpAck struct {
Result int `json:"result"`
Message string `json:"message"`
Data interface{} `json:"data"`
}
var ret HttpAck
ret.Result = code
ret.Message = msg
ret.Data = data
return &ret
}
func parseRlt(result interface{}, err interface{}) (code int, msg string, data interface{}) {
if err != nil {
switch ty := err.(type) {
case *xerror.Error:
code = ty.Code()
msg = ty.Error()
data = ty.Data()
default:
log.Warn().Interface("err", err).Msg("inner error type")
e := xerror.NewError(xerror.CodeInnerError)
code = e.Code()
msg = e.Error()
data = err
}
return
}
code = xerror.CodeOK
if isNil(result) {
data = gin.H{}
} else {
data = result
}
return
}
func isNil(i interface{}) bool {
vi := reflect.ValueOf(i)
if vi.Kind() == reflect.Ptr {
return vi.IsNil()
}
return false
}
func RespMiddleWare() gin.HandlerFunc {
return func(c *gin.Context) {
c.Next()
if v, ok := c.Get(RespMiddleWareDisabled); ok && v == true {
return
}
err := c.MustGet(ReqError)
result, _ := c.Get(ReqResult)
ret := composeHttpResp(parseRlt(result, err))
c.PureJSON(http.StatusOK, ret)
//c.PureJSON()
}
}
func AuthMiddleWare() gin.HandlerFunc {
return func(context *gin.Context) {
sig := context.GetHeader("FZM-SIGNATURE")
uuid := context.GetHeader("FZM-UUID")
device := context.GetHeader("FZM-DEVICE")
deviceName := context.GetHeader("FZM-DEVICE-NAME")
version := context.GetHeader("FZM-VERSION")
//
if sig == "MOCK" || sig == "MOCK2" {
mockAddr := ""
switch sig {
case "MOCK":
mockAddr = "1FKxgaEh5fuSm7a35BfUnKYAmradowpiTR"
case "MOCK2":
mockAddr = "1AsPsahP7FvpR7F2de1LhSB4SU5ShqZ7eu"
}
//set val
context.Set(Signature, sig)
context.Set(Address, mockAddr)
context.Set(Uuid, uuid)
context.Set(DeviceType, device)
context.Set(DeviceName, deviceName)
context.Set(Version, version)
} else {
pubKey, err := VerifyAddress(sig)
if err != nil {
log.Debug().Err(err).Msg("VerifyAddress failed")
context.Set(ReqError, err)
context.Abort()
return
}
addr := address.PublicKeyToAddress(address.NormalVer, pubKey)
if addr == "" {
log.Debug().Msg("PublicKeyToAddress addr is empty")
context.Set(ReqError, err)
context.Abort()
return
}
//set val
context.Set(Signature, sig)
context.Set(Address, addr)
context.Set(Uuid, uuid)
context.Set(DeviceType, device)
context.Set(DeviceName, deviceName)
context.Set(Version, version)
}
}
}
func HeaderMiddleWare() gin.HandlerFunc {
return func(context *gin.Context) {
uuid := context.GetHeader("FZM-UUID")
device := context.GetHeader("FZM-DEVICE")
deviceName := context.GetHeader("FZM-DEVICE-NAME")
version := context.GetHeader("FZM-VERSION")
//set val
context.Set(Uuid, uuid)
context.Set(DeviceType, device)
context.Set(DeviceName, deviceName)
context.Set(Version, version)
}
}
//get pubKey
func VerifyAddress(str string) ([]byte, error) {
//<signature>#<msg>#<address>; <>
ss := strings.SplitN(str, "#", -1)
if len(ss) < 3 {
log.Debug().Err(fmt.Errorf("need length:%v,got:%v", 3, len(ss))).Str("sig", str).Msg("split signature failed")
return nil, xerror.NewError(xerror.SignatureInvalid)
}
sigData := ss[0]
msgData := ss[1]
pubKeyData := ss[2]
msg := strings.SplitN(msgData, "*", -1)
if len(msg) < 2 {
log.Debug().Err(fmt.Errorf("need msg length:%v,got:%v", 2, len(ss))).Str("msgData", msgData).Msg("split msg data failed")
return nil, xerror.NewError(xerror.SignatureInvalid)
}
time, err := strconv.ParseInt(msg[0], 10, 64)
if err != nil {
log.Debug().Err(err).Str("datetime", msg[0]).Msg("ParseInt datetime failed")
return nil, xerror.NewError(xerror.SignatureInvalid)
}
//secp256
sig, err := base64.StdEncoding.DecodeString(sigData)
if err != nil {
log.Debug().Err(err).Str("sigData", sigData).Msg("base64 decode sig data failed")
return nil, xerror.NewError(xerror.SignatureInvalid)
}
pubKey, err := util.HexDecode(pubKeyData)
if err != nil {
log.Debug().Err(err).Str("pubKeyData", pubKeyData).Msg("hex decode pubKey failed")
return nil, xerror.NewError(xerror.SignatureInvalid)
}
msg256 := sha256.Sum256([]byte(msgData))
if !util.Secp256k1Verify(msg256[:], sig, pubKey) {
log.Debug().Err(err).Str("msgData", msgData).Bytes("sig", sig).Bytes("pubKey", pubKey).
Msg("Secp256k1Verify failed")
return nil, xerror.NewError(xerror.SignatureInvalid)
}
//检查时间是否过期
if util.CheckTimeOut(time, HeaderTimeOut) {
log.Debug().Err(err).Int64("time", time).Msg("verify timeout")
return nil, xerror.NewError(xerror.SignatureExpired)
}
return pubKey, nil
}

15
pkg/api/midware_test.go Normal file
View File

@@ -0,0 +1,15 @@
package api
import "testing"
func Test_VerifyAddress(t *testing.T) {
//sig := "5a3tPpKtUwVzOXAmEFuMRNdHJqJnkjbWUPKVYJDLJRV9+AksajpvT9UUSeNFVVL1W1F8EUDQt01bp11jtV8gbwA=#1610336258730*6XofpoSc#0375610055c57e011a0a51457e0ce451849a4ca588b0ff0beb0ba5d929ca2dd82b"
sig := "sjL53g5zbfwREjuBDfTpyaQRfULgip2Ax0Es3tVEIuBCxvWryXm7EVRv/jEmmi6ZlMZZbEXeKlBxrFt41OCPWAE=#1626170987818458*2syd7kfaiw#036801a786cba366d5f62283173681dd5801740d3ef6fe1d4383680f3c0f8e7d4f"
b, err := VerifyAddress(sig)
if err != nil {
t.Errorf("verify failed: %v", err)
return
}
t.Logf("verify:%v", b)
}

Some files were not shown because too many files have changed in this diff Show More