169 lines
7.3 KiB
JavaScript
169 lines
7.3 KiB
JavaScript
const mongoose = require("mongoose");
|
||
const Schema = mongoose.Schema;
|
||
|
||
const ServerStatus = require("../../ServerStatus");
|
||
const Theme = require("./Theme");
|
||
const NewTheme = require("./NewTheme");
|
||
|
||
/**
|
||
* 回呼函式。
|
||
* @callback CallbackFunction
|
||
* @param {Object} err 錯誤資訊物件。
|
||
* @param {Object} obj 傳回的物件。
|
||
*/
|
||
|
||
/**
|
||
* 「季」的模板定義。
|
||
* @prop {number} nth 表示目前的資料是第nth季
|
||
* @prop {Schema.Types.ObjectId[]} themes 這一季的所有主題。
|
||
* @prop {Date} startTime 這一季的開始時間。
|
||
* @prop {Date} endTime 這一季的結束時間。
|
||
*/
|
||
let SeasonSchema = Schema({
|
||
nth: Number,
|
||
themes: [{type : Schema.Types.ObjectId, ref : "Theme"}],
|
||
startTime: {type : Date, default : Date.now},
|
||
endTime: Date
|
||
});
|
||
|
||
/**
|
||
* 新一季的必要的基本資料。
|
||
* @typedef NewSeasonData
|
||
* @prop {number} nth 紀錄此資料為第nth季。
|
||
* @prop {ObjectId[]} themes 這一季所有的活動。以ObjectId做關聯。
|
||
* @prop {Date} startTime 開始這一季時的時間。
|
||
*/
|
||
/**
|
||
* 建立一個新的「季」資料。
|
||
* @param {Object} data 欲儲存的原始資料。
|
||
* @param {function} callback 回呼函式(Error, ObjectId)。
|
||
*/
|
||
SeasonSchema.statics.createNewSeason = function (data, callback) {
|
||
let newSeason = this({
|
||
nth: data.nth,
|
||
themes: data.themes,
|
||
startTime: data.startTime,
|
||
endTime: null
|
||
});
|
||
newSeason.save((err, season) => {
|
||
if (err) return callback(err, null);
|
||
callback(null, season._id);
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 取得「傑作藝廊」頁面所需的資料。
|
||
* 注意: 要取用相關資料時,必須先將Theme與ParticipantInfo資料表引入。
|
||
* @param {CallbackFunction} callback 回呼函式。含有錯誤訊息物件或資料物件。
|
||
*/
|
||
SeasonSchema.statics.GetGalleryNeedInfo = function (callback) {
|
||
// 取得一季之中所有的「主題 (themes)」與「季次 (nth)」
|
||
// 所有主題中的「編號 (order)」、「標題 (title)」與「參加作品 (participants)」
|
||
// 參加作品中的「排行 (rank)」、「作者 (artist)」、「作品敘述 (description)」與「投稿時間 (postTime)」
|
||
let curNthSeason = ServerStatus.status.currentSeason;
|
||
let populateQuery = {
|
||
path : "themes",
|
||
select : {"order": 1, "title": 1, "participants": 1},
|
||
populate : {
|
||
path : "participants",
|
||
select : {"rank": 1, "artist": 1, "description": 1, "postTime": 1}
|
||
}
|
||
};
|
||
// {"nth": {$lt: curNthSeason}} 中的 $lt: curNthSeason 表示不要選取到最新一季。
|
||
this.find({"nth": {$lt: curNthSeason}}).sort({nth: -1}).select("nth themes").populate(populateQuery).exec(callback);
|
||
}
|
||
|
||
/**
|
||
* 取得在「畫作主題」頁面下所需要的插值資料。
|
||
* @param {CallbackFunction} callback 回呼函式。含有錯誤訊息物件或資料物件。
|
||
*/
|
||
SeasonSchema.statics.GetThemePageNeedInfo = function (callback) {
|
||
let result = {currentSeason: null, lastSeason: null};
|
||
let currentSeasonPopulate = {path: "themes", select: {"order": 1, "title": 1, "narrative": 1, "imageURL": 1, "originator": 1, "participentCount": 1, "views": 1, "commentCount": 1}};
|
||
let lastSeasonPopulate = {path: "themes", select: {"order": 1, "title": 1, "originator": 1}};
|
||
let currentNthSeason = ServerStatus.status.currentSeason;
|
||
// 以nth尋找最新的一季,在選擇其中的「nth」與「themes」欄位
|
||
// 並對「themes」展開,選擇其中的指定欄位,然後執行以上動作。
|
||
this.findOne({"nth": currentNthSeason})
|
||
.select("nth themes")
|
||
.populate(currentSeasonPopulate)
|
||
.exec((err, curSeason) => {
|
||
if (err) {
|
||
callback(err, null);
|
||
return;
|
||
}
|
||
result.currentSeason = curSeason;
|
||
// 接著尋找上一季,指定「nth」、「endTime」與「themes」欄位
|
||
// 並對「themes」展開,選擇其中的指定欄位,然後執行以上動作。
|
||
this.findOne({"nth": currentNthSeason - 1})
|
||
.select("nth endTime themes")
|
||
.populate(lastSeasonPopulate)
|
||
.exec((err, lastSeason) => {
|
||
if (err) {
|
||
callback(err, null);
|
||
return;
|
||
}
|
||
result.lastSeason = lastSeason;
|
||
callback(null, result);
|
||
return;
|
||
});
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 將畫作主題活動推進新的一季。
|
||
* 注意,ServerStatus.status中的currentSeason必須要先推進到新的一季,否則會相衝。
|
||
* @param {CallbackFunction} callback 回呼函式。
|
||
*/
|
||
SeasonSchema.statics.PushNewSeason = function (callback) {
|
||
let promotionCount = ServerStatus.status.promotionCount; // 取得要選取多少個候選主題至正規主題中
|
||
let curNthSeason = ServerStatus.status.currentSeason;
|
||
let Season = this;
|
||
// 首先先更新上一季的結束時間
|
||
Season.update({ nth: curNthSeason - 1 }, { $set: { "endTime": new Date() } }, (err, seasonDocs) => {
|
||
if(err) return callback(err, "\n更新前一季的時間時\n");
|
||
});
|
||
// 做統合,取NewTheme中所有的欄位,並建立一個 "voteCount" 欄位來記錄 votes 中有有多少個項目
|
||
// 然後再先後以投票數量(voteCount)與投稿時間(createdTime)來做排序,並選取前N個(promotionCount)作為新一季的新主題。
|
||
NewTheme.aggregate(
|
||
[
|
||
{ "$project": {
|
||
title: 1,
|
||
narrative: 1,
|
||
image: 1,
|
||
sponsor: 1,
|
||
votes: 1,
|
||
createdTime: 1,
|
||
voteCount: { "$size": "$votes" }
|
||
}},
|
||
{ "$sort": { "voteCount": -1 } },
|
||
{ "$sort": { "createdTime": 1 } },
|
||
{ "$limit": promotionCount }
|
||
],
|
||
function (err, docsList) {
|
||
if (err) return callback(err, "\n取得指定候選主題時發生了錯誤。請確認是否有連接MongoDB並重新嘗試。\n");
|
||
// 將指定的候選主題轉換到正規的主題(Theme)中。其中處理完成後回呼_id清單。
|
||
Theme.TransferFromNewTheme(docsList, (err, _idList) => {
|
||
if (err) return callback(err, "\n將候選主題轉換至正規主題時發生了錯誤。請確認是否有連接MongoDB並重新嘗試。\n");
|
||
|
||
// 建立基本新的一季(Season)的資料
|
||
let data = {
|
||
nth: ServerStatus.status.currentSeason,
|
||
themes: _idList,
|
||
startTime: new Date()
|
||
};
|
||
// 利用data建立最新一季的資料
|
||
Season.createNewSeason(data, (err, _id) => {
|
||
if (err) return callback(err, "\n建立新一季的活動資料時發生了錯誤。請確認是否有連接MongoDB並重新嘗試。\n");
|
||
// 將所有的候選主題都清空
|
||
NewTheme.remove({}, (err) => {
|
||
if (err) return callback(err, "\n在清空NewTheme中所有資料時發生了錯誤。請改用手動的方式刪除或檢查是否有連接MongoDB。\n");
|
||
callback(null, true);
|
||
});
|
||
});
|
||
});
|
||
}
|
||
)
|
||
}
|
||
|
||
module.exports = mongoose.model("Season", SeasonSchema); |