Files
2026-moving-helper/README.md
T

443 lines
8.4 KiB
Markdown
Raw Normal View History

2026-04-19 12:36:55 +02:00
# Moving Helper
2026-04-19 12:13:07 +02:00
2026-04-19 12:36:55 +02:00
这是一个面向可信家庭内网环境的小型搬家记录工具,当前采用轻量技术栈:
2026-04-19 12:13:07 +02:00
- FastAPI
2026-04-19 12:36:55 +02:00
- Jinja2 服务端渲染
2026-04-19 12:13:07 +02:00
- SQLAlchemy
- SQLite
2026-04-19 12:54:25 +02:00
- Pillow
2026-04-19 12:13:07 +02:00
- pytest / FastAPI TestClient
- Docker / Docker Compose
2026-04-19 13:33:43 +02:00
项目目标是小而稳、容易继续扩展。它不是企业平台,也不是复杂运维系统,重点是本地开发简单、容器部署稳定、数据持久化清楚、后续几个月后自己回来看也能快速接上。
## 当前已支持
- 固定 3 级结构:`Box -> Item -> SubItem`
- Box / Item / SubItem 基础 CRUD
- Box / Item / SubItem 单图上传、替换、删除、展示
- Box / Item / SubItem 全局搜索
- Docker / Compose 长期运行
- SQLite 数据持久化
- 基础自动化测试
2026-04-19 12:36:55 +02:00
## 当前数据模型
这个项目不是无限树结构,而是固定最多 3 级:
- `Box`
- `Item`
- `SubItem`
关系如下:
- 一个 `Box` 包含多个 `Item`
- 一个 `Item` 属于一个 `Box`
- `Item` 通过 `is_container` 区分是否为“小容器”
- 只有 `is_container = true``Item` 才允许拥有 `SubItem`
- `SubItem` 是最后一级,不允许继续向下嵌套
2026-04-19 12:54:25 +02:00
结构固定为:
2026-04-19 12:36:55 +02:00
```text
Box
└── Item
└── SubItem
```
2026-04-19 12:54:25 +02:00
## 图片能力说明
2026-04-19 13:33:43 +02:00
图片系统保持简单直接:
2026-04-19 12:54:25 +02:00
- `Box` 最多支持 1 张图片
- `Item` 最多支持 1 张图片
- `SubItem` 最多支持 1 张图片
- 支持上传、替换、删除
- 不支持多图
2026-04-19 13:33:43 +02:00
图片主要用于帮助识别物品、提高浏览效率、方便手机拍照后直接附加到记录中。它不是原图归档系统。
2026-04-19 12:54:25 +02:00
上传图片后,系统会使用 Pillow 做统一处理:
- 读取上传图片
- 去除 EXIF 元数据
- 转换为 JPEG
- 按最长边缩放到不超过 `1600px`
- 使用约 `80` 质量保存
- 将处理后的 JPEG 二进制直接写入 SQLite `BLOB`
同时还会记录:
- `image_mime_type`
- `image_width`
- `image_height`
2026-04-19 13:33:43 +02:00
图片访问路由例如:
2026-04-19 12:54:25 +02:00
- `/boxes/{id}/image`
- `/items/{id}/image`
- `/subitems/{id}/image`
2026-04-19 13:00:11 +02:00
## 全局搜索
2026-04-19 13:33:43 +02:00
当前支持一个轻量的全局搜索页:
2026-04-19 13:00:11 +02:00
- 路由:`/search`
2026-04-19 13:33:43 +02:00
- 方式:`GET /search?q=关键词`
2026-04-19 13:00:11 +02:00
搜索范围包括:
- `Box.name`
- `Box.note`
- `Item.name`
- `Item.note`
- `SubItem.name`
- `SubItem.note`
2026-04-19 13:33:43 +02:00
当前使用 SQLite 上的简单模糊匹配,不引入外部搜索引擎或复杂全文系统。
2026-04-19 13:00:11 +02:00
2026-04-19 13:33:43 +02:00
搜索结果会显示:
2026-04-19 13:00:11 +02:00
2026-04-19 13:33:43 +02:00
- 对象类型:`Box / Item / SubItem`
- 名称和备注
- 归属路径
2026-04-19 13:00:11 +02:00
-`Item` 展示所属 `Box`
-`SubItem` 展示所属 `Item``Box`
- 如果对象已有图片,会显示一个小缩略图
2026-04-19 12:36:55 +02:00
## 当前未实现
这一阶段仍然没有实现以下内容:
2026-04-19 12:54:25 +02:00
- 多图上传
- OCR
- AI 识别物品
- 图片标签
- 图片分类
2026-04-19 12:36:55 +02:00
- 登录 / 鉴权
- 标签系统
- 前后端分离
- 复杂 UI
2026-04-19 12:13:07 +02:00
## 项目结构
```text
.
├── app
│ ├── __init__.py
│ ├── config.py
│ ├── db.py
2026-04-19 12:54:25 +02:00
│ ├── images.py
2026-04-19 12:13:07 +02:00
│ ├── main.py
│ ├── models.py
│ ├── static
│ │ └── style.css
│ └── templates
├── data
2026-04-19 13:33:43 +02:00
├── scripts
│ ├── backup_db.sh
│ └── deploy.sh
2026-04-19 12:13:07 +02:00
├── tests
2026-04-19 13:33:43 +02:00
├── .dockerignore
├── .env.example
2026-04-19 12:13:07 +02:00
├── docker-compose.yml
├── Dockerfile
2026-04-19 12:36:55 +02:00
├── pytest.ini
2026-04-19 12:13:07 +02:00
├── README.md
2026-04-19 12:36:55 +02:00
└── requirements.txt
2026-04-19 12:13:07 +02:00
```
## 轻量配置
2026-04-19 13:33:43 +02:00
项目通过环境变量支持以下部署时真正需要关心的配置:
2026-04-19 12:13:07 +02:00
- `HOST`
- `PORT`
2026-04-19 13:33:43 +02:00
- `DATABASE_URL`
- `DATA_DIR`
- `UID`
- `GID`
推荐从示例文件开始:
```bash
cp .env.example .env
```
默认值如下:
2026-04-19 12:13:07 +02:00
2026-04-19 13:33:43 +02:00
```env
HOST=0.0.0.0
PORT=10000
DATABASE_URL=sqlite:////app/data/app.db
DATA_DIR=./data
UID=1000
GID=1000
```
说明:
2026-04-19 12:13:07 +02:00
2026-04-19 13:33:43 +02:00
- 本地开发默认数据库仍然是 `./data/app.db`
- Docker 内建议继续使用 `sqlite:////app/data/app.db`
- `DATA_DIR` 控制宿主机上的持久化目录
- `UID/GID` 用来让容器内文件权限更贴近宿主机用户
2026-04-19 12:13:07 +02:00
## 本地开发模式
推荐使用本地 Python `venv` 开发和调试。
### 1. 创建虚拟环境
```bash
python3 -m venv .venv
source .venv/bin/activate
```
### 2. 安装依赖
```bash
pip install -r requirements.txt
```
### 3. 启动开发服务器
```bash
uvicorn app.main:app --reload --host 0.0.0.0 --port 10000
```
访问:
```text
http://localhost:10000
```
2026-04-19 12:36:55 +02:00
本地默认数据库位置:
2026-04-19 12:13:07 +02:00
```text
./data/app.db
```
2026-04-19 13:33:43 +02:00
## Docker 运行方式
2026-04-19 12:13:07 +02:00
Docker / Compose 是这个项目面向长期运行环境的方式。
2026-04-19 13:33:43 +02:00
### 首次准备
2026-04-19 12:13:07 +02:00
```bash
2026-04-19 13:33:43 +02:00
cp .env.example .env
mkdir -p data
```
### 启动 / 更新
```bash
docker compose up -d --build
```
### 查看状态
```bash
docker compose ps
```
### 查看日志
```bash
docker compose logs -f web
2026-04-19 12:13:07 +02:00
```
访问:
```text
http://localhost:10000
```
2026-04-19 13:33:43 +02:00
### Compose 配置说明
当前 `docker-compose.yml` 保持尽量简单:
2026-04-19 12:13:07 +02:00
- 默认暴露 `10000` 端口
2026-04-19 12:36:55 +02:00
- `restart: unless-stopped`
2026-04-19 13:33:43 +02:00
- 容器用户来自 `UID:GID`
- 宿主机 `DATA_DIR` 挂载到容器内 `/app/data`
- SQLite 默认写入 `/app/data/app.db`
## 自动化部署
这个项目没有复杂 CI/CD,只提供一个适合家用项目的轻量部署脚本:
```bash
./scripts/deploy.sh
```
它会按顺序执行:
1. 检查 `.env`
2. 准备数据目录
3. 如果当前目录是 git 仓库,执行 `git pull --ff-only`
4. 执行 `docker compose up -d --build`
5. 输出容器状态
6. 输出最近日志
这个脚本的目标不是做平台化发布,而是让“更新代码并刷新容器”变成一个稳定、可重复执行的动作。
如果你不想自动拉代码,也可以直接手动运行:
```bash
docker compose up -d --build
```
## 数据持久化
当前 SQLite 文件默认会保存在:
2026-04-19 12:13:07 +02:00
2026-04-19 13:33:43 +02:00
- 宿主机:`./data/app.db`
- 容器内:`/app/data/app.db`
这是因为 `docker-compose.yml` 把:
2026-04-19 12:13:07 +02:00
```text
2026-04-19 13:33:43 +02:00
${DATA_DIR:-./data}
2026-04-19 12:13:07 +02:00
```
2026-04-19 13:33:43 +02:00
挂载到了容器内:
```text
/app/data
```
因此:
- 容器重建不会删除宿主机上的数据库文件
- 更新镜像不会导致 SQLite 数据丢失
- 只要保留 `DATA_DIR` 目录,数据就还在
## 备份与恢复
### 最简单的备份方式
直接复制 SQLite 文件即可:
```bash
cp data/app.db backups/app.db
```
或者使用附带脚本:
```bash
./scripts/backup_db.sh
```
它会在 `backups/` 目录下生成一个带时间戳的副本。
### 最简单的恢复方式
停止容器后,把备份文件覆盖回去:
```bash
docker compose stop
cp backups/app-YYYYMMDD-HHMMSS.db data/app.db
docker compose up -d
```
## 常见排查
### 1. 查看容器日志
```bash
docker compose logs -f web
```
### 2. 确认服务是否已启动
```bash
docker compose ps
```
如果状态是 `Up`,通常说明容器已经跑起来了。
### 3. 确认端口映射
```bash
docker compose port web 10000
```
### 4. 确认数据库文件还在
```bash
ls -lh data
```
如果看到 `app.db`,说明宿主机持久化文件还在。
### 5. 容器更新后数据为什么没丢
因为数据库不放在镜像里,而是放在宿主机挂载目录 `DATA_DIR` 中。
镜像更新只会替换应用代码和运行环境,不会覆盖这个宿主机目录。
2026-04-19 12:13:07 +02:00
## 测试
运行测试:
```bash
2026-04-19 12:36:55 +02:00
python -m pytest
2026-04-19 12:13:07 +02:00
```
2026-04-19 12:36:55 +02:00
测试使用独立测试数据库,不会污染真实开发数据。
2026-04-19 12:13:07 +02:00
2026-04-19 12:36:55 +02:00
当前测试覆盖包括:
2026-04-19 12:13:07 +02:00
2026-04-19 12:36:55 +02:00
- Box / Item / SubItem 基础 CRUD
2026-04-19 13:33:43 +02:00
- 图片上传、替换、删除与错误路径
- 全局搜索 name / note
- 创建后的重定向行为
- 关键页面结构和 UX 文案
2026-04-19 14:28:00 +02:00
## 一次性 Notion 导入
项目内附带了一个一次性迁移脚本:
```bash
python scripts/import_notion.py --dry-run
python scripts/import_notion.py --apply
```
说明:
- 这是一次性 migration / import 工具,不是长期同步功能
- 运行时会交互要求输入:
- Notion API token
- Notion 页面完整 URL
- `--dry-run` 只读取和解析,不写数据库
- `--apply` 会真正写入当前 SQLite 数据库
- 建议导入前先备份 `data/app.db`
### 当前支持的 Notion 结构映射
- `heading_2` -> `Box`
- 某个 `heading_2` 下的一级 bullet -> `Item`
- 如果一级 bullet 下还有二级 bullet
- 一级 bullet -> 容器型 `Item`
- 二级 bullet -> `SubItem`
当前最大只处理到这个层级:
```text
heading_2
└── 一级 bullet
└── 二级 bullet
```
更深层级会在日志中提示,但不会继续扩展成无限树。
### 这一版不导入图片
这一版导入脚本:
- 不下载图片
- 不导入图片
- 遇到图片或其他媒体 block 时会提示已跳过
图片后续可以在应用里手动补录。