面向实验室场景的试剂与耗材管理系统,覆盖申购、审批、到货、入库、借用、归还、公告、设备会话管理等完整流程。项目采用 FastAPI + React 前后端分离架构,后端以 SQLite WAL 模式为核心存储,前端基于 React 19 和 Vite 8 构建,适合单实验室或中小团队快速部署。
LabStorageManager 解决的是实验室中最容易失控的几类问题:
- 试剂和耗材分开管理,但又能共享统一的认证、审批和搜索体验。
- CAS 号、防重复采购、拼音检索、模糊搜索、批量导入这些实验室高频需求由系统直接支持。
- 订单到库存的流转保留审计链路,避免“入库后订单消失”带来的追溯困难。
- 图片不入库,只落文件系统并在数据库中保存 URL,减轻数据库压力。
适用场景:
- 高校课题组或研究平台
- 企业研发实验室
- 需要部署在单机 / 轻量服务器上的库存系统
- 试剂与耗材双流程管理 试剂支持 CAS 号、到货确认、一键入库;耗材支持独立订单流程与完成态管理。
- CAS 防重与库存预警 申购时可按 CAS 查询现有库存和历史订单,降低重复购买概率。
- 中文拼音检索与 FTS 搜索 名称、分类、品牌、位置等字段会预计算拼音,并结合 SQLite FTS5 提供搜索能力。
- 库存借还闭环 支持借出、归还、借用历史、当前借用人、临时保管人等字段。
- 公共货架与常用试剂 支持公共库存场景,便于管理共享样品或常备试剂。
- 用户、设备、会话治理 支持 HttpOnly Cookie 登录、设备列表、批量注销、IP/设备数量限制。
- 公告与图片上传
公告支持图片,图片文件落地到
static/,并受尺寸、类型、上传频率控制。 - CLI 与自动化入口
提供
python -m lsm_cli命令行入口,适合脚本、agent 和无 UI 场景操作。 - 部署简单
内置 Docker Compose,可快速拉起
frontend + backend + redis。
| 层级 | 技术 |
|---|---|
| 后端 | FastAPI, SQLModel, SQLite, Redis, python-jose, bcrypt, Pillow, pypinyin |
| 前端 | React 19, TypeScript 5.9, Vite 8, React Router 7, Zustand, React Hook Form, Valibot |
| UI | Radix UI, Tailwind CSS 4, Lucide React, Framer Motion |
| 表格与数据 | TanStack Table 8, TanStack Virtual, Axios |
| 化学相关 | RDKit(前端分子结构渲染) |
| 构建与校验 | Poetry, npm, ruff, ESLint, TypeScript build |
| 部署 | Docker Compose, Nginx, Uvicorn |
建议本地环境:
- Python 3.11+
- Node.js 20+
- npm 10+
- Redis 6+ 或 7+(本地开发可选,但推荐开启)
git clone <your-repo-url> LabStorageManager
cd LabStorageManager推荐使用 Poetry:
poetry install如果你使用 pip:
python -m venv .venv
# Windows PowerShell
.venv\Scripts\Activate.ps1
# macOS / Linux
source .venv/bin/activate
pip install -r requirements.txtcd frontend
npm install
cd ..# Windows PowerShell
Copy-Item .env.example .env
# macOS / Linux
cp .env.example .env最少需要确认这些字段:
| 变量 | 说明 |
|---|---|
ENV |
本地开发建议 development |
CORS_ORIGINS |
前端地址白名单,开发时通常为 http://localhost:5173 |
DEFAULT_ADMIN_PASSWORD |
必填;后端首次启动会用它初始化管理员 |
ALGORITHM |
默认 RS256 |
DATABASE_URL |
默认 SQLite 文件 |
开发环境建议:
- 将
ENV=development - 把
CORS_ORIGINS改成你的前端地址 - 首次启动前设置
DEFAULT_ADMIN_PASSWORD
说明:
- 当
ENV=development且本地还没有 RSA 密钥时,后端可自动生成临时密钥对。 - 当
ENV=production时,ALGORITHM必须为RS256,并且.keys/private.pem/.keys/public.pem必须可用。
python -m uvicorn app.main:app --reload --host 0.0.0.0 --port 8000启动后可访问:
- 开发文档:
http://localhost:8000/docs - ReDoc:
http://localhost:8000/redoc - 健康检查:
http://localhost:8000/health
注意:
- 只有开发模式会暴露
/docs、/redoc和/openapi.json。 - 首次启动会自动初始化至少一个管理员账户。
cd frontend
npm run dev默认前端地址:
http://localhost:5173
| 变量 | 示例 | 说明 |
|---|---|---|
ENV |
development |
development/dev 或 production |
CORS_ORIGINS |
["http://localhost:5173"] |
JSON 数组字符串 |
DEFAULT_ADMIN_PASSWORD |
your-password |
默认管理员密码,未设置将导致启动失败 |
ALGORITHM |
RS256 |
生产环境必须使用 RS256 |
| 变量 | 默认值 | 说明 |
|---|---|---|
DATABASE_URL |
sqlite:///./lab_inventory.db |
SQLite 数据库连接串 |
TRUST_PROXY_HEADERS |
false |
是否信任反向代理头 |
ACCESS_TOKEN_EXPIRE_MINUTES |
10080 |
登录态默认 7 天 |
SESSION_EXPIRE_HOURS |
72 |
会话有效期 |
MAX_IP_PER_USER |
5 |
每个用户允许的最大 IP 数 |
MAX_DEVICE_PER_USER |
10 |
每个用户允许的最大设备数 |
REDIS_HOST |
127.0.0.1 |
Redis 主机 |
REDIS_PORT |
6379 |
Redis 端口 |
REDIS_DB |
1 |
Redis 逻辑库 |
REDIS_KEY_PREFIX |
lsm |
Redis key 前缀 |
| 变量 | 默认值 | 说明 |
|---|---|---|
MAX_FILE_SIZE_MB |
10 |
单文件体积限制 |
MAX_UPLOAD_REQUEST_SIZE_MB |
12 |
单次请求总上传限制 |
ALLOWED_IMAGE_TYPES |
image/jpeg,image/png,image/webp |
允许的 MIME 类型 |
MAX_IMAGE_WIDTH |
800 |
最大宽度 |
MAX_IMAGE_HEIGHT |
800 |
最大高度 |
MAX_IMAGE_SIZE_KB |
100 |
压缩后图片大小上限 |
UPLOAD_RATE_LIMIT_COUNT |
10 |
上传限流次数 |
UPLOAD_RATE_LIMIT_WINDOW_SECONDS |
300 |
上传限流时间窗口 |
| 变量 | 说明 |
|---|---|
PRIVATE_KEY_PATH |
RS256 私钥路径 |
PUBLIC_KEY_PATH |
RS256 公钥路径 |
SECRET_KEY |
仅 HS256 时使用;开发环境可自动生成临时值 |
# 开发启动
python -m uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
# 代码检查
ruff check app/cd frontend
# 开发启动
npm run dev
# Lint
npm run lint
# 生产构建
npm run build# 构建并启动
APP_PORT=80 docker compose up -d --build
# 查看状态
docker compose ps
# 查看日志
docker compose logs -f backend
docker compose logs -f frontend
docker compose logs -f redis仓库提供本地命令行入口 lsm_cli/README.md,适合 agent、脚本任务和不想打开前端的开发者。CLI 只通过后端 API 工作,不直接访问数据库,也不导入服务层。
auth支持login、logout、whoamiinventory支持列表、详情、CAS 查询、借还、手工入库、更新等日常库存操作reagent-orders支持列表、创建、更新、到货确认、一键入库等试剂订单流程consumable-orders支持列表、创建、更新、完成等耗材订单流程
限制说明:
- 不开放
delete - 不开放
export - 不开放文件上传
- 不开放 CLI 未显式暴露的 API
# 查看顶层帮助
python -m lsm_cli --help
# 交互式登录
python -m lsm_cli auth login --username alice
# 查询库存
python -m lsm_cli inventory list --page 1 --page-size 20 --param search=乙醇
# 创建试剂订单
python -m lsm_cli reagent-orders create --data-file payload.json
# 查看当前登录用户
python -m lsm_cli auth whoami- 默认 API 地址为
http://127.0.0.1:8000/api auth login支持--base-url与--timeout- 自动化脚本推荐使用
--password-stdin传递密码,避免明文出现在 shell history - 所有命令向
stdout输出 JSON,并通过退出码区分错误类别 - 面向 agent 的受限 CLI 操作流程见 AGENTS Skill
- Windows / macOS 目录版安装、配置文件位置、完整退出码契约见
lsm_cli/README.md
Browser / Browser Extension
|
v
React 19 + Vite + Axios
|
v
FastAPI
|- Auth / Session
|- Inventory
|- Reagent Orders
|- Consumable Orders
|- Announcements
|- Event Stream
|
v
SQLite (WAL) + Redis + static/
- 使用
FastAPI暴露 API,开发模式下提供 Swagger/ReDoc。 SQLite在每个连接上都会显式开启PRAGMA journal_mode=WAL。- 数据库初始化时会自动创建表、性能索引、FTS 表与触发器,并检查 schema 一致性。
- 全局中间件处理以下问题:
- 请求日志与
X-Request-ID - 上传请求体积限制
- 生产环境 HTTPS 跳转
- Cookie 鉴权下的 CSRF Origin/Referer 校验
- 安全响应头与 CSP/HSTS
- 请求日志与
- 使用
BrowserRouter管理路由。 - 登录态通过 HttpOnly Cookie 持有,Axios 统一开启
withCredentials。 - 页面采用懒加载,主要模块包括:
- 仪表盘
- 试剂订单
- 耗材订单
- 库存
- 公共货架
- 导入页
- 设备管理
- 用户管理
- 公告管理
- 操作日志
- 登录接口写入 Cookie,不依赖浏览器
localStorage保存 token。 - 支持多设备登录与会话列表管理。
- 会话可按设备名称、IP 等信息追踪。
- 401 会统一触发前端登出与跳转。
| Key | 用途 |
|---|---|
app-ui |
主题、字体来源、Dashboard 页签、公告已读/关闭、Bug 按钮隐藏 |
app-table |
表格 expandAll、fuzzySearch、列宽 |
app-auth-meta |
设备 id/name、remembered user |
auth-storage |
Zustand 登录态持久化,带 TTL |
sidebar-storage |
Zustand 侧栏状态持久化,带 TTL |
chemical_properties_cache |
化学属性缓存,独立长 TTL |
cart_import_batch_latest |
扩展导入桥接批次,2 小时 TTL |
inventory、reagent_order、consumable_order、users建有 SQLite FTS5 虚表。- 名称、拼音、拼音首字母等字段会被索引,便于中文检索。
- 大量列表查询配套了状态、申请人、时间、公共货架等复合索引。
这些规则直接影响系统正确性,开发和运维都应该了解。
这是并发读写的基础约束,项目在数据库连接层已强制设置。
试剂一键入库时会根据订单生成库存记录,但订单本身保留,用于审计和回溯。
CAS 号等关键字段会在服务端清洗,避免由于大小写、空格、分隔符差异导致重复数据。
涉及写操作的接口需要校验当前用户身份,管理员能力与普通用户能力分离。
- 输入校验错误应在表单字段旁展示
- toast 主要用于非字段级错误
- 生产环境必须使用
RS256 - Cookie 场景下会启用更严格的 CSRF 与 HTTPS 策略
- 未配置 HTTPS 时,不应把
ENV直接切到production
.
├── app/ # FastAPI 后端
├── frontend/ # React 前端
├── browser-extension/ # 浏览器扩展,用于购物车导入等场景
├── docker/ # Dockerfile、Nginx、入口脚本
├── static/ # 图片与静态文件
├── docs/ # 项目文档
├── tests/ # 后端测试目录
├── docker-compose.yml # 一体化部署编排
├── pyproject.toml # 后端依赖与工具配置
├── requirements.txt # pip 安装入口
└── README.md
app/
├── main.py # FastAPI 入口、中间件、路由装配
├── database.py # SQLModel 引擎、WAL、FTS、索引初始化
├── api/ # 路由层
├── core/ # 配置、认证、常量、请求工具
├── models/ # SQLModel 数据模型
└── services/ # 业务服务
主要接口模块:
users.pyuser_sessions.pyuser_logs.pyinventory.pyreagent_orders.pyconsumable_orders.pyannouncements.pycart_sync.pyevents.pyerror_logs.py
frontend/src/
├── api/ # Axios API 封装
├── components/ # UI 组件
├── hooks/ # 自定义 hooks
├── lib/ # 工具、常量、校验
├── pages/ # 页面级组件
└── store/ # Zustand 状态管理
这是当前仓库最直接的部署方式。
包含的服务:
frontendbackendredis
步骤:
git clone <your-repo-url> LabStorageManager
cd LabStorageManager
cp .env.example .env至少修改:
DEFAULT_ADMIN_PASSWORDCORS_ORIGINSREDIS_PASSWORDENV
然后启动:
APP_PORT=80 docker compose up -d --build检查服务:
docker compose ps
curl http://127.0.0.1:${APP_PORT:-80}/
curl http://127.0.0.1:${APP_PORT:-80}/health说明:
- 前端镜像基于
node:20-alpine构建,运行层是nginx:1.27-alpine。 - 后端镜像基于
python:3.11-slim,使用uvicorn启动。 - Compose 会把 Redis 地址注入为容器内部服务名
redis。
适合开发调试。
- 本地启动 Redis(推荐)
- 启动后端
uvicorn - 启动前端
vite - 前端通过
CORS_ORIGINS与 API 基地址访问后端
仓库包含 browser-extension/,用于浏览器侧导入或购物车同步相关场景。后端提供了 /cart-import 路由,会将入口跳转到前端页面。
- 静态资源默认挂载在
/static - 响应头附带缓存控制
- 图片安全头由后端统一补齐
原因: 未设置默认管理员密码。
处理:
在 .env 中补充 DEFAULT_ADMIN_PASSWORD 后重启后端。
原因: 生产模式默认关闭 API 文档。
处理:
确认 ENV=development 时再访问 /docs。
原因:
CORS_ORIGINS未正确配置- 浏览器与后端地址不匹配
- 在无 HTTPS 的环境使用了
production
处理:
- 开发时设为
ENV=development - 检查前端域名是否在
CORS_ORIGINS中 - 确认浏览器实际请求携带 Cookie
影响: 会话或限流相关能力可能异常,核心库存数据仍在 SQLite。
处理:
- 确认
REDIS_HOST、REDIS_PORT、REDIS_PASSWORD - 检查容器或本地 Redis 是否已启动
检查以下几项:
- 文件类型是否在
ALLOWED_IMAGE_TYPES - 请求体是否超过
MAX_UPLOAD_REQUEST_SIZE_MB - 单图是否超过限制或服务器目录无写权限
检查以下几项:
- 数据库初始化是否完整执行
- FTS 虚表和触发器是否存在
- 是否误删了 SQLite 索引或数据库文件
仓库在 lsm_cli/lab-storage-manager-cli/ 提供了一个专门用于操作 CLI 的 skill。它的作用不是解释开发约定,而是约束 agent 只能通过 python -m lsm_cli 与系统交互,并严格遵守当前 CLI 已暴露的命令面。
- 需要通过 CLI 登录、查询库存、查看借用状态
- 需要通过 CLI 创建或更新试剂订单、确认到货、一键入库
- 需要通过 CLI 创建、更新或完成耗材订单
- 需要在 agent 自动化中避免 raw HTTP、数据库直连、直接导入后端模块
- 只允许调用
python -m lsm_cli及其已暴露子命令 - 禁止使用
curl、Invoke-RestMethod、requests、httpx、数据库直连或伪造本地 token - 写操作前必须先通过 CLI 查询拿到准确 ID;禁止猜测 ID
- 目标不唯一、字段含义不清或单位/数量有歧义时,必须先确认,不能“先试一下”
- 登录只允许普通用户账号;推荐
--password-stdin - 不支持
delete、export、文件上传、用户管理、会话管理、密码修改、头像修改
- 用
python -m lsm_cli --help确认目标能力是否存在。 - 需要认证时,用
auth login登录,再用auth whoami校验身份。 - 对写操作先用
list、get、cas、code、my-*等读命令定位准确 ID。 - 再执行真正的写操作,并再次核对目标 ID、动作和输入值。
- 如果命令失败,优先按退出码处理;不要切换到 HTTP 或数据库旁路。
- Skill 定义:
lsm_cli/lab-storage-manager-cli/SKILL.md - 命令参考:
lsm_cli/lab-storage-manager-cli/references/commands.md
本项目使用 Apache License 2.0。详见 LICENSE。
