idz: Add SqlTimeAttackRepository
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
import { Client } from "pg";
|
||||
import * as sql from "sql-bricks";
|
||||
|
||||
import { _findProfile } from "./_util";
|
||||
import { ExtId } from "../model/base";
|
||||
import { Profile } from "../model/profile";
|
||||
import { TimeAttackScore } from "../model/timeAttack";
|
||||
import { TimeAttackRepository } from "../repo";
|
||||
import { generateId } from "../../db";
|
||||
|
||||
export class SqlTimeAttackRepository implements TimeAttackRepository {
|
||||
constructor(private readonly _conn: Client) {}
|
||||
|
||||
async loadAll(extId: ExtId<Profile>): Promise<TimeAttackScore[]> {
|
||||
const loadSql = sql
|
||||
.select("ta.*")
|
||||
.from("idz.time_attack ta")
|
||||
.join("idz.profile p", { "ta.profile_id": "p.id" })
|
||||
.where("p.ext_id", extId)
|
||||
.toParams();
|
||||
|
||||
const { rows } = await this._conn.query(loadSql);
|
||||
|
||||
return rows.map(row => ({
|
||||
routeNo: row.route_no,
|
||||
timestamp: new Date(row.timestamp),
|
||||
flags: row.flags,
|
||||
totalTime: row.total_time,
|
||||
sectionTimes: row.section_times,
|
||||
grade: row.grade,
|
||||
}));
|
||||
}
|
||||
|
||||
async save(extId: ExtId<Profile>, score: TimeAttackScore): Promise<void> {
|
||||
const profileId = await _findProfile(this._conn, extId);
|
||||
|
||||
const logSql = sql
|
||||
.insert("idz.ta_result", {
|
||||
id: generateId(),
|
||||
profile_id: profileId,
|
||||
route_no: score.routeNo,
|
||||
total_time: score.totalTime,
|
||||
section_times: score.sectionTimes,
|
||||
flags: score.flags,
|
||||
grade: score.grade,
|
||||
})
|
||||
.toParams();
|
||||
|
||||
await this._conn.query(logSql);
|
||||
|
||||
const existSql = sql
|
||||
.select("ta.total_time")
|
||||
.from("idz.ta_best ta")
|
||||
.where("ta.profile_id", profileId)
|
||||
.where("ta.route_no", score.routeNo)
|
||||
.toParams();
|
||||
|
||||
const { rows } = await this._conn.query(existSql);
|
||||
const row = rows[0];
|
||||
|
||||
if (row === undefined) {
|
||||
const insertSql = sql
|
||||
.insert("idz.ta_best", {
|
||||
id: generateId(),
|
||||
profile_id: profileId,
|
||||
route_no: score.routeNo,
|
||||
total_time: score.totalTime,
|
||||
section_times: score.sectionTimes,
|
||||
flags: score.flags,
|
||||
grade: score.grade,
|
||||
})
|
||||
.toParams();
|
||||
|
||||
await this._conn.query(insertSql);
|
||||
} else if (row.total_time > score.totalTime) {
|
||||
const updateSql = sql
|
||||
.update("idz.ta_best", {
|
||||
total_time: score.totalTime,
|
||||
section_times: score.sectionTimes,
|
||||
flags: score.flags,
|
||||
grade: score.grade,
|
||||
})
|
||||
.where("profile_id", profileId)
|
||||
.where("route_no", score.routeNo)
|
||||
.toParams();
|
||||
|
||||
await this._conn.query(updateSql);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { RequestCode } from "./_defs";
|
||||
import { ExtId } from "../model/base";
|
||||
import { ExtId, RouteNo } from "../model/base";
|
||||
import { Profile } from "../model/profile";
|
||||
import { SaveTimeAttackRequest } from "../request/saveTimeAttack";
|
||||
|
||||
@@ -12,14 +12,14 @@ export function saveTimeAttack(buf: Buffer): SaveTimeAttackRequest {
|
||||
profileId: buf.readUInt32LE(0x0004) as ExtId<Profile>,
|
||||
dayNight: buf.readUInt8(0x0054) & 1,
|
||||
payload: {
|
||||
courseId: buf.readUInt8(0x0054) >> 1,
|
||||
timestamp: new Date(buf.readUInt32LE(0x0058) * 1000).toISOString(), //hck
|
||||
routeNo: (buf.readUInt8(0x0054) >> 1) as RouteNo,
|
||||
timestamp: new Date(buf.readUInt32LE(0x0058) * 1000),
|
||||
flags: buf.readUInt8(0x005c),
|
||||
totalMsec: buf.readUInt32LE(0x0018),
|
||||
stageMsec: [
|
||||
buf.readUInt32LE(0x0024),
|
||||
buf.readUInt32LE(0x0028),
|
||||
buf.readUInt32LE(0x002c),
|
||||
totalTime: buf.readUInt32LE(0x0018) / 1000,
|
||||
sectionTimes: [
|
||||
buf.readUInt32LE(0x0024) / 1000,
|
||||
buf.readUInt32LE(0x0028) / 1000,
|
||||
buf.readUInt32LE(0x002c) / 1000,
|
||||
],
|
||||
grade: buf.readUInt8(0x0062),
|
||||
},
|
||||
|
||||
@@ -17,23 +17,23 @@ export function loadProfile2(res: LoadProfileResponse2) {
|
||||
buf.fill(4, 0x0680, 0x06a0);
|
||||
|
||||
for (const score of res.timeAttack) {
|
||||
const { courseId } = score;
|
||||
const { routeNo } = score;
|
||||
|
||||
buf.writeUInt32LE(
|
||||
(new Date(score.timestamp).getTime() / 1000) | 0, // Date ctor hack
|
||||
0x00e4 + courseId * 4
|
||||
0x00e4 + routeNo * 4
|
||||
);
|
||||
|
||||
buf.writeUInt16LE(0, 0x0560 + 2 * courseId); // ???
|
||||
buf.writeUInt16LE(0xffff, 0x0164 + 2 * courseId); // National rank
|
||||
buf.writeUInt32LE(score.totalMsec, 0x04e0 + 4 * courseId);
|
||||
buf.writeUInt8(score.flags, 0x05a0 + courseId);
|
||||
buf.writeUInt8(score.grade, 0x0680 + courseId);
|
||||
buf.writeUInt16LE(0, 0x0560 + 2 * routeNo); // ???
|
||||
buf.writeUInt16LE(0xffff, 0x0164 + 2 * routeNo); // National rank
|
||||
buf.writeUInt32LE((score.totalTime * 1000) | 0, 0x04e0 + 4 * routeNo);
|
||||
buf.writeUInt8(score.flags, 0x05a0 + routeNo);
|
||||
buf.writeUInt8(score.grade, 0x0680 + routeNo);
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
buf.writeUInt16LE(
|
||||
score.stageMsec[i] >> 2,
|
||||
0x05c0 + 6 * courseId + 2 * i
|
||||
(score.sectionTimes[i] * 1000) >> 2,
|
||||
0x05c0 + 6 * routeNo + 2 * i
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,15 +6,7 @@ export async function saveTimeAttack(
|
||||
w: Repositories,
|
||||
req: SaveTimeAttackRequest
|
||||
): Promise<SaveTimeAttackResponse> {
|
||||
if (req.payload.totalMsec > 0) {
|
||||
const existing = await w
|
||||
.timeAttack()
|
||||
.load(req.profileId, req.payload.courseId);
|
||||
|
||||
if (existing === undefined || existing.totalMsec > req.payload.totalMsec) {
|
||||
await w.timeAttack().save(req.profileId, req.payload);
|
||||
}
|
||||
}
|
||||
await w.timeAttack().save(req.profileId, req.payload);
|
||||
|
||||
return {
|
||||
type: "save_time_attack_res",
|
||||
|
||||
@@ -3,4 +3,5 @@
|
||||
|
||||
export type BackgroundCode = number & { __brand: "backgroundCode" };
|
||||
export type ExtId<T> = number & { __extId: T };
|
||||
export type RouteNo = number & { __brand: "routeNo" };
|
||||
export type TitleCode = number & { __brand: "titleCode" };
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { RouteNo } from "./base";
|
||||
|
||||
export interface TimeAttackScore {
|
||||
courseId: number;
|
||||
timestamp: string; // hack
|
||||
routeNo: RouteNo;
|
||||
timestamp: Date;
|
||||
flags: number;
|
||||
totalMsec: number;
|
||||
stageMsec: number[];
|
||||
totalTime: number;
|
||||
sectionTimes: number[];
|
||||
grade: number;
|
||||
}
|
||||
|
||||
@@ -64,11 +64,6 @@ export interface TimeAttackRepository {
|
||||
profileId: Model.ExtId<Model.Profile>
|
||||
): Promise<Model.TimeAttackScore[]>;
|
||||
|
||||
load(
|
||||
profileId: Model.ExtId<Model.Profile>,
|
||||
courseId: number
|
||||
): Promise<Model.TimeAttackScore | undefined>;
|
||||
|
||||
save(
|
||||
profileId: Model.ExtId<Model.Profile>,
|
||||
score: Model.TimeAttackScore
|
||||
|
||||
Reference in New Issue
Block a user