P0 - AI交互层:
- ai/prompt.js: 模块化 Prompt 模板
- ai/validator.js: JSON Schema 校验 + normalize
- ai/client.js: DeepSeek API 封装
- 金额计算唯一化,时间格式归一化
P1 - 后端架构:
- routes/: auth/rooms/messages/purchases 独立路由
- middleware/auth.js: 认证授权中间件
- ws/index.js: WebSocket 连接管理
- db.js: 启用 foreign_keys
- server.js: 从588行精简到40行入口
P2 - 前端架构:
- public/css/style.css: CSS 独立
- public/js/store.js: 全局状态 Store
- public/js/{chat,ws,purchases,auth}.js: 功能模块拆分
- index.html: 纯 HTML 结构, v2.6
96 lines
2.8 KiB
JavaScript
96 lines
2.8 KiB
JavaScript
const Database = require('better-sqlite3');
|
|
const path = require('path');
|
|
const fs = require('fs');
|
|
|
|
const dataDir = '/app/data';
|
|
if (!fs.existsSync(dataDir)) {
|
|
fs.mkdirSync(dataDir, { recursive: true });
|
|
}
|
|
|
|
const dbPath = path.join(dataDir, 'xiaocai.db');
|
|
const db = new Database(dbPath);
|
|
|
|
// 关键 pragma
|
|
db.pragma('journal_mode = WAL');
|
|
db.pragma('foreign_keys = ON');
|
|
|
|
// 创建基础表
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
username TEXT PRIMARY KEY,
|
|
password TEXT NOT NULL,
|
|
is_admin INTEGER DEFAULT 0
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS rooms (
|
|
id TEXT PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
created_by TEXT NOT NULL,
|
|
white_list TEXT DEFAULT '',
|
|
created_at TEXT DEFAULT (datetime('now','localtime'))
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS messages (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
room_id TEXT NOT NULL,
|
|
user TEXT NOT NULL,
|
|
text TEXT,
|
|
attachments TEXT,
|
|
timestamp TEXT DEFAULT (datetime('now','localtime')),
|
|
FOREIGN KEY(room_id) REFERENCES rooms(id)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS purchases (
|
|
id TEXT PRIMARY KEY,
|
|
room_id TEXT NOT NULL,
|
|
item TEXT,
|
|
quantity INTEGER DEFAULT 1,
|
|
unit_price REAL DEFAULT 0,
|
|
amount REAL DEFAULT 0,
|
|
freight REAL DEFAULT 0,
|
|
payment_method TEXT DEFAULT '未指定',
|
|
invoice_type TEXT DEFAULT '无票',
|
|
status TEXT DEFAULT '待处理',
|
|
applicant TEXT,
|
|
remarks TEXT DEFAULT '',
|
|
created_at TEXT DEFAULT (datetime('now','localtime')),
|
|
updated_at TEXT DEFAULT (datetime('now','localtime')),
|
|
FOREIGN KEY(room_id) REFERENCES rooms(id)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS purchase_history (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
purchase_id TEXT NOT NULL,
|
|
action TEXT NOT NULL,
|
|
user TEXT NOT NULL,
|
|
timestamp TEXT DEFAULT (datetime('now','localtime')),
|
|
attachments TEXT,
|
|
FOREIGN KEY(purchase_id) REFERENCES purchases(id)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS purchase_attachments (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
purchase_id TEXT NOT NULL,
|
|
file_path TEXT NOT NULL,
|
|
uploaded_by TEXT NOT NULL,
|
|
timestamp TEXT DEFAULT (datetime('now','localtime')),
|
|
FOREIGN KEY(purchase_id) REFERENCES purchases(id)
|
|
);
|
|
`);
|
|
|
|
// 安全迁移(忽略已存在的列)
|
|
const migrations = [
|
|
`ALTER TABLE purchases ADD COLUMN quantity INTEGER DEFAULT 1`,
|
|
`ALTER TABLE purchases ADD COLUMN unit_price REAL DEFAULT 0`,
|
|
`ALTER TABLE purchases ADD COLUMN freight REAL DEFAULT 0`,
|
|
`ALTER TABLE purchases ADD COLUMN invoice_type TEXT DEFAULT '无票'`,
|
|
`ALTER TABLE purchases ADD COLUMN payment_method TEXT DEFAULT '未指定'`,
|
|
`ALTER TABLE purchases ADD COLUMN remarks TEXT DEFAULT ''`,
|
|
`ALTER TABLE rooms ADD COLUMN white_list TEXT DEFAULT ''`,
|
|
];
|
|
for (const sql of migrations) {
|
|
try { db.exec(sql); } catch (e) { /* 列已存在 */ }
|
|
}
|
|
|
|
module.exports = db;
|