Skip to content

Commit a2ec0bd

Browse files
committed
convert proxy to service code
1 parent 44ef2a1 commit a2ec0bd

File tree

12 files changed

+874
-3
lines changed

12 files changed

+874
-3
lines changed

.eslintrc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
11
{
2-
"extends": "eslint-config-egg"
2+
"extends": "eslint-config-egg",
3+
"parserOptions": {
4+
"ecmaVersion": 8,
5+
"sourceType": "script",
6+
"ecmaFeatures": {
7+
}
8+
}
39
}

app/common/tools.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
'use strict';
2+
3+
const bcrypt = require('bcryptjs');
4+
const moment = require('moment');
5+
6+
moment.locale('zh-cn'); // 使用中文
7+
8+
// 格式化时间
9+
exports.formatDate = function(date, friendly) {
10+
date = moment(date);
11+
12+
if (friendly) {
13+
return date.fromNow();
14+
}
15+
16+
return date.format('YYYY-MM-DD HH:mm');
17+
};
18+
19+
exports.validateId = function(str) {
20+
return (/^[a-zA-Z0-9\-_]+$/i).test(str);
21+
};
22+
23+
exports.bhash = function(str, callback) {
24+
bcrypt.hash(str, 10, callback);
25+
};
26+
27+
exports.bcompare = function(str, hash, callback) {
28+
bcrypt.compare(str, hash, callback);
29+
};

app/extend/application.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
const mongoose = require('mongoose');
44

55
const MONGODB = Symbol('Application#mongodb');
6-
const REDIS = Symbol('Application#redis');
76

87
// 扩展一些框架便利的方法
98
module.exports = {

app/models/base_model.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
'use strict';
2+
3+
/**
4+
* 给所有的 Model 扩展功能
5+
* http://mongoosejs.com/docs/plugins.html
6+
*/
7+
const tools = require('../common/tools');
8+
9+
module.exports = function(schema) {
10+
schema.methods.create_at_ago = function() {
11+
return tools.formatDate(this.create_at, true);
12+
};
13+
14+
schema.methods.update_at_ago = function() {
15+
return tools.formatDate(this.update_at, true);
16+
};
17+
};

app/service/at.js

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
'use strict';
2+
3+
module.exports = app => {
4+
5+
class AtService extends app.Service {
6+
/**
7+
* 从文本中提取出@username 标记的用户名数组
8+
* @param {String} text 文本内容
9+
* @return {Array} 用户名数组
10+
*/
11+
fetchUsers(text) {
12+
if (!text) {
13+
return [];
14+
}
15+
16+
const ignoreRegexs = [
17+
/```.+?```/g, // 去除单行的 ```
18+
/^```[\s\S]+?^```/gm, // ``` 里面的是 pre 标签内容
19+
/`[\s\S]+?`/g, // 同一行中,`some code` 中内容也不该被解析
20+
/^ {4}.*/gm, // 4个空格也是 pre 标签,在这里 . 不会匹配换行
21+
/\b\S*?@[^\s]*?\..+?\b/g, // somebody@gmail.com 会被去除
22+
/\[@.+?\]\(\/.+?\)/g, // 已经被 link 的 username
23+
];
24+
25+
ignoreRegexs.forEach(ignore_regex => {
26+
text = text.replace(ignore_regex, '');
27+
});
28+
29+
const results = text.match(/@[a-z0-9\-_]+\b/igm);
30+
const names = [];
31+
if (results) {
32+
for (let i = 0, l = results.length; i < l; i++) {
33+
let s = results[i];
34+
// remove leading char @
35+
s = s.slice(1);
36+
names.push(s);
37+
}
38+
}
39+
return [ ...new Set(names) ];
40+
}
41+
42+
/*
43+
* 根据文本内容中读取用户,并发送消息给提到的用户
44+
* @param {String} text 文本内容
45+
* @param {String} topicId 主题ID
46+
* @param {String} authorId 作者ID
47+
* @param {String} reply_id 回复ID
48+
*/
49+
async sendMessageToMentionUsers(text, topicId, authorId, reply_id = null) {
50+
let users = await this.service.user.getUsersByNames(this.fetchUsers(text));
51+
52+
users = users.filter(user => {
53+
return !user._id.equals(authorId);
54+
});
55+
56+
return Promise.all(users.map(user => {
57+
return this.service.message.sendAtMessage(user._id, authorId, topicId, reply_id);
58+
}));
59+
}
60+
61+
/**
62+
* 根据文本内容,替换为数据库中的数据
63+
* @param {String} text 文本内容
64+
* @return {String} 替换后的文本内容
65+
*/
66+
linkUsers(text) {
67+
const users = AtService.fetchUsers(text);
68+
for (let i = 0; i < users.length; i++) {
69+
const name = users[i];
70+
text = text.replace(new RegExp('@' + name + '\\b(?!\\])', 'g'), '[@' + name + '](/user/' + name + ')');
71+
}
72+
return text;
73+
}
74+
}
75+
76+
return AtService;
77+
};

app/service/message.js

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
'use strict';
2+
3+
const mongoose = require('mongoose');
4+
5+
const BaseModel = require('./base_model');
6+
const Schema = mongoose.Schema;
7+
const ObjectId = Schema.ObjectId;
8+
9+
module.exports = app => {
10+
11+
/*
12+
* type:
13+
* reply: xx 回复了你的话题
14+
* reply2: xx 在话题中回复了你
15+
* follow: xx 关注了你
16+
* at: xx @了你
17+
*/
18+
const MessageSchema = new Schema({
19+
type: { type: String },
20+
master_id: { type: ObjectId },
21+
author_id: { type: ObjectId },
22+
topic_id: { type: ObjectId },
23+
reply_id: { type: ObjectId },
24+
has_read: { type: Boolean, default: false },
25+
create_at: { type: Date, default: Date.now },
26+
});
27+
MessageSchema.plugin(BaseModel);
28+
MessageSchema.index({ master_id: 1, has_read: -1, create_at: -1 });
29+
const Message = mongoose.model('Message', MessageSchema);
30+
31+
return class extends app.Service {
32+
33+
/*
34+
* 根据用户ID,获取未读消息的数量
35+
* Callback:
36+
* @param {String} id 用户ID
37+
* @return {Promise[messagesCount]} 承载消息列表的 Promise 对象
38+
*/
39+
getMessagesCount(id) {
40+
return Message.count({ master_id: id, has_read: false }).exec();
41+
}
42+
43+
async getMessageRelations(message) {
44+
if (message.type === 'reply' || message.type === 'reply2' || message.type === 'at') {
45+
const [ author, topic, reply ] = await Promise.all([
46+
this.services.user.getUserById(message.author_id),
47+
this.services.topic.getTopicById(message.topic_id),
48+
this.services.reply.getReplyById(message.reply_id),
49+
]);
50+
51+
message.author = author;
52+
message.topic = topic;
53+
message.reply = reply;
54+
55+
if (!author || !topic) {
56+
message.is_invalid = true;
57+
}
58+
59+
return message;
60+
}
61+
62+
return { is_invalid: true };
63+
}
64+
65+
/*
66+
* 根据消息Id获取消息
67+
* @param {String} id 消息ID
68+
* @return {Promise[message]} 承载消息的 Promise 对象
69+
*/
70+
async getMessageById(id) {
71+
const message = await Message.findOne({ _id: id }).exec();
72+
return this.getMessageRelations(message);
73+
}
74+
75+
/*
76+
* 根据用户ID,获取已读消息列表
77+
* @param {String} userId 用户ID
78+
* @return {Promise[messages]} 承载消息列表的 Promise 对象
79+
*/
80+
getReadMessagesByUserId(userId) {
81+
const query = { master_id: userId, has_read: true };
82+
return Message.find(query, null,
83+
{ sort: '-create_at', limit: 20 }).exec();
84+
}
85+
86+
/*
87+
* 根据用户ID,获取未读消息列表
88+
* @param {String} userId 用户ID
89+
* @return {Promise[messages]} 承载消息列表的 Promise 对象
90+
*/
91+
getUnreadMessagesByUserId(userId) {
92+
const query = { master_id: userId, has_read: false };
93+
return Message.find(query, null,
94+
{ sort: '-create_at' }).exec();
95+
}
96+
97+
/*
98+
* 将消息设置成已读
99+
* @return {Promise[messages]} 承载消息列表的 Promise 对象
100+
*/
101+
async updateMessagesToRead(userId, messages) {
102+
if (messages.length === 0) {
103+
return;
104+
}
105+
106+
const ids = messages.map(function(m) {
107+
return m.id;
108+
});
109+
110+
const query = { master_id: userId, _id: { $in: ids } };
111+
const update = { $set: { has_read: true } };
112+
const opts = { multi: true };
113+
return Message.update(query, update, opts).exec();
114+
}
115+
116+
/**
117+
* 将单个消息设置成已读
118+
* @param {String} msgId 消息 ID
119+
* @return {Promise} 更新消息返回的 Promise 对象
120+
*/
121+
async updateOneMessageToRead(msgId) {
122+
if (!msgId) {
123+
return;
124+
}
125+
const query = { _id: msgId };
126+
const update = { $set: { has_read: true } };
127+
return Message.update(query, update, { multi: true }).exec();
128+
}
129+
};
130+
131+
};

0 commit comments

Comments
 (0)