feat: 补充平台应用入口与模板(main/storage/templates)

This commit is contained in:
2026-08-23 22:37:18 +08:00
parent 6edcec4048
commit 2f13af2352
43 changed files with 1836 additions and 0 deletions
+87
View File
@@ -0,0 +1,87 @@
# -*- coding: utf-8 -*-
"""JSON 存储引擎:一个文件 = 一张表。
当前阶段用本地 JSON 文件代替数据库。每个文件是一个 JSON 数组,
元素为一行记录;存储层只做最朴素的"读全量/写全量 + 原子替换"
业务逻辑全部收敛在 ``repositories.py``。
未来数据库映射(下一阶段据此设计):
JsonTable(path) -> 一张数据库表(表名 = 文件名)
数组元素 -> 一行记录(主键 = 记录里的 ``id`` 字段)
记录字段 -> 一列
"""
from __future__ import annotations
import json
import threading
from pathlib import Path
from typing import Callable
class JsonTable:
"""对单个 JSON 数组文件的最小 CRUD 封装。
- 写入使用「写临时文件 + rename 替换」,避免崩溃/并发读到半截数据。
- 内部用 RLock 保证同一进程内的读写互斥(演示级并发足够)。
"""
def __init__(self, path: Path):
self.path = path
self._lock = threading.RLock()
def all(self) -> list[dict]:
"""读全部记录。文件不存在/损坏时返回空表(下次写入时重建)。"""
with self._lock:
if not self.path.exists():
return []
try:
with open(self.path, "r", encoding="utf-8") as fh:
data = json.load(fh)
except (json.JSONDecodeError, OSError):
return []
return data if isinstance(data, list) else []
def save(self, records: list[dict]) -> None:
"""整体写回。原子:先写 ``<name>.tmp`` 再 rename。"""
with self._lock:
self.path.parent.mkdir(parents=True, exist_ok=True)
tmp = self.path.with_name(self.path.name + ".tmp")
with open(tmp, "w", encoding="utf-8") as fh:
json.dump(records, fh, ensure_ascii=False, indent=2)
tmp.replace(self.path)
# ------------------------------------------------------------------
# 便捷操作
# ------------------------------------------------------------------
def find(self, predicate: Callable[[dict], bool]) -> dict | None:
"""返回第一条满足条件的记录,没有则 None。"""
for record in self.all():
if predicate(record):
return record
return None
def insert(self, record: dict) -> dict:
"""追加一条记录并返回它。"""
records = self.all()
records.append(record)
self.save(records)
return record
def update(self, record_id: str, fields: dict) -> dict | None:
"""按 ``id`` 合并更新字段,返回更新后的记录(不存在则 None)。"""
records = self.all()
for record in records:
if record.get("id") == record_id:
record.update(fields)
self.save(records)
return record
return None
def delete_matching(self, predicate: Callable[[dict], bool]) -> int:
"""删除所有满足条件的记录,返回删除条数。"""
records = self.all()
kept = [r for r in records if not predicate(r)]
removed = len(records) - len(kept)
if removed:
self.save(kept)
return removed