# doc_stamp
**Repository Path**: maju-blogs/doc_stamp
## Basic Information
- **Project Name**: doc_stamp
- **Description**: 盖章合成工具
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-09-03
- **Last Updated**: 2026-09-03
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# doc-stamp
[在线体验](https://sign.ydfm.cc/)
基于 FastAPI 的 doc-stamp 后端服务骨架,内置「盖章合成」工具:
上传文档(图片或 Word .doc/.docx)与多张指纹 / 手写签名素材,自动抠图、识别签名区、把截图文档转成打印/扫描效果,再合成导出。
Word 文档由服务端自动转成逐页图片,默认取最后一页,多页时可在页面上选择。
## 技术栈
- Python 3.13 + FastAPI(conda 管理环境)
- OpenCV + Pillow + NumPy(图像处理)
- Pydantic v2 + pydantic-settings(配置管理)
- React + Vite + antd + Konva(前端)
- pytest + httpx(接口测试)
- ruff(代码质量)
## 快速开始
### 1. 创建并激活 conda 环境
```bash
conda env create -f environment.yml
conda activate doc_stamp
```
### 2. 准备本地配置(可选)
```bash
cp .env.example .env
```
### 3. 启动开发服务
```bash
python -m app.main
```
等效于 `uvicorn app.main:app --reload --host 127.0.0.1 --port 8000`;
绑定地址与端口由 `.env` 中的 `HOST` / `PORT` 控制,开发模式(`APP_ENV=dev`)自动开启热重载。
访问地址:
- 指纹盖章工具页面:
- API 文档(Swagger UI):
- 健康检查:
### 4. 前端开发(修改 web/ 时使用)
```bash
cd web
npm install
npm run dev # http://127.0.0.1:5173 ,/api 自动代理到 8000
npm run build # 产物输出到 web/dist,由 FastAPI 直接托管
```
### 5. 运行测试
```bash
pytest
```
### 6. 代码检查
```bash
ruff check .
ruff format --check .
```
## Docker 镜像构建与运行
仓库内置多阶段构建配置(`Dockerfile` + `docker-compose.yml`):
- 阶段 1(web-builder):使用 Node 编译 React/Vite 前端,产物输出到 `web/dist`;
- 阶段 2(runtime):基于 `python:3.13-slim` 安装 Python 依赖并托管前端产物与 API。
镜像默认安装 LibreOffice,用于 Word(.doc/.docx)转 PDF;不需要该能力时可在构建阶段关闭以显著缩小镜像体积。
### 1. 构建镜像
常规构建(包含 LibreOffice,默认):
```bash
docker build -t doc-stamp:0.1.0 .
```
关闭 LibreOffice(不提供 Word 转换,镜像更小):
```bash
docker build --build-arg WITH_OFFICE=false -t doc-stamp:0.1.0 .
```
更换 Python 依赖下载源(例如使用国内镜像加速构建):
```bash
docker build --build-arg PIP_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple -t doc-stamp:0.1.0 .
```
### 2. 使用 docker compose 构建并启动
```bash
docker compose up -d --build
```
常用命令:
```bash
docker compose logs -f app # 跟踪应用日志
docker compose ps # 查看服务状态
docker compose down # 停止服务(保留会话数据卷)
docker compose down -v # 停止并删除数据卷(会清空 /data/stamp-sessions 下的会话文件)
```
### 3. 直接使用 docker run
```bash
docker run -d \
--name doc-stamp \
-p 8000:8000 \
-v doc-stamp-data:/data/stamp-sessions \
doc-stamp:0.1.0
```
### 4. 验证与注意事项
容器启动后访问:
- 指纹盖章工具页面:
- API 文档(Swagger UI):
- 健康检查:`curl http://127.0.0.1:8000/api/v1/health`
注意事项:
- 容器内服务绑定 `0.0.0.0:8000`,会话文件保存在 `/data/stamp-sessions`(默认 30 分钟自动清理),建议挂载命名卷持久化;
- 浏览器跨域访问时需按实际前端地址设置 `CORS_ORIGINS`,例如 `CORS_ORIGINS='["http://your-host:8000"]'`;
- 使用 `WITH_OFFICE=false` 构建的镜像不包含 LibreOffice,Word 转换接口会返回明确错误提示;
- `docker compose down -v` 会删除会话数据卷,请先确认无需保留的会话文件。
### 5. 推送到镜像仓库(含常见报错)
构建成功后按需推送镜像(`/` 替换为实际仓库地址):
```bash
docker tag doc-stamp:0.1.0 //doc-stamp:0.1.0
docker push //doc-stamp:0.1.0
```
#### 常见报错:unknown manifest class for application/vnd.oci.empty.v1+json
推送中断在 manifest 提交阶段,报错示例:
```text
6c0f2568ec38: Pushed
error from registry: unknown manifest class for application/vnd.oci.empty.v1+json
```
含义:镜像层(如上例的 `6c0f2568ec38`,这是层 digest 前缀,不是 git 提交)已全部上传成功,但最后写入 manifest / manifest index 时被仓库拒绝,因此仓库中不会出现可用镜像。
原因:BuildKit v0.32+(`docker buildx` 的默认构建后端)构建时默认生成 provenance / SBOM attestations,且 attestation manifest 按 OCI artifact 格式输出——其 config 描述符使用 OCI 1.1 新增的空配置类型 `application/vnd.oci.empty.v1+json`。Docker Hub 等仓库已支持该格式,而部分镜像仓库(如阿里云 ACR、GitLab Container Registry 等)尚未支持,会返回 `unknown manifest class` 拒绝整个 index。
确认方法:若同一 tag 在支持该格式的仓库(如 Docker Hub)上有成功推送,可执行以下命令查看 manifest 是否含平台为 `unknown/unknown` 的 attestation 条目:
```bash
docker buildx imagetools inspect //doc-stamp:0.1.0
```
解决方案(任选其一):
1. 构建并推送时关闭默认 attestation(推荐,产物为普通单架构镜像,不含 attestation index):
```bash
docker buildx build --platform linux/amd64 --push \
--provenance=false --sbom=false \
-t //doc-stamp:0.1.0 .
```
2. 不便修改构建命令时,用环境变量全局关闭(适合 CI/CD):
```bash
export BUILDX_NO_DEFAULT_ATTESTATIONS=1
docker buildx build --push -t //doc-stamp:0.1.0 .
```
GitHub Actions 使用 `docker/build-push-action` 时,可在 job 中设置环境变量 `BUILDX_NO_DEFAULT_ATTESTATIONS: "1"`,或在 `with` 中显式传 `provenance: false`、`sbom: false`。
3. 希望保留 attestation、仅关闭新的 OCI artifact 输出格式(buildx v0.36+):
```bash
export BUILDX_NO_DEFAULT_OCI_ARTIFACT=1
docker buildx build --push -t //doc-stamp:0.1.0 .
```
修复后重新推送,成功时输出会以 `digest: sha256:...` 结尾,不再出现 `unknown manifest class`。
## 目录结构
```text
doc-stamp/
├── app/ # 应用主目录
│ ├── main.py # 应用入口(应用工厂 + 中间件 + 路由注册)
│ ├── core/ # 核心配置
│ │ └── config.py # Settings(环境变量 / .env 覆盖)
│ ├── api/ # 接口层
│ │ └── v1/
│ │ ├── router.py # v1 路由聚合入口
│ │ └── endpoints/ # 具体接口(health.py、stamp.py 等)
│ ├── schemas/ # Pydantic 请求/响应模型
│ ├── services/ # 业务逻辑层(stamp_processing.py 图像处理)
│ └── models/ # 数据模型层(ORM 模型等)
├── web/ # React + Vite 前端
│ ├── src/ # 页面、画布编辑、API 封装
│ └── dist/ # 构建产物(.gitignore,FastAPI 托管)
├── tests/ # 测试
│ └── test_health.py
├── .env.example # 配置模板
├── .dockerignore # Docker 构建忽略规则
├── Dockerfile # 多阶段镜像构建(前端编译 + Python 运行时)
├── docker-compose.yml # 一键构建/启动编排(含命名卷与健康检查)
├── environment.yml # conda 环境与依赖清单
└── README.md
```
## 新增业务模块约定
1. 在 `app/api/v1/endpoints/` 新建 `xxx.py`,内部定义 `router = APIRouter()`。
2. 在 `app/api/v1/router.py` 中挂载该 router(带前缀与 tags)。
3. 请求/响应模型放入 `app/schemas/`,业务逻辑放入 `app/services/`。
4. 在 `tests/` 下补充对应接口测试。
## 指纹盖章 API
- `POST /api/v1/stamp/sessions`:上传文档与素材(`fingerprints[]` / `signatures[]` 可多张)
- `POST /api/v1/stamp/word/import`:Word(.doc/.docx)转逐页 PNG,返回页面列表(默认末页);页面资源见 `GET /word/imports/{id}/preview|page/{n}`
- `POST /api/v1/stamp/sessions/{id}/process-document`:截图转打印(`screenshot` / `scan_texture`)、透视校正(auto/manual)、签名区识别
- `POST /api/v1/stamp/sessions/{id}/process-materials`:对所有素材抠图(`sensitivity` 灵敏度)
- `POST/DELETE /api/v1/stamp/sessions/{id}/materials`:会话内增删素材
- `GET /api/v1/stamp/sessions/{id}/assets/document/{raw|clean|corrected}`、`assets/cutout/{material_id}`:访问中间图像
- `POST /api/v1/stamp/sessions/{id}/export/pdf`:合成 PNG 转 PDF
会话文件存放于系统临时目录(默认 30 分钟自动清理),不落库。
> Word 转换依赖本机已安装 Microsoft Word、WPS Office 或 LibreOffice 中的任意一种(自动探测:LibreOffice 命令行 → Word COM → WPS COM);未安装时会给出明确提示。