Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 65 additions & 59 deletions Source/Process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ export class Process {
return this.DenyBadgeEditList.indexOf(this.Username) !== -1;
}
public VerifyCaptcha = async (CaptchaToken: string): Promise<Result> => {
const ErrorDescriptions: Object = {
const ErrorDescriptions: object = {
"missing-input-secret": "密钥为空",
"invalid-input-secret": "密钥不正确",
"missing-input-response": "验证码令牌为空",
Expand Down Expand Up @@ -566,79 +566,85 @@ export class Process {
"Page": "number",
"BoardID": "number"
}));
let WhereClause = "";
const FilterBindData: (string | number)[] = [];
if (Data["ProblemID"] !== 0) {
WhereClause += "WHERE p.problem_id = ? ";
FilterBindData.push(Data["ProblemID"]);
}
if (Data["BoardID"] !== -1) {
WhereClause += (WhereClause === "" ? "WHERE " : "AND ") + "p.board_id = ? ";
FilterBindData.push(Data["BoardID"]);
}

// Count and page query must share this.RawDatabase's session (rather than
// this.XMOJDatabase's own session) so D1 read replication reads a
// consistent snapshot across both - otherwise the count can observe a
// newer version than the page query, corrupting pagination.
const PostCount = (await this.RawDatabase.prepare(
"SELECT COUNT(*) AS count FROM bbs_post p " + WhereClause + ";"
).bind(...FilterBindData).all())["results"][0]["count"];

let ResponseData = {
Posts: new Array<Object>,
PageCount: Data["BoardID"] !== -1 ? (Data["ProblemID"] !== 0 ? Math.ceil(ThrowErrorIfFailed(await this.XMOJDatabase.GetTableSize("bbs_post", {
board_id: Data["BoardID"],
problem_id: Data["ProblemID"]
}))["TableSize"] / 15) : Math.ceil(ThrowErrorIfFailed(await this.XMOJDatabase.GetTableSize("bbs_post", {
board_id: Data["BoardID"]
}))["TableSize"] / 15)) : (Data["ProblemID"] !== 0 ? Math.ceil(ThrowErrorIfFailed(await this.XMOJDatabase.GetTableSize("bbs_post", {
problem_id: Data["ProblemID"]
}))["TableSize"] / 15) : Math.ceil(ThrowErrorIfFailed(await this.XMOJDatabase.GetTableSize("bbs_post"))["TableSize"] / 15))
Posts: new Array<object>,
PageCount: Math.ceil(PostCount / 15)
};
if (ResponseData.PageCount === 0) {
return new Result(true, "获得讨论列表成功", ResponseData);
}
if (Data["Page"] < 1 || Data["Page"] > ResponseData.PageCount) {
return new Result(false, "参数页数不在范围1~" + ResponseData.PageCount + "内");
}
const SearchCondition = {};
if (Data["ProblemID"] !== 0) {
SearchCondition["problem_id"] = Data["ProblemID"];
}
if (Data["BoardID"] !== -1) {
SearchCondition["board_id"] = Data["BoardID"];
}
const Posts = ThrowErrorIfFailed(await this.XMOJDatabase.Select("bbs_post", [], SearchCondition, {
Order: "post_id",
OrderIncreasing: false,
Limit: 15,
Offset: (Data["Page"] - 1) * 15
}));
for (const Post of Posts) {

const ReplyCount: number = ThrowErrorIfFailed(await this.XMOJDatabase.GetTableSize("bbs_reply", {post_id: Post["post_id"]}))["TableSize"];
const LastReply = ThrowErrorIfFailed(await this.XMOJDatabase.Select("bbs_reply", ["user_id", "reply_time"], {post_id: Post["post_id"]}, {
Order: "reply_time",
OrderIncreasing: false,
Limit: 1
}));
if (ReplyCount === 0) {
const BindData: (string | number)[] = [...FilterBindData, 15, (Data["Page"] - 1) * 15];

// Single query with correlated subqueries/joins instead of 4 extra
// round trips per post (was causing an N+1 query bottleneck).
const Posts = (await this.RawDatabase.prepare(
Comment thread
boomzero marked this conversation as resolved.
"SELECT p.post_id AS post_id, p.user_id AS user_id, p.problem_id AS problem_id, " +
"p.title AS title, p.post_time AS post_time, p.board_id AS board_id, " +
"b.board_name AS board_name, " +
"(SELECT COUNT(*) FROM bbs_reply r WHERE r.post_id = p.post_id) AS reply_count, " +
"lr.user_id AS last_reply_user_id, lr.reply_time AS last_reply_time, " +
"l.lock_person AS lock_person, l.lock_time AS lock_time " +
"FROM bbs_post p " +
"LEFT JOIN bbs_board b ON b.board_id = p.board_id " +
"LEFT JOIN bbs_lock l ON l.post_id = p.post_id " +
"LEFT JOIN (" +
" SELECT post_id, user_id, reply_time FROM (" +
" SELECT post_id, user_id, reply_time, " +
" ROW_NUMBER() OVER (PARTITION BY post_id ORDER BY reply_time DESC) AS rn " +
" FROM bbs_reply" +
" ) WHERE rn = 1" +
") lr ON lr.post_id = p.post_id " +
WhereClause +
"ORDER BY p.post_id DESC LIMIT ? OFFSET ?;"
).bind(...BindData).all())["results"];

for (const Post of Posts) {
if (Post["reply_count"] === 0) {
await this.XMOJDatabase.Delete("bbs_post", {
post_id: Post["post_id"]
});
continue;
}

const LockData = {
Locked: false,
LockPerson: "",
LockTime: 0
};
const Locked = ThrowErrorIfFailed(await this.XMOJDatabase.Select("bbs_lock", [], {
post_id: Post["post_id"]
}));
if (Locked.toString() !== "") {
LockData.Locked = true;
LockData.LockPerson = Locked[0]["lock_person"];
LockData.LockTime = Locked[0]["lock_time"];
}

ResponseData.Posts.push({
PostID: Post["post_id"],
UserID: Post["user_id"],
ProblemID: Post["problem_id"],
Title: Post["title"],
PostTime: Post["post_time"],
BoardID: Post["board_id"],
BoardName: ThrowErrorIfFailed(await this.XMOJDatabase.Select("bbs_board", ["board_name"], {
board_id: Post["board_id"]
}))[0]["board_name"],
ReplyCount: ReplyCount,
LastReplyUserID: LastReply[0]["user_id"],
LastReplyTime: LastReply[0]["reply_time"],
Lock: LockData
BoardName: Post["board_name"],
ReplyCount: Post["reply_count"],
LastReplyUserID: Post["last_reply_user_id"],
LastReplyTime: Post["last_reply_time"],
Lock: {
Locked: Post["lock_person"] !== null,
LockPerson: Post["lock_person"] ?? "",
LockTime: Post["lock_time"] ?? 0
}
});
}
return new Result(true, "获得讨论列表成功", ResponseData);
Expand All @@ -655,7 +661,7 @@ export class Process {
BoardID: 0,
BoardName: "",
PostTime: 0,
Reply: new Array<Object>(),
Reply: new Array<object>(),
PageCount: 0,
Lock: {
Locked: false,
Expand Down Expand Up @@ -870,7 +876,7 @@ export class Process {
GetBBSMentionList: async (Data: object): Promise<Result> => {
ThrowErrorIfFailed(this.CheckParams(Data, {}));
const ResponseData = {
MentionList: new Array<Object>()
MentionList: new Array<object>()
};
const Mentions = ThrowErrorIfFailed(await this.XMOJDatabase.Select("bbs_mention", ["bbs_mention_id", "post_id", "bbs_mention_time", "reply_id"], {
to_user_id: this.Username
Expand All @@ -896,7 +902,7 @@ export class Process {
GetMailMentionList: async (Data: object): Promise<Result> => {
ThrowErrorIfFailed(this.CheckParams(Data, {}));
const ResponseData = {
MentionList: new Array<Object>()
MentionList: new Array<object>()
};
const Mentions = ThrowErrorIfFailed(await this.XMOJDatabase.Select("short_message_mention", ["mail_mention_id", "from_user_id", "mail_mention_time"], {
to_user_id: this.Username
Expand Down Expand Up @@ -959,7 +965,7 @@ export class Process {
GetMailList: async (Data: object): Promise<Result> => {
ThrowErrorIfFailed(this.CheckParams(Data, {}));
const ResponseData = {
MailList: new Array<Object>()
MailList: new Array<object>()
};
let OtherUsernameList = new Array<string>();
let Mails = ThrowErrorIfFailed(await this.XMOJDatabase.Select("short_message", ["message_from"], {message_to: this.Username}, {}, true));
Expand Down Expand Up @@ -988,7 +994,7 @@ export class Process {
OrderIncreasing: false,
Limit: 1
}));
let LastMessage: Object;
let LastMessage: object;
if (LastMessageFrom.toString() === "") {
LastMessage = LastMessageTo;

Expand Down Expand Up @@ -1069,7 +1075,7 @@ export class Process {
"OtherUser": "string"
}));
const ResponseData = {
Mail: new Array<Object>()
Mail: new Array<object>()
};
let Mails = ThrowErrorIfFailed(await this.XMOJDatabase.Select("short_message", [], {
message_from: Data["OtherUser"],
Expand Down Expand Up @@ -1385,7 +1391,7 @@ export class Process {
},
GetBoards: async (Data: object): Promise<Result> => {
ThrowErrorIfFailed(this.CheckParams(Data, {}));
const Boards: Array<Object> = new Array<Object>();
const Boards: Array<object> = new Array<object>();
const BoardsData = ThrowErrorIfFailed(await this.XMOJDatabase.Select("bbs_board", []));
for (const Board of BoardsData) {
Boards.push({
Expand Down
Loading