Skip to content

Commit cb9ad33

Browse files
PseudoCowboyJacksonTian
authored andcommitted
reply controller & reply service unit test
1 parent b8a843f commit cb9ad33

File tree

13 files changed

+821
-13
lines changed

13 files changed

+821
-13
lines changed

app/controller/reply.js

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
'use strict';
2+
3+
const Controller = require('egg').Controller;
4+
5+
class ReplyController extends Controller {
6+
/**
7+
* 添加回复
8+
*/
9+
async add() {
10+
const { ctx, service } = this;
11+
const content = ctx.request.body.r_content;
12+
const reply_id = ctx.params.reply_id;
13+
14+
if (content.trim() === '') {
15+
ctx.status = 422;
16+
ctx.body = {
17+
error: '回复内容不能为空!',
18+
};
19+
return;
20+
}
21+
22+
const topic_id = ctx.params.topic_id;
23+
let topic = await service.topic.getTopicById(topic_id);
24+
topic = topic.topic;
25+
26+
if (!topic) {
27+
ctx.status = 404;
28+
ctx.message = '这个主题不存在。';
29+
return;
30+
}
31+
if (topic.lock) {
32+
ctx.status = 403;
33+
ctx.body = {
34+
error: '该主题已锁定',
35+
};
36+
return;
37+
}
38+
39+
const user_id = ctx.user._id;
40+
const topicAuthor = await service.user.getUserById(topic.author_id);
41+
const newContent = content.replace('@' + topicAuthor.loginname + ' ', '');
42+
const [
43+
reply, user,
44+
] = await Promise.all([
45+
service.reply.newAndSave(content, topic_id, user_id, reply_id),
46+
service.user.getUserById(user_id),
47+
]);
48+
49+
user.score += 5;
50+
user.reply_count += 1;
51+
await Promise.all([
52+
service.topic.updateLastReply(topic_id, reply._id),
53+
user.save(),
54+
]);
55+
service.at.sendMessageToMentionUsers(newContent, topic_id, user_id, reply._id);
56+
if (topic.author_id.toString() !== user_id.toString()) {
57+
await service.message.sendReplyMessage(topic.author_id, user_id, topic._id, reply._id);
58+
}
59+
60+
ctx.redirect('/topic/' + topic_id + '#' + reply._id);
61+
}
62+
/**
63+
* 打开回复编辑器
64+
*/
65+
async showEdit() {
66+
const { ctx, service } = this;
67+
const reply_id = ctx.params.reply_id;
68+
const reply = await service.reply.getReplyById(reply_id);
69+
70+
if (!reply) {
71+
ctx.status = 404;
72+
ctx.message = '此回复不存在或已被删除。';
73+
return;
74+
}
75+
if (ctx.user._id.toString() === reply.author_id.toString() || ctx.user.is_admin) {
76+
await ctx.render('reply/edit', {
77+
reply_id: reply._id,
78+
content: reply.content,
79+
});
80+
return;
81+
}
82+
ctx.status = 403;
83+
ctx.body = {
84+
error: '对不起,你不能编辑此回复',
85+
};
86+
return;
87+
}
88+
/**
89+
* 提交编辑回复
90+
*/
91+
async update() {
92+
const { ctx, service } = this;
93+
const reply_id = ctx.params.reply_id;
94+
const content = ctx.request.body.t_content;
95+
const reply = await service.reply.getReplyById(reply_id);
96+
97+
if (!reply) {
98+
ctx.status = 404;
99+
ctx.message = '此回复不存在或已被删除。';
100+
return;
101+
}
102+
if (ctx.user._id.toString() === reply.author_id.toString() || ctx.user.is_admin) {
103+
if (content.trim() !== '') {
104+
reply.content = content;
105+
reply.update_at = new Date();
106+
await reply.save();
107+
ctx.redirect('/topic/' + reply.topic_id + '#' + reply._id);
108+
return;
109+
}
110+
ctx.status = 400;
111+
ctx.body = {
112+
error: '回复的字数太少。',
113+
};
114+
return;
115+
}
116+
ctx.status = 403;
117+
ctx.body = {
118+
error: '对不起,你不能编辑此回复',
119+
};
120+
return;
121+
}
122+
/**
123+
* 删除回复
124+
*/
125+
async delete() {
126+
const { ctx, service } = this;
127+
const reply_id = ctx.params.reply_id;
128+
const reply = await service.reply.getReplyById(reply_id);
129+
130+
if (!reply) {
131+
ctx.status = 422;
132+
ctx.body = { status: 'no reply ' + reply_id + ' exists' };
133+
return;
134+
}
135+
if (reply.author_id.toString() === ctx.user._id.toString() || ctx.user.is_admin) {
136+
reply.deleted = true;
137+
reply.save();
138+
ctx.status = 200;
139+
ctx.body = { status: 'success' };
140+
reply.author.score -= 5;
141+
reply.author.reply_count -= 1;
142+
reply.author.save();
143+
} else {
144+
ctx.status = 200;
145+
ctx.body = { status: 'failed' };
146+
}
147+
await service.topic.reduceCount(reply.topic_id);
148+
return;
149+
}
150+
/**
151+
* 回复点赞
152+
*/
153+
async up() {
154+
const { ctx, service } = this;
155+
const reply_id = ctx.params.reply_id;
156+
const user_id = ctx.user._id;
157+
const reply = await service.reply.getReplyById(reply_id);
158+
159+
if (!reply) {
160+
ctx.status = 404;
161+
ctx.message = '此回复不存在或已被删除。';
162+
return;
163+
}
164+
if (reply.author_id.toString() === user_id.toString()) {
165+
ctx.body = {
166+
success: false,
167+
message: '呵呵,不能帮自己点赞。',
168+
};
169+
return;
170+
}
171+
let action;
172+
reply.ups = reply.ups || [];
173+
const upIndex = reply.ups.indexOf(user_id);
174+
if (upIndex === -1) {
175+
reply.ups.push(user_id);
176+
action = 'up';
177+
} else {
178+
reply.ups.splice(upIndex, 1);
179+
action = 'down';
180+
}
181+
await reply.save();
182+
ctx.body = {
183+
success: true,
184+
action,
185+
};
186+
}
187+
}
188+
189+
module.exports = ReplyController;

app/model/reply.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ module.exports = app => {
1717
content_is_html: { type: Boolean },
1818
ups: [ Schema.Types.ObjectId ],
1919
deleted: { type: Boolean, default: false },
20+
}, {
21+
usePushEach: true,
2022
});
2123

2224
ReplySchema.plugin(BaseModel);

app/router.js

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
module.exports = app => {
77
const { router, controller, config, middleware } = app;
88

9-
const { site, sign, user, topic, rss, search, page } = controller;
9+
const { site, sign, user, topic, rss, search, page, reply } = controller;
1010

1111
const userRequired = middleware.userRequired();
1212
const adminRequired = middleware.adminRequired();
@@ -85,13 +85,14 @@ module.exports = app => {
8585
// router.post('/topic/de_collect', userRequired, topic.de_collect); // 取消关注某话题
8686

8787
// // reply controller
88-
// router.post('/:topic_id/reply', userRequired, limit.peruserperday('create_reply', config.create_reply_per_day, { showJson: false }), reply.add); // 提交一级回复
89-
// router.get('/reply/:reply_id/edit', userRequired, reply.showEdit); // 修改自己的评论页
90-
// router.post('/reply/:reply_id/edit', userRequired, reply.update); // 修改某评论
91-
// router.post('/reply/:reply_id/delete', userRequired, reply.delete); // 删除某评论
92-
// router.post('/reply/:reply_id/up', userRequired, reply.up); // 为评论点赞
93-
// router.post('/upload', userRequired, topic.upload); // 上传图片
94-
88+
router.post('/:topic_id/reply', userRequired,
89+
// limit.peruserperday('create_reply', config.create_reply_per_day, { showJson: false }),
90+
reply.add); // 提交一级回复
91+
router.get('/reply/:reply_id/edit', userRequired, reply.showEdit); // 修改自己的评论页
92+
router.post('/reply/:reply_id/edit', userRequired, reply.update); // 修改某评论
93+
router.post('/reply/:reply_id/delete', userRequired, reply.delete); // 删除某评论
94+
router.post('/reply/:reply_id/up', userRequired, reply.up); // 为评论点赞
95+
// router.post('/upload', auth.userRequired, topic.upload); // 上传图片
9596
// static page
9697
router.get('/about', page.about);
9798
router.get('/faq', page.faq);

app/service/reply.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ class ReplyService extends Service {
6868
return;
6969
}
7070
item.content = await this.service.at.linkUsers(item.content);
71+
return item;
7172
})
7273
);
7374
}

app/view/editor_sidebar.html

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<div id='sidebar'>
2+
<div class='panel'>
3+
<div class='header'>
4+
<span class='col_fade'>Markdown 语法参考</span>
5+
</div>
6+
<div class='inner'>
7+
<ol>
8+
<li><tt>### 单行的标题</tt></li>
9+
<li><tt>**粗体**</tt></li>
10+
<li><tt>`console.log('行内代码')`</tt></li>
11+
<li><tt>```js\n code \n```</tt> 标记代码块</li>
12+
<li><tt>[内容](链接)</tt></li>
13+
<li><tt>![文字说明](图片链接)</tt></li>
14+
</ol>
15+
<span><a href='https://segmentfault.com/markdown' target='_blank'>Markdown 文档</a></span>
16+
</div>
17+
</div>
18+
19+
<div class='panel'>
20+
<div class='header'>
21+
<span class='col_fade'>话题发布指南</span>
22+
</div>
23+
<div class='inner'>
24+
<ol>
25+
<li>尽量把话题要点浓缩到标题里</li>
26+
<li>代码含义和报错可在 <a href="http://segmentfault.com/t/node.js">SegmentFault</a> 提问</li>
27+
</ol>
28+
</div>
29+
</div>
30+
31+
</div>

app/view/includes/editor.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<%- Loader('/public/editor.min.js')
2+
.js('/public/libs/editor/editor.js')
3+
.js('/public/libs/webuploader/webuploader.withoutimage.js')
4+
.js('/public/libs/editor/ext.js')
5+
.done(assets, config.site_static_host, config.mini_assets)
6+
%>

app/view/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<% include sidebar.html %>
1+
<% include ./sidebar.html %>
22

33
<div id="content">
44
<div class="panel">
@@ -12,7 +12,7 @@
1212
</div>
1313
<% if (typeof topics !== 'undefined' && topics.length > 0) { %>
1414
<div class="inner no-padding">
15-
<%- include('topic/list', {
15+
<%- include('./topic/list.html', {
1616
topics: topics,
1717
pages: pages,
1818
current_page: current_page,

app/view/reply/edit.html

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<%- include('../editor_sidebar.html') %>
2+
3+
<div id='content'>
4+
<div class='panel'>
5+
<div class='header'>
6+
<ol class='breadcrumb'>
7+
<li><a href='/'>主页</a><span class='divider'>/</span></li>
8+
<li class='active'>编辑回复</li>
9+
</ol>
10+
</div>
11+
<div class='inner post'>
12+
<% if (typeof edit_error !== 'undefined' && edit_error) { %>
13+
<div class="alert alert-error">
14+
<a class="close" data-dismiss="alert" href="#">&times;</a>
15+
<strong><%= edit_error %></strong>
16+
</div>
17+
<% } %>
18+
<% if (typeof error !== 'undefined' && error) { %>
19+
<div class="alert alert-error">
20+
<strong><%= error %></strong>
21+
</div>
22+
<% } else { %>
23+
<form id='edit_reply_form' action='/reply/<%= reply_id %>/edit' method='post'>
24+
<fieldset>
25+
<div class='markdown_editor in_editor'>
26+
<div class='markdown_in_editor'>
27+
<textarea class='editor' name='t_content' rows='20'
28+
placeholder='回复支持 Markdown 语法, 请注意标记代码'
29+
autofocus
30+
><%= typeof content !== 'undefined' && content || '' %></textarea>
31+
32+
<div class='editor_buttons'>
33+
<input type="submit" class='span-primary submit_btn' data-loading-text="提交中.."
34+
value="提交">
35+
</div>
36+
</div>
37+
38+
</div>
39+
40+
<input type='hidden' name='_csrf' value='<%= csrf %>'/>
41+
</fieldset>
42+
</form>
43+
</div>
44+
<% } %>
45+
</div>
46+
</div>
47+
48+
<!-- markdown editor -->
49+
<%- include('../includes/editor.html') %>
50+
<script>
51+
(function () {
52+
var editor = new Editor();
53+
editor.render($('.editor')[0]);
54+
})();
55+
</script>

0 commit comments

Comments
 (0)