服务接口初步完成
This commit is contained in:
parent
f9d35b1ba8
commit
57578f94dc
5
app/api/v1/api.py
Normal file
5
app/api/v1/api.py
Normal 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"])
|
||||
79
app/api/v1/endpoints/events.py
Normal file
79
app/api/v1/endpoints/events.py
Normal 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
93
app/crud/event.py
Normal 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
26
app/main.py
Normal 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服务正在运行"}
|
||||
@ -90,6 +90,7 @@ class Temperature(Base):
|
||||
eventId = Column(String(50), ForeignKey('event.eventId'), nullable=False, comment='关联事件ID')
|
||||
imageId = Column(BigInteger, ForeignKey('image.imageId'), nullable=False, comment='关联图片ID')
|
||||
temperature = Column(String(20), nullable=False, comment='温度值')
|
||||
# status = Column(String(2), comment='温度是否正常')
|
||||
confidence = Column(String(20), nullable=False, comment='识别置信度')
|
||||
createTime = Column(DateTime, default=datetime.now, nullable=False, comment='创建时间')
|
||||
|
||||
|
||||
102
app/schemas/event.py
Normal file
102
app/schemas/event.py
Normal 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)
|
||||
@ -45,62 +45,40 @@
|
||||
|
||||
## 3. 详细设计
|
||||
|
||||
### 3.1 数据采集模块
|
||||
### 3.1 数据同步模块
|
||||
|
||||
```python
|
||||
class DataCollector:
|
||||
class EventSyncService:
|
||||
def __init__(self):
|
||||
self.token_manager = TokenManager()
|
||||
self.http_client = AsyncHTTPClient()
|
||||
self.kangda = Kangda()
|
||||
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)
|
||||
# 获取事件详情
|
||||
for event in events:
|
||||
await self.process_event(event, token)
|
||||
```
|
||||
|
||||
### 3.2 OCR处理模块
|
||||
|
||||
```python
|
||||
class OCRProcessor:
|
||||
def __init__(self):
|
||||
self.ocr = PaddleOCR()
|
||||
event_list = self.kangda.get_event_list()
|
||||
if not event_list:
|
||||
print("获取事件列表失败")
|
||||
return
|
||||
|
||||
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()
|
||||
# 获取新事件列表
|
||||
new_events = await self._get_new_events(event_list)
|
||||
if not new_events:
|
||||
print("没有新事件需要同步")
|
||||
return
|
||||
|
||||
async def save_event(self, event_data: Dict):
|
||||
event = Event(**event_data)
|
||||
self.session.add(event)
|
||||
await self.session.commit()
|
||||
print(f"发现 {len(new_events)} 个新事件")
|
||||
|
||||
# 获取事件详情并更新
|
||||
await self._update_event_details(new_events)
|
||||
|
||||
print("事件同步完成
|
||||
```
|
||||
|
||||
### 3.4 API接口模块
|
||||
|
||||
### 3.2 API接口模块
|
||||
|
||||
```python
|
||||
@router.get("/temperatures")
|
||||
@ -113,7 +91,6 @@ async def get_temperatures(
|
||||
db, start_time, end_time
|
||||
)
|
||||
```
|
||||
|
||||
## 4. 数据库设计
|
||||
|
||||
### 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_create_time (create_time);
|
||||
```
|
||||
|
||||
### 4.3 数据库配置
|
||||
|
||||
```ini
|
||||
@ -152,7 +128,6 @@ max_overflow = 10
|
||||
pool_timeout = 30
|
||||
pool_recycle = 1800
|
||||
```
|
||||
|
||||
## 5. 接口设计
|
||||
|
||||
### 5.1 内部接口
|
||||
@ -309,7 +284,6 @@ pool_recycle = 1800
|
||||
| |
|
||||
[Redis集群] [文件存储]
|
||||
```
|
||||
|
||||
### 9.3 MySQL配置建议
|
||||
|
||||
```ini
|
||||
@ -337,7 +311,6 @@ server-id = 1
|
||||
log-bin = mysql-bin
|
||||
binlog_format = ROW
|
||||
```
|
||||
|
||||
## 10. 测试方案
|
||||
|
||||
### 10.1 单元测试
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
# 需求:
|
||||
|
||||
- 设计一个Python后台服务程序,用于自动化采集和处理温度数据
|
||||
- 通过WebSocket接收外部系统的消息触发
|
||||
- 支持事件驱动的数据采集和处理流程
|
||||
|
||||
# 功能:
|
||||
|
||||
- WebSocket服务
|
||||
- 接收外部系统的消息
|
||||
- 处理消息并触发相应的数据采集流程
|
||||
@ -18,6 +20,7 @@
|
||||
- 支持事件的搜索和过滤
|
||||
|
||||
# 系统架构:
|
||||
|
||||
- 采用分层架构设计
|
||||
- 数据采集层:负责与第三方API交互
|
||||
- 业务处理层:负责数据处理和OCR识别
|
||||
@ -25,6 +28,7 @@
|
||||
- API接口层:提供对外服务接口
|
||||
|
||||
# 技术选型:
|
||||
|
||||
- 开发语言:Python 3.8+
|
||||
- Web框架:FastAPI
|
||||
- 数据库:MySQL 8.0+
|
||||
@ -36,160 +40,91 @@
|
||||
- 日志: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
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- 获取事件详情接口
|
||||
- url:
|
||||
- http://erpapi.concoai.com/robot/event/{eventId}
|
||||
- 传递参数:
|
||||
- 路径变量:
|
||||
- eventId
|
||||
- 请求头参数:
|
||||
- token:
|
||||
- refreshToken
|
||||
- 响应参数: 主要获取imgList中的图片url.
|
||||
```json
|
||||
{
|
||||
"code": "2000",
|
||||
"message": "成功",
|
||||
"total": null,
|
||||
"data": {
|
||||
- 获取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": "bb96f28e14d944c79f44321edca4395f",
|
||||
"reportEventId": null,
|
||||
"number": "ROB23100098",
|
||||
"name": "X32305000019",
|
||||
"eclassify": "2",
|
||||
"operationType": "2",
|
||||
"etype": "E000007",
|
||||
"etypeName": "日常巡检",
|
||||
"enTypeName": null,
|
||||
"hkTypeName": null,
|
||||
"enTypeName": "Routine patrol",
|
||||
"hkTypeName": "日常巡檢\r",
|
||||
"reportStatus": "0",
|
||||
"results": "2",
|
||||
"insDate": null,
|
||||
"insDate": "2025-05-06T16:44:31",
|
||||
"insDateShow": "2025-05-06 16:44:31",
|
||||
"updDate": null,
|
||||
"updDateShow": null,
|
||||
"fileType": "2",
|
||||
"area": "区域1",
|
||||
"floor": "",
|
||||
"fileType": null,
|
||||
"area": null,
|
||||
"floor": null,
|
||||
"map": null,
|
||||
"staffId": null,
|
||||
"targetUserId": null,
|
||||
"position": "75.104378,90.402679",
|
||||
"position": null,
|
||||
"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"
|
||||
],
|
||||
"phoneAddress": null,
|
||||
"width": null,
|
||||
"height": null,
|
||||
"resolution": null,
|
||||
"originX": null,
|
||||
"originY": null,
|
||||
"imgList": null,
|
||||
"robotType": "08",
|
||||
"eventFloor": null,
|
||||
"floorName": null,
|
||||
@ -205,54 +140,128 @@
|
||||
"eno": null,
|
||||
"instrument": 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服务
|
||||
|
||||
- 实现WebSocket服务器
|
||||
- 处理WebSocket连接管理
|
||||
- 实现消息的接收和响应
|
||||
- 支持心跳检测和重连机制
|
||||
|
||||
2. 认证管理
|
||||
|
||||
- 通过第三方的获取token和refresh接口获取token和refreshToken
|
||||
- 实现token自动刷新机制
|
||||
- token失效自动重试机制
|
||||
|
||||
3. 数据采集
|
||||
|
||||
- 定时调用列表页接口获取事件数据
|
||||
- 实现增量采集,避免重复处理
|
||||
- 异常重试机制
|
||||
- 并发控制
|
||||
|
||||
4. 数据处理
|
||||
|
||||
- 图片下载和预处理
|
||||
- OCR识别温度数据
|
||||
- 数据清洗和验证
|
||||
- 异常数据处理
|
||||
|
||||
5. 数据存储
|
||||
|
||||
- 事件信息表
|
||||
- 图片信息表
|
||||
- 温度数据表
|
||||
- 处理日志表
|
||||
|
||||
6. API接口
|
||||
|
||||
- WebSocket接口
|
||||
- 温度数据查询接口
|
||||
- 处理状态查询接口
|
||||
- 事件管理接口(增删改查)
|
||||
- 手动触发处理接口
|
||||
- 系统状态监控接口
|
||||
- 条件获取列表接口
|
||||
- 查看列表项详情接口,支持分页查询
|
||||
- 修改列表详情接口
|
||||
- 删除列表某一项接口
|
||||
|
||||
# 数据库设计:
|
||||
|
||||
1. 事件表(event)
|
||||
|
||||
- event_id varchar(50) primary key 事件Id
|
||||
- tenant_info_id varchar(100)
|
||||
- tenant_info_id varchar(100)
|
||||
- report_event_id varchar(100)
|
||||
- number varchar(20)
|
||||
- name varchar(20)
|
||||
@ -301,8 +310,8 @@
|
||||
- evideo varchar(40)
|
||||
- create_time datetime COMMENT '本地后台创建时间'
|
||||
- update_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT '本地后台更新时间'
|
||||
|
||||
2. 图片表(image)
|
||||
|
||||
- image_id: VARCHAR(40) PRIMARY KEY COMMENT '图片ID'
|
||||
- event_id: VARCHAR(50) NOT NULL COMMENT '关联事件ID'
|
||||
- image_url: VARCHAR(500) NOT NULL COMMENT '图片URL'
|
||||
@ -310,8 +319,8 @@
|
||||
- create_time: DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'
|
||||
- INDEX idx_event_id (event_id),
|
||||
- FOREIGN KEY (event_id) REFERENCES event(event_id)
|
||||
|
||||
3. 温度数据表(temperature)
|
||||
|
||||
- temp_id: VARCHAR(32) PRIMARY KEY COMMENT '温度记录ID'
|
||||
- event_id: VARCHAR(50) NOT NULL COMMENT '关联事件ID'
|
||||
- image_id: VARCHAR(40) NOT NULL COMMENT '关联图片ID'
|
||||
@ -322,8 +331,8 @@
|
||||
- INDEX idx_create_time (create_time),
|
||||
- FOREIGN KEY (event_id) REFERENCES event(event_id),
|
||||
- FOREIGN KEY (image_id) REFERENCES image(image_id)
|
||||
|
||||
4. 处理日志表(process_log)
|
||||
|
||||
- log_id: VARCHAR(32) PRIMARY KEY COMMENT '日志ID'
|
||||
- event_id: VARCHAR(32) NOT NULL COMMENT '关联事件ID'
|
||||
- process_status: TINYINT NOT NULL COMMENT '处理状态'
|
||||
@ -334,22 +343,21 @@
|
||||
- FOREIGN KEY (event_id) REFERENCES event(event_id)
|
||||
|
||||
# 性能要求:
|
||||
|
||||
- 系统响应时间:API接口响应时间<500ms
|
||||
- 并发处理能力:支持10个并发请求
|
||||
- 数据处理能力:每分钟处理不少于100张图片
|
||||
- 系统可用性:99.9%
|
||||
|
||||
# 安全要求:
|
||||
|
||||
- 实现接口认证机制
|
||||
- 敏感数据加密存储
|
||||
- 操作日志记录
|
||||
- 异常监控告警
|
||||
|
||||
# 部署要求:
|
||||
|
||||
- 支持Docker容器化部署
|
||||
- 支持多环境配置(开发、测试、生产)
|
||||
- 提供部署文档和运维手册
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
27
doc/接口.md
Normal file
27
doc/接口.md
Normal 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
|
||||
52
ocrModel/PP-OCRv4_server_det_inference/inference.yml
Normal file
52
ocrModel/PP-OCRv4_server_det_inference/inference.yml
Normal 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
|
||||
6666
ocrModel/rec_ppocr_v4_hgnet_kangda_inference/inference.yml
Normal file
6666
ocrModel/rec_ppocr_v4_hgnet_kangda_inference/inference.yml
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user