diff --git a/README-zh_CN.md b/README-zh_CN.md index 3865f5fa3..9234666f4 100644 --- a/README-zh_CN.md +++ b/README-zh_CN.md @@ -29,330 +29,106 @@

-## ✨ 关于 OpenIM +## Ⓜ️ 关于 OpenIM -Open-IM-Server 是使用纯 Golang 精心制作的强大的即时消息服务器。其通过 JSON over WebSocket 进行通信的独特方法将每次交互都视为消息。这简化了定制并消除了修改服务器代码的需求。通过利用微服务架构,服务器可以通过集群部署,保证出色的性能和可伸缩性。 +OpenIM 不仅仅是一个开源的即时消息组件,它是你的应用程序生态系统的一个不可或缺的部分。查看下面的图表,了解 AppServer、AppClient、OpenIMServer 和 OpenIMSDK 是如何交互的。 -Open-IM-Server 不仅仅是一个即时消息服务器;它是将实时网络集成到您的应用程序中的强大工具,定位为您集成的首选选择!🚀 +![App-OpenIM 关系](./docs/images/oepnim-design.png) -请注意,Open-IM-Server 不作为独立产品运行,也不提供内置的帐户注册或登录服务。为了简化您的实施过程,我们已开源了 [chat repository](https://github.com/OpenIMSDK/chat),其中包括这些功能。与 Open-IM-Server 一起部署此聊天业务服务器可加快全面的聊天产品的设置。👥 +## 🚀 关于 OpenIMSDK -为了进一步增强您的体验,我们还提供了 SDK 客户端,在其中实现了大多数复杂逻辑。可以在 [此链接](https://github.com/OpenIMSDK/openim-sdk-core) 找到 [SDK repository](https://github.com/OpenIMSDK/openim-sdk-core)。[chat repository](https://github.com/OpenIMSDK/chat) 是我们的业务服务器,而 'core' 代表 SDK 的高级封装,它们协同工作以提供卓越的结果。✨ +**OpenIMSDK** 无缝集成到您的应用中,提供丰富、实时的消息体验,无需复杂的 UI 集成。它提供: -## :star2: 为什么选择 OpenIM ++ **本地存储**:用于快速数据检索和消息同步。 ++ **监听器回调**:确保实时消息交互性。 ++ **API 封装**:简化开发流程。 ++ **连接管理**:保证可靠的消息传递。 -**🔍 功能截图显示** +它使用 Golang 构建,并支持跨平台部署,确保在所有平台上提供一致的消息体验。 -
+👉 **[探索 GO SDK](https://github.com/openimsdk/openim-sdk-core)** -| 💻🔄📱 多终端同步 🔄🖥️ | 📅⚡ 高效会议 🚀💼 | -| :----------------------------------------------------------: | :----------------------------------------------------------: | -| ![multiple-message](./assets/demo/multi-terminal-synchronization.png) | ![efficient-meetings](./assets/demo/efficient-meetings.png) | -| 📲🔄 **一对一和群聊** 👥🗣️ | 🎁💻 **特殊功能 - 自定义消息** ✉️🎨 | -| ![group-chat](./assets/demo/group-chat.png) | ![special-function](./assets/demo/special-function.png) | +## 🌐 关于 OpenIMServer -
+精心用 Golang 开发的 **OpenIMServer** 通过多重方式确保了卓越的即时消息服务器能力: -1. **全面的消息类型支持 :speech_balloon:** ++ **模块组成**:它由多个模块组成,例如网关和多个 RPC 服务,提供一个多功能的消息环境。 ++ **微服务架构**:支持集群模式,确保出色的性能和可伸缩性,以有效管理各个实例间的通信。 ++ **多样的部署选项**:适应你的操作偏好,通过源代码、Kubernetes 或 Docker 提供部署选项。 - ✅ 支持几乎所有类型的消息,包括文本、图片、表情符号、语音、视频、地理位置、文件、报价、名片、系统通知、自定义消息等 +### 增强的业务功能: - ✅ 支持一对一和多人音视频通话 ++ **REST API**:OpenIMServer 为业务系统提供 REST API,旨在通过后端接口为您的操作提供附加功能,如群组创建和消息推送。 ++ **回调**:为了扩展其在各种业务形式中的实用性,OpenIMServer 提供了回调能力。即,在事件发生之前或之后,它向业务服务器发送请求,比如发送消息,丰富通信过程中的交互和数据交换流。 - ✅ 为 iOS、Android、Flutter、uni-app、ReactNative、Electron、Web、H5 等多个平台提供终端支持 - -2. **随时随地的高效会议 :earth_americas:** - - ✅ 基于具有 100% 可靠强制信令功能的 IM (Instant Messaging),为与聊天应用程序深度集成的 IM 系统铺平了道路 - - ✅ 支持单次会议中的数百人,订阅人数达到数千,以及服务器端音视频录制 - -3. **适用于各种社交场景的一对一和群聊 :busts_in_silhouette:** - - ✅ OpenIM 有四种角色:应用程序管理员、群主、群管理员和普通成员 - - ✅ 强大的群特性,如静音、群公告、群验证、无限群成员和根据需要加载群消息 - -4. **独特的功能 :star2:** - - ✅ 支持读取并烧毁私人聊天,可自定义时长 - - ✅ 消息编辑功能扩大了社交场景,使即时通讯变得更加多样化和有趣 - -5. **开源 :open_hands:** - - ✅ OpenIM 的代码是开源的,数据自控,旨在构建一个全球领先的 IM 开源社区,包括客户端 SDK 和服务器 - - ✅ 基于开源服务器,已经开发了许多出色的开源项目,例如 [OpenKF](https://github.com/OpenIMSDK/OpenKF) (开源 AI 客户服务系统) - -6. **易于扩展 :wrench:** - - ✅ OpenIM 服务器是用 Golang 实现的,引入了创新的 "一切都是消息" 通信模型,简化了自定义消息和扩展功能的实现 - -7. **高性能 :racing_car:** - - ✅ OpenIM 支持集群中的分层治理架构,经过大量用户的测试,并抽象了在线消息、离线消息和历史消息的存储模型 - -8. **全平台支持 :tv:** - - ✅ 支持原生 iOS、Android;跨平台 Flutter、uni-app、ReactNative;主要的 Web 前端框架如 React、Vue;小程序和 Electron 支持的 PC 平台 - -9. **终极部署体验 🤖** - - ✅ 支持 [集群部署](https://github.com/openimsdk/open-im-server/edit/main/deployments/README.md) - - ✅ 支持多架构镜像,我们的 Docker 镜像不仅托管在 GitHub 上,而且还在阿里云和 Docker Hub 上支持多个架构。请访问 [我们的 GitHub packages](https://github.com/orgs/OpenIMSDK/packages?repo_name=Open-IM-Server) 并阅读我们的 [版本管理文档](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/version.md) 以获取更多信息。 - -10. **开源社区的大生态系统 🤲** - - ✅ 我们有数万用户和许多解决方案来解决问题。 - - ✅ 我们有一个大型的开源社区叫 [OpenIMSDK](https://github.com/OpenIMSDK),它运行核心模块,我们还有一个开源社区叫 [openim-sigs](https://github.com/openim-sigs) 以探索更多基于 IM 的基础设施产品。 +👉 **[了解更多](https://doc.rentsoft.cn/guides/introduction/product)** ## :rocket: 快速开始 -
使用 Docker Compose 部署 - -1. 克隆项目 - -``` -# 选择您需要的 -BRANCH=release-v3.1 -git clone -b $BRANCH https://github.com/openimsdk/open-im-server openim && export openim=$(pwd)/openim && cd $openim && make build -``` - -> **注意** 阅读我们的发布策略:https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/version.md - -1. 修改 `.env` +你只需要一个简单的命令,就可以快速学习 OpenIM 的工程解决方案: ``` -USER=root #无需修改 -PASSWORD=openIM123 #8位或更多数字和字母的组合,此密码适用于redis、mysql、mongo,以及config/config.yaml中的accessSecret -ENDPOINT=http://127.0.0.1:10005 #minio的外部服务IP和端口,或使用域名storage.xx.xx,应用程序必须能够访问此IP和端口或域名, -API_URL=http://127.0.0.1:10002/object/ #应用程序必须能够访问此IP和端口或域名, -DATA_DIR=./ #指定大磁盘目录 -``` - -1. 部署并启动 - -> **注意** 此命令只能执行一次。它会基于 `.env` 中的 `PASSWORD` 变量修改 docker-compose 中的组件密码,并修改 `config/config.yaml` 中的组件密码。如果 `.env` 中的密码发生变化,您需要首先执行 `docker-compose down`;`rm components -rf` 然后执行此命令。 - -``` - -make install +bashCopy code +$ make demo ``` -1. 检查服务 - -``` - -make check -``` - -![https://github.com/openimsdk/open-im-server/blob/main/docs/images/docker_build.png](https://github.com/openimsdk/open-im-server/blob/main/docs/images/docker_build.png) - -
从源码编译 - -您需要 `Go 1.18` 或更高版本,以及 `make`。 - -版本详情:https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/version.md - -``` -# 选择您需要的 -BRANCH=release-v3.1 -git clone -b $BRANCH https://github.com/openimsdk/open-im-server openim && export openim=$(pwd)/openim && cd $openim && make build -``` +🤲 为了方便用户体验,我们提供了多种部署解决方案,您可以根据下面的列表选择自己的部署方法: -阅读关于 [OpenIM 版本策略](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/version.md) ++ **[源代码部署指南](https://doc.rentsoft.cn/guides/gettingStarted/imSourceCodeDeployment)** ++ **[Docker 部署指南](https://doc.rentsoft.cn/guides/gettingStarted/dockerCompose)** ++ **[Kubernetes 部署指南](https://github.com/openimsdk/open-im-server/tree/main/deployments)** -使用 `make help` 来查看 OpenIM 支持的指令。 - -如图所示,所有服务已成功构建 - -![成功编译](https://github.com/openimsdk/open-im-server/blob/main/docs/images/build.png) - -
组件配置说明 - -config/config.yaml 文件为存储组件提供了详细的配置说明。 - -- Zookeeper - - - 用于 RPC 服务发现和注册,支持集群。 - - ``` - zookeeper: - schema: openim #不建议修改 - address: [ 127.0.0.1:2181 ] #地址 - username: #用户名 - password: #密码 - ``` - -- MySQL - - - 用于存储用户、关系和群组,支持主从数据库。 - - ``` - mysql: - address: [ 127.0.0.1:13306 ] #地址 - username: root #用户名 - password: openIM123 #密码 - database: openIM_v2 #不建议修改 - maxOpenConn: 1000 #最大连接 - maxIdleConn: 100 #最大空闲连接 - maxLifeTime: 60 #连接可重用的最大时间(秒) - logLevel: 4 #日志级别 1=静音 2=错误 3=警告 4=信息 - slowThreshold: 500 #慢语句阈值(毫秒) - ``` - -- Mongo - - - 用于存储离线消息,支持 mongo 分片集群。 - - ``` - mongo: - uri: #如果不为空,则直接使用此值 - address: [ 127.0.0.1:37017 ] #地址 - database: openIM #默认 mongo 数据库 - username: root #用户名 - password: openIM123 #密码 - maxPoolSize: 100 #最大连接数 - ``` - -- Redis - - - 用于存储消息序列号、最新消息、用户令牌和 mysql 缓存,支持集群部署。 - - ``` - redis: - clusterMode: false #是否为 redis cluster 模式 - address: [ 127.0.0.1:16379 ] #地址 - username: #用户名 - password: openIM123 #密码 - ``` - -- Kafka - - - 用于消息队列,用于消息解耦,支持集群部署。 - - ``` - kafka: - username: #用户名 - password: #密码 - addr: [ 127.0.0.1:9092 ] #地址 - latestMsgToRedis: - topic: "latestMsgToRedis" - offlineMsgToMongo: - topic: "offlineMsgToMongoMysql" - msgToPush: - topic: "msqToPush" - msgToModify: - topic: "msgToModify" - consumerGroupID: - msgToRedis: redis - msgToMongo: mongo - msgToMySql: mysql - msgToPush: push - msgToModify: modify - ``` - -
启动和停止服务 - -启动服务 - -``` - -./scripts/start-all.sh; -``` - -检查服务 - -``` - -./scripts/check-all.sh -``` - -停止服务 - -``` - -./scripts/stop-all.sh -``` - -
- -
开放 IM 端口 - -| TCP 端口 | 描述 | 操作 | -| --------- | --------------------------------------------------- | --------------------------------------- | -| TCP:10001 | ws 协议,消息端口如消息发送、推送等,用于客户端 SDK | 端口释放或 nginx 反向代理,并关闭防火墙 | -| TCP:10002 | api 端口,如用户、朋友、组、消息接口。 | 端口释放或 nginx 反向代理,并关闭防火墙 | -| TCP:10005 | 选择 minio 存储时所需 (openIM 默认使用 minio 存储) | 端口释放或 nginx 反向代理,并关闭防火墙 | - -
开放聊天端口 - -- 聊天仓库: https://github.com/OpenIMSDK/chat - -| TCP 端口 | 描述 | 操作 | -| --------- | ------------------------ | --------------------------------------- | -| TCP:10008 | 业务系统,如注册、登录等 | 端口释放或 nginx 反向代理,并关闭防火墙 | -| TCP:10009 | 管理后台,如统计、封禁等 | 端口释放或 nginx 反向代理,并关闭防火墙 | - -
- -## :link: APP 和 OpenIM 之间的关系 - -OpenIM 不仅仅是一个开源的即时消息组件,它是您的应用程序生态系统的一个不可分割的部分。查看此图表以了解 AppServer、AppClient、Open-IM-Server 和 Open-IM-SDK 如何互动。 +## :hammer_and_wrench: 开始开发 OpenIM -![App-OpenIM 关系](https://github.com/openimsdk/open-im-server/blob/main/docs/images/open-im-server.png) +OpenIM 我们的目标是建立一个顶级的开源社区。我们有一套标准,在[社区仓库](https://github.com/OpenIMSDK/community)中。 -## :building_construction: 总体架构 +如果你想为这个 Open-IM-Server 仓库做贡献,请阅读我们的[贡献者文档](https://github.com/openimsdk/open-im-server/blob/main/CONTRIBUTING.md)。 -深入了解 Open-IM-Server 的功能与我们的架构图。 +在开始之前,请确保你的更改是有需求的。最好的方法是创建一个[新的讨论](https://github.com/openimsdk/open-im-server/discussions/new/choose) 或 [Slack 通信](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q),或者如果你发现一个问题,首先[报告它](https://github.com/openimsdk/open-im-server/issues/new/choose)。 -![总体架构](https://github.com/openimsdk/open-im-server/blob/main/docs/images/Architecture.jpg) ++ [代码标准](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/go_code.md) -## :hammer_and_wrench: 开始开发 OpenIM ++ [Docker 镜像标准](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/images.md) -OpenIM 我们的目标是建立一个顶级的开源社区。我们有一套标准,在 [Community repository](https://github.com/OpenIMSDK/community) 中。 ++ [目录标准](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/directory.md) -如果您想为这个 Open-IM-Server 仓库做贡献,请阅读我们的 [贡献者文档](https://github.com/openimsdk/open-im-server/blob/main/CONTRIBUTING.md)。 ++ [提交标准](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/commit.md) -在您开始之前,请确保您的更改是需要的。最好的方法是创建一个 [新的讨论](https://github.com/openimsdk/open-im-server/discussions/new/choose) 或 [Slack 通讯](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q),或者如果您发现一个问题,首先 [报告它](https://github.com/openimsdk/open-im-server/issues/new/choose)。 ++ [版本控制标准](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/version.md) -- [代码标准](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/go_code.md) -- [Docker 图像标准](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/images.md) -- [目录标准](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/directory.md) -- [提交标准](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/commit.md) -- [版本控制标准](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/version.md) -- [接口标准](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/api.md) -- [日志标准](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/) -- [错误代码标准](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/error_code.md) ++ [接口标准](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/interface.md) -## :busts_in_silhouette: 社区 +## :link: 链接 -- 📚 [OpenIM 社区](https://github.com/OpenIMSDK/community) -- 💕 [OpenIM 兴趣小组](https://github.com/Openim-sigs) -- 🚀 [加入我们的 Slack 社区](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) -- :eyes: [加入我们的微信群 (微信群)](https://openim-1253691595.cos.ap-nanjing.myqcloud.com/WechatIMG20.jpeg) + + **[完整文档](https://doc.rentsoft.cn/)** + + **[更新日志](https://github.com/openimsdk/open-im-server/blob/main/CHANGELOG.md)** + + **[FAQ](https://github.com/openimsdk/open-im-server/blob/main/FAQ.md)** + + **[代码示例](https://github.com/openimsdk/open-im-server/blob/main/examples)** -## :calendar: 社区会议 +## :handshake: 社区 -我们希望任何人都可以参与我们的社区并贡献代码,我们提供礼物和奖励,欢迎您每周四晚上加入我们。 + + **[GitHub Discussions](https://github.com/openimsdk/open-im-server/discussions)** + + **[Slack 通信](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q)** + + **[GitHub Issues](https://github.com/openimsdk/open-im-server/issues)** -我们的会议在 [OpenIM Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) 🎯,然后您可以搜索 Open-IM-Server 管道加入。 + 您可以加入这些平台,讨论问题,提出建议,或分享您的成功故事! -我们在 [GitHub 讨论](https://github.com/openimsdk/open-im-server/discussions/categories/meeting) 中记下每次 [双周会议](https://github.com/orgs/OpenIMSDK/discussions/categories/meeting) 的笔记,我们的历史会议记录以及会议回放都可在 [Google Docs :bookmark_tabs:](https://docs.google.com/document/d/1nx8MDpuG74NASx081JcCpxPgDITNTpIIos0DS6Vr9GU/edit?usp=sharing) 中找到。 +## :writing_hand: 贡献 -## :eyes: 谁在使用 OpenIM + 我们欢迎任何形式的贡献!请确保在提交 Pull Request 之前阅读我们的[贡献者文档](https://github.com/openimsdk/open-im-server/blob/main/CONTRIBUTING.md)。 -查看我们的 [用户案例研究](https://github.com/OpenIMSDK/community/blob/main/ADOPTERS.md) 页面以获取项目用户列表。不要犹豫,留下一个 [📝评论](https://github.com/openimsdk/open-im-server/issues/379) 并分享您的使用案例。 + + **[报告 Bug](https://github.com/openimsdk/open-im-server/issues/new?assignees=&labels=bug&template=bug_report.md&title=)** + + **[提出新特性](https://github.com/openimsdk/open-im-server/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=)** + + **[提交 Pull Request](https://github.com/openimsdk/open-im-server/pulls)** -## :page_facing_up: 许可证 + 感谢您的贡献,我们一起打造一个强大的即时通信解决方案! -OpenIM 根据 Apache 2.0 许可证授权。请查看 [LICENSE](https://github.com/openimsdk/open-im-server/tree/main/LICENSE) 以获取完整的许可证文本。 +## :closed_book: 许可证 -OpenIM logo,包括其变体和动画版本,在此存储库 [OpenIM](https://github.com/openimsdk/open-im-server) 下的 [assets/logo](./assets/logo) 和 [assets/logo-gif](./assets/logo-gif) 目录中显示,受版权法保护。 + OpenIMSDK 在 Apache License 2.0 许可下可用。查看[LICENSE 文件](https://github.com/openimsdk/open-im-server/blob/main/LICENSE)了解更多信息。 -## 🔮 感谢我们的贡献者! +## 🔮 Thanks to our contributors! - \ No newline at end of file + + + diff --git a/go.sum b/go.sum index a72bd884a..6fcc8ebfd 100644 --- a/go.sum +++ b/go.sum @@ -18,8 +18,8 @@ firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIw github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/IBM/sarama v1.41.2 h1:ZDBZfGPHAD4uuAtSv4U22fRZBgst0eEwGFzLj0fb85c= github.com/IBM/sarama v1.41.2/go.mod h1:xdpu7sd6OE1uxNdjYTSKUfY8FaKkJES9/+EyjSgiGQk= -github.com/OpenIMSDK/protocol v0.0.26 h1:jzq7EemhkO8W5kvOOg2f0b7OAMzMPrIBUG0GVbSNhDA= -github.com/OpenIMSDK/protocol v0.0.26/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= +github.com/OpenIMSDK/protocol v0.0.25 h1:AtB0Ia5LO26oqPoPJDIS4UMH3Wb2li96fMgfzI2cr4I= +github.com/OpenIMSDK/protocol v0.0.25/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= github.com/OpenIMSDK/tools v0.0.14 h1:WLof/+WxyPyRST+QkoTKubYCiV73uCLiL8pgnpH/yKQ= github.com/OpenIMSDK/tools v0.0.14/go.mod h1:eg+q4A34Qmu73xkY0mt37FHGMCMfC6CtmOnm0kFEGFI= github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= diff --git a/internal/msggateway/client.go b/internal/msggateway/client.go index 739fa9688..b1eaaf057 100644 --- a/internal/msggateway/client.go +++ b/internal/msggateway/client.go @@ -207,8 +207,8 @@ func (c *Client) handleMessage(message []byte) error { binaryReq.ReqIdentifier, ) } - c.replyMessage(ctx, &binaryReq, messageErr, resp) - return nil + + return c.replyMessage(ctx, &binaryReq, messageErr, resp) } func (c *Client) setAppBackgroundStatus(ctx context.Context, req Req) ([]byte, error) { @@ -229,7 +229,7 @@ func (c *Client) close() { c.longConnServer.UnRegister(c) } -func (c *Client) replyMessage(ctx context.Context, binaryReq *Req, err error, resp []byte) { +func (c *Client) replyMessage(ctx context.Context, binaryReq *Req, err error, resp []byte) error { errResp := apiresp.ParseError(err) mReply := Resp{ ReqIdentifier: binaryReq.ReqIdentifier, @@ -244,6 +244,10 @@ func (c *Client) replyMessage(ctx context.Context, binaryReq *Req, err error, re if err != nil { log.ZWarn(ctx, "wireBinaryMsg replyMessage", err, "resp", mReply.String()) } + if binaryReq.ReqIdentifier == WsLogoutMsg { + return errors.New("user logout") + } + return nil } func (c *Client) PushMessage(ctx context.Context, msgData *sdkws.MsgData) error { diff --git a/internal/rpc/msg/as_read.go b/internal/rpc/msg/as_read.go index 656849d1c..c31cd02dd 100644 --- a/internal/rpc/msg/as_read.go +++ b/internal/rpc/msg/as_read.go @@ -81,7 +81,8 @@ func (m *msgServer) SetConversationHasReadSeq( if err := m.MsgDatabase.SetHasReadSeq(ctx, req.UserID, req.ConversationID, req.HasReadSeq); err != nil { return nil, err } - if err = m.sendMarkAsReadNotification(ctx, req.ConversationID, constant.SingleChatType, req.UserID, req.UserID, nil, req.HasReadSeq); err != nil { + if err = m.sendMarkAsReadNotification(ctx, req.ConversationID, constant.SingleChatType, req.UserID, + req.UserID, nil, req.HasReadSeq); err != nil { return } return &msg.SetConversationHasReadSeqResp{}, nil @@ -119,7 +120,8 @@ func (m *msgServer) MarkMsgsAsRead( return } } - if err = m.sendMarkAsReadNotification(ctx, req.ConversationID, conversation.ConversationType, req.UserID, m.conversationAndGetRecvID(conversation, req.UserID), req.Seqs, hasReadSeq); err != nil { + if err = m.sendMarkAsReadNotification(ctx, req.ConversationID, conversation.ConversationType, req.UserID, + m.conversationAndGetRecvID(conversation, req.UserID), req.Seqs, hasReadSeq); err != nil { return } return &msg.MarkMsgsAsReadResp{}, nil @@ -131,44 +133,61 @@ func (m *msgServer) MarkConversationAsRead( ) (resp *msg.MarkConversationAsReadResp, err error) { conversation, err := m.Conversation.GetConversation(ctx, req.UserID, req.ConversationID) if err != nil { - return + return nil, err } hasReadSeq, err := m.MsgDatabase.GetHasReadSeq(ctx, req.UserID, req.ConversationID) if err != nil && errs.Unwrap(err) != redis.Nil { - return + return nil, err } - log.ZDebug(ctx, "MarkConversationAsRead", "hasReadSeq", hasReadSeq, "req.HasReadSeq", req.HasReadSeq) var seqs []int64 - if len(req.Seqs) == 0 { + + log.ZDebug(ctx, "MarkConversationAsRead", "hasReadSeq", hasReadSeq, + "req.HasReadSeq", req.HasReadSeq) + if conversation.ConversationType == constant.SingleChatType { for i := hasReadSeq + 1; i <= req.HasReadSeq; i++ { seqs = append(seqs, i) } - } else { - seqs = req.Seqs - } - if len(seqs) > 0 { - log.ZDebug(ctx, "MarkConversationAsRead", "seqs", seqs, "conversationID", req.ConversationID) - if err = m.MsgDatabase.MarkSingleChatMsgsAsRead(ctx, req.UserID, req.ConversationID, seqs); err != nil { - return + + if len(seqs) > 0 { + log.ZDebug(ctx, "MarkConversationAsRead", "seqs", seqs, "conversationID", req.ConversationID) + if err = m.MsgDatabase.MarkSingleChatMsgsAsRead(ctx, req.UserID, req.ConversationID, seqs); err != nil { + return nil, err + } } - } - if req.HasReadSeq > hasReadSeq { - err = m.MsgDatabase.SetHasReadSeq(ctx, req.UserID, req.ConversationID, req.HasReadSeq) - if err != nil { - return + if req.HasReadSeq > hasReadSeq { + err = m.MsgDatabase.SetHasReadSeq(ctx, req.UserID, req.ConversationID, req.HasReadSeq) + if err != nil { + return nil, err + } + hasReadSeq = req.HasReadSeq } - hasReadSeq = req.HasReadSeq - } - if err = m.sendMarkAsReadNotification(ctx, req.ConversationID, conversation.ConversationType, req.UserID, m.conversationAndGetRecvID(conversation, req.UserID), seqs, hasReadSeq); err != nil { - return + if err = m.sendMarkAsReadNotification(ctx, req.ConversationID, conversation.ConversationType, req.UserID, + m.conversationAndGetRecvID(conversation, req.UserID), seqs, hasReadSeq); err != nil { + return nil, err + } + + } else if conversation.ConversationType == constant.SuperGroupChatType { + if req.HasReadSeq > hasReadSeq { + err = m.MsgDatabase.SetHasReadSeq(ctx, req.UserID, req.ConversationID, req.HasReadSeq) + if err != nil { + return nil, err + } + hasReadSeq = req.HasReadSeq + } + if err = m.sendMarkAsReadNotification(ctx, req.ConversationID, constant.SingleChatType, req.UserID, + req.UserID, seqs, hasReadSeq); err != nil { + return nil, err + } + } + return &msg.MarkConversationAsReadResp{}, nil } func (m *msgServer) sendMarkAsReadNotification( ctx context.Context, conversationID string, - sesstionType int32, + sessionType int32, sendID, recvID string, seqs []int64, hasReadSeq int64, @@ -179,6 +198,9 @@ func (m *msgServer) sendMarkAsReadNotification( Seqs: seqs, HasReadSeq: hasReadSeq, } - m.notificationSender.NotificationWithSesstionType(ctx, sendID, recvID, constant.HasReadReceipt, sesstionType, tips) + err := m.notificationSender.NotificationWithSesstionType(ctx, sendID, recvID, constant.HasReadReceipt, sessionType, tips) + if err != nil { + log.ZWarn(ctx, "send has read Receipt err", err) + } return nil } diff --git a/internal/rpc/user/user.go b/internal/rpc/user/user.go index d22715f0c..f2ceb3beb 100644 --- a/internal/rpc/user/user.go +++ b/internal/rpc/user/user.go @@ -289,7 +289,8 @@ func (s *userServer) SubscribeOrCancelUsersStatus(ctx context.Context, req *pbus } // GetUserStatus Get the online status of the user. -func (s *userServer) GetUserStatus(ctx context.Context, req *pbuser.GetUserStatusReq) (resp *pbuser.GetUserStatusResp, err error) { +func (s *userServer) GetUserStatus(ctx context.Context, req *pbuser.GetUserStatusReq) (resp *pbuser.GetUserStatusResp, + err error) { onlineStatusList, err := s.UserDatabase.GetUserStatus(ctx, req.UserIDs) if err != nil { return nil, err @@ -298,31 +299,32 @@ func (s *userServer) GetUserStatus(ctx context.Context, req *pbuser.GetUserStatu } // SetUserStatus Synchronize user's online status. -func (s *userServer) SetUserStatus(ctx context.Context, req *pbuser.SetUserStatusReq) (resp *pbuser.SetUserStatusResp, err error) { - err = s.UserDatabase.SetUserStatus(ctx, req.StatusList) +func (s *userServer) SetUserStatus(ctx context.Context, req *pbuser.SetUserStatusReq) (resp *pbuser.SetUserStatusResp, + err error) { + err = s.UserDatabase.SetUserStatus(ctx, req.UserID, req.Status, req.PlatformID) if err != nil { return nil, err } - for _, value := range req.StatusList { - list, err := s.UserDatabase.GetSubscribedList(ctx, value.UserID) - if err != nil { - return nil, err - } - for _, userID := range list { - tips := &sdkws.UserStatusChangeTips{ - FromUserID: value.UserID, - ToUserID: userID, - Status: value.Status, - PlatformID: value.PlatformIDs[0], - } - s.userNotificationSender.UserStatusChangeNotification(ctx, tips) + list, err := s.UserDatabase.GetSubscribedList(ctx, req.UserID) + if err != nil { + return nil, err + } + for _, userID := range list { + tips := &sdkws.UserStatusChangeTips{ + FromUserID: req.UserID, + ToUserID: userID, + Status: req.Status, + PlatformID: req.PlatformID, } + s.userNotificationSender.UserStatusChangeNotification(ctx, tips) } + return &pbuser.SetUserStatusResp{}, nil } // GetSubscribeUsersStatus Get the online status of subscribers. -func (s *userServer) GetSubscribeUsersStatus(ctx context.Context, req *pbuser.GetSubscribeUsersStatusReq) (*pbuser.GetSubscribeUsersStatusResp, error) { +func (s *userServer) GetSubscribeUsersStatus(ctx context.Context, + req *pbuser.GetSubscribeUsersStatusReq) (*pbuser.GetSubscribeUsersStatusResp, error) { userList, err := s.UserDatabase.GetAllSubscribeList(ctx, req.UserID) if err != nil { return nil, err diff --git a/pkg/common/db/cache/user.go b/pkg/common/db/cache/user.go index 8c270c6e4..5fb0fdde6 100644 --- a/pkg/common/db/cache/user.go +++ b/pkg/common/db/cache/user.go @@ -21,6 +21,8 @@ import ( "strconv" "time" + "github.com/OpenIMSDK/tools/log" + "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/user" @@ -51,7 +53,7 @@ type UserCache interface { GetUserGlobalRecvMsgOpt(ctx context.Context, userID string) (opt int, err error) DelUsersGlobalRecvMsgOpt(userIDs ...string) UserCache GetUserStatus(ctx context.Context, userIDs []string) ([]*user.OnlineStatus, error) - SetUserStatus(ctx context.Context, list []*user.OnlineStatus) error + SetUserStatus(ctx context.Context, userID string, status, platformID int32) error } type UserCacheRedis struct { @@ -198,95 +200,107 @@ func (u *UserCacheRedis) GetUserStatus(ctx context.Context, userIDs []string) ([ return nil, errs.Wrap(err) } onlineStatus.UserID = userID + onlineStatus.Status = constant.Online res = append(res, &onlineStatus) } return res, nil } // SetUserStatus Set the user status and save it in redis. -func (u *UserCacheRedis) SetUserStatus(ctx context.Context, list []*user.OnlineStatus) error { - for _, status := range list { - var isNewKey int64 - UserIDNum := crc32.ChecksumIEEE([]byte(status.UserID)) - modKey := strconv.Itoa(int(UserIDNum % statusMod)) - key := olineStatusKey + modKey - jsonData, err := json.Marshal(status) - if err != nil { +func (u *UserCacheRedis) SetUserStatus(ctx context.Context, userID string, status, platformID int32) error { + UserIDNum := crc32.ChecksumIEEE([]byte(userID)) + modKey := strconv.Itoa(int(UserIDNum % statusMod)) + key := olineStatusKey + modKey + log.ZDebug(ctx, "SetUserStatus args", "userID", userID, "status", status, + "platformID", platformID, "modKey", modKey, "key", key) + isNewKey, err := u.rdb.Exists(ctx, key).Result() + if err != nil { + return errs.Wrap(err) + } + if isNewKey == 0 { + if status == constant.Online { + onlineStatus := user.OnlineStatus{ + UserID: userID, + Status: constant.Online, + PlatformIDs: []int32{platformID}, + } + jsonData, err := json.Marshal(onlineStatus) + if err != nil { + return errs.Wrap(err) + } + _, err = u.rdb.HSet(ctx, key, userID, string(jsonData)).Result() + if err != nil { + return errs.Wrap(err) + } + u.rdb.Expire(ctx, key, userOlineStatusExpireTime) + return nil + } + } + + isNil := false + result, err := u.rdb.HGet(ctx, key, userID).Result() + if err != nil { + if err == redis.Nil { + isNil = true + } else { return errs.Wrap(err) } - isNewKey, err = u.rdb.Exists(ctx, key).Result() + } + + if status == constant.Offline { + if isNil { + log.ZWarn(ctx, "this user not online,maybe trigger order not right", + err, "userStatus", status) + return nil + } + var onlineStatus user.OnlineStatus + err = json.Unmarshal([]byte(result), &onlineStatus) if err != nil { return errs.Wrap(err) } - if isNewKey == 0 { - _, err = u.rdb.HSet(ctx, key, status.UserID, string(jsonData)).Result() + var newPlatformIDs []int32 + for _, val := range onlineStatus.PlatformIDs { + if val != platformID { + newPlatformIDs = append(newPlatformIDs, val) + } + } + if newPlatformIDs == nil { + _, err = u.rdb.HDel(ctx, key, userID).Result() if err != nil { return errs.Wrap(err) } - u.rdb.Expire(ctx, key, userOlineStatusExpireTime) } else { - result, err := u.rdb.HGet(ctx, key, status.UserID).Result() + onlineStatus.PlatformIDs = newPlatformIDs + newjsonData, err := json.Marshal(&onlineStatus) if err != nil { return errs.Wrap(err) } - var onlineStatus user.OnlineStatus - err = json.Unmarshal([]byte(result), &onlineStatus) + _, err = u.rdb.HSet(ctx, key, userID, string(newjsonData)).Result() if err != nil { return errs.Wrap(err) } - onlineStatus.UserID = status.UserID - if status.Status == constant.Offline { - var newPlatformIDs []int32 - for _, val := range onlineStatus.PlatformIDs { - if val != status.PlatformIDs[0] { - newPlatformIDs = append(newPlatformIDs, val) - } - } - if newPlatformIDs == nil { - onlineStatus.Status = constant.Offline - onlineStatus.PlatformIDs = []int32{} - newjsonData, err := json.Marshal(&onlineStatus) - if err != nil { - return errs.Wrap(err) - } - _, err = u.rdb.HSet(ctx, key, status.UserID, string(newjsonData)).Result() - if err != nil { - return errs.Wrap(err) - } - } else { - onlineStatus.PlatformIDs = newPlatformIDs - newjsonData, err := json.Marshal(&onlineStatus) - if err != nil { - return errs.Wrap(err) - } - _, err = u.rdb.HSet(ctx, key, status.UserID, string(newjsonData)).Result() - if err != nil { - return errs.Wrap(err) - } - } - } else { - onlineStatus.Status = constant.Online - // Judging whether to be kicked out. - flag := false - for _, val := range onlineStatus.PlatformIDs { - if val == status.PlatformIDs[0] { - flag = true - break - } - } - if !flag { - onlineStatus.PlatformIDs = append(onlineStatus.PlatformIDs, status.PlatformIDs[0]) - } - newjsonData, err := json.Marshal(&onlineStatus) - if err != nil { - return errs.Wrap(err) - } - _, err = u.rdb.HSet(ctx, key, status.UserID, string(newjsonData)).Result() - if err != nil { - return errs.Wrap(err) - } + } + } else { + var onlineStatus user.OnlineStatus + if !isNil { + err = json.Unmarshal([]byte(result), &onlineStatus) + if err != nil { + return errs.Wrap(err) } } + onlineStatus.Status = constant.Online + onlineStatus.UserID = userID + onlineStatus.PlatformIDs = append(onlineStatus.PlatformIDs, platformID) + newjsonData, err := json.Marshal(&onlineStatus) + if err != nil { + return errs.Wrap(err) + } + _, err = u.rdb.HSet(ctx, key, userID, string(newjsonData)).Result() + if err != nil { + return errs.Wrap(err) + } + } + return nil } diff --git a/pkg/common/db/controller/user.go b/pkg/common/db/controller/user.go index ab86cfd27..9c6fdc5c4 100644 --- a/pkg/common/db/controller/user.go +++ b/pkg/common/db/controller/user.go @@ -64,7 +64,7 @@ type UserDatabase interface { // GetUserStatus Get the online status of the user GetUserStatus(ctx context.Context, userIDs []string) ([]*user.OnlineStatus, error) // SetUserStatus Set the user status and store the user status in redis - SetUserStatus(ctx context.Context, list []*user.OnlineStatus) error + SetUserStatus(ctx context.Context, userID string, status, platformID int32) error } type userDatabase struct { @@ -217,6 +217,6 @@ func (u *userDatabase) GetUserStatus(ctx context.Context, userIDs []string) ([]* } // SetUserStatus Set the user status and save it in redis. -func (u *userDatabase) SetUserStatus(ctx context.Context, list []*user.OnlineStatus) error { - return u.cache.SetUserStatus(ctx, list) +func (u *userDatabase) SetUserStatus(ctx context.Context, userID string, status, platformID int32) error { + return u.cache.SetUserStatus(ctx, userID, status, platformID) } diff --git a/pkg/common/db/unrelation/user.go b/pkg/common/db/unrelation/user.go index 777f27386..4b4a78c79 100644 --- a/pkg/common/db/unrelation/user.go +++ b/pkg/common/db/unrelation/user.go @@ -163,7 +163,11 @@ func (u *UserMongoDriver) GetAllSubscribeList(ctx context.Context, userID string bson.M{"user_id": SubscriptionPrefix + userID}) err = cursor.Decode(&user) if err != nil { - return nil, errs.Wrap(err) + if err == mongo.ErrNoDocuments { + return []string{}, nil + } else { + return nil, errs.Wrap(err) + } } return user.UserIDList, nil } @@ -176,7 +180,11 @@ func (u *UserMongoDriver) GetSubscribedList(ctx context.Context, userID string) bson.M{"user_id": SubscribedPrefix + userID}) err = cursor.Decode(&user) if err != nil { - return nil, errs.Wrap(err) + if err == mongo.ErrNoDocuments { + return []string{}, nil + } else { + return nil, errs.Wrap(err) + } } return user.UserIDList, nil } diff --git a/pkg/rpcclient/user.go b/pkg/rpcclient/user.go index 6929b4fd9..c40d95727 100644 --- a/pkg/rpcclient/user.go +++ b/pkg/rpcclient/user.go @@ -173,6 +173,9 @@ func (u *UserRpcClient) GetAllUserIDs(ctx context.Context, pageNumber, showNumbe // SetUserStatus sets the status for a user based on the provided user ID, status, and platform ID. func (u *UserRpcClient) SetUserStatus(ctx context.Context, userID string, status int32, platformID int) error { - _, err := u.Client.SetUserStatus(ctx, &user.SetUserStatusReq{StatusList: []*user.OnlineStatus{{UserID: userID, Status: status, PlatformIDs: []int32{int32(platformID)}}}}) + _, err := u.Client.SetUserStatus(ctx, &user.SetUserStatusReq{ + UserID: userID, + Status: status, PlatformID: int32(platformID), + }) return err } diff --git a/tools/component/component.go b/tools/component/component.go index 9aecfa704..c995da5f1 100644 --- a/tools/component/component.go +++ b/tools/component/component.go @@ -129,11 +129,10 @@ func exactIP(urll string) string { if strings.HasSuffix(host, ":") { host = host[0 : len(host)-1] } - + return host } - func checkMysql() error { var sqlDB *sql.DB defer func() { diff --git a/tools/data-conversion/chat/chat.go b/tools/data-conversion/chat/chat.go index c71da3dc1..77c62ee1f 100644 --- a/tools/data-conversion/chat/chat.go +++ b/tools/data-conversion/chat/chat.go @@ -2,12 +2,14 @@ package main import ( "fmt" - "github.com/openimsdk/open-im-server/v3/tools/data-conversion/chat/conversion" - "github.com/openimsdk/open-im-server/v3/tools/data-conversion/utils" + "log" + "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/logger" - "log" + + "github.com/openimsdk/open-im-server/v3/tools/data-conversion/chat/conversion" + "github.com/openimsdk/open-im-server/v3/tools/data-conversion/utils" ) func main() { diff --git a/tools/data-conversion/chat/v2/admin.go b/tools/data-conversion/chat/v2/admin.go index 4e23e19c2..7bc1b6c1b 100644 --- a/tools/data-conversion/chat/v2/admin.go +++ b/tools/data-conversion/chat/v2/admin.go @@ -6,45 +6,45 @@ import ( // AppVersion pc端版本管理 type AppVersion struct { - Version string `gorm:"column:version;size:64" json:"version"` + Version string `gorm:"column:version;size:64" json:"version"` Type int `gorm:"column:type;primary_key" json:"type"` - UpdateTime int `gorm:"column:update_time" json:"update_time"` - ForceUpdate bool `gorm:"column:force_update" json:"force_update"` - FileName string `gorm:"column:file_name" json:"file_name"` - YamlName string `gorm:"column:yaml_name" json:"yaml_name"` - UpdateLog string `gorm:"column:update_log" json:"update_log"` + UpdateTime int `gorm:"column:update_time" json:"update_time"` + ForceUpdate bool `gorm:"column:force_update" json:"force_update"` + FileName string `gorm:"column:file_name" json:"file_name"` + YamlName string `gorm:"column:yaml_name" json:"yaml_name"` + UpdateLog string `gorm:"column:update_log" json:"update_log"` } // Admin 后台管理员 type Admin struct { Account string `gorm:"column:account;primary_key;type:char(64)" json:"account"` - Password string `gorm:"column:Password;type:char(64)" json:"password"` - FaceURL string `gorm:"column:FaceURL;type:char(64)" json:"faceURL"` - Nickname string `gorm:"column:Nickname;type:char(64)" json:"nickname"` - UserID string `gorm:"column:UserID;type:char(64)" json:"userID"` //openIM userID - Level int32 `gorm:"column:level;default:1" json:"level"` - CreateTime time.Time `gorm:"column:create_time" json:"createTime"` + Password string `gorm:"column:Password;type:char(64)" json:"password"` + FaceURL string `gorm:"column:FaceURL;type:char(64)" json:"faceURL"` + Nickname string `gorm:"column:Nickname;type:char(64)" json:"nickname"` + UserID string `gorm:"column:UserID;type:char(64)" json:"userID"` //openIM userID + Level int32 `gorm:"column:level;default:1" json:"level"` + CreateTime time.Time `gorm:"column:create_time" json:"createTime"` } // RegisterAddFriend 注册时默认好友 type RegisterAddFriend struct { UserID string `gorm:"column:user_id;primary_key;type:char(64)" json:"userID"` - CreateTime time.Time `gorm:"column:create_time" json:"createTime"` + CreateTime time.Time `gorm:"column:create_time" json:"createTime"` } // RegisterAddGroup 注册时默认群组 type RegisterAddGroup struct { GroupID string `gorm:"column:group_id;primary_key;type:char(64)" json:"userID"` - CreateTime time.Time `gorm:"column:create_time" json:"createTime"` + CreateTime time.Time `gorm:"column:create_time" json:"createTime"` } // ClientInitConfig 系统相关配置项 type ClientInitConfig struct { - DiscoverPageURL string `gorm:"column:discover_page_url;size:128" json:"discoverPageURL"` - OrdinaryUserAddFriend int32 `gorm:"column:ordinary_user_add_friend; default:1" json:"ordinaryUserAddFriend"` - BossUserID string `gorm:"column:boss_user_id;type:char(64)" json:"bossUserID"` - AdminURL string `gorm:"column:admin_url;type:char(128)" json:"adminURL"` - AllowSendMsgNotFriend int32 `gorm:"column:allow_send_msg_not_friend;default:1" json:"allowSendMsgNotFriend"` + DiscoverPageURL string `gorm:"column:discover_page_url;size:128" json:"discoverPageURL"` + OrdinaryUserAddFriend int32 `gorm:"column:ordinary_user_add_friend; default:1" json:"ordinaryUserAddFriend"` + BossUserID string `gorm:"column:boss_user_id;type:char(64)" json:"bossUserID"` + AdminURL string `gorm:"column:admin_url;type:char(128)" json:"adminURL"` + AllowSendMsgNotFriend int32 `gorm:"column:allow_send_msg_not_friend;default:1" json:"allowSendMsgNotFriend"` NeedInvitationCodeRegister int32 `gorm:"column:need_invitation_code_register;default:0" json:"needInvitationCodeRegister"` } diff --git a/tools/data-conversion/chat/v2/chat.go b/tools/data-conversion/chat/v2/chat.go index 100e2566f..6690e110b 100644 --- a/tools/data-conversion/chat/v2/chat.go +++ b/tools/data-conversion/chat/v2/chat.go @@ -7,89 +7,89 @@ import ( // Register 注册信息表 type Register struct { UserID string `gorm:"column:user_id;primary_key;type:char(64)" json:"userID"` - DeviceID string `gorm:"column:device_id;type:varchar(255)" json:"deviceID"` - IP string `gorm:"column:ip;type:varchar(32)" json:"ip"` - Platform string `gorm:"column:platform;type:varchar(32)" json:"platform"` - AccountType string `gorm:"column:account_type;type:varchar(32)" json:"accountType"` //email phone account - Mode string `gorm:"column:mode;type:varchar(32)"` //user admin - CreateTime time.Time `gorm:"column:create_time" json:"createTime"` + DeviceID string `gorm:"column:device_id;type:varchar(255)" json:"deviceID"` + IP string `gorm:"column:ip;type:varchar(32)" json:"ip"` + Platform string `gorm:"column:platform;type:varchar(32)" json:"platform"` + AccountType string `gorm:"column:account_type;type:varchar(32)" json:"accountType"` //email phone account + Mode string `gorm:"column:mode;type:varchar(32)"` //user admin + CreateTime time.Time `gorm:"column:create_time" json:"createTime"` } // Account 账号密码表 type Account struct { UserID string `gorm:"column:user_id;primary_key;type:char(64)" json:"userID"` - Password string `gorm:"column:password;type:varchar(255)" json:"password"` - CreateTime time.Time `gorm:"column:create_time" json:"createTime"` - ChangeTime time.Time `gorm:"column:change_time" json:"changeTime"` + Password string `gorm:"column:password;type:varchar(255)" json:"password"` + CreateTime time.Time `gorm:"column:create_time" json:"createTime"` + ChangeTime time.Time `gorm:"column:change_time" json:"changeTime"` OperatorUserID string `gorm:"column:operator_user_id;type:varchar(64)" json:"operatorUserID"` } // Attribute 用户属性表 type Attribute struct { UserID string `gorm:"column:user_id;primary_key;type:char(64)" json:"userID"` - Account string `gorm:"column:account;type:char(64)" json:"account"` - PhoneNumber string `gorm:"column:phone_number;type:varchar(32)" json:"phoneNumber"` - AreaCode string `gorm:"column:area_code;type:varchar(8)" json:"areaCode"` - Email string `gorm:"column:email;type:varchar(64)" json:"email"` - Nickname string `gorm:"column:nickname;type:varchar(64)" json:"nickname"` - FaceURL string `gorm:"column:face_url;type:varchar(255)" json:"faceURL"` - Gender int32 `gorm:"column:gender" json:"gender"` - Birth uint32 `gorm:"column:birth" json:"birth"` - CreateTime time.Time `gorm:"column:create_time" json:"createTime"` - ChangeTime time.Time `gorm:"column:change_time" json:"changeTime"` - BirthTime time.Time `gorm:"column:birth_time" json:"birthTime"` - Level int32 `gorm:"column:level;default:1" json:"level"` - AllowVibration int32 `gorm:"column:allow_vibration;default:1" json:"allowVibration"` - AllowBeep int32 `gorm:"column:allow_beep;default:1" json:"allowBeep"` - AllowAddFriend int32 `gorm:"column:allow_add_friend;default:1" json:"allowAddFriend"` + Account string `gorm:"column:account;type:char(64)" json:"account"` + PhoneNumber string `gorm:"column:phone_number;type:varchar(32)" json:"phoneNumber"` + AreaCode string `gorm:"column:area_code;type:varchar(8)" json:"areaCode"` + Email string `gorm:"column:email;type:varchar(64)" json:"email"` + Nickname string `gorm:"column:nickname;type:varchar(64)" json:"nickname"` + FaceURL string `gorm:"column:face_url;type:varchar(255)" json:"faceURL"` + Gender int32 `gorm:"column:gender" json:"gender"` + Birth uint32 `gorm:"column:birth" json:"birth"` + CreateTime time.Time `gorm:"column:create_time" json:"createTime"` + ChangeTime time.Time `gorm:"column:change_time" json:"changeTime"` + BirthTime time.Time `gorm:"column:birth_time" json:"birthTime"` + Level int32 `gorm:"column:level;default:1" json:"level"` + AllowVibration int32 `gorm:"column:allow_vibration;default:1" json:"allowVibration"` + AllowBeep int32 `gorm:"column:allow_beep;default:1" json:"allowBeep"` + AllowAddFriend int32 `gorm:"column:allow_add_friend;default:1" json:"allowAddFriend"` } // 封号表 type ForbiddenAccount struct { UserID string `gorm:"column:user_id;index:userID;primary_key;type:char(64)" json:"userID"` - CreateTime time.Time `gorm:"column:create_time" json:"createTime"` - Reason string `gorm:"column:reason;type:varchar(255)" json:"reason"` - OperatorUserID string `gorm:"column:operator_user_id;type:varchar(255)" json:"operatorUserID"` + CreateTime time.Time `gorm:"column:create_time" json:"createTime"` + Reason string `gorm:"column:reason;type:varchar(255)" json:"reason"` + OperatorUserID string `gorm:"column:operator_user_id;type:varchar(255)" json:"operatorUserID"` } // 用户登录信息表 type UserLoginRecord struct { - UserID string `gorm:"column:user_id;size:64" json:"userID"` - LoginTime time.Time `gorm:"column:login_time" json:"loginTime"` - IP string `gorm:"column:ip;type:varchar(32)" json:"ip"` + UserID string `gorm:"column:user_id;size:64" json:"userID"` + LoginTime time.Time `gorm:"column:login_time" json:"loginTime"` + IP string `gorm:"column:ip;type:varchar(32)" json:"ip"` DeviceID string `gorm:"column:device_id;type:varchar(255)" json:"deviceID"` - Platform string `gorm:"column:platform;type:varchar(32)" json:"platform"` + Platform string `gorm:"column:platform;type:varchar(32)" json:"platform"` } // 禁止ip登录 注册 type IPForbidden struct { IP string `gorm:"column:ip;primary_key;type:char(32)" json:"ip"` - LimitRegister int32 `gorm:"column:limit_register" json:"limitRegister"` - LimitLogin int32 `gorm:"column:limit_login" json:"limitLogin"` - CreateTime time.Time `gorm:"column:create_time" json:"createTime"` + LimitRegister int32 `gorm:"column:limit_register" json:"limitRegister"` + LimitLogin int32 `gorm:"column:limit_login" json:"limitLogin"` + CreateTime time.Time `gorm:"column:create_time" json:"createTime"` } // 限制userID只能在某些ip登录 type LimitUserLoginIP struct { UserID string `gorm:"column:user_id;primary_key;type:char(64)" json:"userID"` - IP string `gorm:"column:ip;primary_key;type:char(32)" json:"ip"` - CreateTime time.Time `gorm:"column:create_time" json:"createTime"` + IP string `gorm:"column:ip;primary_key;type:char(32)" json:"ip"` + CreateTime time.Time `gorm:"column:create_time" json:"createTime"` } // 邀请码被注册使用 type InvitationRegister struct { InvitationCode string `gorm:"column:invitation_code;primary_key;type:char(32)" json:"invitationCode"` - CreateTime time.Time `gorm:"column:create_time" json:"createTime"` - UsedByUserID string `gorm:"column:user_id;index:userID;type:char(64)" json:"usedByUserID"` + CreateTime time.Time `gorm:"column:create_time" json:"createTime"` + UsedByUserID string `gorm:"column:user_id;index:userID;type:char(64)" json:"usedByUserID"` } type SignalRecord struct { - FileName string `gorm:"column:file_name;primary_key;type:char(128)" json:"fileName"` + FileName string `gorm:"column:file_name;primary_key;type:char(128)" json:"fileName"` MediaType string `gorm:"column:media_type;type:char(64);index:media_type_index" json:"mediaType"` - RoomType string `gorm:"column:room_type;type:char(20)" json:"roomType"` - SenderID string `gorm:"column:sender_id;type:char(64);index:sender_id_index" json:"senderID"` - RecvID string `gorm:"column:recv_id;type:char(64);index:recv_id_index" json:"recvID"` - GroupID string `gorm:"column:group_id;type:char(64)" json:"groupID"` - DownloadURL string `gorm:"column:download_url;type:text" json:"downloadURL"` - CreateTime time.Time `gorm:"create_time;index:create_time_index" json:"createTime"` + RoomType string `gorm:"column:room_type;type:char(20)" json:"roomType"` + SenderID string `gorm:"column:sender_id;type:char(64);index:sender_id_index" json:"senderID"` + RecvID string `gorm:"column:recv_id;type:char(64);index:recv_id_index" json:"recvID"` + GroupID string `gorm:"column:group_id;type:char(64)" json:"groupID"` + DownloadURL string `gorm:"column:download_url;type:text" json:"downloadURL"` + CreateTime time.Time `gorm:"create_time;index:create_time_index" json:"createTime"` } diff --git a/tools/data-conversion/openim/common/config.go b/tools/data-conversion/openim/common/config.go index ddfe779cc..e2bd14a05 100644 --- a/tools/data-conversion/openim/common/config.go +++ b/tools/data-conversion/openim/common/config.go @@ -16,7 +16,7 @@ package common // =================================== V2 ===================================== // MySQL -// V2 +// V2. const ( UsernameV2 = "root" PasswordV2 = "openIM" @@ -24,7 +24,7 @@ const ( DatabaseV2 = "openIM_v2" ) -// V2 chat +// V2 chat. const ( ChatUsernameV2 = "root" ChatPasswordV2 = "openIM" @@ -32,14 +32,14 @@ const ( ChatDatabaseV2 = "admin_chat" ) -// Kafka +// Kafka. const ( Topic = "ws2ms_chat" KafkaAddr = "121.5.182.23:9092" ) // =================================== V3 ===================================== -// V3 +// V3. const ( UsernameV3 = "root" PasswordV3 = "openIM123" @@ -47,7 +47,7 @@ const ( DatabaseV3 = "openIM_v3" ) -// V3 chat +// V3 chat. const ( ChatUsernameV3 = "root" ChatPasswordV3 = "openIM123" @@ -55,7 +55,7 @@ const ( ChatDatabaseV3 = "openim_enterprise" ) -// Zookeeper +// Zookeeper. const ( ZkAddr = "43.134.63.160:12181" ZKSchema = "openim" diff --git a/tools/data-conversion/openim/msg.go b/tools/data-conversion/openim/msg.go index dc526e1bb..338fbf111 100644 --- a/tools/data-conversion/openim/msg.go +++ b/tools/data-conversion/openim/msg.go @@ -3,20 +3,22 @@ package main import ( "context" "encoding/json" + "log" + "sync" + "sync/atomic" + "time" + "github.com/IBM/sarama" "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/msg" "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/mw" "github.com/golang/protobuf/proto" - "github.com/openimsdk/open-im-server/v3/pkg/apistruct" - pbmsg "github.com/openimsdk/open-im-server/v3/tools/data-conversion/openim/proto/msg" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" - "log" - "sync" - "sync/atomic" - "time" + + "github.com/openimsdk/open-im-server/v3/pkg/apistruct" + pbmsg "github.com/openimsdk/open-im-server/v3/tools/data-conversion/openim/proto/msg" ) func main() { diff --git a/tools/data-conversion/openim/mysql.go b/tools/data-conversion/openim/mysql.go index 6cffae8e6..8992e12c4 100644 --- a/tools/data-conversion/openim/mysql.go +++ b/tools/data-conversion/openim/mysql.go @@ -16,12 +16,14 @@ package main import ( "fmt" - "github.com/openimsdk/open-im-server/v3/tools/data-conversion/openim/mysql/conversion" - "github.com/openimsdk/open-im-server/v3/tools/data-conversion/utils" + "log" + "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/logger" - "log" + + "github.com/openimsdk/open-im-server/v3/tools/data-conversion/openim/mysql/conversion" + "github.com/openimsdk/open-im-server/v3/tools/data-conversion/utils" ) func main() { diff --git a/tools/data-conversion/openim/mysql/cmd.go b/tools/data-conversion/openim/mysql/cmd.go index 76e39210b..924b0a206 100644 --- a/tools/data-conversion/openim/mysql/cmd.go +++ b/tools/data-conversion/openim/mysql/cmd.go @@ -2,12 +2,14 @@ package mysql import ( "fmt" - "github.com/openimsdk/open-im-server/v3/tools/data-conversion/openim/mysql/conversion" - "github.com/openimsdk/open-im-server/v3/tools/data-conversion/utils" + "log" + "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/logger" - "log" + + "github.com/openimsdk/open-im-server/v3/tools/data-conversion/openim/mysql/conversion" + "github.com/openimsdk/open-im-server/v3/tools/data-conversion/utils" ) func Cmd() { diff --git a/tools/data-conversion/openim/mysql/conversion/conversion.go b/tools/data-conversion/openim/mysql/conversion/conversion.go index b0e22f696..298eefb50 100644 --- a/tools/data-conversion/openim/mysql/conversion/conversion.go +++ b/tools/data-conversion/openim/mysql/conversion/conversion.go @@ -2,6 +2,7 @@ package conversion import ( "github.com/OpenIMSDK/protocol/constant" + v3 "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" v2 "github.com/openimsdk/open-im-server/v3/tools/data-conversion/openim/mysql/v2" "github.com/openimsdk/open-im-server/v3/tools/data-conversion/utils" diff --git a/tools/data-conversion/openim/mysql/v2/model_struct.go b/tools/data-conversion/openim/mysql/v2/model_struct.go index c9fafc4b0..9da33f2a5 100644 --- a/tools/data-conversion/openim/mysql/v2/model_struct.go +++ b/tools/data-conversion/openim/mysql/v2/model_struct.go @@ -29,19 +29,19 @@ func (FriendRequest) TableName() string { } type Group struct { - GroupID string `gorm:"column:group_id;primary_key;size:64" json:"groupID" binding:"required"` - GroupName string `gorm:"column:name;size:255" json:"groupName"` - Notification string `gorm:"column:notification;size:255" json:"notification"` - Introduction string `gorm:"column:introduction;size:255" json:"introduction"` - FaceURL string `gorm:"column:face_url;size:255" json:"faceURL"` + GroupID string `gorm:"column:group_id;primary_key;size:64" json:"groupID" binding:"required"` + GroupName string `gorm:"column:name;size:255" json:"groupName"` + Notification string `gorm:"column:notification;size:255" json:"notification"` + Introduction string `gorm:"column:introduction;size:255" json:"introduction"` + FaceURL string `gorm:"column:face_url;size:255" json:"faceURL"` CreateTime time.Time `gorm:"column:create_time;index:create_time"` - Ex string `gorm:"column:ex" json:"ex;size:1024" json:"ex"` + Ex string `gorm:"column:ex" json:"ex;size:1024"` Status int32 `gorm:"column:status"` CreatorUserID string `gorm:"column:creator_user_id;size:64"` GroupType int32 `gorm:"column:group_type"` NeedVerification int32 `gorm:"column:need_verification"` - LookMemberInfo int32 `gorm:"column:look_member_info" json:"lookMemberInfo"` - ApplyMemberFriend int32 `gorm:"column:apply_member_friend" json:"applyMemberFriend"` + LookMemberInfo int32 `gorm:"column:look_member_info" json:"lookMemberInfo"` + ApplyMemberFriend int32 `gorm:"column:apply_member_friend" json:"applyMemberFriend"` NotificationUpdateTime time.Time `gorm:"column:notification_update_time"` NotificationUserID string `gorm:"column:notification_user_id;size:64"` } diff --git a/tools/data-conversion/utils/find_insert.go b/tools/data-conversion/utils/find_insert.go index 5fe0fbfde..4789cd554 100644 --- a/tools/data-conversion/utils/find_insert.go +++ b/tools/data-conversion/utils/find_insert.go @@ -2,11 +2,12 @@ package utils import ( "fmt" - "gorm.io/gorm" - "gorm.io/gorm/schema" "log" "sync" "sync/atomic" + + "gorm.io/gorm" + "gorm.io/gorm/schema" ) func FindAndInsert[V2 any, V3 schema.Tabler](v2db *gorm.DB, v3db *gorm.DB, fn func(V2) (V3, bool)) (string, error) {