From 4c2edc3948204d5e1dcbc7c30519511eabf331a9 Mon Sep 17 00:00:00 2001 From: masnn Date: Fri, 10 Apr 2020 22:05:29 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A2=98=E8=A7=A3=E7=82=B9=E8=B5=9E=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hydro/error.js | 15 +++++++++------ hydro/handler/problem.js | 16 ++++++++++++++-- hydro/model/problem.js | 1 - hydro/model/solution.js | 25 ++++++++++++++++++++++--- ui/components/DOMAttachedObject.js | 12 +++--------- 5 files changed, 48 insertions(+), 21 deletions(-) diff --git a/hydro/error.js b/hydro/error.js index b9971442..19911dfa 100644 --- a/hydro/error.js +++ b/hydro/error.js @@ -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 { constructor(uname) { super('LoginError'); @@ -122,7 +129,8 @@ module.exports = { UserNotFoundError, VerifyPasswordError, ProblemDataNotFoundError, OpcountExceededError, PermissionError, NoProblemError, ValidationError, ProblemNotFoundError, TrainingNotFoundError, - ContestNotFoundError, RecordNotFoundError, SolutionNotFoundError + ContestNotFoundError, RecordNotFoundError, SolutionNotFoundError, + AlreadyVotedError }; /* @@ -199,11 +207,6 @@ class InvalidOperationError(ForbiddenError): pass -class AlreadyVotedError(ForbiddenError): - @property - def message(self): - return "You've already voted." - class InvalidTokenDigestError(ForbiddenError): pass diff --git a/hydro/handler/problem.js b/hydro/handler/problem.js index 0078dbd5..2f170ae4 100644 --- a/hydro/handler/problem.js +++ b/hydro/handler/problem.js @@ -10,7 +10,8 @@ const { ROUTE } = require('../service/server'), gridfs = require('../service/gridfs'), queue = require('../service/queue'), - { NoProblemError, ProblemDataNotFoundError, BadRequestError } = require('../error'), + { NoProblemError, ProblemDataNotFoundError, BadRequestError, + SolutionNotFoundError } = require('../error'), { constants } = require('../options'), { PERM_VIEW_PROBLEM, @@ -242,13 +243,14 @@ class ProblemSolutionHandler extends ProblemDetailHandler { } } let udict = await user.getList(uids); + let pssdict = solution.getListStatus(docids, this.uid); this.ctx.body.path = [ ['problem_main', '/p'], [this.pdoc.title, `/p/${this.pdoc.pid}`, true], ['problem_solution', null] ]; this.ctx.body = Object.assign(this.ctx.body, { - psdocs, page, pcount, pscount, udict + psdocs, page, pcount, pscount, udict, pssdict }); } async post({ psid }) { @@ -289,6 +291,16 @@ class ProblemSolutionHandler extends ProblemDetailHandler { await solution.delReply(psid, psrid); 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 { diff --git a/hydro/model/problem.js b/hydro/model/problem.js index a78216d2..78efde18 100644 --- a/hydro/model/problem.js +++ b/hydro/model/problem.js @@ -81,7 +81,6 @@ async function random(query) { let pdoc = await pdocs.skip(Math.floor(Math.random() * pcount)).limit(1).toArray()[0]; return pdoc.pid; } else return null; - } module.exports = { diff --git a/hydro/model/solution.js b/hydro/model/solution.js index 9bda7a3b..ac03ade3 100644 --- a/hydro/model/solution.js +++ b/hydro/model/solution.js @@ -1,9 +1,10 @@ const { ObjectID } = require('bson'), - { SolutionNotFoundError } = require('../error'), + { SolutionNotFoundError, AlreadyVotedError } = require('../error'), validator = require('../lib/validator'), db = require('../service/db.js'), - coll = db.collection('solution'); + coll = db.collection('solution'), + coll_status = db.collection('solution.status'); /** * @param {string} pid @@ -73,6 +74,22 @@ async function editReply(psid, psrid, content) { async function delReply(psid, 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 = { count, add, @@ -84,5 +101,7 @@ module.exports = { reply, getReply, editReply, - delReply + delReply, + vote, + getListStatus }; \ No newline at end of file diff --git a/ui/components/DOMAttachedObject.js b/ui/components/DOMAttachedObject.js index 1341c6c5..bb55e70a 100644 --- a/ui/components/DOMAttachedObject.js +++ b/ui/components/DOMAttachedObject.js @@ -4,9 +4,7 @@ const res = []; function checkResources() { for (let i = res.length - 1; i >= 0; --i) { if (res[i].detached || !document.body.contains(res[i].$dom[0])) { - if (!res[i].detached) { - res[i].detach(); - } + if (!res[i].detached) res[i].detach(); res.splice(i, 1); } } @@ -36,15 +34,11 @@ export default class DOMAttachedObject { const key = this.DOMAttachKey; const Protoclass = this; const instance = this.get($singleObj); - if (instance !== undefined) { - return instance; - } + if (instance !== undefined) return instance; const newInstance = new Protoclass($singleObj, ...args); // $dom may be updated in constructor so that we use $dom instead // of $singleObj here. - if (!newInstance.$dom) { - return null; - } + if (!newInstance.$dom) return null; newInstance.$dom.data(key, newInstance); return newInstance; }