题解点赞功能

pull/1/head
masnn 5 years ago
parent 41261f8c4b
commit 4c2edc3948

@ -25,6 +25,13 @@ class NotFoundError extends UserFacingError {
} }
} }
class AlreadyVotedError extends BadRequestError {
constructor(psid, uid) {
super('You\'ve already voted.');
this.params = [psid, uid];
}
}
class LoginError extends ForbiddenError { class LoginError extends ForbiddenError {
constructor(uname) { constructor(uname) {
super('LoginError'); super('LoginError');
@ -122,7 +129,8 @@ module.exports = {
UserNotFoundError, VerifyPasswordError, ProblemDataNotFoundError, UserNotFoundError, VerifyPasswordError, ProblemDataNotFoundError,
OpcountExceededError, PermissionError, NoProblemError, OpcountExceededError, PermissionError, NoProblemError,
ValidationError, ProblemNotFoundError, TrainingNotFoundError, ValidationError, ProblemNotFoundError, TrainingNotFoundError,
ContestNotFoundError, RecordNotFoundError, SolutionNotFoundError ContestNotFoundError, RecordNotFoundError, SolutionNotFoundError,
AlreadyVotedError
}; };
/* /*
@ -199,11 +207,6 @@ class InvalidOperationError(ForbiddenError):
pass pass
class AlreadyVotedError(ForbiddenError):
@property
def message(self):
return "You've already voted."
class InvalidTokenDigestError(ForbiddenError): class InvalidTokenDigestError(ForbiddenError):
pass pass

@ -10,7 +10,8 @@ const
{ ROUTE } = require('../service/server'), { ROUTE } = require('../service/server'),
gridfs = require('../service/gridfs'), gridfs = require('../service/gridfs'),
queue = require('../service/queue'), queue = require('../service/queue'),
{ NoProblemError, ProblemDataNotFoundError, BadRequestError } = require('../error'), { NoProblemError, ProblemDataNotFoundError, BadRequestError,
SolutionNotFoundError } = require('../error'),
{ constants } = require('../options'), { constants } = require('../options'),
{ {
PERM_VIEW_PROBLEM, PERM_VIEW_PROBLEM,
@ -242,13 +243,14 @@ class ProblemSolutionHandler extends ProblemDetailHandler {
} }
} }
let udict = await user.getList(uids); let udict = await user.getList(uids);
let pssdict = solution.getListStatus(docids, this.uid);
this.ctx.body.path = [ this.ctx.body.path = [
['problem_main', '/p'], ['problem_main', '/p'],
[this.pdoc.title, `/p/${this.pdoc.pid}`, true], [this.pdoc.title, `/p/${this.pdoc.pid}`, true],
['problem_solution', null] ['problem_solution', null]
]; ];
this.ctx.body = Object.assign(this.ctx.body, { this.ctx.body = Object.assign(this.ctx.body, {
psdocs, page, pcount, pscount, udict psdocs, page, pcount, pscount, udict, pssdict
}); });
} }
async post({ psid }) { async post({ psid }) {
@ -289,6 +291,16 @@ class ProblemSolutionHandler extends ProblemDetailHandler {
await solution.delReply(psid, psrid); await solution.delReply(psid, psrid);
this.ctx.back(); this.ctx.back();
} }
async post_upvote() {
let [psdoc, pssdoc] = await solution.vote(this.psdoc._id, this.uid, 1);
this.ctx.body = { vote: psdoc.vote, user_vote: pssdoc.vote };
if (!this.ctx.preferJson) this.ctx.back();
}
async post_downvote() {
let [psdoc, pssdoc] = await solution.vote(this.psdoc._id, this.uid, -1);
this.ctx.body = { vote: psdoc.vote, user_vote: pssdoc.vote };
if (!this.ctx.preferJson) this.ctx.back();
}
} }
class ProblemSolutionRawHandler extends ProblemDetailHandler { class ProblemSolutionRawHandler extends ProblemDetailHandler {

@ -81,7 +81,6 @@ async function random(query) {
let pdoc = await pdocs.skip(Math.floor(Math.random() * pcount)).limit(1).toArray()[0]; let pdoc = await pdocs.skip(Math.floor(Math.random() * pcount)).limit(1).toArray()[0];
return pdoc.pid; return pdoc.pid;
} else return null; } else return null;
} }
module.exports = { module.exports = {

@ -1,9 +1,10 @@
const const
{ ObjectID } = require('bson'), { ObjectID } = require('bson'),
{ SolutionNotFoundError } = require('../error'), { SolutionNotFoundError, AlreadyVotedError } = require('../error'),
validator = require('../lib/validator'), validator = require('../lib/validator'),
db = require('../service/db.js'), db = require('../service/db.js'),
coll = db.collection('solution'); coll = db.collection('solution'),
coll_status = db.collection('solution.status');
/** /**
* @param {string} pid * @param {string} pid
@ -73,6 +74,22 @@ async function editReply(psid, psrid, content) {
async function delReply(psid, psrid) { async function delReply(psid, psrid) {
return await coll.findOneAndUpdate({ _id: psid }, { $pull: { reply: { _id: psrid } } }); return await coll.findOneAndUpdate({ _id: psid }, { $pull: { reply: { _id: psrid } } });
} }
async function vote(psid, uid, value) {
let pssdoc = await coll_status.findOne({ psid, uid });
if (pssdoc) await coll_status.deleteOne({ psid, uid });
await coll_status.insertOne({ psid, uid, vote: value });
if (pssdoc) value += -pssdoc.vote;
await coll.findOneAndUpdate({ _id: psid }, { $inc: { vote: value } });
pssdoc = await coll_status.findOne({ psid, uid });
let psdoc = await coll.findOne({ _id: psid });
return [psdoc, pssdoc];
}
async function getListStatus(list, uid) {
let result = {};
let res = await coll_status.find({ uid, psid: { $in: list } }).toArray();
for (let i of res) result[i.psid] = i;
return result;
}
module.exports = { module.exports = {
count, count,
add, add,
@ -84,5 +101,7 @@ module.exports = {
reply, reply,
getReply, getReply,
editReply, editReply,
delReply delReply,
vote,
getListStatus
}; };

@ -4,9 +4,7 @@ const res = [];
function checkResources() { function checkResources() {
for (let i = res.length - 1; i >= 0; --i) { for (let i = res.length - 1; i >= 0; --i) {
if (res[i].detached || !document.body.contains(res[i].$dom[0])) { if (res[i].detached || !document.body.contains(res[i].$dom[0])) {
if (!res[i].detached) { if (!res[i].detached) res[i].detach();
res[i].detach();
}
res.splice(i, 1); res.splice(i, 1);
} }
} }
@ -36,15 +34,11 @@ export default class DOMAttachedObject {
const key = this.DOMAttachKey; const key = this.DOMAttachKey;
const Protoclass = this; const Protoclass = this;
const instance = this.get($singleObj); const instance = this.get($singleObj);
if (instance !== undefined) { if (instance !== undefined) return instance;
return instance;
}
const newInstance = new Protoclass($singleObj, ...args); const newInstance = new Protoclass($singleObj, ...args);
// $dom may be updated in constructor so that we use $dom instead // $dom may be updated in constructor so that we use $dom instead
// of $singleObj here. // of $singleObj here.
if (!newInstance.$dom) { if (!newInstance.$dom) return null;
return null;
}
newInstance.$dom.data(key, newInstance); newInstance.$dom.data(key, newInstance);
return newInstance; return newInstance;
} }

Loading…
Cancel
Save