feat: v1.1.0 - 图片按日期分文件夹保存、设置面板、序号显示模式、自动刷新

This commit is contained in:
2026-04-16 10:42:36 +08:00
parent 3503ee35ab
commit f703c0491c
9 changed files with 639 additions and 220 deletions

View File

@@ -3,12 +3,11 @@ Web 后端 - FastAPI
"""
from fastapi import FastAPI, HTTPException
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse, HTMLResponse
from fastapi.responses import FileResponse, HTMLResponse, JSONResponse
from pathlib import Path
import uvicorn
import json
from config import WEB_PORT, WEB_HOST, IMAGES_DIR
from config import WEB_PORT, WEB_HOST, config_mgr, DATA_DIR
from database import db
from scheduler import scheduler
from camera import list_available_cameras, CameraCapture
@@ -20,7 +19,17 @@ app = FastAPI(title="视觉记录系统")
# 静态文件目录
STATIC_DIR = Path(__file__).parent / "static"
app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")
app.mount("/images", StaticFiles(directory=str(IMAGES_DIR)), name="images")
# 图片目录 - 使用配置中的目录
@app.get("/images/{filename}")
async def get_image(filename: str):
"""获取图片(需要在实际目录中查找)"""
# 从数据库查找图片路径
images = db.get_images(limit=1000)
for img in images:
if img['path'].endswith(filename):
return FileResponse(img['path'])
raise HTTPException(status_code=404, detail="图片不存在")
# ============== 页面路由 ==============
@@ -34,17 +43,55 @@ async def index():
return HTMLResponse("<h1>请创建 index.html</h1>")
# ============== API 路由 ==============
# ============== 配置 API ==============
@app.get("/api/config")
async def get_config():
"""获取所有配置"""
return config_mgr.get_all()
@app.post("/api/config")
async def update_config(data: dict):
"""更新配置"""
config_mgr.update(data)
return {"success": True, "config": config_mgr.get_all()}
@app.get("/api/config/images-dir")
async def get_images_dir():
"""获取图片保存目录"""
return {
"images_dir": config_mgr.get('images_dir'),
"today_folder": str(config_mgr.get_images_dir())
}
@app.post("/api/config/images-dir")
async def set_images_dir(path: str):
"""设置图片保存目录"""
from pathlib import Path
try:
Path(path).mkdir(parents=True, exist_ok=True)
config_mgr.set('images_dir', path)
return {"success": True, "images_dir": path}
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
# ============== 状态 API ==============
@app.get("/api/status")
async def get_status():
"""获取系统状态"""
stats = db.get_stats()
scheduler_status = scheduler.get_status()
config = config_mgr.get_all()
return {
"scheduler": scheduler_status,
"stats": stats
"stats": stats,
"config": config
}
@@ -54,16 +101,19 @@ async def get_cameras():
cameras = list_available_cameras()
details = []
for cam_index in cameras:
cam = CameraCapture(cam_index)
cam = CameraCapture()
cam.camera_index = cam_index
details.append(cam.get_camera_info())
cam.close()
return {"cameras": details}
# ============== 调度控制 API ==============
@app.post("/api/scheduler/start")
async def start_scheduler(interval: int = None, auto_analyze: bool = None):
async def start_scheduler():
"""启动定时拍照"""
result = scheduler.start(interval=interval, auto_analyze=auto_analyze)
result = scheduler.start()
if not result['success']:
raise HTTPException(status_code=400, detail=result['error'])
return result
@@ -78,6 +128,8 @@ async def stop_scheduler():
@app.post("/api/scheduler/interval")
async def set_interval(interval: int):
"""设置拍照间隔"""
if interval < 10:
raise HTTPException(status_code=400, detail="间隔不能小于10秒")
return scheduler.set_interval(interval)
@@ -90,6 +142,8 @@ async def capture_now():
return result
# ============== 分析 API ==============
@app.post("/api/analyze/{image_id}")
async def analyze_image(image_id: int):
"""分析指定图片"""
@@ -106,10 +160,29 @@ async def analyze_unanalyzed():
return {"results": results}
# ============== 图片 API ==============
@app.get("/api/images")
async def get_images(limit: int = 50, offset: int = 0, analyzed_only: bool = False):
async def get_images(
limit: int = 50,
offset: int = 0,
analyzed_only: bool = False,
date_folder: str = None
):
"""获取图片列表"""
images = db.get_images(limit=limit, offset=offset, analyzed_only=analyzed_only)
# 使用配置中的显示数量
config_limit = config_mgr.get('display_limit', 20)
if limit > config_limit:
limit = config_limit
images = db.get_images(limit=limit, offset=offset, analyzed_only=analyzed_only, date_folder=date_folder)
# 为每张图片添加事件摘要
for img in images:
events = db.get_events_by_image(img['id'])
img['events_summary'] = ', '.join([e['event_type'] for e in events]) if events else ''
img['events_count'] = len(events)
return {"images": images, "total": len(images)}
@@ -134,9 +207,22 @@ async def delete_image(image_id: int):
return {"success": True}
@app.get("/api/date-folders")
async def get_date_folders():
"""获取日期文件夹列表"""
stats = db.get_stats()
return {"folders": stats['date_folders']}
# ============== 事件 API ==============
@app.get("/api/events")
async def get_events(limit: int = 50, offset: int = 0, event_type: str = None):
"""获取事件列表"""
config_limit = config_mgr.get('display_limit', 20)
if limit > config_limit:
limit = config_limit
events = db.get_events(limit=limit, offset=offset, event_type=event_type)
return {"events": events, "total": len(events)}