服务接口初步完成

This commit is contained in:
haotian 2025-05-16 17:22:50 +08:00
parent f9d35b1ba8
commit 57578f94dc
12 changed files with 7251 additions and 208 deletions

5
app/api/v1/api.py Normal file
View File

@ -0,0 +1,5 @@
from fastapi import APIRouter
from app.api.v1.endpoints import events
api_router = APIRouter()
api_router.include_router(events.router, prefix="/api/v1", tags=["events"])

View File

@ -0,0 +1,79 @@
from typing import List, Optional
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.database import get_db
from app.crud.event import event
from app.schemas.event import EventList, EventDetail, EventUpdate, EventQuery
router = APIRouter()
@router.get("/events", response_model=List[EventList])
async def get_events(
db: AsyncSession = Depends(get_db),
start_time: Optional[str] = None,
end_time: Optional[str] = None,
etypeName: Optional[str] = None,
area: Optional[str] = None,
skip: int = Query(0, ge=0),
limit: int = Query(100, ge=1, le=1000)
):
"""
获取事件列表
- 支持时间范围查询
- 支持事件类型和区域筛选
- 支持分页
"""
query = EventQuery(
start_time=start_time,
end_time=end_time,
etypeName=etypeName,
area=area,
skip=skip,
limit=limit
)
events = await event.get_multi_with_query(db, query=query)
return [EventList.model_validate(event) for event in events]
@router.get("/events/{event_id}", response_model=EventDetail)
async def get_event(
event_id: str,
db: AsyncSession = Depends(get_db)
):
"""
获取事件详情
"""
event_obj = await event.get_by_id(db, event_id=event_id)
if not event_obj:
raise HTTPException(status_code=404, detail="事件不存在")
return EventDetail.model_validate(event_obj)
@router.put("/events/{event_id}", response_model=EventDetail)
async def update_event(
event_id: str,
event_in: EventUpdate,
db: AsyncSession = Depends(get_db)
):
"""
更新事件信息
"""
event_obj = await event.update_event(
db,
event_id=event_id,
obj_in=event_in
)
if not event_obj:
raise HTTPException(status_code=404, detail="事件不存在")
return EventDetail.model_validate(event_obj)
@router.delete("/events/{event_id}", response_model=EventDetail)
async def delete_event(
event_id: str,
db: AsyncSession = Depends(get_db)
):
"""
删除事件
"""
event_obj = await event.delete_event(db, event_id=event_id)
if not event_obj:
raise HTTPException(status_code=404, detail="事件不存在")
return EventDetail.model_validate(event_obj)

93
app/crud/event.py Normal file
View File

@ -0,0 +1,93 @@
from typing import List, Optional, Dict, Any
from sqlalchemy import select, and_, or_
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload
from app.crud.base import CRUDBase
from app.models.models import Event, Image, Temperature
from app.schemas.event import EventUpdate, EventQuery
class CRUDEvent(CRUDBase[Event, EventUpdate, EventUpdate]):
async def get_by_id(self, db: AsyncSession, *, event_id: str) -> Optional[Event]:
"""根据ID获取事件"""
query = (
select(Event)
.options(
selectinload(Event.images),
selectinload(Event.temperatures)
)
.where(Event.eventId == event_id)
)
result = await db.execute(query)
return result.scalar_one_or_none()
async def get_multi_with_query(
self,
db: AsyncSession,
*,
query: EventQuery
) -> List[Event]:
"""根据查询条件获取事件列表"""
conditions = []
if query.start_time:
conditions.append(Event.insDate >= query.start_time)
if query.end_time:
conditions.append(Event.insDate <= query.end_time)
if query.etypeName:
conditions.append(Event.etypeName == query.etypeName)
if query.area:
conditions.append(Event.area == query.area)
query_stmt = (
select(Event)
.options(
selectinload(Event.images),
selectinload(Event.temperatures)
)
)
if conditions:
query_stmt = query_stmt.where(and_(*conditions))
query_stmt = query_stmt.offset(query.skip).limit(query.limit)
result = await db.execute(query_stmt)
return result.scalars().all()
async def update_event(
self,
db: AsyncSession,
*,
event_id: str,
obj_in: EventUpdate
) -> Optional[Event]:
"""更新事件信息"""
event = await self.get_by_id(db, event_id=event_id)
if not event:
return None
update_data = obj_in.model_dump()
for field, value in update_data.items():
setattr(event, field, value)
# db.add(event)
await db.commit()
await db.refresh(event)
return event
async def delete_event(
self,
db: AsyncSession,
*,
event_id: str
) -> Optional[Event]:
"""删除事件"""
event = await self.get_by_id(db, event_id=event_id)
if not event:
return None
await db.delete(event)
await db.commit()
return event
event = CRUDEvent(Event)

26
app/main.py Normal file
View File

@ -0,0 +1,26 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.api.v1.api import api_router
from app.core.config import settings
app = FastAPI(
title="温度数据采集系统",
description="温度数据采集系统API接口",
version="1.0.0"
)
# 配置CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # 在生产环境中应该设置具体的域名
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 注册路由
app.include_router(api_router)
@app.get("/")
async def root():
return {"message": "温度数据采集系统API服务正在运行"}

View File

@ -90,6 +90,7 @@ class Temperature(Base):
eventId = Column(String(50), ForeignKey('event.eventId'), nullable=False, comment='关联事件ID') eventId = Column(String(50), ForeignKey('event.eventId'), nullable=False, comment='关联事件ID')
imageId = Column(BigInteger, ForeignKey('image.imageId'), nullable=False, comment='关联图片ID') imageId = Column(BigInteger, ForeignKey('image.imageId'), nullable=False, comment='关联图片ID')
temperature = Column(String(20), nullable=False, comment='温度值') temperature = Column(String(20), nullable=False, comment='温度值')
# status = Column(String(2), comment='温度是否正常')
confidence = Column(String(20), nullable=False, comment='识别置信度') confidence = Column(String(20), nullable=False, comment='识别置信度')
createTime = Column(DateTime, default=datetime.now, nullable=False, comment='创建时间') createTime = Column(DateTime, default=datetime.now, nullable=False, comment='创建时间')

102
app/schemas/event.py Normal file
View File

@ -0,0 +1,102 @@
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel, ConfigDict
class ImageBase(BaseModel):
imageUrl: str
localPath: Optional[str] = None
model_config = ConfigDict(from_attributes=True)
class TemperatureBase(BaseModel):
temperature: str
confidence: str
createTime: datetime
model_config = ConfigDict(from_attributes=True)
class EventBase(BaseModel):
eventId: str
tenantInfoId: Optional[str] = None
reportEventId: Optional[str] = None
number: Optional[str] = None
name: Optional[str] = None
etypeName: Optional[str] = None
insDate: Optional[datetime] = None
model_config = ConfigDict(from_attributes=True)
class EventList(EventBase):
images: List[ImageBase] = []
temperatures: List[TemperatureBase] = []
model_config = ConfigDict(from_attributes=True)
class EventDetail(EventBase):
eclassify: Optional[str] = None
operationType: Optional[str] = None
etype: Optional[str] = None
enTypeName: Optional[str] = None
hkTypeName: Optional[str] = None
reportStatus: Optional[str] = None
results: Optional[str] = None
insDateShow: Optional[datetime] = None
updDate: Optional[datetime] = None
updDateShow: Optional[datetime] = None
fileType: Optional[str] = None
area: Optional[str] = None
floor: Optional[str] = None
map: Optional[str] = None
staffId: Optional[str] = None
targetUserId: Optional[str] = None
position: Optional[str] = None
actualStaffName: Optional[str] = None
targetStaffName: Optional[str] = None
routeName: Optional[str] = None
phoneAddress: Optional[str] = None
width: Optional[str] = None
height: Optional[str] = None
resolution: Optional[str] = None
originX: Optional[str] = None
originY: Optional[str] = None
robotType: Optional[str] = None
eventFloor: Optional[str] = None
floorName: Optional[str] = None
coordId: Optional[str] = None
coord: Optional[str] = None
coordName: Optional[str] = None
positonName: Optional[str] = None
processingRemark: Optional[str] = None
carId: Optional[str] = None
parkingSpaceType: Optional[str] = None
parkingSpaceNumber: Optional[str] = None
carNumber: Optional[str] = None
eno: Optional[str] = None
instrument: Optional[str] = None
evideo: Optional[str] = None
createTime: Optional[datetime] = None
updateTime: Optional[datetime] = None
images: List[ImageBase] = []
temperatures: List[TemperatureBase] = []
model_config = ConfigDict(from_attributes=True)
class EventUpdate(BaseModel):
number: Optional[str] = None
name: Optional[str] = None
etypeName: Optional[str] = None
area: Optional[str] = None
position: Optional[str] = None
processingRemark: Optional[str] = None
model_config = ConfigDict(from_attributes=True)
class EventQuery(BaseModel):
start_time: Optional[datetime] = None
end_time: Optional[datetime] = None
etypeName: Optional[str] = None
area: Optional[str] = None
skip: int = 0
limit: int = 100
model_config = ConfigDict(from_attributes=True)

View File

@ -45,62 +45,40 @@
## 3. 详细设计 ## 3. 详细设计
### 3.1 数据采集模块 ### 3.1 数据同步模块
```python ```python
class DataCollector: class EventSyncService:
def __init__(self): def __init__(self):
self.token_manager = TokenManager() self.kangda = Kangda()
self.http_client = AsyncHTTPClient() self.ocr = BadiduOcr()
async def sync_events(self):
"""同步事件数据"""
print("开始同步事件数据...")
async def collect_events(self):
# 获取token
token = await self.token_manager.get_token()
# 获取事件列表 # 获取事件列表
events = await self.fetch_events(token) event_list = self.kangda.get_event_list()
# 获取事件详情 if not event_list:
for event in events: print("获取事件列表失败")
await self.process_event(event, token) return
# 获取新事件列表
new_events = await self._get_new_events(event_list)
if not new_events:
print("没有新事件需要同步")
return
print(f"发现 {len(new_events)} 个新事件")
# 获取事件详情并更新
await self._update_event_details(new_events)
print("事件同步完成
``` ```
### 3.2 OCR处理模块
```python ### 3.2 API接口模块
class OCRProcessor:
def __init__(self):
self.ocr = PaddleOCR()
async def process_image(self, image_path: str) -> Dict:
# 图片预处理
image = self.preprocess_image(image_path)
# OCR识别
result = self.ocr.ocr(image)
# 提取温度数据
temperature = self.extract_temperature(result)
return temperature
```
### 3.3 数据存储模块
```python
class DatabaseManager:
def __init__(self):
self.engine = create_engine(
'mysql+aiomysql://user:password@localhost:3306/temp_db',
pool_size=5,
max_overflow=10,
pool_timeout=30,
pool_recycle=1800
)
self.session = SessionLocal()
async def save_event(self, event_data: Dict):
event = Event(**event_data)
self.session.add(event)
await self.session.commit()
```
### 3.4 API接口模块
```python ```python
@router.get("/temperatures") @router.get("/temperatures")
@ -113,7 +91,6 @@ async def get_temperatures(
db, start_time, end_time db, start_time, end_time
) )
``` ```
## 4. 数据库设计 ## 4. 数据库设计
### 4.1 表结构 ### 4.1 表结构
@ -136,7 +113,6 @@ ALTER TABLE image ADD INDEX idx_image_event_id (event_id);
ALTER TABLE process_log ADD INDEX idx_log_event_id (event_id); ALTER TABLE process_log ADD INDEX idx_log_event_id (event_id);
ALTER TABLE process_log ADD INDEX idx_log_create_time (create_time); ALTER TABLE process_log ADD INDEX idx_log_create_time (create_time);
``` ```
### 4.3 数据库配置 ### 4.3 数据库配置
```ini ```ini
@ -152,7 +128,6 @@ max_overflow = 10
pool_timeout = 30 pool_timeout = 30
pool_recycle = 1800 pool_recycle = 1800
``` ```
## 5. 接口设计 ## 5. 接口设计
### 5.1 内部接口 ### 5.1 内部接口
@ -309,7 +284,6 @@ pool_recycle = 1800
| | | |
[Redis集群] [文件存储] [Redis集群] [文件存储]
``` ```
### 9.3 MySQL配置建议 ### 9.3 MySQL配置建议
```ini ```ini
@ -337,7 +311,6 @@ server-id = 1
log-bin = mysql-bin log-bin = mysql-bin
binlog_format = ROW binlog_format = ROW
``` ```
## 10. 测试方案 ## 10. 测试方案
### 10.1 单元测试 ### 10.1 单元测试

View File

@ -1,9 +1,11 @@
# 需求: # 需求:
- 设计一个Python后台服务程序用于自动化采集和处理温度数据 - 设计一个Python后台服务程序用于自动化采集和处理温度数据
- 通过WebSocket接收外部系统的消息触发 - 通过WebSocket接收外部系统的消息触发
- 支持事件驱动的数据采集和处理流程 - 支持事件驱动的数据采集和处理流程
# 功能: # 功能:
- WebSocket服务 - WebSocket服务
- 接收外部系统的消息 - 接收外部系统的消息
- 处理消息并触发相应的数据采集流程 - 处理消息并触发相应的数据采集流程
@ -18,6 +20,7 @@
- 支持事件的搜索和过滤 - 支持事件的搜索和过滤
# 系统架构: # 系统架构:
- 采用分层架构设计 - 采用分层架构设计
- 数据采集层负责与第三方API交互 - 数据采集层负责与第三方API交互
- 业务处理层负责数据处理和OCR识别 - 业务处理层负责数据处理和OCR识别
@ -25,6 +28,7 @@
- API接口层提供对外服务接口 - API接口层提供对外服务接口
# 技术选型: # 技术选型:
- 开发语言Python 3.8+ - 开发语言Python 3.8+
- Web框架FastAPI - Web框架FastAPI
- 数据库MySQL 8.0+ - 数据库MySQL 8.0+
@ -36,160 +40,91 @@
- 日志loguru - 日志loguru
# 具体第三方接口: # 具体第三方接口:
- 获取token和refresh接口
- url:
- http://erpapi.concoai.com/basis-api/user/login
- 传递参数:
- Query参数
- cookie_flag: Optional[str] = None
- device_id: Optional[str] = None
- is_login: Optional[str] = None
- lang: Optional[str] = None
- password: Optional[str] = None
- username: Optional[str] = None
- 响应参数: 重点保存 data中的token和refreshtoken
```json
{
"code": "2000",
"message": "成功",
"total": null,
"data": {
"token": "xxx",
"refreshToken": "xxx",
"isLogin": null,
"username": null,
"password": null,
"rolePath": "/index/staff-page"
},
"rows": null
}
```
- 获取列表页接口
- url:
- http://erpapi.concoai.com/robot/event/page
- 传递参数:
- Header 参数
- token
- refreshToken
- Body 参数
```json
{"pageNo": 1, "pageSize": 10000}
```
- 响应参数: 获取rows中 的eventId ,insDate, insDateShow
```json
{
"code": "2000",
"message": "成功",
"total": 1,
"data": null,
"rows": [
{
"eventId": "a462a29d7481495380cd47035b19edc0",
"tenantInfoId": "4fff5d4bcc4b4239941ff077a0da8958",
"reportEventId": null,
"number": "ROB23100098",
"name": "X32305000019",
"eclassify": "2",
"operationType": "2",
"etype": "E000007",
"etypeName": "日常巡检",
"enTypeName": "Routine patrol",
"hkTypeName": "日常巡檢\r",
"reportStatus": "0",
"results": "2",
"insDate": "2025-05-06T16:44:31",
"insDateShow": "2025-05-06 16:44:31",
"updDate": null,
"updDateShow": null,
"fileType": null,
"area": null,
"floor": null,
"map": null,
"staffId": null,
"targetUserId": null,
"position": null,
"actualStaffName": null,
"targetStaffName": "员工1",
"routeName": "test",
"phoneAddress": null,
"width": null,
"height": null,
"resolution": null,
"originX": null,
"originY": null,
"imgList": null,
"robotType": "08",
"eventFloor": null,
"floorName": null,
"coordId": null,
"coord": null,
"coordName": null,
"positonName": "A3",
"processingRemark": null,
"carId": null,
"parkingSpaceType": null,
"parkingSpaceNumber": null,
"carNumber": null,
"eno": null,
"instrument": null,
"evideo": null
}
]
}
```
- 获取事件详情接口 - 获取token和refresh接口
- url:
- http://erpapi.concoai.com/robot/event/{eventId} - url:
- 传递参数: - http://erpapi.concoai.com/basis-api/user/login
- 路径变量: - 传递参数:
- eventId - Query参数
- 请求头参数: - cookie_flag: Optional[str] = None
- token: - device_id: Optional[str] = None
- refreshToken - is_login: Optional[str] = None
- 响应参数: 主要获取imgList中的图片url. - lang: Optional[str] = None
```json - password: Optional[str] = None
{ - username: Optional[str] = None
"code": "2000", - 响应参数: 重点保存 data中的token和refreshtoken
"message": "成功", ```json
"total": null, {
"data": { "code": "2000",
"message": "成功",
"total": null,
"data": {
"token": "xxx",
"refreshToken": "xxx",
"isLogin": null,
"username": null,
"password": null,
"rolePath": "/index/staff-page"
},
"rows": null
}
```
- 获取列表页接口
- url:
- http://erpapi.concoai.com/robot/event/page
- 传递参数:
- Header 参数
- token
- refreshToken
- Body 参数
```json
{"pageNo": 1, "pageSize": 10000}
```
- 响应参数: 获取rows中 的eventId ,insDate, insDateShow
```json
{
"code": "2000",
"message": "成功",
"total": 1,
"data": null,
"rows": [
{
"eventId": "a462a29d7481495380cd47035b19edc0", "eventId": "a462a29d7481495380cd47035b19edc0",
"tenantInfoId": "4fff5d4bcc4b4239941ff077a0da8958", "tenantInfoId": "4fff5d4bcc4b4239941ff077a0da8958",
"reportEventId": "bb96f28e14d944c79f44321edca4395f", "reportEventId": null,
"number": "ROB23100098", "number": "ROB23100098",
"name": "X32305000019", "name": "X32305000019",
"eclassify": "2", "eclassify": "2",
"operationType": "2", "operationType": "2",
"etype": "E000007", "etype": "E000007",
"etypeName": "日常巡检", "etypeName": "日常巡检",
"enTypeName": null, "enTypeName": "Routine patrol",
"hkTypeName": null, "hkTypeName": "日常巡檢\r",
"reportStatus": "0", "reportStatus": "0",
"results": "2", "results": "2",
"insDate": null, "insDate": "2025-05-06T16:44:31",
"insDateShow": "2025-05-06 16:44:31", "insDateShow": "2025-05-06 16:44:31",
"updDate": null, "updDate": null,
"updDateShow": null, "updDateShow": null,
"fileType": "2", "fileType": null,
"area": "区域1", "area": null,
"floor": "", "floor": null,
"map": null, "map": null,
"staffId": null, "staffId": null,
"targetUserId": null, "targetUserId": null,
"position": "75.104378,90.402679", "position": null,
"actualStaffName": null, "actualStaffName": null,
"targetStaffName": "员工1", "targetStaffName": "员工1",
"routeName": "test", "routeName": "test",
"phoneAddress": "http://file.prod.concoai.com/image/4fff5d4bcc4b4239941ff077a0da8958/4f60fbd391e98327f6ee13df1455f23e.jpg", "phoneAddress": null,
"width": "3994.0", "width": null,
"height": "4816.0", "height": null,
"resolution": "0.100000", "resolution": null,
"originX": "-111.361710", "originX": null,
"originY": " -255.272873", "originY": null,
"imgList": [ "imgList": null,
"http://file.prod.concoai.com/image/4fff5d4bcc4b4239941ff077a0da8958/168c66b32421d8e8d5bc13a0bbb87a30.jpeg"
],
"robotType": "08", "robotType": "08",
"eventFloor": null, "eventFloor": null,
"floorName": null, "floorName": null,
@ -205,50 +140,124 @@
"eno": null, "eno": null,
"instrument": null, "instrument": null,
"evideo": null "evideo": null
}, }
"rows": null ]
} }
``` ```
- 获取事件详情接口
- url:
- http://erpapi.concoai.com/robot/event/{eventId}
- 传递参数:
- 路径变量:
- eventId
- 请求头参数:
- token:
- refreshToken
- 响应参数: 主要获取imgList中的图片url.
```json
{
"code": "2000",
"message": "成功",
"total": null,
"data": {
"eventId": "a462a29d7481495380cd47035b19edc0",
"tenantInfoId": "4fff5d4bcc4b4239941ff077a0da8958",
"reportEventId": "bb96f28e14d944c79f44321edca4395f",
"number": "ROB23100098",
"name": "X32305000019",
"eclassify": "2",
"operationType": "2",
"etype": "E000007",
"etypeName": "日常巡检",
"enTypeName": null,
"hkTypeName": null,
"reportStatus": "0",
"results": "2",
"insDate": null,
"insDateShow": "2025-05-06 16:44:31",
"updDate": null,
"updDateShow": null,
"fileType": "2",
"area": "区域1",
"floor": "",
"map": null,
"staffId": null,
"targetUserId": null,
"position": "75.104378,90.402679",
"actualStaffName": null,
"targetStaffName": "员工1",
"routeName": "test",
"phoneAddress": "http://file.prod.concoai.com/image/4fff5d4bcc4b4239941ff077a0da8958/4f60fbd391e98327f6ee13df1455f23e.jpg",
"width": "3994.0",
"height": "4816.0",
"resolution": "0.100000",
"originX": "-111.361710",
"originY": " -255.272873",
"imgList": [
"http://file.prod.concoai.com/image/4fff5d4bcc4b4239941ff077a0da8958/168c66b32421d8e8d5bc13a0bbb87a30.jpeg"
],
"robotType": "08",
"eventFloor": null,
"floorName": null,
"coordId": null,
"coord": null,
"coordName": null,
"positonName": "A3",
"processingRemark": null,
"carId": null,
"parkingSpaceType": null,
"parkingSpaceNumber": null,
"carNumber": null,
"eno": null,
"instrument": null,
"evideo": null
},
"rows": null
}
```
# 后台系统功能: # 后台系统功能:
1. WebSocket服务 1. WebSocket服务
- 实现WebSocket服务器 - 实现WebSocket服务器
- 处理WebSocket连接管理 - 处理WebSocket连接管理
- 实现消息的接收和响应 - 实现消息的接收和响应
- 支持心跳检测和重连机制 - 支持心跳检测和重连机制
2. 认证管理 2. 认证管理
- 通过第三方的获取token和refresh接口获取token和refreshToken - 通过第三方的获取token和refresh接口获取token和refreshToken
- 实现token自动刷新机制 - 实现token自动刷新机制
- token失效自动重试机制 - token失效自动重试机制
3. 数据采集 3. 数据采集
- 定时调用列表页接口获取事件数据 - 定时调用列表页接口获取事件数据
- 实现增量采集,避免重复处理 - 实现增量采集,避免重复处理
- 异常重试机制 - 异常重试机制
- 并发控制 - 并发控制
4. 数据处理 4. 数据处理
- 图片下载和预处理 - 图片下载和预处理
- OCR识别温度数据 - OCR识别温度数据
- 数据清洗和验证 - 数据清洗和验证
- 异常数据处理 - 异常数据处理
5. 数据存储 5. 数据存储
- 事件信息表 - 事件信息表
- 图片信息表 - 图片信息表
- 温度数据表 - 温度数据表
- 处理日志表 - 处理日志表
6. API接口 6. API接口
- WebSocket接口 - WebSocket接口
- 温度数据查询接口 - 条件获取列表接口
- 处理状态查询接口 - 查看列表项详情接口,支持分页查询
- 事件管理接口(增删改查) - 修改列表详情接口
- 手动触发处理接口 - 删除列表某一项接口
- 系统状态监控接口
# 数据库设计: # 数据库设计:
1. 事件表(event) 1. 事件表(event)
- event_id varchar(50) primary key 事件Id - event_id varchar(50) primary key 事件Id
@ -301,8 +310,8 @@
- evideo varchar(40) - evideo varchar(40)
- create_time datetime COMMENT '本地后台创建时间' - create_time datetime COMMENT '本地后台创建时间'
- update_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT '本地后台更新时间' - update_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT '本地后台更新时间'
2. 图片表(image) 2. 图片表(image)
- image_id: VARCHAR(40) PRIMARY KEY COMMENT '图片ID' - image_id: VARCHAR(40) PRIMARY KEY COMMENT '图片ID'
- event_id: VARCHAR(50) NOT NULL COMMENT '关联事件ID' - event_id: VARCHAR(50) NOT NULL COMMENT '关联事件ID'
- image_url: VARCHAR(500) NOT NULL COMMENT '图片URL' - image_url: VARCHAR(500) NOT NULL COMMENT '图片URL'
@ -310,8 +319,8 @@
- create_time: DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间' - create_time: DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'
- INDEX idx_event_id (event_id), - INDEX idx_event_id (event_id),
- FOREIGN KEY (event_id) REFERENCES event(event_id) - FOREIGN KEY (event_id) REFERENCES event(event_id)
3. 温度数据表(temperature) 3. 温度数据表(temperature)
- temp_id: VARCHAR(32) PRIMARY KEY COMMENT '温度记录ID' - temp_id: VARCHAR(32) PRIMARY KEY COMMENT '温度记录ID'
- event_id: VARCHAR(50) NOT NULL COMMENT '关联事件ID' - event_id: VARCHAR(50) NOT NULL COMMENT '关联事件ID'
- image_id: VARCHAR(40) NOT NULL COMMENT '关联图片ID' - image_id: VARCHAR(40) NOT NULL COMMENT '关联图片ID'
@ -322,8 +331,8 @@
- INDEX idx_create_time (create_time), - INDEX idx_create_time (create_time),
- FOREIGN KEY (event_id) REFERENCES event(event_id), - FOREIGN KEY (event_id) REFERENCES event(event_id),
- FOREIGN KEY (image_id) REFERENCES image(image_id) - FOREIGN KEY (image_id) REFERENCES image(image_id)
4. 处理日志表(process_log) 4. 处理日志表(process_log)
- log_id: VARCHAR(32) PRIMARY KEY COMMENT '日志ID' - log_id: VARCHAR(32) PRIMARY KEY COMMENT '日志ID'
- event_id: VARCHAR(32) NOT NULL COMMENT '关联事件ID' - event_id: VARCHAR(32) NOT NULL COMMENT '关联事件ID'
- process_status: TINYINT NOT NULL COMMENT '处理状态' - process_status: TINYINT NOT NULL COMMENT '处理状态'
@ -334,22 +343,21 @@
- FOREIGN KEY (event_id) REFERENCES event(event_id) - FOREIGN KEY (event_id) REFERENCES event(event_id)
# 性能要求: # 性能要求:
- 系统响应时间API接口响应时间<500ms - 系统响应时间API接口响应时间<500ms
- 并发处理能力支持10个并发请求 - 并发处理能力支持10个并发请求
- 数据处理能力每分钟处理不少于100张图片 - 数据处理能力每分钟处理不少于100张图片
- 系统可用性99.9% - 系统可用性99.9%
# 安全要求: # 安全要求:
- 实现接口认证机制 - 实现接口认证机制
- 敏感数据加密存储 - 敏感数据加密存储
- 操作日志记录 - 操作日志记录
- 异常监控告警 - 异常监控告警
# 部署要求: # 部署要求:
- 支持Docker容器化部署 - 支持Docker容器化部署
- 支持多环境配置(开发、测试、生产) - 支持多环境配置(开发、测试、生产)
- 提供部署文档和运维手册 - 提供部署文档和运维手册

27
doc/接口.md Normal file
View File

@ -0,0 +1,27 @@
## 获取事件列表
- GET /api/v1/events?start_time=2024-01-01T00:00:00&end_time=2024-12-31T23:59:59&etype=日常巡检&area=区域1&skip=0&limit=100
## 获取事件详情
- GET /api/v1/events/{event_id}
## 更新事件
- PUT /api/v1/events/{event_id}
Content-Type: application/json
- ```json
{
"name": "新名称",
"area": "新区域",
"processingRemark": "新备注"
}
```
## 删除事件
- DELETE /api/v1/events/{event_id}
# 运行程序
- python run.py

View File

@ -0,0 +1,52 @@
Global:
model_name: PP-OCRv4_server_det
Hpi:
backend_configs:
paddle_infer:
trt_dynamic_shapes: &id001
x:
- - 1
- 3
- 32
- 32
- - 1
- 3
- 736
- 736
- - 1
- 3
- 4000
- 4000
tensorrt:
dynamic_shapes: *id001
PreProcess:
transform_ops:
- DecodeImage:
channel_first: false
img_mode: BGR
- DetLabelEncode: null
- DetResizeForTest: null
- NormalizeImage:
mean:
- 0.485
- 0.456
- 0.406
order: hwc
scale: 1./255.
std:
- 0.229
- 0.224
- 0.225
- ToCHWImage: null
- KeepKeys:
keep_keys:
- image
- shape
- polys
- ignore_tags
PostProcess:
name: DBPostProcess
thresh: 0.3
box_thresh: 0.6
max_candidates: 1000
unclip_ratio: 1.5

File diff suppressed because it is too large Load Diff

11
run.py Normal file
View File

@ -0,0 +1,11 @@
import uvicorn
if __name__ == "__main__":
uvicorn.run(
"app.main:app",
host="10.0.0.202",
port=9123,
# reload=True, # 开发模式下启用热重载
workers=1, # 生产环境可以增加worker数量
log_level="info"
)