refactor(admin): 提取数据库表创建逻辑为共享函数

重构导出和导入统计数据的 API,将重复的 CREATE TABLE 语句提取为独立的 ensureStatsTables 函数,以消除代码重复并提高可维护性。同时统一代码格式和缩进。
This commit is contained in:
anghunk
2026-01-29 20:28:14 +08:00
parent d126e2c4c3
commit 5618dea2a4
2 changed files with 129 additions and 152 deletions

View File

@@ -1,35 +1,22 @@
import { Context } from 'hono'; import { Context } from 'hono';
import { Bindings } from '../../bindings'; import { Bindings } from '../../bindings';
async function ensureStatsTables(env: Bindings) {
await env.CWD_DB.prepare(
'CREATE TABLE IF NOT EXISTS page_stats (id INTEGER PRIMARY KEY AUTOINCREMENT, post_slug TEXT UNIQUE NOT NULL, post_title TEXT, post_url TEXT, pv INTEGER NOT NULL DEFAULT 0, last_visit_at INTEGER, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL)'
).run();
await env.CWD_DB.prepare(
'CREATE TABLE IF NOT EXISTS page_visit_daily (id INTEGER PRIMARY KEY AUTOINCREMENT, date TEXT NOT NULL, domain TEXT, count INTEGER NOT NULL DEFAULT 0, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL)'
).run();
await env.CWD_DB.prepare(
'CREATE TABLE IF NOT EXISTS Likes (id INTEGER PRIMARY KEY AUTOINCREMENT, page_slug TEXT NOT NULL, user_id TEXT NOT NULL, created_at INTEGER NOT NULL, UNIQUE(page_slug, user_id))'
).run();
}
export const getStatsData = async (env: Bindings) => { export const getStatsData = async (env: Bindings) => {
// Ensure tables exist await ensureStatsTables(env);
await env.CWD_DB.exec(`
CREATE TABLE IF NOT EXISTS page_stats (
id INTEGER PRIMARY KEY AUTOINCREMENT,
post_slug TEXT UNIQUE NOT NULL,
post_title TEXT,
post_url TEXT,
pv INTEGER NOT NULL DEFAULT 0,
last_visit_at INTEGER,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS page_visit_daily (
id INTEGER PRIMARY KEY AUTOINCREMENT,
date TEXT NOT NULL,
domain TEXT,
count INTEGER NOT NULL DEFAULT 0,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS Likes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
page_slug TEXT NOT NULL,
user_id TEXT NOT NULL,
created_at INTEGER NOT NULL,
UNIQUE(page_slug, user_id)
);
`);
const { results: pageStats } = await env.CWD_DB.prepare('SELECT * FROM page_stats').all(); const { results: pageStats } = await env.CWD_DB.prepare('SELECT * FROM page_stats').all();
const { results: dailyVisits } = await env.CWD_DB.prepare('SELECT * FROM page_visit_daily').all(); const { results: dailyVisits } = await env.CWD_DB.prepare('SELECT * FROM page_visit_daily').all();

View File

@@ -1,42 +1,36 @@
import { Context } from 'hono'; import { Context } from 'hono';
import { Bindings } from '../../bindings'; import { Bindings } from '../../bindings';
async function ensureStatsTables(env: Bindings) {
await env.CWD_DB.prepare(
'CREATE TABLE IF NOT EXISTS page_stats (id INTEGER PRIMARY KEY AUTOINCREMENT, post_slug TEXT UNIQUE NOT NULL, post_title TEXT, post_url TEXT, pv INTEGER NOT NULL DEFAULT 0, last_visit_at INTEGER, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL)'
).run();
await env.CWD_DB.prepare(
'CREATE TABLE IF NOT EXISTS page_visit_daily (id INTEGER PRIMARY KEY AUTOINCREMENT, date TEXT NOT NULL, domain TEXT, count INTEGER NOT NULL DEFAULT 0, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL)'
).run();
await env.CWD_DB.prepare(
'CREATE TABLE IF NOT EXISTS Likes (id INTEGER PRIMARY KEY AUTOINCREMENT, page_slug TEXT NOT NULL, user_id TEXT NOT NULL, created_at INTEGER NOT NULL, UNIQUE(page_slug, user_id))'
).run();
}
export const saveStatsData = async (env: Bindings, data: any) => { export const saveStatsData = async (env: Bindings, data: any) => {
// Ensure tables exist await ensureStatsTables(env);
await env.CWD_DB.exec(`
CREATE TABLE IF NOT EXISTS page_stats (
id INTEGER PRIMARY KEY AUTOINCREMENT,
post_slug TEXT UNIQUE NOT NULL,
post_title TEXT,
post_url TEXT,
pv INTEGER NOT NULL DEFAULT 0,
last_visit_at INTEGER,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS page_visit_daily (
id INTEGER PRIMARY KEY AUTOINCREMENT,
date TEXT NOT NULL,
domain TEXT,
count INTEGER NOT NULL DEFAULT 0,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS Likes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
page_slug TEXT NOT NULL,
user_id TEXT NOT NULL,
created_at INTEGER NOT NULL,
UNIQUE(page_slug, user_id)
);
`);
const stmts: any[] = []; const stmts: any[] = [];
// 1. page_stats
if (Array.isArray(data.page_stats)) { if (Array.isArray(data.page_stats)) {
for (const item of data.page_stats) { for (const item of data.page_stats) {
const fields = ['post_slug', 'post_title', 'post_url', 'pv', 'last_visit_at', 'created_at', 'updated_at']; const fields = [
'post_slug',
'post_title',
'post_url',
'pv',
'last_visit_at',
'created_at',
'updated_at'
];
const values = [ const values = [
item.post_slug, item.post_slug,
item.post_title, item.post_title,
@@ -52,54 +46,47 @@ export const saveStatsData = async (env: Bindings, data: any) => {
} }
const placeholders = fields.map(() => '?').join(', '); const placeholders = fields.map(() => '?').join(', ');
stmts.push( stmts.push(
env.CWD_DB.prepare(`INSERT OR REPLACE INTO page_stats (${fields.join(', ')}) VALUES (${placeholders})`).bind(...values) env.CWD_DB.prepare(
`INSERT OR REPLACE INTO page_stats (${fields.join(', ')}) VALUES (${placeholders})`
).bind(...values)
); );
} }
} }
// 2. page_visit_daily
if (Array.isArray(data.page_visit_daily)) { if (Array.isArray(data.page_visit_daily)) {
for (const item of data.page_visit_daily) { for (const item of data.page_visit_daily) {
const fields = ['date', 'domain', 'count', 'created_at', 'updated_at']; const fields = ['date', 'domain', 'count', 'created_at', 'updated_at'];
const values = [ const values = [item.date, item.domain, item.count, item.created_at, item.updated_at];
item.date,
item.domain,
item.count,
item.created_at,
item.updated_at
];
if (item.id) { if (item.id) {
fields.unshift('id'); fields.unshift('id');
values.unshift(item.id); values.unshift(item.id);
} }
const placeholders = fields.map(() => '?').join(', '); const placeholders = fields.map(() => '?').join(', ');
stmts.push( stmts.push(
env.CWD_DB.prepare(`INSERT OR REPLACE INTO page_visit_daily (${fields.join(', ')}) VALUES (${placeholders})`).bind(...values) env.CWD_DB.prepare(
`INSERT OR REPLACE INTO page_visit_daily (${fields.join(', ')}) VALUES (${placeholders})`
).bind(...values)
); );
} }
} }
// 3. Likes
if (Array.isArray(data.likes)) { if (Array.isArray(data.likes)) {
for (const item of data.likes) { for (const item of data.likes) {
const fields = ['page_slug', 'user_id', 'created_at']; const fields = ['page_slug', 'user_id', 'created_at'];
const values = [ const values = [item.page_slug, item.user_id, item.created_at];
item.page_slug,
item.user_id,
item.created_at
];
if (item.id) { if (item.id) {
fields.unshift('id'); fields.unshift('id');
values.unshift(item.id); values.unshift(item.id);
} }
const placeholders = fields.map(() => '?').join(', '); const placeholders = fields.map(() => '?').join(', ');
stmts.push( stmts.push(
env.CWD_DB.prepare(`INSERT OR REPLACE INTO Likes (${fields.join(', ')}) VALUES (${placeholders})`).bind(...values) env.CWD_DB.prepare(
`INSERT OR REPLACE INTO Likes (${fields.join(', ')}) VALUES (${placeholders})`
).bind(...values)
); );
} }
} }
// Batch execute
const BATCH_SIZE = 50; const BATCH_SIZE = 50;
for (let i = 0; i < stmts.length; i += BATCH_SIZE) { for (let i = 0; i < stmts.length; i += BATCH_SIZE) {
const batch = stmts.slice(i, i + BATCH_SIZE); const batch = stmts.slice(i, i + BATCH_SIZE);
@@ -116,7 +103,10 @@ export const importStats = async (c: Context<{ Bindings: Bindings }>) => {
await saveStatsData(c.env, body); await saveStatsData(c.env, body);
const statsCount = (body.page_stats?.length || 0) + (body.page_visit_daily?.length || 0) + (body.likes?.length || 0); const statsCount =
(body.page_stats?.length || 0) +
(body.page_visit_daily?.length || 0) +
(body.likes?.length || 0);
return c.json({ message: `成功导入 ${statsCount} 条统计数据` }); return c.json({ message: `成功导入 ${statsCount} 条统计数据` });
} catch (e: any) { } catch (e: any) {
console.error(e); console.error(e);