mirror of
https://github.com/djhackersdev/minime.git
synced 2026-07-06 20:13:57 -05:00
chuni: save and load user course result
This commit is contained in:
parent
0f2811197a
commit
afa02c0fc7
22
schema/migrate/M0011-cm-user-course.sql
Normal file
22
schema/migrate/M0011-cm-user-course.sql
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
create table "cm_user_course" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"course_id" integer not null,
|
||||
"class_id" integer not null,
|
||||
"play_count" integer not null,
|
||||
"score_max" integer not null,
|
||||
"is_full_combo" text not null,
|
||||
"is_all_justice" text not null,
|
||||
"is_success" text not null,
|
||||
"score_rank" integer not null,
|
||||
"event_id" integer not null,
|
||||
"last_play_date" text not null,
|
||||
"param1" integer not null,
|
||||
"param2" integer not null,
|
||||
"param3" integer not null,
|
||||
"param4" integer not null,
|
||||
"is_clear" text not null,
|
||||
constraint "cm_user_course_uq" unique ("profile_id", "course_id")
|
||||
);
|
||||
|
|
@ -1,17 +1,28 @@
|
|||
import { Repositories } from "../repo";
|
||||
import { GetUserCourseRequest } from "../request/getUserCourse";
|
||||
import { GetUserCourseResponse } from "../response/getUserCourse";
|
||||
import { readAimeId } from "../proto/base";
|
||||
import { paginationCookie } from "./_util";
|
||||
import { writeUserCourse } from "../proto/userCourse";
|
||||
|
||||
export default async function getUserCourse(
|
||||
rep: Repositories,
|
||||
req: GetUserCourseRequest
|
||||
): Promise<GetUserCourseResponse> {
|
||||
const aimeId = readAimeId(req.userId);
|
||||
const maxCount = parseInt(req.maxCount);
|
||||
const nextIndex = parseInt(req.nextIndex);
|
||||
|
||||
const profileId = await rep.userData().lookup(aimeId);
|
||||
const items = await rep
|
||||
.userCourse()
|
||||
.load(profileId, { limit: maxCount, offset: nextIndex });
|
||||
|
||||
|
||||
return {
|
||||
userId: req.userId,
|
||||
length: "0",
|
||||
nextIndex: "-1",
|
||||
userCourseList: [
|
||||
// This will get saved at some point I think
|
||||
],
|
||||
length: items.length.toString(),
|
||||
nextIndex: paginationCookie(items, { maxCount, nextIndex }),
|
||||
userCourseList: items.map(writeUserCourse)
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import { readUserActivity } from "../proto/userActivity";
|
|||
import { readUserDataEx } from "../proto/userDataEx";
|
||||
import { readUserDuelList } from "../proto/userDuelList";
|
||||
import { readUserPlaylog } from "../proto/userPlaylog";
|
||||
import { readUserCourse } from "../proto/userCourse";
|
||||
|
||||
// It shouldn't need to be said really, but seeing as this message (A) requires
|
||||
// fairly lengthy processing and (B) is only sent right at the end of a credit,
|
||||
|
|
@ -66,6 +67,10 @@ export default async function upsertUserAll(
|
|||
await rep.userPlaylog().save(profileId, readUserPlaylog(item));
|
||||
}
|
||||
|
||||
for (const item of payload.userCourseList || []) {
|
||||
await rep.userCourse().save(profileId, readUserCourse(item));
|
||||
}
|
||||
|
||||
for (const item of payload.userDataEx || []) {
|
||||
await rep.userDataEx().save(profileId, readUserDataEx(item));
|
||||
}
|
||||
|
|
|
|||
17
src/chunithm/model/userCourse.ts
Normal file
17
src/chunithm/model/userCourse.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
export interface UserCourseItem {
|
||||
courseId: number;
|
||||
classId: number;
|
||||
playCount: number;
|
||||
scoreMax: number;
|
||||
isFullCombo: boolean;
|
||||
isAllJustice: boolean;
|
||||
isSuccess: boolean;
|
||||
scoreRank: number;
|
||||
eventId: number;
|
||||
lastPlayDate: Date;
|
||||
param1: number;
|
||||
param2: number;
|
||||
param3: number;
|
||||
param4: number;
|
||||
isClear: boolean;
|
||||
}
|
||||
28
src/chunithm/proto/userCourse.ts
Normal file
28
src/chunithm/proto/userCourse.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import { UserCourseItem } from "../model/userCourse";
|
||||
import { Crush, readBoolean, readDate, writeObject } from "./base";
|
||||
|
||||
export type UserCourseJson = Crush<UserCourseItem>;
|
||||
|
||||
export function readUserCourse(json: UserCourseJson): UserCourseItem {
|
||||
return {
|
||||
classId: parseInt(json.classId),
|
||||
courseId: parseInt(json.courseId),
|
||||
eventId: parseInt(json.eventId),
|
||||
isAllJustice: readBoolean(json.isAllJustice),
|
||||
isClear: readBoolean(json.isClear),
|
||||
isFullCombo: readBoolean(json.isFullCombo),
|
||||
isSuccess: readBoolean(json.isSuccess),
|
||||
lastPlayDate: readDate(json.lastPlayDate),
|
||||
param1: parseInt(json.param1),
|
||||
param2: parseInt(json.param2),
|
||||
param3: parseInt(json.param3),
|
||||
param4: parseInt(json.param4),
|
||||
playCount: parseInt(json.playCount),
|
||||
scoreMax: parseInt(json.scoreMax),
|
||||
scoreRank: parseInt(json.scoreRank)
|
||||
}
|
||||
}
|
||||
|
||||
export function writeUserCourse(obj: UserCourseItem): UserCourseJson {
|
||||
return writeObject(obj);
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
import { UserCourseRepository } from "./userCourse";
|
||||
|
||||
export { Page } from "./_defs";
|
||||
|
||||
import { UserActivityRepository } from "./userActivity";
|
||||
|
|
@ -34,4 +36,6 @@ export interface Repositories {
|
|||
userMusic(): UserMusicRepository;
|
||||
|
||||
userPlaylog(): UserPlaylogRepository;
|
||||
|
||||
userCourse(): UserCourseRepository;
|
||||
}
|
||||
|
|
|
|||
4
src/chunithm/repo/userCourse.ts
Normal file
4
src/chunithm/repo/userCourse.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
import { UserCourseItem } from "../model/userCourse";
|
||||
import { RepositoryN } from "./_defs";
|
||||
|
||||
export type UserCourseRepository = RepositoryN<UserCourseItem>;
|
||||
|
|
@ -8,6 +8,7 @@ import { UserMusicDetailJson } from "../proto/userMusic";
|
|||
import { UserActivityJson } from "../proto/userActivity";
|
||||
import { UserRecentRatingJson } from "../proto/userRecentRating";
|
||||
import { UserPlaylogJson } from "../proto/userPlaylog";
|
||||
import { UserCourseJson } from "../proto/userCourse";
|
||||
import { UserDataExJson } from "../proto/userDataEx";
|
||||
import { UserDuelListJson } from "../proto/userDuelList";
|
||||
|
||||
|
|
@ -26,6 +27,7 @@ export interface UpsertUserAllRequest {
|
|||
userActivityList?: UserActivityJson[];
|
||||
userRecentRatingList?: UserRecentRatingJson[];
|
||||
userPlaylogList?: UserPlaylogJson[];
|
||||
userCourseList?: UserCourseJson[];
|
||||
userDataEx?: UserDataExJson[];
|
||||
userDuelList?: UserDuelListJson[];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { UserCourseJson } from "../proto/userCourse";
|
||||
|
||||
export interface GetUserCourseResponse {
|
||||
/** Integer, AiMe ID */
|
||||
userId: string;
|
||||
|
|
@ -8,6 +10,5 @@ export interface GetUserCourseResponse {
|
|||
/** Integer, pagination cookie */
|
||||
nextIndex: string;
|
||||
|
||||
/** TBD */
|
||||
userCourseList: [];
|
||||
userCourseList: UserCourseJson[];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ import { UserMapRepository } from "../repo/userMap";
|
|||
import { UserMusicRepository } from "../repo/userMusic";
|
||||
import { UserPlaylogRepository } from "../repo/userPlaylog";
|
||||
import { Transaction } from "../../sql";
|
||||
import { UserCourseRepository } from "../repo/userCourse";
|
||||
import { SqlUserCourseRepository } from "./userCourse";
|
||||
|
||||
export class SqlRepositories implements Repositories {
|
||||
constructor(private readonly _txn: Transaction) {}
|
||||
|
|
@ -69,4 +71,8 @@ export class SqlRepositories implements Repositories {
|
|||
userPlaylog(): UserPlaylogRepository {
|
||||
return new SqlUserPlaylogRepository(this._txn);
|
||||
}
|
||||
|
||||
userCourse(): UserCourseRepository {
|
||||
return new SqlUserCourseRepository(this._txn);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
62
src/chunithm/sql/userCourse.ts
Normal file
62
src/chunithm/sql/userCourse.ts
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import sql from "sql-bricks-postgres";
|
||||
import { createSqlMapper, T } from "../../sql/util";
|
||||
import { UserCourseRepository } from "../repo/userCourse";
|
||||
import { Id } from "../../model";
|
||||
import { Page } from "../repo";
|
||||
import { UserDataItem } from "../model/userData";
|
||||
import { UserCourseItem } from "../model/userCourse";
|
||||
import { Transaction } from "../../sql";
|
||||
|
||||
const { readRow, writeRow, colNames } = createSqlMapper({
|
||||
courseId: T.number,
|
||||
classId: T.number,
|
||||
playCount: T.number,
|
||||
scoreMax: T.number,
|
||||
isFullCombo: T.boolean,
|
||||
isAllJustice: T.boolean,
|
||||
isSuccess: T.boolean,
|
||||
scoreRank: T.number,
|
||||
eventId: T.number,
|
||||
lastPlayDate: T.Date,
|
||||
param1: T.number,
|
||||
param2: T.number,
|
||||
param3: T.number,
|
||||
param4: T.number,
|
||||
isClear: T.boolean
|
||||
});
|
||||
|
||||
export class SqlUserCourseRepository implements UserCourseRepository {
|
||||
constructor(private readonly _txn: Transaction) {}
|
||||
|
||||
async load(
|
||||
profileId: Id<UserDataItem>,
|
||||
page?: Page
|
||||
): Promise<UserCourseItem[]> {
|
||||
const stmt = sql
|
||||
.select("*")
|
||||
.from("cm_user_course")
|
||||
.where("profile_id", profileId);
|
||||
|
||||
if (page) {
|
||||
stmt.limit(page.limit).offset(page.offset);
|
||||
}
|
||||
|
||||
const rows = await this._txn.fetchRows(stmt);
|
||||
|
||||
return rows.map(readRow);
|
||||
}
|
||||
|
||||
save(profileId: Id<UserDataItem>, obj: UserCourseItem): Promise<void> {
|
||||
const stmt = sql
|
||||
.insert("cm_user_course", {
|
||||
id: this._txn.generateId(),
|
||||
profile_id: profileId,
|
||||
...writeRow(obj),
|
||||
})
|
||||
.onConflict("profile_id", "course_id")
|
||||
.doUpdate(colNames);
|
||||
|
||||
return this._txn.modify(stmt);
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user