Files
GarbageCanCenter_Woer_face/management-console/db/store.ts
T

185 lines
13 KiB
TypeScript

import { env } from "cloudflare:workers";
const schemaStatements = [
`CREATE TABLE IF NOT EXISTS people (
id TEXT PRIMARY KEY, name TEXT NOT NULL, workshop TEXT NOT NULL, role TEXT NOT NULL,
face_status TEXT NOT NULL, face_version INTEGER NOT NULL DEFAULT 1,
sync_status TEXT NOT NULL, active INTEGER NOT NULL DEFAULT 1, updated_at INTEGER NOT NULL
)`,
`CREATE TABLE IF NOT EXISTS workshops (
code TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, location TEXT NOT NULL, note TEXT NOT NULL
)`,
`CREATE TABLE IF NOT EXISTS devices (
id TEXT PRIMARY KEY, type TEXT NOT NULL, ip TEXT NOT NULL, status TEXT NOT NULL,
last_seen INTEGER NOT NULL, heartbeat_seconds INTEGER NOT NULL,
fill_percent INTEGER NOT NULL DEFAULT 0, category TEXT NOT NULL DEFAULT ''
)`,
`CREATE TABLE IF NOT EXISTS weighings (
id TEXT PRIMARY KEY, created_at INTEGER NOT NULL, operator TEXT NOT NULL,
machine TEXT NOT NULL, category TEXT NOT NULL, source TEXT NOT NULL,
weight REAL NOT NULL, event_id TEXT NOT NULL, status TEXT NOT NULL
)`,
`CREATE TABLE IF NOT EXISTS events (
id TEXT PRIMARY KEY, operator TEXT NOT NULL, entered_at INTEGER NOT NULL,
left_at INTEGER NOT NULL, atomic_count INTEGER NOT NULL,
scale_total REAL NOT NULL, verdict TEXT NOT NULL
)`,
`CREATE TABLE IF NOT EXISTS reconciliations (
id TEXT PRIMARY KEY, event_id TEXT NOT NULL, category TEXT NOT NULL,
small_scale REAL NOT NULL, floor_scale REAL NOT NULL, difference REAL NOT NULL,
difference_rate REAL NOT NULL, verdict TEXT NOT NULL, storage_status TEXT NOT NULL
)`,
`CREATE TABLE IF NOT EXISTS traces (
id TEXT PRIMARY KEY, created_at INTEGER NOT NULL, workshop TEXT NOT NULL,
task_id TEXT NOT NULL, rfid TEXT NOT NULL, batch TEXT NOT NULL,
tare REAL NOT NULL, gross REAL NOT NULL, net REAL NOT NULL,
machine TEXT NOT NULL, operator TEXT NOT NULL, mes_status TEXT NOT NULL
)`,
`CREATE TABLE IF NOT EXISTS rfid_tags (
id TEXT PRIMARY KEY, status TEXT NOT NULL, name TEXT NOT NULL, last_checked INTEGER
)`,
`CREATE TABLE IF NOT EXISTS alerts (
id TEXT PRIMARY KEY, created_at INTEGER NOT NULL, type TEXT NOT NULL,
severity TEXT NOT NULL, source TEXT NOT NULL, reference TEXT NOT NULL,
description TEXT NOT NULL, status TEXT NOT NULL
)`,
`CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY, value TEXT NOT NULL, updated_at INTEGER NOT NULL
)`,
`CREATE TABLE IF NOT EXISTS audit_logs (
id TEXT PRIMARY KEY, created_at INTEGER NOT NULL, actor TEXT NOT NULL,
action TEXT NOT NULL, details TEXT NOT NULL, level TEXT NOT NULL
)`,
`CREATE TABLE IF NOT EXISTS deliveries (
record_id TEXT PRIMARY KEY, device_id TEXT NOT NULL, created_at INTEGER NOT NULL,
employee_name TEXT NOT NULL, employee_no TEXT NOT NULL, machine_no TEXT NOT NULL,
production_batch TEXT NOT NULL, garbage_type INTEGER NOT NULL, weight REAL NOT NULL,
payload TEXT NOT NULL, status TEXT NOT NULL
)`,
`CREATE INDEX IF NOT EXISTS weighings_created_at_idx ON weighings(created_at)`,
`CREATE INDEX IF NOT EXISTS alerts_status_idx ON alerts(status)`,
`CREATE INDEX IF NOT EXISTS people_updated_at_idx ON people(updated_at)`,
];
const now = Date.now();
const minute = 60_000;
const seedStatements: Array<[string, unknown[]]> = [
["INSERT OR IGNORE INTO workshops(code,name,location,note) VALUES(?,?,?,?)", ["WS-01", "一楼新能源车间", "1F · 东侧", "废料回收主区域"]],
["INSERT OR IGNORE INTO workshops(code,name,location,note) VALUES(?,?,?,?)", ["WS-02", "二楼导体车间", "2F · 南侧", "导体称重与追溯"]],
["INSERT OR IGNORE INTO workshops(code,name,location,note) VALUES(?,?,?,?)", ["WS-03", "仓储中心", "1F · 西侧", "地磅与成品暂存"]],
["INSERT OR IGNORE INTO people(id,name,workshop,role,face_status,face_version,sync_status,active,updated_at) VALUES(?,?,?,?,?,?,?,?,?)", ["P1001", "张三", "一楼新能源车间", "仓管员", "已录入", 3, "已同步", 1, now - 3 * minute]],
["INSERT OR IGNORE INTO people(id,name,workshop,role,face_status,face_version,sync_status,active,updated_at) VALUES(?,?,?,?,?,?,?,?,?)", ["P1002", "李四", "二楼导体车间", "产线工人", "已录入", 2, "已同步", 1, now - 8 * minute]],
["INSERT OR IGNORE INTO people(id,name,workshop,role,face_status,face_version,sync_status,active,updated_at) VALUES(?,?,?,?,?,?,?,?,?)", ["P1003", "赵六", "一楼新能源车间", "仓管员", "需更新", 4, "同步失败", 1, now - 12 * minute]],
["INSERT OR IGNORE INTO people(id,name,workshop,role,face_status,face_version,sync_status,active,updated_at) VALUES(?,?,?,?,?,?,?,?,?)", ["P1004", "王工", "仓储中心", "管理员", "已录入", 1, "已同步", 1, now - 20 * minute]],
["INSERT OR IGNORE INTO devices(id,type,ip,status,last_seen,heartbeat_seconds,fill_percent,category) VALUES(?,?,?,?,?,?,?,?)", ["WKS-01", "入库触摸屏", "192.168.10.11", "在线", now - 2_000, 30, 0, "终端"]],
["INSERT OR IGNORE INTO devices(id,type,ip,status,last_seen,heartbeat_seconds,fill_percent,category) VALUES(?,?,?,?,?,?,?,?)", ["SB-01", "小秤台", "192.168.10.12", "在线", now - 3_000, 30, 0, "称重"]],
["INSERT OR IGNORE INTO devices(id,type,ip,status,last_seen,heartbeat_seconds,fill_percent,category) VALUES(?,?,?,?,?,?,?,?)", ["IB-01", "智能回收箱 A", "192.168.10.13", "在线", now - 5_000, 30, 72, "废铜"]],
["INSERT OR IGNORE INTO devices(id,type,ip,status,last_seen,heartbeat_seconds,fill_percent,category) VALUES(?,?,?,?,?,?,?,?)", ["IB-02", "智能回收箱 B", "192.168.10.14", "在线", now - 4_000, 30, 46, "废电线"]],
["INSERT OR IGNORE INTO devices(id,type,ip,status,last_seen,heartbeat_seconds,fill_percent,category) VALUES(?,?,?,?,?,?,?,?)", ["FS-01", "3 吨地磅", "192.168.10.15", "在线", now - 1_000, 30, 0, "称重"]],
["INSERT OR IGNORE INTO devices(id,type,ip,status,last_seen,heartbeat_seconds,fill_percent,category) VALUES(?,?,?,?,?,?,?,?)", ["RFID-01", "RFID 读写器", "192.168.10.16", "在线", now - 6_000, 30, 0, "追溯"]],
["INSERT OR IGNORE INTO weighings(id,created_at,operator,machine,category,source,weight,event_id,status) VALUES(?,?,?,?,?,?,?,?,?)", ["W-0001", now - 26 * minute, "张三", "MC-101", "废铜", "SB-01", 12.48, "EV-0728-001", "已确认"]],
["INSERT OR IGNORE INTO weighings(id,created_at,operator,machine,category,source,weight,event_id,status) VALUES(?,?,?,?,?,?,?,?,?)", ["W-0002", now - 24 * minute, "张三", "MC-101", "废铜", "SB-01", 9.82, "EV-0728-001", "已确认"]],
["INSERT OR IGNORE INTO weighings(id,created_at,operator,machine,category,source,weight,event_id,status) VALUES(?,?,?,?,?,?,?,?,?)", ["W-0003", now - 18 * minute, "赵六", "MC-108", "废电线", "IB-02", 14.50, "EV-0728-002", "偏差超阈"]],
["INSERT OR IGNORE INTO weighings(id,created_at,operator,machine,category,source,weight,event_id,status) VALUES(?,?,?,?,?,?,?,?,?)", ["W-0004", now - 9 * minute, "李四", "MC-105", "废锡", "IB-01", 19.50, "EV-0728-003", "已确认"]],
["INSERT OR IGNORE INTO events(id,operator,entered_at,left_at,atomic_count,scale_total,verdict) VALUES(?,?,?,?,?,?,?)", ["EV-0728-001", "张三", now - 30 * minute, now - 22 * minute, 2, 22.30, "正常"]],
["INSERT OR IGNORE INTO events(id,operator,entered_at,left_at,atomic_count,scale_total,verdict) VALUES(?,?,?,?,?,?,?)", ["EV-0728-002", "赵六", now - 21 * minute, now - 14 * minute, 1, 14.50, "超标"]],
["INSERT OR IGNORE INTO events(id,operator,entered_at,left_at,atomic_count,scale_total,verdict) VALUES(?,?,?,?,?,?,?)", ["EV-0728-003", "李四", now - 12 * minute, now - 6 * minute, 1, 19.50, "警告"]],
["INSERT OR IGNORE INTO reconciliations(id,event_id,category,small_scale,floor_scale,difference,difference_rate,verdict,storage_status) VALUES(?,?,?,?,?,?,?,?,?)", ["RC-001", "EV-0728-001", "废铜", 22.30, 22.60, 0.30, 1.35, "正常", "已自动入库"]],
["INSERT OR IGNORE INTO reconciliations(id,event_id,category,small_scale,floor_scale,difference,difference_rate,verdict,storage_status) VALUES(?,?,?,?,?,?,?,?,?)", ["RC-002", "EV-0728-002", "废电线", 14.50, 15.40, 0.90, 6.21, "超标", "已入库 · 异常"]],
["INSERT OR IGNORE INTO reconciliations(id,event_id,category,small_scale,floor_scale,difference,difference_rate,verdict,storage_status) VALUES(?,?,?,?,?,?,?,?,?)", ["RC-003", "EV-0728-003", "废锡", 19.50, 20.10, 0.60, 3.08, "警告", "已入库 · 预警"]],
["INSERT OR IGNORE INTO traces(id,created_at,workshop,task_id,rfid,batch,tare,gross,net,machine,operator,mes_status) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)", ["TR-001", now - 48 * minute, "二楼导体车间", "WT-260728-03", "E280-6102-0345", "B2607-018", 152.30, 268.10, 115.80, "MC-201", "李四", "已推送"]],
["INSERT OR IGNORE INTO traces(id,created_at,workshop,task_id,rfid,batch,tare,gross,net,machine,operator,mes_status) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)", ["TR-002", now - 90 * minute, "二楼导体车间", "WT-260728-02", "E280-6102-0288", "B2607-017", 148.20, 252.70, 104.50, "MC-203", "周七", "待推送"]],
["INSERT OR IGNORE INTO rfid_tags(id,status,name,last_checked) VALUES(?,?,?,?)", ["E280-6102-0345", "在用", "8# 导体轴标签", now - 48 * minute]],
["INSERT OR IGNORE INTO rfid_tags(id,status,name,last_checked) VALUES(?,?,?,?)", ["E280-6102-0288", "在用", "7# 导体轴标签", now - 90 * minute]],
["INSERT OR IGNORE INTO rfid_tags(id,status,name,last_checked) VALUES(?,?,?,?)", ["E280-6102-0411", "未用", "备用标签", null]],
["INSERT OR IGNORE INTO alerts(id,created_at,type,severity,source,reference,description,status) VALUES(?,?,?,?,?,?,?,?)", ["AL-001", now - 16 * minute, "满溢预警", "警告", "废料库", "IB-01", "满溢率达到 72%,建议安排清运", "待处理"]],
["INSERT OR IGNORE INTO alerts(id,created_at,type,severity,source,reference,description,status) VALUES(?,?,?,?,?,?,?,?)", ["AL-002", now - 42 * minute, "双秤偏差", "严重", "地磅核对", "EV-0728-002", "地磅增量与台秤汇总偏差 6.21%", "处理中"]],
["INSERT OR IGNORE INTO settings(key,value,updated_at) VALUES(?,?,?)", ["mes_url", "http://mes.local/api/v2", now]],
["INSERT OR IGNORE INTO settings(key,value,updated_at) VALUES(?,?,?)", ["mes_timeout", "10", now]],
["INSERT OR IGNORE INTO settings(key,value,updated_at) VALUES(?,?,?)", ["deviation_warn", "2", now]],
["INSERT OR IGNORE INTO settings(key,value,updated_at) VALUES(?,?,?)", ["deviation_critical", "5", now]],
["INSERT OR IGNORE INTO settings(key,value,updated_at) VALUES(?,?,?)", ["fill_warn", "70", now]],
["INSERT OR IGNORE INTO settings(key,value,updated_at) VALUES(?,?,?)", ["fill_critical", "90", now]],
["INSERT OR IGNORE INTO audit_logs(id,created_at,actor,action,details,level) VALUES(?,?,?,?,?,?)", ["LOG-001", now - 15 * minute, "系统", "设备心跳", "WKS-01 状态恢复为在线", "信息"]],
["INSERT OR IGNORE INTO audit_logs(id,created_at,actor,action,details,level) VALUES(?,?,?,?,?,?)", ["LOG-002", now - 38 * minute, "王工", "处理告警", "AL-002 标记为处理中", "重要"]],
];
export function getD1(): D1Database {
if (!env.DB) {
throw new Error("D1 数据库未绑定,请将 .openai/hosting.json 的 d1 设置为 DB。");
}
return env.DB;
}
export async function ensureDatabase() {
const db = getD1();
await db.batch(schemaStatements.map((statement) => db.prepare(statement)));
await db.batch(
seedStatements.map(([statement, bindings]) =>
db.prepare(statement).bind(...bindings),
),
);
return db;
}
export async function addAudit(
actor: string,
action: string,
details: string,
level = "信息",
) {
const db = getD1();
await db
.prepare(
"INSERT INTO audit_logs(id,created_at,actor,action,details,level) VALUES(?,?,?,?,?,?)",
)
.bind(crypto.randomUUID(), Date.now(), actor, action, details, level)
.run();
}
export async function loadConsoleData() {
const db = await ensureDatabase();
const [
peopleResult,
workshopResult,
deviceResult,
weighingResult,
eventResult,
reconciliationResult,
traceResult,
tagResult,
alertResult,
settingResult,
logResult,
] = await Promise.all([
db.prepare("SELECT * FROM people ORDER BY active DESC, name").all(),
db.prepare("SELECT * FROM workshops ORDER BY code").all(),
db.prepare("SELECT * FROM devices ORDER BY id").all(),
db.prepare("SELECT * FROM weighings ORDER BY created_at DESC LIMIT 200").all(),
db.prepare("SELECT * FROM events ORDER BY entered_at DESC LIMIT 100").all(),
db.prepare("SELECT * FROM reconciliations ORDER BY event_id DESC LIMIT 100").all(),
db.prepare("SELECT * FROM traces ORDER BY created_at DESC LIMIT 200").all(),
db.prepare("SELECT * FROM rfid_tags ORDER BY status, id").all(),
db.prepare("SELECT * FROM alerts ORDER BY created_at DESC LIMIT 200").all(),
db.prepare("SELECT * FROM settings ORDER BY key").all(),
db.prepare("SELECT * FROM audit_logs ORDER BY created_at DESC LIMIT 200").all(),
]);
return {
people: peopleResult.results,
workshops: workshopResult.results,
devices: deviceResult.results,
weighings: weighingResult.results,
events: eventResult.results,
reconciliations: reconciliationResult.results,
traces: traceResult.results,
tags: tagResult.results,
alerts: alertResult.results,
settings: Object.fromEntries(
settingResult.results.map((row) => [String(row.key), String(row.value)]),
),
logs: logResult.results,
serverTime: Date.now(),
};
}