chore: initialize deployable website and CMS
This commit is contained in:
@@ -0,0 +1,348 @@
|
||||
import Database from 'better-sqlite3';
|
||||
import { fileURLToPath } from 'url';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import 'dotenv/config';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const DB_PATH = process.env.CMS_DB_PATH || path.join(__dirname, '../cms.db');
|
||||
const UPLOAD_DIR = process.env.UPLOAD_DIR || path.join(__dirname, '../uploads');
|
||||
if (!fs.existsSync(UPLOAD_DIR)) fs.mkdirSync(UPLOAD_DIR, { recursive: true });
|
||||
|
||||
const db = new Database(DB_PATH);
|
||||
|
||||
// 启用 WAL 模式提高性能
|
||||
db.pragma('journal_mode = WAL');
|
||||
|
||||
export function initDB() {
|
||||
// 全局配置
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS global_config (
|
||||
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||
site_title TEXT,
|
||||
slogan TEXT,
|
||||
seo_title TEXT,
|
||||
seo_desc TEXT,
|
||||
seo_keywords TEXT,
|
||||
hotline TEXT,
|
||||
contact_email TEXT,
|
||||
contact_address TEXT,
|
||||
cta_text TEXT,
|
||||
cta_link TEXT,
|
||||
updated_at TEXT
|
||||
)`);
|
||||
|
||||
// 首页板块
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS home_sections (
|
||||
id TEXT PRIMARY KEY,
|
||||
key TEXT UNIQUE,
|
||||
title TEXT,
|
||||
subtitle TEXT,
|
||||
summary TEXT,
|
||||
visible INTEGER DEFAULT 1,
|
||||
sort_order INTEGER DEFAULT 0,
|
||||
updated_at TEXT
|
||||
)`);
|
||||
|
||||
// 阿美巴页面
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS ameba_page (
|
||||
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||
brand_position TEXT,
|
||||
mission TEXT,
|
||||
philosophy TEXT,
|
||||
practice TEXT,
|
||||
updated_at TEXT
|
||||
)`);
|
||||
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS experts (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
title TEXT,
|
||||
bio TEXT,
|
||||
avatar_url TEXT,
|
||||
sort_weight INTEGER DEFAULT 0,
|
||||
status TEXT DEFAULT 'published',
|
||||
created_at TEXT,
|
||||
updated_at TEXT
|
||||
)`);
|
||||
|
||||
// 企业进化
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS evolution_thought (
|
||||
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||
content TEXT,
|
||||
updated_at TEXT
|
||||
)`);
|
||||
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS four_values (
|
||||
id TEXT PRIMARY KEY,
|
||||
key TEXT UNIQUE,
|
||||
name TEXT,
|
||||
definition TEXT,
|
||||
description TEXT,
|
||||
image_url TEXT,
|
||||
sort_order INTEGER DEFAULT 0,
|
||||
status TEXT DEFAULT 'published',
|
||||
updated_at TEXT
|
||||
)`);
|
||||
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS amb_states (
|
||||
id TEXT PRIMARY KEY,
|
||||
key TEXT UNIQUE,
|
||||
name TEXT,
|
||||
definition TEXT,
|
||||
description TEXT,
|
||||
image_url TEXT,
|
||||
sort_order INTEGER DEFAULT 0,
|
||||
fixed_note TEXT,
|
||||
status TEXT DEFAULT 'published',
|
||||
updated_at TEXT
|
||||
)`);
|
||||
|
||||
// 方法与能力
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS evolution_process (
|
||||
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||
steps TEXT,
|
||||
scenarios TEXT,
|
||||
description TEXT,
|
||||
updated_at TEXT
|
||||
)`);
|
||||
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS ten_systems (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT,
|
||||
description TEXT,
|
||||
sort_order INTEGER DEFAULT 0,
|
||||
status TEXT DEFAULT 'published',
|
||||
updated_at TEXT
|
||||
)`);
|
||||
|
||||
// 产品服务
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS services (
|
||||
id TEXT PRIMARY KEY,
|
||||
slug TEXT UNIQUE,
|
||||
name TEXT,
|
||||
stage TEXT,
|
||||
intro TEXT,
|
||||
service_content TEXT,
|
||||
delivery TEXT,
|
||||
suitable TEXT,
|
||||
price REAL,
|
||||
price_unit TEXT,
|
||||
advantage TEXT,
|
||||
detail_html TEXT,
|
||||
status TEXT DEFAULT 'draft',
|
||||
sort_weight INTEGER DEFAULT 0,
|
||||
type TEXT DEFAULT 'custom',
|
||||
created_at TEXT,
|
||||
updated_at TEXT
|
||||
)`);
|
||||
|
||||
// 案例 9段
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS cases (
|
||||
id TEXT PRIMARY KEY,
|
||||
slug TEXT UNIQUE,
|
||||
industry TEXT,
|
||||
scale TEXT,
|
||||
amb_tags TEXT,
|
||||
value_tags TEXT,
|
||||
title TEXT,
|
||||
cover_url TEXT,
|
||||
original_state TEXT,
|
||||
goal TEXT,
|
||||
gap TEXT,
|
||||
root_cause TEXT,
|
||||
ameba_intervention TEXT,
|
||||
evolution_path TEXT,
|
||||
actions TEXT,
|
||||
phased_result TEXT,
|
||||
continuous_iteration TEXT,
|
||||
publish_time TEXT,
|
||||
status TEXT DEFAULT 'draft',
|
||||
sort_weight INTEGER DEFAULT 0,
|
||||
created_at TEXT,
|
||||
updated_at TEXT
|
||||
)`);
|
||||
|
||||
// 认知观察
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS articles (
|
||||
id TEXT PRIMARY KEY,
|
||||
slug TEXT UNIQUE,
|
||||
title TEXT,
|
||||
subtitle TEXT,
|
||||
category TEXT,
|
||||
cover_url TEXT,
|
||||
content_html TEXT,
|
||||
publish_time TEXT,
|
||||
recommend_weight INTEGER DEFAULT 0,
|
||||
status TEXT DEFAULT 'draft',
|
||||
created_at TEXT,
|
||||
updated_at TEXT
|
||||
)`);
|
||||
|
||||
// 轻量自我对照
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS self_check (
|
||||
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||
guide TEXT,
|
||||
questions TEXT,
|
||||
logic_text TEXT,
|
||||
next_guide TEXT,
|
||||
updated_at TEXT
|
||||
)`);
|
||||
|
||||
// 诊断问卷题目
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS diagnosis_questions (
|
||||
id TEXT PRIMARY KEY,
|
||||
group_key TEXT,
|
||||
group_name TEXT,
|
||||
type TEXT,
|
||||
title TEXT,
|
||||
options TEXT,
|
||||
score_config TEXT,
|
||||
sort_order INTEGER DEFAULT 0,
|
||||
status TEXT DEFAULT 'published',
|
||||
skip_logic TEXT,
|
||||
created_at TEXT,
|
||||
updated_at TEXT
|
||||
)`);
|
||||
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS diagnosis_sessions (
|
||||
id TEXT PRIMARY KEY,
|
||||
token TEXT UNIQUE,
|
||||
answers TEXT,
|
||||
progress TEXT,
|
||||
created_at TEXT,
|
||||
updated_at TEXT,
|
||||
completed_at TEXT,
|
||||
ip TEXT
|
||||
)`);
|
||||
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS diagnosis_reports (
|
||||
id TEXT PRIMARY KEY,
|
||||
session_id TEXT,
|
||||
token TEXT UNIQUE,
|
||||
share_token TEXT UNIQUE,
|
||||
scores TEXT,
|
||||
amb TEXT,
|
||||
gap TEXT,
|
||||
root_cause TEXT,
|
||||
path_suggestion TEXT,
|
||||
expires_at TEXT,
|
||||
created_at TEXT
|
||||
)`);
|
||||
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS diagnosis_leads (
|
||||
id TEXT PRIMARY KEY,
|
||||
session_id TEXT,
|
||||
report_id TEXT,
|
||||
name TEXT,
|
||||
company TEXT,
|
||||
phone TEXT,
|
||||
created_at TEXT
|
||||
)`);
|
||||
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS contact_leads (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
company TEXT,
|
||||
phone TEXT NOT NULL,
|
||||
need TEXT,
|
||||
created_at TEXT
|
||||
)`);
|
||||
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS diagnosis_config (
|
||||
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||
weights TEXT,
|
||||
thresholds TEXT,
|
||||
templates TEXT,
|
||||
share_expire_days INTEGER DEFAULT 30,
|
||||
updated_at TEXT
|
||||
)`);
|
||||
|
||||
// 管理员
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS admins (
|
||||
id TEXT PRIMARY KEY,
|
||||
username TEXT UNIQUE,
|
||||
password_hash TEXT,
|
||||
created_at TEXT
|
||||
)`);
|
||||
}
|
||||
|
||||
// 创建兼容 sqlite3 回调风格的包装器
|
||||
export function getDB() {
|
||||
return {
|
||||
// 同步查询单行
|
||||
get(sql, params, callback) {
|
||||
try {
|
||||
if (typeof params === 'function') {
|
||||
callback = params;
|
||||
params = [];
|
||||
}
|
||||
const row = db.prepare(sql).get(...(Array.isArray(params) ? params : []));
|
||||
callback(null, row || null);
|
||||
} catch (err) {
|
||||
callback(err);
|
||||
}
|
||||
},
|
||||
|
||||
// 同步查询所有行
|
||||
all(sql, params, callback) {
|
||||
try {
|
||||
if (typeof params === 'function') {
|
||||
callback = params;
|
||||
params = [];
|
||||
}
|
||||
const rows = db.prepare(sql).all(...(Array.isArray(params) ? params : []));
|
||||
callback(null, rows);
|
||||
} catch (err) {
|
||||
callback(err);
|
||||
}
|
||||
},
|
||||
|
||||
// 同步执行
|
||||
run(sql, params, callback) {
|
||||
try {
|
||||
if (typeof params === 'function') {
|
||||
callback = params;
|
||||
params = [];
|
||||
}
|
||||
const stmt = db.prepare(sql);
|
||||
const result = stmt.run(...(Array.isArray(params) ? params : []));
|
||||
// 绑定 this 上下文
|
||||
const context = {
|
||||
lastID: result.lastInsertRowid,
|
||||
changes: result.changes
|
||||
};
|
||||
if (callback) callback.call(context, null);
|
||||
return context;
|
||||
} catch (err) {
|
||||
if (callback) callback(err);
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
|
||||
// 序列化执行(兼容性)
|
||||
serialize(callback) {
|
||||
if (callback) callback();
|
||||
},
|
||||
|
||||
// 准备语句
|
||||
prepare(sql) {
|
||||
const stmt = db.prepare(sql);
|
||||
return {
|
||||
run(...args) {
|
||||
const callback = args.pop();
|
||||
try {
|
||||
const result = stmt.run(...args);
|
||||
callback.call({ lastID: result.lastInsertRowid, changes: result.changes }, null);
|
||||
} catch (err) {
|
||||
callback(err);
|
||||
}
|
||||
},
|
||||
finalize(callback) {
|
||||
if (callback) callback();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export { DB_PATH, UPLOAD_DIR };
|
||||
Reference in New Issue
Block a user