feat: 补充平台应用入口与模板(main/storage/templates)
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user