mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2025-12-13 09:28:14 +08:00
🤔 feat(SQLite): 尝试另一种实现
This commit is contained in:
23
src/types/BBS.d.ts
vendored
Normal file
23
src/types/BBS.d.ts
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* @file types BBS.d.ts
|
||||
* @description 米游社BBS相关类型定义
|
||||
* @auther BTMuli<bt-muli@outlook.com>
|
||||
* @since Alpha v0.1.4
|
||||
*/
|
||||
|
||||
declare namespace BTMuli.SQLite {
|
||||
/**
|
||||
* @description BBS帖子数据
|
||||
* @interface BBSPost
|
||||
* @since Alpha v0.1.4
|
||||
* @property {number} id 帖子ID
|
||||
* @property {created} created 帖子创建时间
|
||||
* @property {modified} modified 帖子修改时间
|
||||
* @return BBSPost
|
||||
*/
|
||||
export interface BBSPost {
|
||||
id: number
|
||||
created: string
|
||||
modified: string
|
||||
}
|
||||
}
|
||||
350
src/utils/TGSql.ts
Normal file
350
src/utils/TGSql.ts
Normal file
@@ -0,0 +1,350 @@
|
||||
/**
|
||||
* @file utils TGSql.ts
|
||||
* @description 数据库sql语句
|
||||
* @auther BTMuli<bt-muli@outlook.com>
|
||||
* @since Alpha v0.1.4
|
||||
*/
|
||||
|
||||
import { app } from "@tauri-apps/api";
|
||||
import { getBuildTime } from "./TGBuild";
|
||||
import { TGAppData } from "../data";
|
||||
|
||||
const TGSql = {
|
||||
initTable: {
|
||||
all: initSQLiteTable(),
|
||||
app: initAppTable(),
|
||||
achievement: initAchievementTable(),
|
||||
achievementSeries: initAchievementSeriesTable(),
|
||||
bbsPost: initBbsPostTable(),
|
||||
},
|
||||
initData: {
|
||||
all: initSQLiteData(),
|
||||
app: initAppData(),
|
||||
achievement: initAchievementData(),
|
||||
achievementSeries: initAchievementSeriesData(),
|
||||
},
|
||||
insert: {
|
||||
achievement: insertAchievementData,
|
||||
achievementSeries: insertAchievementSeriesData,
|
||||
bbsPost: insertBBSPostData,
|
||||
},
|
||||
update: {
|
||||
achievement: updateAchievementData,
|
||||
bbsPost: updateBBSPostData,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 初始化应用数据表
|
||||
* @since Alpha v0.1.4
|
||||
* @returns {string[]} sql
|
||||
*/
|
||||
function initAppTable (): string[] {
|
||||
const sqlRes = [];
|
||||
// 创建应用数据表
|
||||
sqlRes.push(`
|
||||
CREATE TABLE IF NOT EXISTS AppData
|
||||
(
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT DEFAULT NULL,
|
||||
updated TEXT DEFAULT NULL
|
||||
);
|
||||
`);
|
||||
// 创建触发器
|
||||
sqlRes.push(`
|
||||
CREATE TRIGGER IF NOT EXISTS updateAppData
|
||||
AFTER UPDATE OF value ON AppData
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE BBSPost SET isRead = 1, updated = datetime('now', 'localtime')
|
||||
WHERE created < NEW.value OR modified < NEW.value AND NEW.key = 'bbsPost';
|
||||
END;
|
||||
`);
|
||||
return sqlRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 初始化成就系列数据表
|
||||
* @since Alpha v0.1.4
|
||||
* @returns {string[]} sql
|
||||
*/
|
||||
function initAchievementSeriesTable (): string[] {
|
||||
const sqlRes = [];
|
||||
// 创建成就系列数据表
|
||||
sqlRes.push(`
|
||||
CREATE TABLE IF NOT EXISTS AchievementSeries
|
||||
(
|
||||
id INTEGER PRIMARY KEY,
|
||||
"order" INTEGER,
|
||||
name TEXT DEFAULT NULL,
|
||||
version TEXT DEFAULT NULL,
|
||||
totalCount INTEGER DEFAULT 0,
|
||||
finCount INTEGER DEFAULT 0,
|
||||
icon TEXT NOT NULL,
|
||||
nameCard TEXT DEFAULT NULL,
|
||||
updated TEXT DEFAULT NULL
|
||||
);
|
||||
`);
|
||||
return sqlRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 初始化成就数据表
|
||||
* @since Alpha v0.1.4
|
||||
* @returns {string[]} sql
|
||||
*/
|
||||
function initAchievementTable (): string[] {
|
||||
const sqlRes = [];
|
||||
// 创建成就数据表
|
||||
sqlRes.push(`
|
||||
CREATE TABLE IF NOT EXISTS Achievements
|
||||
(
|
||||
id INTEGER PRIMARY KEY,
|
||||
series INTEGER,
|
||||
"order" INTEGER,
|
||||
name TEXT DEFAULT NULL,
|
||||
description TEXT DEFAULT NULL,
|
||||
reward INTEGER DEFAULT 0,
|
||||
isCompleted BOOLEAN DEFAULT 0,
|
||||
completedTime TEXT DEFAULT NULL,
|
||||
progress INTEGER DEFAULT 0,
|
||||
version TEXT DEFAULT NULL,
|
||||
updated TEXT DEFAULT NULL
|
||||
);
|
||||
`);
|
||||
// 创建触发器
|
||||
sqlRes.push(`
|
||||
CREATE TRIGGER IF NOT EXISTS updateAchievement
|
||||
AFTER UPDATE ON Achievements
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE AchievementSeries SET finCount = finCount + 1, updated = datetime('now', 'localtime')
|
||||
WHERE id = NEW.series AND NEW.isCompleted = 1 AND OLD.isCompleted = 0;
|
||||
UPDATE AchievementSeries SET finCount = finCount - 1, updated = datetime('now', 'localtime')
|
||||
WHERE id = NEW.series AND NEW.isCompleted = 0 AND OLD.isCompleted = 1;
|
||||
END;
|
||||
`);
|
||||
sqlRes.push(`
|
||||
CREATE TRIGGER IF NOT EXISTS insertAchievement
|
||||
AFTER INSERT ON Achievements
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE AchievementSeries SET totalCount = totalCount + 1, updated = datetime('now', 'localtime')
|
||||
WHERE id = NEW.series;
|
||||
END;
|
||||
`);
|
||||
return sqlRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 初始化米游社帖子数据表
|
||||
* @since Alpha v0.1.4
|
||||
* @returns {string[]} sql
|
||||
*/
|
||||
function initBbsPostTable (): string[] {
|
||||
const sqlRes = [];
|
||||
// 创建米游社帖子数据表
|
||||
sqlRes.push(`
|
||||
CREATE TABLE IF NOT EXISTS BBSPost
|
||||
(
|
||||
id INTEGER PRIMARY KEY,
|
||||
created TEXT DEFAULT NULL,
|
||||
modified TEXT DEFAULT NULL,
|
||||
isRead BOOLEAN DEFAULT 0,
|
||||
updated TEXT DEFAULT NULL,
|
||||
);
|
||||
`);
|
||||
return sqlRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 初始化数据库表
|
||||
* @since Alpha v0.1.4
|
||||
* @returns {string[]} sql
|
||||
*/
|
||||
function initSQLiteTable (): string[] {
|
||||
const sqlRes = [];
|
||||
sqlRes.push(...initAppTable());
|
||||
sqlRes.push(...initAchievementSeriesTable());
|
||||
sqlRes.push(...initAchievementTable());
|
||||
sqlRes.push(...initBbsPostTable());
|
||||
return sqlRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 初始化应用数据
|
||||
* @since Alpha v0.1.4
|
||||
* @returns {Promise<string[]>} sql
|
||||
*/
|
||||
async function initAppData (): Promise<string[]> {
|
||||
const sqlRes = [];
|
||||
const appVersion = await app.getVersion();
|
||||
const buildTime = getBuildTime();
|
||||
const dataUpdated = buildTime.startsWith("dev") ? buildTime.slice(4) : buildTime;
|
||||
// 初始化应用版本
|
||||
sqlRes.push(`
|
||||
INSERT INTO AppData (key, value, updated)
|
||||
VALUES ('appVersion', '${appVersion}', datetime('now', 'localtime'));
|
||||
`);
|
||||
// 初始化应用数据更新时间
|
||||
sqlRes.push(`
|
||||
INSERT INTO AppData (key, value, updated)
|
||||
VALUES ('dataUpdated', '${dataUpdated}', datetime('now', 'localtime'));
|
||||
`);
|
||||
// 初始化米游社帖子数据更新时间
|
||||
sqlRes.push(`
|
||||
INSERT INTO AppData (key, value, updated)
|
||||
VALUES ('bbsPostLine', datetime('now', 'localtime'), datetime('now', 'localtime'));
|
||||
`);
|
||||
return sqlRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 初始化成就系列数据
|
||||
* @since Alpha v0.1.4
|
||||
* @returns {string[]} sql
|
||||
*/
|
||||
function initAchievementSeriesData (): string[] {
|
||||
const sqlRes: string[] = [];
|
||||
const oriData = TGAppData.achievementSeries;
|
||||
Object.values(oriData).map((series) => {
|
||||
const sql = insertAchievementSeriesData(series);
|
||||
return sqlRes.push(sql);
|
||||
});
|
||||
return sqlRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 初始化成就数据
|
||||
* @since Alpha v0.1.4
|
||||
* @returns {string[]} sql
|
||||
*/
|
||||
function initAchievementData (): string[] {
|
||||
const sqlRes: string[] = [];
|
||||
const oriData = TGAppData.achievements;
|
||||
Object.values(oriData).map((achievement) => {
|
||||
const sql = insertAchievementData(achievement);
|
||||
return sqlRes.push(sql);
|
||||
});
|
||||
return sqlRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 初始化数据库数据
|
||||
* @since Alpha v0.1.4
|
||||
* @returns {Promise<string[]>} sql
|
||||
*/
|
||||
async function initSQLiteData (): Promise<string[]> {
|
||||
const sqlRes = [];
|
||||
sqlRes.push(...initAchievementSeriesData());
|
||||
sqlRes.push(...initAchievementData());
|
||||
sqlRes.push(...await initAppData());
|
||||
return sqlRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 插入数据-成就系列
|
||||
* @since Alpha v0.1.4
|
||||
* @param {BTMuli.Genshin.AchievementSeries} data
|
||||
* @returns {string} sql
|
||||
*/
|
||||
function insertAchievementSeriesData (data: BTMuli.Genshin.AchievementSeries): string {
|
||||
let sql;
|
||||
if (data.card) {
|
||||
sql = `
|
||||
INSERT INTO AchievementSeries (id, "order", name, version, icon, nameCard, updated)
|
||||
VALUES (${data.id}, ${data.order}, '${data.name}', '${data.version}', '${data.icon}', '${data.card}', datetime('now', 'localtime'));
|
||||
`;
|
||||
} else {
|
||||
sql = `
|
||||
INSERT INTO AchievementSeries (id, "order", name, version, icon, updated)
|
||||
VALUES (${data.id}, ${data.order}, '${data.name}', '${data.version}', '${data.icon}', datetime('now', 'localtime'));
|
||||
`;
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 插入数据-成就
|
||||
* @since Alpha v0.1.4
|
||||
* @param {BTMuli.Genshin.Achievement} data
|
||||
* @returns {string} sql
|
||||
*/
|
||||
function insertAchievementData (data: BTMuli.Genshin.Achievement): string {
|
||||
const sql = `
|
||||
INSERT INTO Achievements (id, series, "order", name, description, reward, version, updated)
|
||||
VALUES (${data.id}, ${data.series}, ${data.order}, '${data.name}', '${data.description}', ${data.reward}, '${data.version}', datetime('now', 'localtime'));
|
||||
`;
|
||||
return sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 插入数据-米游社帖子
|
||||
* @since Alpha v0.1.4
|
||||
* @param {BTMuli.SQLite.BBSPost} data
|
||||
* @param {string} bbsPostLine 米游社帖子数据更新时间
|
||||
* @returns {string} sql
|
||||
*/
|
||||
function insertBBSPostData (data: BTMuli.SQLite.BBSPost, bbsPostLine: string): string {
|
||||
// 将 bbsPostLine 与 data.created 跟 data.modified 比较,取最新的时间
|
||||
const isRead = data.created <= bbsPostLine && data.modified <= bbsPostLine;
|
||||
const sql = `
|
||||
INSERT INTO BBSPost (id, created, modified, isRead, updated)
|
||||
VALUES (${data.id}, '${data.created}', '${data.modified}', ${isRead ? 1 : 0}, datetime('now', 'localtime'))
|
||||
ON CONFLICT(id) DO UPDATE
|
||||
SET modified = '${data.modified}' WHERE id = ${data.id} AND modified != '${data.modified}';
|
||||
`;
|
||||
return sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 更新数据-成就
|
||||
* @since Alpha v0.1.4
|
||||
* @param {BTMuli.Genshin.Achievement} data
|
||||
* @returns {string} sql
|
||||
*/
|
||||
function updateAchievementData (data: BTMuli.Genshin.Achievement): string {
|
||||
let sql;
|
||||
const isCompleted = data.completed ? 1 : 0;
|
||||
if (data.completed && data.completed_time) {
|
||||
sql = `
|
||||
UPDATE Achievements
|
||||
SET completed = ${isCompleted}, completedTime = '${data.completed_time}', progress = ${data.progress}, updated = datetime('now', 'localtime')
|
||||
WHERE id = ${data.id};
|
||||
`;
|
||||
} else {
|
||||
sql = `
|
||||
UPDATE Achievements
|
||||
SET completed = ${isCompleted}, progress = ${data.progress}, updated = datetime('now', 'localtime')
|
||||
WHERE id = ${data.id};
|
||||
`;
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 更新数据-米游社帖子
|
||||
* @since Alpha v0.1.4
|
||||
* @param {BTMuli.SQLite.BBSPost} data
|
||||
* @param {boolean} isRead 是否已读
|
||||
* @returns {string} sql
|
||||
*/
|
||||
function updateBBSPostData (data: BTMuli.SQLite.BBSPost, isRead: boolean = false): string {
|
||||
let sql;
|
||||
if (isRead) {
|
||||
sql = `
|
||||
UPDATE BBSPost
|
||||
SET isRead = 1, updated = datetime('now', 'localtime')
|
||||
WHERE id = ${data.id};
|
||||
`;
|
||||
} else {
|
||||
sql = `
|
||||
UPDATE BBSPost
|
||||
SET modified = '${data.modified}', isRead = 0, updated = datetime('now', 'localtime')
|
||||
WHERE id = ${data.id} AND OLD.modified != '${data.modified}';
|
||||
`;
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
|
||||
export default TGSql;
|
||||
185
src/utils/TGSqlite.ts
Normal file
185
src/utils/TGSqlite.ts
Normal file
@@ -0,0 +1,185 @@
|
||||
/**
|
||||
* @description 数据库操作类
|
||||
* @class TGSqlite
|
||||
* @author BTMuli<bt-muli@outlook.com>
|
||||
* @since Alpha v0.1.4
|
||||
*/
|
||||
|
||||
import Database from "tauri-plugin-sql-api";
|
||||
import TGSql from "./TGSql";
|
||||
|
||||
class TGSqlite {
|
||||
/**
|
||||
* @description 数据库地址
|
||||
* @private
|
||||
* @type {string}
|
||||
* @memberof TGSqlite
|
||||
* @since Alpha v0.1.4
|
||||
*/
|
||||
private readonly dbPath: string = "sqlite:tauri-genshin.db";
|
||||
/**
|
||||
* @description 数据库实例
|
||||
* @private
|
||||
* @type {Database}
|
||||
* @memberof TGSqlite
|
||||
* @since Alpha v0.1.4
|
||||
*/
|
||||
private readonly db: Database;
|
||||
/**
|
||||
* @description 数据库包含的表
|
||||
* @private
|
||||
* @type {string[]}
|
||||
* @memberof TGSqlite
|
||||
* @since Alpha v0.1.4
|
||||
*/
|
||||
private readonly tables: string[] = [
|
||||
"AppData",
|
||||
"Achievements",
|
||||
"AchievementSeries",
|
||||
"BBSPost",
|
||||
];
|
||||
|
||||
/**
|
||||
* @description 构造函数
|
||||
* @constructor
|
||||
* @memberof TGSqlite
|
||||
* @since Alpha v0.1.4
|
||||
*/
|
||||
constructor () {
|
||||
this.db = new Database(this.dbPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 初始化数据库
|
||||
* @memberof TGSqlite
|
||||
* @since Alpha v0.1.4
|
||||
* @returns {Promise<void>}
|
||||
* @memberof TGSqlite
|
||||
*/
|
||||
public async init (): Promise<void> {
|
||||
const sqlT = TGSql.initTable.all;
|
||||
for (const item of sqlT) {
|
||||
await this.db.execute(item);
|
||||
}
|
||||
const sqlD = await TGSql.initData.all;
|
||||
for (const item of sqlD) {
|
||||
await this.db.execute(item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 检测数据库完整性
|
||||
* @memberof TGSqlite
|
||||
* @since Alpha v0.1.4
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
public async check (): Promise<boolean> {
|
||||
// 检测数据表是否都存在
|
||||
const sqlT = "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;";
|
||||
const res: Array<{ name: string }> = await this.db.select(sqlT);
|
||||
if (res.length !== this.tables.length) return false;
|
||||
for (const item of res) {
|
||||
if (!this.tables.includes(item.name)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 重置数据库
|
||||
* @memberof TGSqlite
|
||||
* @since Alpha v0.1.4
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public async reset (): Promise<void> {
|
||||
const sql = "DROP TABLE IF EXISTS AppData, Achievement, AchievementSeries, BbsPost;";
|
||||
await this.db.execute(sql);
|
||||
await this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取数据库版本及构建时间
|
||||
* @memberof TGSqlite
|
||||
* @since Alpha v0.1.4
|
||||
* @returns {Promise<{ version: string, buildTime: string }>}
|
||||
*/
|
||||
public async getMetadata (): Promise<{ version: string, buildTime: string }> {
|
||||
const sql = "SELECT * FROM AppData WHERE key='appVersion' OR key='dataUpdated';";
|
||||
const res: Array<{ key: string, value: string }> = await this.db.select(sql);
|
||||
const version = res.find((item) => item.key === "appVersion")?.value ?? "0.0.0";
|
||||
const buildTime = res.find((item) => item.key === "dataUpdated")?.value ?? "1970-01-01 00:00:00";
|
||||
return { version, buildTime };
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 清除 BBSPost 数据
|
||||
* @memberof TGSqlite
|
||||
* @since Alpha v0.1.4
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public async clearBbsPost (): Promise<void> {
|
||||
const sql = [];
|
||||
sql.push("DROP TABLE IF EXISTS BBSPost;");
|
||||
sql.push(...TGSql.initTable.bbsPost);
|
||||
sql.push("UPDATE AppData SET value=datetime('now', 'localtime') WHERE key='bbsPostLine';");
|
||||
for (const item of sql) {
|
||||
await this.db.execute(item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取成就系列列表
|
||||
* @memberof TGSqlite
|
||||
* @param {number} [seriesId] 系列 ID
|
||||
* @since Alpha v0.1.4
|
||||
* @returns {Promise<BTMuli.SQLite.AchievementSeries[]>}
|
||||
*/
|
||||
public async getAchievementSeries (seriesId?: number): Promise<BTMuli.SQLite.AchievementSeries[]> {
|
||||
let sql;
|
||||
if (seriesId) {
|
||||
sql = `SELECT * FROM AchievementSeries WHERE id=${seriesId};`;
|
||||
} else {
|
||||
sql = "SELECT * FROM AchievementSeries;";
|
||||
}
|
||||
const res: BTMuli.SQLite.AchievementSeries[] = await this.db.select(sql);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取成就列表
|
||||
* @memberof TGSqlite
|
||||
* @param {number} [seriesId] 系列 ID
|
||||
* @since Alpha v0.1.4
|
||||
* @returns {Promise<BTMuli.SQLite.Achievements[]>}
|
||||
*/
|
||||
public async getAchievements (seriesId?: number): Promise<BTMuli.SQLite.Achievements[]> {
|
||||
let sql;
|
||||
if (seriesId) {
|
||||
sql = `SELECT * FROM Achievement WHERE seriesId=${seriesId};`;
|
||||
} else {
|
||||
sql = "SELECT * FROM Achievement;";
|
||||
}
|
||||
const res: BTMuli.SQLite.Achievements[] = await this.db.select(sql);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 查询成就
|
||||
* @memberof TGSqlite
|
||||
* @param {string} keyword 关键词
|
||||
* @since Alpha v0.1.4
|
||||
* @returns {Promise<BTMuli.SQLite.Achievements[]>}
|
||||
*/
|
||||
public async searchAchievements (keyword: string): Promise<BTMuli.SQLite.Achievements[]> {
|
||||
let sql;
|
||||
if (keyword.startsWith("v")) {
|
||||
const version = keyword.replace("v", "");
|
||||
sql = `SELECT * FROM Achievement WHERE version='${version}';`;
|
||||
} else {
|
||||
sql = `SELECT * FROM Achievement WHERE name LIKE '%${keyword}%' OR description LIKE '%${keyword}%';`;
|
||||
}
|
||||
const res: BTMuli.SQLite.Achievements[] = await this.db.select(sql);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
export default TGSqlite;
|
||||
Reference in New Issue
Block a user