サーバー、管理、クライアントを含むインスタントメッセージングアプリケーション
展開されて発売されました。クライアントと管理側を体験できるようになりました
デフォルトの役割と許可を自由に変更しないでください。少し愛情を感じて、文明化されていない名前を作らないでください。
卵のフレームワークを使用して、IMサービスのサーバー側面
モバイルインターネットの開発以来、WeChatが率いるインスタントメッセージングサービスは、私たちの生活のあらゆる角に統合されており、一部の会社のビジネスでも重要な役割を果たしています。当社はインスタントメッセージングサービスを使用していましたが、多くのカスタマイズされたニーズを達成することはできません。したがって、カスタマイズされたニーズを内部的に満たすインスタントメッセージングマイクロサービスを開発することが決定されました。
socket.ioフレームワークは、その時点でバックエンドが人々の不足だったために使用されました。いくつかの例を読んだ後、私はプラットフォーム全体で使用し、サポートするのが本当に便利だと思いました。したがって、このマイクロサービスはフロントエンドチームに実装されており、現在、効果はかなり良いです。
コミュニティは現在、この分野ではそれほど少ないか単純なコンテンツを持っています(パブリックチャットルームは1つだけです)。さらに、事業開発プロセス中にPMになることは非常に不快なので、ユニークなビジネスのものから脱却し、企業ビジネスとサーバー、管理、クライアントなどの単純な機能を組み合わないシンプルで完全なIMアプリケーションを実現したいと思います。クライアントの模倣の目的はWeChatです。なぜなら、私はそれに非常に精通しており、製品についてあまり考える必要がないからです。さらに、それを試してみる人はそれに非常に精通しており、あまり多くのコミュニケーションコストを必要としません。
インスタントメッセージングサービスの完全なセットを開発するには、次の部品が必要です。
エンタープライズレベルのフレームワークとアプリケーション用に生まれました
私がAlibabaのEgg.JSフレームワークをサポートとして選んだ理由は、彼らがその中の大規模な実装とセキュリティで良い仕事をしたからです。彼らが巣を選択しなかった理由は、 socket.ioを統合するのが面倒なからです。 ORMは後遺症を使用し、データベースはMySQLです。以前に使用したことがあるので、始めるのは難しくありません。
すぐにボックスのフロントエンド/デザインソリューション
Ant Design Proを選択する理由は、Vue Family Bucketに精通しているからです。この機会を利用して、反応エコシステム全体の開発プロセスに精通し、中国の2つの主要な開発フレームワークの本質的な違いと異なるパスを感じたいと思います。 Ant Design Proは数年間リリースされており、実際、中小企業に効率の改善をもたらしてきました。これは私のニーズにぴったりです。
vue.jsによって開発された標準ツール
@Vue/CLIを使用してIMサービスクライアント、モバイルH5プロジェクトを構築し、UIフレームワークはYouzan Vantを使用します。これは、私のオープンソースコンポーネントVue-PageスタックとMr. Huangのより良いスロールを統合してIMの基本機能を実現します
フロントエンドエンジニアとして、ほとんどの毎日のタスクでは、エンティティ関係について考える必要はありません。ただし、私の実際の経験に基づいて、エンティティの関係を理解することは、ビジネスモデルをよりよく理解するのに役立ちます。製品とビジネスの理解の改善は、私たちにとって大きな助けになります。需要レビュー中に多くの非論理的側面を見つけることができます(なぜ製品マネージャーについて不平を言う必要があるのですか)。現時点でそれを提案できれば、後続のプロセスでの繰り返しの開発を避けるためにイニシアチブを取り、同時に、製品側の学生と比較的良い相互作用を形成することができます(対立ではなく)。より重要なエンティティ関係の一部は次のとおりです。
上記の図から、ユーザーが関係図全体のコアであることがわかります。以下は、さまざまなエンティティ間の関係です。
以下は、セッション、役割、許可の詳細な紹介です。
インスタントメッセージングアプリケーションを完了するとき、最初に考慮する必要があるのは会話です。これはWeChatの会話ウィンドウです。会話とメッセージ、ユーザー、グループの関係について考えるには多くの努力が必要であり、最後に次の基本的な関係を形成します。
言い換えれば、ユーザーはセッションと直接的な関係を持たず、ユーザーに対応するシングルチャットとグループチャットを介してのみセッションを取得できます。これには次の利点があります。
柔軟で普遍的で便利な許可管理システムを設計するために、このシステムはRBAC(ロールベースのアクセス制御)コントロールを採用して、後の拡張の利便性のために一般的な「ユーザーロール許可」プラットフォームを設計します。
RBAC(ロールベースのアクセス制御)は、ユーザーがロールを通じて権限に関連付けられていることを指します。つまり、ユーザーにはいくつかの役割があり、各役割にはいくつかの許可があります(もちろん、競合する役割と権限を一緒に組み合わせることはありません)。このようにして、「ユーザーロール拡張」の承認モデルが構築されます。このモデルでは、一般に、ユーザーと役割の間に、および役割と権限の間には多くの多くの関係があります。
このシステムには、管理者、一般ユーザー、禁止されたユーザー、禁止ユーザーのデフォルトの役割があり、異なるアクセス許可が異なる役割に割り当てられています。したがって、管理や音声などのインターフェイスルーティングに対して(ミドルウェアを介した)処理を実行する必要があります。特定の方法と方法は、バックエンドプロジェクトで詳細に説明されます。このシステムは、事前定義された役割と権限の方法を一時的に使用します。将来拡張したい場合は、役割と権限を編集できます。
WeChatの管理側を見たことはありませんが、管理者がユーザーの役割と権限を構成し、グループステータスを編集できると想像できます。
登録してログインした後、友人を追加してグループに普通に参加し、個人の基本情報とプロセスアプリケーションを変更できます。
ログインできません
たとえば、オンラインでテストする必要がある個人センターの新しいバージョンがあります。まず、「テストパーソナルセンター」という新しい役割を作成し、対応する権限を役割に割り当てます。次に、通常のユーザーをグループ化し、この役割を構成するために一部の人を選択して、テストできるようにします。
インスタントメッセージングサービスのコアコミュニケーションの原則について話しましょう。一般的なHTTPサービスと同様に、コミュニケーション用のサーバーとクライアントがありますが、詳細なプロトコルと処理方法は異なります。
歴史的な理由により、主流のHTTPプロトコルは現在、ステートレスプロトコルになっています(HTTP2は当面は広く使用されていません)。一般的に、クライアントはリクエストを積極的に開始し、それに応答します。そのため、サーバーがクライアントに情報をプッシュすることを認識するために、フロントエンドはバックエンドを積極的に投票する必要があります。この方法は非効率的でエラーが発生しやすいです。これは確かに(5秒)前に管理ホームページで行われます。
サーバー側が情報を積極的にプッシュするこの必要性を達成するために、HTML5は単一のTCP接続、つまりWebSocketで全二重通信のプロトコルを提供し始めました。 WebSocketにより、クライアントとサーバー間のデータ交換が容易になり、サーバーがクライアントにデータを積極的にプッシュできます。 Websocketプロトコルは2008年に生まれ、2011年に国際標準になりました。現在、ほとんどのブラウザはすでにそれをサポートしています。
WebSocketの使用は非常に簡単です:
var ws = new WebSocket("wss://echo.websocket.org");
ws.onopen = function(evt) {
console.log("Connection open ...");
ws.send("Hello WebSockets!");
};
ws.onmessage = function(evt) {
console.log( "Received Message: " + evt.data);
ws.close();
};
ws.onclose = function(evt) {
console.log("Connection closed.");
};
WebSocketプロトコルを使用すると、サーバーには高度な武器があり、情報を積極的にプッシュします。それでは、古いブラウザや新しいブラウザと互換性がある方法はありますか?実際、多くの人がこれを考えており、答えはsocket.ioです
socket.io socket.io WebSocketインターフェイスをさらにカプセル化し、通信のために古いブラウザでのポーリングの使用に自動的に切り替えることができます(ユーザーは知覚しません)。主に次の利点があります。
これはsocket.ioホームページです
最も速く、最も信頼性の高いインスタントメッセージングエンジン(最も速くて最も信頼性の高いリアルタイムエンジンを備えています)
使いやすいです:
var io = require('socket.io')(80);
var cfg = require('./config.json');
var tw = require('node-tweet-stream')(cfg);
tw.track('socket.io');
tw.track('javascript');
tw.on('tweet', function(tweet){
io.emit('tweet', tweet);
});
サーバーサイドプロジェクトのいくつかの重要なポイントに焦点を当てましょう。コンテンツのほとんどは、Egg公式Webサイトで表示する必要があります。
scaffolding npm init egg --type=simple Server Projectを初期化し、MySQL(My ISバージョン8.0)をインストールし、Secelizeに必要なデータベースリンクパスワードなどを構成し、起動できます。
// 目录结构说明
├── package.json // 项目信息
├── app.js // 启动文件,其中有一些钩子函数
├── app
| ├── router.js // 路由
│ ├── controller
│ ├── service
│ ├── middleware // 中间件
│ ├── model // 实体模型
│ └── io // socket.io 相关
│ ├── controller
│ └── middleware // io独有的中间件
├── config // 配置文件
| ├── plugin.js // 插件配置文件
| └── config.default.js // 默认的配置文件
├── logs // server运行期间产生的log文件
└── public // 静态文件和上传文件目录
ルーターは、主に、実行アクション、つまりapp/routerを特に実施するリクエストURLとコントローラーの間の対応を説明するために使用されます
app/middleware/auth.jsにあります。app/middleware/admin.jsにあるすべての管理インターフェイスでの管理権許可のチェックを追加しましたこのシステムは管理者や一般的なコミュニケーションユーザーとは異なる役割を持っているため、管理と通信のインターフェイスルーティングには統一された認証処理が必要です。
たとえば、Management Side Route /v1/admin/... 、このシリーズのすべてのルートに管理者認証を追加したいと思います。この時点で、ミドルウェアを使用して認証できます。以下は、管理ルーターでミドルウェアを使用する具体的な例です。
// middware
module.exports = () => {
return async function admin(ctx, next) {
let { session } = ctx;
// 判断admin权限
if (session.user && session.user.rights.some(right => right.keyName === 'admin')) {
await next();
} else {
ctx.redirect('/login');
}
};
};
// router
const admin = app.middleware.admin();
router.get('/api/v1/admin/rights', admin, controller.v1.admin.rightsIndex);
使用されている後遺症+MySQLの組み合わせ、および卵には関連するプラグインも続けています。 Sequelizeは、ノード環境で使用されるORMで、Postgres、MySQL、Mariadb、SQLite、Microsoft SQL Serverをサポートしています。最初にモデルとモデルの間の直接的な関係を定義する必要があります。関係が確立された後、いくつかのプリセット方法を使用できます。
モデルの基本情報は処理が簡単です。注意する必要があるのは、エンティティ間の関係デザイン、つまりアソシエイトです。以下は、ユーザーの関係の説明です
// User.js
module.exports = app => {
const { STRING } = app.Sequelize;
const User = app.model.define('user', {
provider: {
type: STRING
},
username: {
type: STRING,
unique: 'username'
},
password: {
type: STRING
}
});
User.associate = function() {
// One-To-One associations
app.model.User.hasOne(app.model.UserInfo);
// One-To-Many associations
app.model.User.hasMany(app.model.Apply);
// Many-To-Many associations
app.model.User.belongsToMany(app.model.Group, { through: 'user_group' });
app.model.User.belongsToMany(app.model.Role, { through: 'user_role' });
};
return User;
};
たとえば、ユーザーとuserInfoの関係は1対1の関係です。定義された後、新しいユーザーを作成するときにuser.setUserInfo(userInfo)を使用できます。このユーザーの基本情報を取得する場合は、 user.getUserInfo()を使用することもできます。
ユーザーとApplyの関係は1対多いです。つまり、ユーザーは複数のアプリケーションに対応でき、現在は友人とグループアプリケーションのみが適用されます。
アプリケーションを追加するときは、 user.addApply(apply)を使用できます。取得する場合は、次のように使用できます。
const result = await ctx.model.Apply.findAndCountAll({
where: {
userId: ctx.session.user.id,
hasHandled: false
}
});
ユーザーとグループの関係は多くの人から多いです。つまり、ユーザーは複数のグループに対応でき、グループは複数のユーザーに対応できます。このようにして、Sequelizeはこの関係を達成するために中間のテーブルUser_Groupを確立します。
私は通常これを使用します:
group.addUser(user); // 建立群组和用户的关系
user.getGroups(); // 获取用户的群组信息
卵はegg-socket.ioプラグインを提供します。 egg-socket.ioをインストールした後、config/plugin.jsでプラグインを開く必要があります。 IOには独自のミドルウェアとコントローラーがあります。
IOのルートは、一般的なHTTP要求のルートとは異なります。ここのルートはミドルウェアで処理できない(成功しなかった)ため、コントローラーで禁止処理を処理したことに注意してください。
// 加入群
io.of('/').route('/v1/im/join', app.io.controller.im.join);
// 发送消息
io.of('/').route('/v1/im/new-message', app.io.controller.im.newMessage);
// 查询消息
io.of('/').route('/v1/im/get-messages', app.io.controller.im.getMessages);
注:グループと友人の関係の両方を部屋(つまり、セッション)と見なしているので、ROMMに直接メッセージを送信でき、内部の全員が受信できるようにします。
2つのデフォルトのミドルウェアがあります。1つは、接続および切断時に呼び出される接続ミドルウェアです。これは、ログインステータスを検証し、ビジネスロジックを処理するために使用されます。もう1つは、メッセージが送信されるたびに呼ばれるパケットミドルウェアです。これはログの印刷に使用されます
通話許可はプリセットであるため、コントローラーで処理されます
// 对用户发言的权限进行判断
if (!ctx.session.user.rights.some(right => right.keyName === 'speak')) {
return;
}
チャットは、シングルチャットとグループチャットに分かれています。チャット情報には一時的に、一般的なテキスト、写真、ビデオ、ポジショニングメッセージが含まれており、ビジネスに応じて注文または製品に拡張できます。
メッセージの構造設計とは、いくつかのサードパーティサービスの設計を指し、このプロジェクト自体の状況と組み合わせて調整されています。それは自由に拡張することができ、次の説明がなされます。
const Message = app.model.define('message', {
/**
* 消息类型:
* 0:单聊
* 1:群聊
*/
type: {
type: STRING
},
// 消息体
body: {
type: JSON
},
fromId: { type: INTEGER },
toId: { type: INTEGER }
});
ボディは、JSONを使用してさまざまなメッセージ形式を保存するために使用されるメッセージボディを保存します。
// 文本消息
{
"type": "txt",
"msg":"哈哈哈" //消息内容
}
// 图片消息
{
"type": "img",
"url": "http://nimtest.nos.netease.com/cbc500e8-e19c-4b0f-834b-c32d4dc1075e",
"ext":"jpg",
"w":360, //宽
"h":480, //高
"size": 388245
}
// 视频消息
{
"type": 'video',
"url": "http://nimtest.nos.netease.com/cbc500e8-e19c-4b0f-834b-c32d4dc1075e",
"ext":"mp4",
"w":360, //宽
"h":480, //高
"size": 388245
}
// 地理位置消息
{
"type": "loc",
"title":"中国 浙江省 杭州市 网商路 599号", //地理位置title
"lng":120.1908686708565, // 经度
"lat":30.18704515647036 // 纬度
}
現在、1つしかありません。これは、Baiduのトークンを更新するためです。ここでは非常に簡単です。公式のドキュメントを参照してください。
インテリジェントな対話のカスタマイズとサービスプラットフォームユニット
これは非常に興味深いです。新しいロボットを作成し、 https://ai.baidu.com/で対応するスキルを追加できます。ここでおしゃべりしていますが、スマートなQ&Aなどがあります。
起動したくない場合は、 ctx.service.baidu.getToken();
まず、構成ファイルで構成する必要があります。ここでファイルサイズを制限し、クロスサイトiOSビデオファイル形式:
config.multipart = {
mode: 'file',
fileSize: '3mb',
fileExtensions: ['.mov']
};
統一されたインターフェイスを使用して、ファイルのアップロードを処理します。コアの問題はファイルの書き込みであり、ファイルはフロントエンドから送信されるファイルリストです
for (const file of ctx.request.files) {
// 生成文件路径,注意upload文件路径需要存在
const filePath = `./public/upload/${
Date.now() + Math.floor(Math.random() * 100000).toString() + '.' + file.filename.split('.').pop()
}`;
const reader = fs.createReadStream(file.filepath); // 创建可读流
const upStream = fs.createWriteStream(filePath); // 创建可写流
reader.pipe(upStream); // 可读流通过管道写入可写流
data.push({
url: filePath.slice(1)
});
}
サーバーディレクトリに保存/public/upload/ 。このディレクトリには、静的ファイルの構成が必要です。
config.static = {
prefix: '/public/',
dir: path.join(appInfo.baseDir, 'public')
};
この章の公式卵の文書はあなたを殺すことです、例は何もありません、あなたはソースコードを読む必要があります。とてもひどいです。何が起こっているのかを理解する前に、私はそれを長い間勉強してきました。
アカウントのパスワードログインをより自由に制御したいので、アカウントパスワードログインにパスポートを使用するのではなく、通常のインターフェイス認証とセッションを使用します。
以下は、サードパーティのプラットフォームを使用してログインするプロセスの詳細な説明です(Githubを選択しました)。
プラグインを開きます:
// config/plugin.js
module.exports.passport = {
enable: true,
package: 'egg-passport',
};
module.exports.passportGithub = {
enable: true,
package: 'egg-passport-github',
};
// config.default.js
config.passportGithub = {
key: 'your_clientID',
secret: 'your_clientSecret',
callbackURL: 'http://localhost:3000/api/v1/passport/github/callback' // 注意这里非常的关键,这里需要和你在github上面设置的Authorization callback URL一致
};
this.app.passport.verify(verify);
const github = app.passport.authenticate('github', { successRedirect: '/' }); // successRedirect就是最后校验完毕后前端会跳转的路由,我这里直接跳转到主页了
router.get('/v1/passport/github', github);
router.get('/v1/passport/github/callback', github);
/v1/passport/githubクリックし、このアプリケーションのGitHub許可を開始します。成功後、githubはhttp://localhost:3000/v1/passport/github/callback?code=12313123123に行きます。 GitHubPassportプラグインは、GitHubでユーザーの情報を取得します。詳細情報を取得した後、 app/passport/verify.jsでユーザー情報を確認し、独自のプラットフォームのユーザー情報に関連付け、セッションに値を割り当てる必要があります。 // verify.js
module.exports = async (ctx, githubUser) => {
const { service } = ctx;
const { provider, name, photo, displayName } = githubUser;
ctx.logger.info('githubUser', { provider, name, photo, displayName });
let user = await ctx.model.User.findOne({
where: {
username: name
}
});
if (!user) {
user = await ctx.model.User.create({
provider,
username: name
});
const userInfo = await ctx.model.UserInfo.create({
nickname: displayName,
photo
});
const role = await ctx.model.Role.findOne({
where: {
keyName: 'user'
}
});
user.setUserInfo(userInfo);
user.addRole(role);
await user.save();
}
const { rights, roles } = await service.user.getUserAttribute(user.id);
// 权限判断
if (!rights.some(item => item.keyName === 'login')) {
ctx.body = {
statusCode: '1',
errorMessage: '不具备登录权限'
};
return;
}
ctx.session.user = {
id: user.id,
roles,
rights
};
return githubUser;
};
上記のコードに注意してください。それが最初の承認である場合、ユーザーが作成されます。それが2番目の承認である場合、ユーザーは作成されています。
システムが展開または実行されるとき、一部のデータとテーブルをプリセットする必要があり、コードはapp.jsとapp/service/startup.jsにあります
ロジックは、プロジェクトが開始された後、モデルを使用してテーブル構造をデータベースに同期し、基本的なデータの作成を開始することです。
上記を完了した後、初期データが完了し、正常に動作する可能性があります
Alibaba Cloudで購入したドメイン名であるTencent CloudでサーバーCentosを購入し、Node(12.18.2)、Nginx、MySQL8.0をインストールし、Centosで直接開始しました。フロントエンドは、Nginxを逆プロキシに使用します。サーバーのリソースが限られているため、JenkinsとDockerの自動化ツールはありません。これにより、更新時に手動操作が発生します。