mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
core: 支持联合排名
This commit is contained in:
1 parent
c0e67917a2
commit
f906178de3
5 files changed
+70
-42
No files matched your search
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hydrooj",
|
"name": "hydrooj",
|
||||||
"version": "2.28.40",
|
"version": "2.28.41",
|
||||||
"bin": "bin/hydrooj.js",
|
"bin": "bin/hydrooj.js",
|
||||||
"main": "dist/loader.js",
|
"main": "dist/loader.js",
|
||||||
"typings": "dist/loader.d.ts",
|
"typings": "dist/loader.d.ts",
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ async function cli() {
|
|||||||
const [, modelName, func, ...args] = argv.args as [string, string, string, ...any[]];
|
const [, modelName, func, ...args] = argv.args as [string, string, string, ...any[]];
|
||||||
if (modelName === 'script') {
|
if (modelName === 'script') {
|
||||||
let arg: any;
|
let arg: any;
|
||||||
|
console.log(args.join(' '));
|
||||||
try {
|
try {
|
||||||
arg = JSON.parse(args.join(' '));
|
arg = JSON.parse(args.join(' '));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -341,6 +341,11 @@ export interface DomainDoc extends Record<string, any> {
|
|||||||
host?: string[],
|
host?: string[],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DomainUnion {
|
||||||
|
_id: string;
|
||||||
|
union: string[];
|
||||||
|
}
|
||||||
|
|
||||||
// Message
|
// Message
|
||||||
export interface MessageDoc {
|
export interface MessageDoc {
|
||||||
_id: ObjectID,
|
_id: ObjectID,
|
||||||
@@ -481,7 +486,7 @@ export interface Task {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface UploadStream extends Writable {
|
export interface UploadStream extends Writable {
|
||||||
id: ObjectID
|
id: ObjectID;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HydroFileSystem {
|
export interface HydroFileSystem {
|
||||||
@@ -516,27 +521,28 @@ export interface FileNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Collections {
|
export interface Collections {
|
||||||
'blacklist': BlacklistDoc,
|
'blacklist': BlacklistDoc;
|
||||||
'contest': Tdoc,
|
'contest': Tdoc;
|
||||||
'domain': DomainDoc,
|
'domain': DomainDoc;
|
||||||
'domain.user': any,
|
'domain.user': any;
|
||||||
'record': RecordDoc,
|
'domain.union': DomainUnion;
|
||||||
'document': any,
|
'record': RecordDoc;
|
||||||
'problem': ProblemDoc,
|
'document': any;
|
||||||
'user': Udoc,
|
'problem': ProblemDoc;
|
||||||
'check': any,
|
'user': Udoc;
|
||||||
'message': MessageDoc,
|
'check': any;
|
||||||
'token': TokenDoc,
|
'message': MessageDoc;
|
||||||
'status': any,
|
'token': TokenDoc;
|
||||||
'oauth': any,
|
'status': any;
|
||||||
'system': System,
|
'oauth': any;
|
||||||
'task': Task,
|
'system': System;
|
||||||
'storage': FileNode,
|
'task': Task;
|
||||||
'oplog': OplogDoc,
|
'storage': FileNode;
|
||||||
'opcount': any,
|
'oplog': OplogDoc;
|
||||||
'fs.chunks': any,
|
'opcount': any;
|
||||||
'fs.files': any,
|
'fs.chunks': any;
|
||||||
'document.status': any,
|
'fs.files': any;
|
||||||
|
'document.status': any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Model {
|
export interface Model {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import UserModel, { deleteUserCache } from './user';
|
|||||||
|
|
||||||
const coll = db.collection('domain');
|
const coll = db.collection('domain');
|
||||||
const collUser = db.collection('domain.user');
|
const collUser = db.collection('domain.user');
|
||||||
|
const collUnion = db.collection('domain.union');
|
||||||
|
|
||||||
interface DomainUserArg {
|
interface DomainUserArg {
|
||||||
_id: number,
|
_id: number,
|
||||||
@@ -256,6 +257,21 @@ class DomainModel {
|
|||||||
await collUser.deleteMany({ domainId });
|
await collUser.deleteMany({ domainId });
|
||||||
await bus.parallel('domain/delete', domainId);
|
await bus.parallel('domain/delete', domainId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ArgMethod
|
||||||
|
static async addUnion(domainId: string, union: string[]) {
|
||||||
|
return await collUnion.updateOne({ _id: domainId }, { $set: { union } }, { upsert: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
@ArgMethod
|
||||||
|
static async removeUnion(domainId: string) {
|
||||||
|
return await collUnion.deleteOne({ _id: domainId });
|
||||||
|
}
|
||||||
|
|
||||||
|
@ArgMethod
|
||||||
|
static async getUnion(domainId: string) {
|
||||||
|
return await collUnion.findOne({ _id: domainId });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DomainModel;
|
export default DomainModel;
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
import { NumericDictionary } from 'lodash';
|
import { unionWith, NumericDictionary } from 'lodash';
|
||||||
import { ObjectID, FilterQuery } from 'mongodb';
|
import { ObjectID, FilterQuery } from 'mongodb';
|
||||||
import { Tdoc, ProblemDoc, Udoc } from '../interface';
|
import { Tdoc, ProblemDoc, Udoc } from '../interface';
|
||||||
|
import db from '../service/db';
|
||||||
import domain from '../model/domain';
|
import domain from '../model/domain';
|
||||||
import * as contest from '../model/contest';
|
import * as contest from '../model/contest';
|
||||||
import problem from '../model/problem';
|
import problem from '../model/problem';
|
||||||
@@ -39,7 +40,7 @@ async function runProblem(...arg: any[]) {
|
|||||||
),
|
),
|
||||||
page,
|
page,
|
||||||
100,
|
100,
|
||||||
);
|
) as any[][];
|
||||||
const rdict = await record.getList(pdoc.domainId, psdocs.map((psdoc) => psdoc.rid), true);
|
const rdict = await record.getList(pdoc.domainId, psdocs.map((psdoc) => psdoc.rid), true);
|
||||||
for (const psdoc of psdocs) {
|
for (const psdoc of psdocs) {
|
||||||
if (rdict[psdoc.rid.toHexString()]) {
|
if (rdict[psdoc.rid.toHexString()]) {
|
||||||
@@ -61,7 +62,7 @@ async function runContest(...arg: any[]) {
|
|||||||
: arg[0];
|
: arg[0];
|
||||||
const udict: ND = (typeof arg[0] === 'string') ? arg[2] : arg[1];
|
const udict: ND = (typeof arg[0] === 'string') ? arg[2] : arg[1];
|
||||||
const report = (typeof arg[0] === 'string') ? arg[3] : arg[2];
|
const report = (typeof arg[0] === 'string') ? arg[3] : arg[2];
|
||||||
const cursor = await contest.getMultiStatus(tdoc.domainId, tdoc.docId, tdoc.docType)
|
const cursor = contest.getMultiStatus(tdoc.domainId, tdoc.docId, tdoc.docType)
|
||||||
.sort(contest.RULES[tdoc.rule].statusSort);
|
.sort(contest.RULES[tdoc.rule].statusSort);
|
||||||
if (!await cursor.count()) return;
|
if (!await cursor.count()) return;
|
||||||
const rankedTsdocs = await contest.RULES[tdoc.rule].ranked(tdoc, cursor);
|
const rankedTsdocs = await contest.RULES[tdoc.rule].ranked(tdoc, cursor);
|
||||||
@@ -90,7 +91,7 @@ export async function calcLevel(domainId: string, report: Function) {
|
|||||||
let last = { rp: null };
|
let last = { rp: null };
|
||||||
let rank = 0;
|
let rank = 0;
|
||||||
let count = 0;
|
let count = 0;
|
||||||
const coll = global.Hydro.service.db.collection('domain.user');
|
const coll = db.collection('domain.user');
|
||||||
const ducur = domain.getMultiUserInDomain(domainId, filter).project({ rp: 1 }).sort({ rp: -1 });
|
const ducur = domain.getMultiUserInDomain(domainId, filter).project({ rp: 1 }).sort({ rp: -1 });
|
||||||
let bulk = coll.initializeUnorderedBulkOp();
|
let bulk = coll.initializeUnorderedBulkOp();
|
||||||
while (await ducur.hasNext()) {
|
while (await ducur.hasNext()) {
|
||||||
@@ -111,22 +112,26 @@ export async function calcLevel(domainId: string, report: Function) {
|
|||||||
domainId,
|
domainId,
|
||||||
$and: [{ rank: { $lte: (levels[i] * count) / 100 } }],
|
$and: [{ rank: { $lte: (levels[i] * count) / 100 } }],
|
||||||
};
|
};
|
||||||
if (i < levels.length) query.$and.push({ rank: { $gt: (levels[i + 1] * count) / 100 } });
|
if (i < levels.length - 1) query.$and.push({ rank: { $gt: (levels[i + 1] * count) / 100 } });
|
||||||
bulk.find(query).update({ $set: { level: i } });
|
bulk.find(query).update({ $set: { level: i } });
|
||||||
}
|
}
|
||||||
await bulk.execute();
|
await bulk.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runInDomain(domainId: string, isSub: boolean, report: Function) {
|
async function runInDomain(id: string, isSub: boolean, report: Function) {
|
||||||
|
const info = await domain.getUnion(id);
|
||||||
|
info.union.push(id);
|
||||||
const udict: ND = {};
|
const udict: ND = {};
|
||||||
const deltaudict: ND = {};
|
const deltaudict: ND = {};
|
||||||
const dudocs = await domain.getMultiUserInDomain(domainId).toArray();
|
const domainId = info ? { $in: info.union } : { $exists: false };
|
||||||
for (const dudoc of dudocs) {
|
const dudocs = unionWith([
|
||||||
deltaudict[dudoc.uid] = dudoc.rpdelta || 0;
|
...await domain.getMultiUserInDomain(id).toArray(),
|
||||||
}
|
...await domain.getMultiUserInDomain('', { domainId }).toArray(),
|
||||||
const contests: Tdoc<30 | 60>[] = await contest.getMulti(domainId, { rated: true })
|
], (a, b) => a.uid === b.uid);
|
||||||
|
for (const dudoc of dudocs) deltaudict[dudoc.uid] = dudoc.rpdelta;
|
||||||
|
const contests: Tdoc<30 | 60>[] = await contest.getMulti('', { domainId, rated: true })
|
||||||
.sort('endAt', -1).toArray() as any;
|
.sort('endAt', -1).toArray() as any;
|
||||||
await report({ message: `Found ${contests.length} contests in ${domainId}` });
|
await report({ message: `Found ${contests.length} contests in ${id}` });
|
||||||
for (const i in contests) {
|
for (const i in contests) {
|
||||||
const tdoc = contests[i];
|
const tdoc = contests[i];
|
||||||
await runContest(tdoc, udict, report);
|
await runContest(tdoc, udict, report);
|
||||||
@@ -137,8 +142,8 @@ async function runInDomain(domainId: string, isSub: boolean, report: Function) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO pagination
|
// TODO pagination
|
||||||
const problems = await problem.getMulti(domainId, { hidden: false }).toArray();
|
const problems = await problem.getMulti('', { domainId, hidden: false }).toArray();
|
||||||
await report({ message: `Found ${problems.length} problems in ${domainId}` });
|
await report({ message: `Found ${problems.length} problems in ${id}` });
|
||||||
for (const i in problems) {
|
for (const i in problems) {
|
||||||
const pdoc = problems[i];
|
const pdoc = problems[i];
|
||||||
await runProblem(pdoc, udict);
|
await runProblem(pdoc, udict);
|
||||||
@@ -148,16 +153,16 @@ async function runInDomain(domainId: string, isSub: boolean, report: Function) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await domain.setMultiUserInDomain(domainId, {}, { rp: 1500 });
|
await domain.setMultiUserInDomain(id, {}, { rp: 1500 });
|
||||||
const tasks = [];
|
const tasks = [];
|
||||||
for (const uid in udict) {
|
for (const uid in udict) {
|
||||||
const rp = udict[uid] + (deltaudict[uid] || 0);
|
const rp = udict[uid] + (deltaudict[uid] || 0);
|
||||||
tasks.push(
|
const update: any = { $set: { rp } };
|
||||||
domain.updateUserInDomain(domainId, +uid, { $set: { rp }, $push: { ratingHistory: rp } }),
|
if (isSub) update.$push = { ratingHistory: rp };
|
||||||
);
|
tasks.push(domain.updateUserInDomain(id, +uid, update));
|
||||||
}
|
}
|
||||||
await Promise.all(tasks);
|
await Promise.all(tasks);
|
||||||
await calcLevel(domainId, report);
|
await calcLevel(id, report);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function run({ domainId }, report: Function) {
|
export async function run({ domainId }, report: Function) {
|
||||||
|
|||||||
Reference in new issue
Block a user