This commit is contained in:
2024-10-24 09:23:39 +08:00
parent 641c34b1b3
commit 16c005556f
36 changed files with 7780 additions and 0 deletions

View File

@@ -0,0 +1,77 @@
# -*- coding:utf-8 -*-
"""
@Author : xuxingchen
@Contact : xuxingchen@sinochem.com
@Desc : 通行设备界面控制逻辑
"""
from fastapi import APIRouter, Request, Header, Query
from models.devices import DevicesTable, AccessDevicesScope, BuildingDevicesScope
from routers.login import authenticate_token
from utils import logger
from utils.logger import TOKEN_ERROR
from utils.database import get_table_handler
from utils.misc import InvalidException
router = APIRouter()
@router.get("/getAccessDevicesInfo", summary="获取通行设备信息")
async def get_access_devices_info(request: Request,
is_associated: bool = Query(...),
device_name: str = Query(None),
product_name: str = Query(None),
device_mac: str = Query(None),
device_id: int = Query(None),
token: str = Header(...)):
"""获取通行设备信息"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
if device_name:
resp = DevicesTable.get_access_devices_info(th, is_associated, device_name=device_name)
elif product_name:
resp = DevicesTable.get_access_devices_info(th, is_associated, product_name=product_name)
elif device_mac:
resp = DevicesTable.get_access_devices_info(th, is_associated, device_mac=device_mac)
elif device_id:
resp = DevicesTable.get_access_devices_info(th, is_associated, device_id=device_id)
else:
resp = DevicesTable.get_access_devices_info(th, is_associated)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.post("/addAccessDevicesAssociateInfo", summary="批量增加大门门禁/大门闸机关联信息")
async def add_access_devices_associate_info(request: Request, item: AccessDevicesScope, token: str = Header(...)):
"""批量增加大门门禁/大门闸机关联信息"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
_callback = DevicesTable.add_access_devices(th, item.device_type, item.info)
resp = {"status": _callback}
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.post("/addBuildingDevicesAssociateInfo", summary="批量增加楼栋门禁关联信息")
async def add_building_devices_associate_info(request: Request, item: BuildingDevicesScope, token: str = Header(...)):
"""批量增加楼栋门禁关联信息"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
_callback = DevicesTable.add_building_devices(th, "楼栋门禁", item.info)
resp = {"status": _callback}
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.delete("/deleteAssociatedAccessDeviceInfo", summary="移除完成关联的设备及其已授权的人员信息")
async def delete_access_device_info(request: Request, device_id: int = Query(alias="id"), token: str = Header(...)):
"""移除完成关联的设备及其已授权的人员信息"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
resp = DevicesTable.delete_access_device_info(th, device_id)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp

View File

@@ -0,0 +1,193 @@
# -*- coding:utf-8 -*-
"""
@Author : xuxingchen
@Contact : xuxingchen@sinochem.com
@Desc : 通行授权界面控制逻辑
"""
import os
import time
from fastapi import APIRouter, Request, Header, File, UploadFile, Query
from config import PREFIX_PATH, SUB_PATH
from config import APP_HOST as HOST_IP
from config import IMAGE_SERVER_PORT as PORT
from models.devices import SearchDevicesInfo, DevicesTable
from models.householders import HouseholdersTable, GetAuthHouseholdersInfo, GetAuthUsersInfo, \
UpdateHouseholderFace, AddAuthUserInfo, UpdateAuthUserInfo
from routers.login import authenticate_token
from utils import logger
from utils.logger import TOKEN_ERROR
from utils.database import get_table_handler
from utils.misc import InvalidException
router = APIRouter()
@router.get("/getHouseholdersInfo", summary="授权住户查询")
async def get_householders_info(request: Request,
page: int = Query(..., gt=0),
limit: int = Query(..., gt=0),
token: str = Header(...)):
"""获取已授权的住户信息"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
resp = HouseholdersTable.get_auth_householder_info(th, None, None, None, None,
page, limit)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.post("/getHouseholdersInfo", summary="授权住户条件查询")
async def get_householders_info_by_condition(request: Request,
item: GetAuthHouseholdersInfo,
token: str = Header(...)):
"""根据条件获取已授权的住户信息查询"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
logger.Logger.debug(f"{request.url.path} <- {item.__dict__}")
resp = HouseholdersTable.get_auth_householder_info(th, item.search_type, item.search_key,
item.space_type, item.space_id,
item.page, item.limit)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.get("/getHouseholderDetailInfo", summary="住户授权详细信息查询")
async def get_householder_detail_info(request: Request, householder_id: int = Query(...), token: str = Header(...)):
"""获取指定住户授权详细信息查询"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
resp = HouseholdersTable.get_auth_householder_detail_info(th, householder_id)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.get("/getUsersInfo", summary="通用授权人员信息查询")
async def get_users_info(request: Request,
page: int = Query(..., gt=0),
limit: int = Query(..., gt=0),
token: str = Header(...)):
"""获取已授权的通用人员信息"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
resp = HouseholdersTable.get_auth_users_info(th, None, None, None, page, limit)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.post("/getUsersInfo", summary="通用授权人员信息条件查询")
async def get_users_info_by_condition(request: Request,
item: GetAuthUsersInfo,
token: str = Header(...)):
"""根据条件获取已授权的通用人员信息"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
resp = HouseholdersTable.get_auth_users_info(th, item.search_type, item.search_key,
item.user_type, item.page, item.limit)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.get("/getUserDetailInfo", summary="通用授权人员详细信息查询")
async def get_user_detail_info(request: Request,
user_id: int = Query(alias="id"),
token: str = Header(...)):
"""获取指定通用人员的详细信息"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
resp = HouseholdersTable.get_auth_user_info(th, user_id)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.delete("/deleteUserInfo", summary="删除通用授权人员信息及关联设备授权")
async def get_users_info(request: Request, user_id: int = Query(...), token: str = Header(...)):
"""通用授权人员删除 & 移除关联设备授权"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
resp = HouseholdersTable.delete_auth_user_info(th, user_id)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.post("/addUserInfo", summary="新增通用授权人员信息及关联设备授权")
async def add_user_info(request: Request, item: AddAuthUserInfo, token: str = Header(...)):
"""新增通用授权人员 & 人脸图像绑定 & 添加关联设备授权"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
resp = HouseholdersTable.add_auth_user_info(th, item)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.post("/updateUserInfo", summary="更新通用授权人员信息及关联设备授权")
async def update_user_info(request: Request, item: UpdateAuthUserInfo, token: str = Header(...)):
"""更新通用授权人员信息 & 人脸图像绑定 & 判断是否需要刷新关联设备授权"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
resp = HouseholdersTable.update_auth_user_info(th, item)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.post("/uploadHouseholderFace", summary="人脸图片上传")
async def upload_householder_face(request: Request,
face_file: UploadFile = File(...),
token: str = Header(...)):
"""接收人脸图片上传并保存"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
# 检查文件类型是否为图像
if not face_file.content_type.startswith("image"):
resp = {"status": False, "message": "仅支持上传图片"}
else:
file_content = await face_file.read()
max_size_mb = 10 # 设置最大允许的文件大小为 10MB
if face_file.file.seek(0, os.SEEK_END) > max_size_mb * 1024 * 1024:
resp = {"status": False, "message": f"文件大小不得超过 {max_size_mb}MB"}
else:
save_path = f"./data/FaceImages"
filename = f"_{int(time.time() * 1000)}"
if not os.path.exists(save_path):
os.mkdir(save_path)
file_path = f"{save_path}/{filename}.jpg"
with open(file_path, "wb") as f:
f.write(file_content)
# 返回成功消息及人脸照片的 URL
face_url = f"http://{HOST_IP}:{PORT}{PREFIX_PATH}/{SUB_PATH}/{filename}.jpg"
resp = {"face_url": face_url}
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.post("/updateHouseholderFace", summary="更新住户人脸信息")
async def get_users_info(request: Request, item: UpdateHouseholderFace, token: str = Header(...)):
"""人脸图像与住户ID绑定 & 刷新关联设备授权"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
resp = HouseholdersTable.update_householder_face(th, item.id, item.face_url)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.post("/getAssociatedAccessDevicesInfo", summary="通用授权-门禁设备条件查询")
async def get_associated_access_devices_info(request: Request, item: SearchDevicesInfo, token: str = Header(...)):
"""已具备关联空间信息的门禁设备条件查询"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
resp = DevicesTable.get_associated_access_devices_info(th, item.search_type, item.search_key)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp

View File

@@ -0,0 +1,30 @@
# -*- coding:utf-8 -*-
"""
@Author : xuxingchen
@Created :
@Updated :
@Contact : xuxingchen@sinochem.com
@Desc : 设备管理界面控制逻辑
"""
from fastapi import APIRouter, Request, Header
from models.devices import DevicesTable
from routers.login import authenticate_token
from utils import logger
from utils.logger import TOKEN_ERROR
from utils.database import get_table_handler
from utils.misc import InvalidException
router = APIRouter()
@router.get("/getAllDevicesInfo", summary="获取全量的设备信息")
async def get_all_devices_info(request: Request, token: str = Header(...)):
"""获取全量的设备信息列表"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
devices_info = DevicesTable.get_devices_info(th)
resp = {"devices": devices_info}
logger.Logger.debug(f"{request.url.path} {resp}")
return resp

View File

@@ -0,0 +1,163 @@
# -*- coding:utf-8 -*-
"""
@Author : xuxingchen
@Contact : xuxingchen@sinochem.com
@Desc : 模拟边缘对接所需的平台api接口
"""
from fastapi import APIRouter, Request, Query
from models.householders import HouseholdersTable
from models.houses import HousesTable
from models.spaces import SpacesTable
from models.subsystem import SubsystemTable, HouseDetailInfo
from routers.login import authenticate_token
from utils import logger
from utils.database import get_table_handler
from utils.misc import InvalidException, snake2camel_list_dict
router = APIRouter()
@router.post("/v2/accesskey_auth", summary="回传一个无效的随机字符串")
async def access_key_auth(_item: dict):
return {"access_token": "Q0E5QjU4QUNDMDA4MTY4RDNFNkRGRjNGNzQ4NDAzMTQ5OTVBMDQwODMyNjBFMjBCQkIwQjA2QzlCNUQ3MUY3NA=="}
@router.get("/v3/realty-master-data/owners/{user_id}", summary="根据用户 id 获取房产信息")
async def get_property_info_by_user_id(request: Request, user_id: int):
logger.Logger.debug(f"{request.url.path} <- {user_id}")
th = get_table_handler()
house_ids = HouseholdersTable.get_room_ids(th, user_id)
resp = {
"status": 200,
"msg": "ok",
"data": {
"house_ids": house_ids
},
"code": 200,
"det": 0
}
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.post("/v3/realty-master-data/houses", summary="根据房产 id 获取房产详细信息")
async def get_house_detail_info(request: Request, item: dict):
try:
logger.Logger.debug(f"{request.url.path} <- {item}")
house_ids = item["query"]["id"]["$in"]
house_detail_list = []
th = get_table_handler()
for house_id in house_ids:
house_detail_info = HousesTable.get_house_detail_info(th, house_id)
if house_detail_info is not None:
house_detail_list.append(house_detail_info)
resp = {
"status": 200,
"msg": "ok",
"data": {
"total_count": len(house_detail_list),
"total_page": 0,
"list": house_detail_list
},
"code": 200,
"det": 0
}
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
except (KeyError, TypeError):
raise InvalidException("请求参数结构异常")
@router.post("/v2/realty-master-data/SubsystemSpaceMap/list", summary="查找空间结构中的房间信息")
async def get_subsystem_house_detail_info(request: Request, item: dict):
try:
logger.Logger.debug(f"{request.url.path} <- {item}")
project_id = item["query"]["project_id"]["$eq"]
th = get_table_handler()
data_list = SubsystemTable.get_house_detail_info_by_project_id(th, project_id)
resp = {
"status": 200,
"msg": "ok",
"data": {
"total_count": len(data_list),
"total_page": 0,
"list": data_list
},
"code": 200,
"det": 0
}
logger.Logger.debug(f"{request.url.path} -> {resp}")
return resp
except (KeyError, TypeError):
raise InvalidException("请求参数结构异常")
@router.post("/v2/realty-master-data/SubsystemSpaceMap/added", summary="添加房产信息")
async def add_subsystem_house_info(request: Request, item: HouseDetailInfo):
try:
logger.Logger.debug(f"{request.url.path} <- {item}")
th = get_table_handler()
SubsystemTable.add_sub_system_house_info(th, item)
resp = {
"status": 200,
"msg": "ok",
"data": item.message_id,
"code": 200,
"det": 0
}
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
except (KeyError, TypeError):
raise InvalidException("请求参数结构异常")
@router.post("/v2/realty-master-data/SubsystemSpaceMap/update", summary="更新房产信息")
async def update_subsystem_house_info(request: Request, item: HouseDetailInfo):
try:
logger.Logger.debug(f"{request.url.path} <- {item}")
th = get_table_handler()
item.action = "update"
SubsystemTable.add_sub_system_house_info(th, item)
resp = {
"status": 200,
"msg": "ok",
"data": item.message_id,
"code": 200,
"det": 0
}
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
except (KeyError, TypeError):
raise InvalidException("请求参数结构异常")
@router.post("/v2/smart-access/house/house_ty_maps", summary="查询房产的真实信息")
async def get_house_ty_info(request: Request, item: dict):
try:
logger.Logger.debug(f"{request.url.path} <- {item}")
project_id = item["query"]["project_id"]["$eq"]
room_id = item["query"]["_id"]["$eq"]
th = get_table_handler()
house_ty_info = HousesTable.get_house_ty_info(th, project_id, room_id)
if house_ty_info is None:
house_ty_infos = []
else:
house_ty_infos = [house_ty_info]
resp = {
"status": 200,
"msg": "ok",
"data": {
"count": 1,
"list": house_ty_infos,
"query_res": {
"count": 1,
"list": snake2camel_list_dict(house_ty_infos)
}
},
"det": 0
}
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
except (KeyError, TypeError):
raise InvalidException("请求参数结构异常")

View File

@@ -0,0 +1,84 @@
# -*- coding:utf-8 -*-
"""
@Author : xuxingchen
@Contact : xuxingchen@sinochem.com
@Desc : 住户管理界面控制逻辑
"""
from fastapi import APIRouter, Request, Header, Query
from models.householders import HouseholdersTable, GetHouseholdersInfo, AddHouseholderInfo, UpdateHouseholderInfo
from models.houses import HousesTable
from routers.login import authenticate_token
from utils import logger
from utils.logger import TOKEN_ERROR
from utils.database import get_table_handler
from utils.misc import InvalidException
router = APIRouter()
@router.post("/addHouseholderInfo", summary="新增住户信息")
async def add_householder_info(request: Request, item: AddHouseholderInfo, token: str = Header(...)):
"""新增住户信息"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
# 进行住户信息插入
householder_id = HouseholdersTable.insert(th, item.name, item.sex, item.phone, "住户")
if householder_id:
for room_id, _type in item.property_info:
# 房产关联表信息插入
HouseholdersTable.insert_type(th, room_id, householder_id, _type)
# 更新房产中的住户数量
old_count = HousesTable.get_householder_count(th, room_id)
HousesTable.update_householder_count(th, room_id, old_count + 1)
resp = {"status": True}
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.post("/getHouseholderInfo", summary="获取住户信息")
async def get_householder_info(request: Request, item: GetHouseholdersInfo, token: str = Header(...)):
"""获取住户信息"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
resp = HouseholdersTable.get_householder_info(th, item.search_type, item.search_key, item.role_type,
item.page, item.limit)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.get("/getHouseholderInfo", summary="查询指定住户信息")
async def get_householder_detail_info(request: Request,
householder_id: int = Query(alias="id"),
token: str = Header(...)):
"""查询指定住户信息"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
resp = HouseholdersTable.get_householder_info_by_id(th, householder_id)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.post("/updateHouseholderInfo", summary="更新指定住户信息")
async def update_householder_info(request: Request, item: UpdateHouseholderInfo, token: str = Header(...)):
"""更新指定住户信息"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
resp = HouseholdersTable.update_householder_info(th, item.householder_id, item.property_info)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.delete("/deleteHouseholderInfo", summary="删除指定住户信息及移除其关联设备授权")
async def delete_householder_info(request: Request, householder_id: int = Query(alias="id"), token: str = Header(...)):
"""删除指定住户的信息及其关联授权"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
resp = HouseholdersTable.delete_householder_info(th, householder_id)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp

View File

@@ -0,0 +1,86 @@
# -*- coding:utf-8 -*-
"""
@Author : xuxingchen
@Contact : xuxingchen@sinochem.com
@Desc : 房产信息控制逻辑
"""
from typing import Optional
from fastapi import APIRouter, Header, Request, Query
from pydantic import BaseModel, field_validator, Field
from routers.login import authenticate_token
from utils import logger
from utils.logger import TOKEN_ERROR
from utils.database import get_table_handler
from models.houses import HousesTable
from utils.misc import InvalidException
router = APIRouter()
class ListsReq(BaseModel):
project: str = Field(description="必填项,用于限定项目编号")
area: Optional[str] = Field(None, description="可选项,用于限定区域")
building: Optional[str] = Field(None, description="可选项,用于限定楼栋")
unit: Optional[str] = Field(None, description="可选项,用于限定单元")
floor: Optional[str] = Field(None, description="可选项,用于限定楼层")
target: str = Field(description="必填项用于控制输出结果取值范围area, building, unit, floor, room")
def dict(self, *args, **kwargs):
_dict = self.__dict__.copy()
for key, value in self.__dict__.items():
if value is None:
_dict.pop(key)
_dict.pop("target")
return _dict
@field_validator("target")
def validate_target(cls, value):
if value not in ["area", "building", "unit", "floor", "room"]:
raise InvalidException("请提供正确的请求target [area, building, unit, floor, room]")
else:
return value
@router.get("/projects", summary="获取所有项目列表")
async def get_projects_list(request: Request, token: str = Header(...)):
"""获取项目列表"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
projects_list = HousesTable.get_items_by_dynamic_item(th, "project", {})
resp = {"projects": projects_list}
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.post("/project/lists", summary="动态获取房产信息")
async def get_items(request: Request,
item: ListsReq,
token: str = Header(...)):
"""根据请求获取列表"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
item_list = HousesTable.get_items_by_dynamic_item(th, item.target, item.dict())
resp = {f"{item.target.split('_')[0]}s": item_list}
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.get("/getHouseholderCount", summary="获取房产住户数量")
async def get_householder_count(request: Request,
room_id: str = Query(...),
token: str = Header(...)):
"""根据请求获取对应房屋中当前住户数量"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
count = HousesTable.get_householder_count(th, room_id)
if count is not None:
resp = {"count": count}
else:
raise InvalidException("room_id 不存在")
logger.Logger.debug(f"{request.url.path} {resp}")
return resp

140
SCLP/routers/login.py Normal file
View File

@@ -0,0 +1,140 @@
# -*- coding:utf-8 -*-
"""
@Author : xuxingchen
@Contact : xuxingchen@sinochem.com
@Desc : 登陆界面控制逻辑
"""
import hashlib
import os.path
import random
import io
import time
from typing import Optional
from starlette.responses import StreamingResponse
from pydantic import BaseModel
from fastapi import APIRouter, Query, Request
from PIL import Image, ImageDraw, ImageFont
from utils.database import get_table_handler
from utils import logger
from utils.misc import generate_captcha_text
from models.sessions import SessionsTable
router = APIRouter()
class LoginItem(BaseModel):
username: str
password: str
session_id: str
captcha: str
class LoginResp(BaseModel):
status: bool
message: str
token: Optional[str] = None
def generate_captcha_image(text, size=(120, 40), font_path='./data/mvboli.ttf', font_size=24):
"""生成验证码图片"""
image = Image.new('RGB', size, (73, 109, 137)) # 设置背景色
d = ImageDraw.Draw(image)
assert os.path.exists(font_path), "字体文件不存在"
font = ImageFont.truetype(font_path, font_size)
# d.text((5, 5), text, font=font, fill=(255, 255, 0)) # 设置字体颜色和位置
font_box = font.getbbox(text)
text_width, text_height = font_box[2] - font_box[0], font_box[3] - font_box[1]
text_x = (size[0] - text_width) // 2
text_y = (size[1] - text_height) // 2
d.text((text_x, text_y - 8), text, font=font, fill=(255, 255, 0))
# 添加噪点
for _ in range(100): # 可以根据需要调整噪点数量
x = random.randint(0, size[0] - 1)
y = random.randint(0, size[1] - 1)
image.putpixel((x, y), (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)))
# 添加线条
for _ in range(3): # 可以根据需要调整线条数量
x1, y1 = random.randint(0, size[0] - 1), random.randint(0, size[1] - 1)
x2, y2 = random.randint(0, size[0] - 1), random.randint(0, size[1] - 1)
d.line([(x1, y1), (x2, y2)], fill=(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)),
width=2)
# 将图片保存为字节流
bytes_io = io.BytesIO()
image.save(bytes_io, format='PNG')
# image.save("./data/tmp.png", format='PNG')
bytes_io.seek(0)
return bytes_io
def hash_password(password):
"""对密码进行 SHA-256 哈希加密"""
password_bytes = password.encode('utf-8')
sha256_hash = hashlib.sha256()
sha256_hash.update(password_bytes)
return sha256_hash.hexdigest()
def verify_password(password, hashed_password):
"""比对密码和哈希值"""
password_hash = hash_password(password)
return password_hash.lower() == hashed_password.lower()
def authenticate_token(token: str):
th = get_table_handler()
timeout_timestamp = str(int(time.time() * 1000 - 4 * 60 * 60 * 1000))
return SessionsTable.check_token(th, timeout_timestamp, token)
@router.get("/generateCaptcha", summary="生成验证码")
async def generate_captcha_endpoint(request: Request,
session_id: str = Query(None, description="Session ID")):
"""生成验证码图片并返回"""
if session_id:
captcha_text = generate_captcha_text()
logger.Logger.debug(f"{request.url.path} 验证码生成:{captcha_text}")
# session_id 入库
th = get_table_handler()
SessionsTable.insert(th, session_id, captcha_text)
captcha_image = generate_captcha_image(captcha_text)
# 设置HTTP响应的头部指定内容类型为PNG图片
headers = {"Content-Type": "image/png"}
return StreamingResponse(captcha_image, headers=headers)
else:
logger.Logger.error(f"{request.url.path} 请求参数缺少 session_id - {request.client.host}")
return {"status": False, "message": "请求参数缺少 session_id"}
@router.post("/login", response_model=LoginResp, response_model_exclude_none=True, summary="登录请求")
async def login(item: LoginItem):
"""简单的账户登录逻辑"""
status = False
token = None
th = get_table_handler()
# 验证码校验
right_captcha = SessionsTable.get_captcha(th, item.session_id)
if right_captcha is not None:
if item.captcha.lower() == right_captcha.lower():
# 账户密码校验
if item.username == "admin":
if verify_password(item.password, "B12AD23C7E230E2CA365508DD16635DD3D7214BCD9BEA27457A356FD5C15F8BF"):
status = True
message = "校验通过"
token = generate_captcha_text(16)
SessionsTable.update(th, item.session_id, item.username, token)
else:
message = "密码错误"
else:
message = "账户不存在"
else:
message = "验证码错误"
else:
message = "无效 session_id"
resp = LoginResp(status=status, message=message, token=token)
logger.Logger.debug("/login " + str(resp.__dict__))
return resp

View File

@@ -0,0 +1,85 @@
# -*- coding:utf-8 -*-
"""
@Author : xuxingchen
@Contact : xuxingchen@sinochem.com
@Desc : 车场管理界面控制逻辑
"""
from fastapi import APIRouter, Request, Header, Query
from models.parkinglots import ParkinglotsTable, AddParkingLot
from routers.login import authenticate_token
from utils import logger
from utils.logger import TOKEN_ERROR
from utils.database import get_table_handler
from utils.misc import InvalidException
router = APIRouter()
@router.get("/getParkingLotsInfo", summary="车场信息查询")
async def get_parkinglots_info(request: Request, token: str = Header(...)):
"""获取车场信息"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
resp = ParkinglotsTable.get_parkinglots_info(th)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.post("/addParkingLot", summary="添加车场")
async def add_parkinglot(request: Request, item: AddParkingLot, token: str = Header(...)):
"""添加显示信息的车场 & 更新车场编号对应的名字和供应商"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
factory_name = item.factory_name if item.factory_name else ''
resp = ParkinglotsTable.update_parkinglot_show(th, item.parkinglot_number, item.parkinglot_name, factory_name, True)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.delete("/deleteParkingLot", summary="移除车场")
async def delete_parkinglot(request: Request,
parkinglot_number: str = Query(alias="number", description="车场编号"),
token: str = Header(...)):
"""移除显示信息的车场"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
resp = ParkinglotsTable.update_parkinglot_show(th, parkinglot_number, None, None, False)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.get("/getParkingLotsAreaInfo", summary="区域信息查询")
async def get_parkinglots_area_info(request: Request, token: str = Header(...)):
"""区域信息查询"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
resp = ParkinglotsTable.get_showed_parkinglot_area_info(th)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.get("/getParkingLotsChannelInfo", summary="通道信息查询")
async def get_parkinglots_channel_info(request: Request, token: str = Header(...)):
"""通道信息查询"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
resp = ParkinglotsTable.get_showed_parkinglot_channel_info(th)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.get("/getParkingLotsGateInfo", summary="道闸信息查询")
async def get_parkinglots_channel_info(request: Request, token: str = Header(...)):
"""道闸信息查询"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
resp = ParkinglotsTable.get_showed_parkinglot_gate_info(th)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp

View File

@@ -0,0 +1,108 @@
# -*- coding:utf-8 -*-
"""
@Author : xuxingchen
@Contact : xuxingchen@sinochem.com
@Desc : 车辆授权界面控制逻辑
"""
from fastapi import APIRouter, Query, Request, Header
from models.parkinglots import ParkinglotsTable, GetCarsInfo, AddCarsCard, UpdateCarsCard
from routers.login import authenticate_token
from utils import logger
from utils.logger import TOKEN_ERROR
from utils.database import get_table_handler
from utils.misc import InvalidException
router = APIRouter()
@router.get("/getCarsInfo", summary="授权车辆信息查询")
async def get_cars_info(request: Request,
page: int = Query(None),
limit: int = Query(None),
token: str = Header(...)):
"""授权车辆信息全量查询"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
resp = ParkinglotsTable.get_cars_info(th, None, None, page, limit)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.post("/getCarsInfo", summary="授权车辆信息条件查询")
async def get_cars_info(request: Request,
item: GetCarsInfo,
token: str = Header(...)):
"""授权车辆信息全量查询"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
resp = ParkinglotsTable.get_cars_info(th, item.search_type, item.search_key, item.page, item.limit)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.get("/getCarDetailInfo", summary="详细授权信息查询")
async def get_car_detail_info(request: Request,
auth_id: int = Query(...),
token: str = Header(...)):
"""授权车辆信息全量查询"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
resp = ParkinglotsTable.get_auth_info(th, auth_id)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.get("/getCarIdAuthStatus", summary="获取车牌号授权状态")
async def get_car_id_auth_status(request: Request,
car_id: str = Query(...),
token: str = Header(...)):
"""获取车牌号授权状态"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
resp = ParkinglotsTable.get_car_id_auth_status(th, car_id)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.delete("/deleteCarAuthId", summary="删除车辆授权")
async def delete_car_auth_id(request: Request,
auth_id: int = Query(...),
token: str = Header(...)):
"""删除车辆月卡授权"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
resp = ParkinglotsTable.delete_car_auth_id(th, auth_id)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.post("/addCarAuthInfo", summary="新增车辆授权")
async def add_car_card_auth(request: Request,
item: AddCarsCard,
token: str = Header(...)):
"""新增车辆月卡授权"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
resp = ParkinglotsTable.add_car_card_auth(th, item)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.post("/updateCarInfo", summary="更新车辆授权")
async def add_car_card_auth(request: Request,
item: UpdateCarsCard,
token: str = Header(...)):
"""更新车辆月卡授权"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
resp = ParkinglotsTable.update_car_card_auth(th, item)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp

View File

@@ -0,0 +1,101 @@
# -*- coding:utf-8 -*-
"""
@Author : xuxingchen
@Contact : xuxingchen@sinochem.com
@Desc : 通行空间映射界面控制逻辑
"""
import os.path
from io import BytesIO
from fastapi import APIRouter, Request, Header, BackgroundTasks, UploadFile, File
from fastapi.responses import FileResponse
from models.brands import BrandsTable, UpdateBrand
from models.spaces import SpacesTable
from routers.login import authenticate_token
from utils import logger
from utils.logger import TOKEN_ERROR
from utils.database import get_table_handler
from utils.misc import InvalidException, valid_xls
router = APIRouter()
@router.post("/updateSubSystemPlatformName", summary="更新当前使用的可视对讲厂家")
async def update_sub_system_platform_name(request: Request, item: UpdateBrand, token: str = Header(...)):
"""更新当前使用的可视对讲厂家"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
resp = BrandsTable.update_checked_factory(th, item.name)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.get("/getSubSystemPlatformName", summary="查询当前使用的可视对讲厂家")
async def get_sub_system_platform_name(request: Request, token: str = Header(...)):
"""获取当前启用的可视对讲厂家"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
resp = BrandsTable.get_checked_factory(th)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp
@router.get("/exportAiotPlatformData", summary="导出AIOT平台空间数据")
async def export_aiot_platform_data(request: Request, background_tasks: BackgroundTasks, token: str = Header(...)):
"""导出AIOT平台空间数据"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
file_path = SpacesTable.get_aiot_platform_data(th)
background_tasks.add_task(os.remove, file_path)
logger.Logger.debug(f"{request.url.path} 完成AIOT平台空间数据导出")
return FileResponse(path=file_path,
filename=os.path.basename(file_path),
media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
@router.get("/exportSubSystemData", summary="导出子系统空间结构数据表")
async def export_aiot_platform_data(request: Request, background_tasks: BackgroundTasks, token: str = Header(...)):
"""导出子系统空间结构数据表"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
th = get_table_handler()
file_path = SpacesTable.get_sub_system_data(th)
background_tasks.add_task(os.remove, file_path)
logger.Logger.debug(f"{request.url.path} 完成子系统空间数据导出")
return FileResponse(path=file_path,
filename=os.path.basename(file_path),
media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
@router.post("/importIdMapData", summary="导入空间映射表")
async def import_id_map_data(request: Request,
xls_file: UploadFile = File(...),
token: str = Header(...)):
"""接收空间映射表xls表格上传、校验并更新对应映射信息"""
if not authenticate_token(token):
raise InvalidException(TOKEN_ERROR)
# 检查文件类型是否为xls
if xls_file.content_type not in ["application/vnd.ms-excel",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"]:
resp = {"status": False, "message": "仅支持上传xls文件"}
else:
file_content = await xls_file.read()
max_size_mb = 10 # 设置最大允许的文件大小为 10MB
if xls_file.file.seek(0, os.SEEK_END) > max_size_mb * 1024 * 1024:
resp = {"status": False, "message": f"文件大小不得超过 {max_size_mb}MB"}
else:
# 解析xls校验数据格式是否符合标准
required_columns = ["房屋id", "子系统id", "子系统空间名称"]
_callback, message, data = valid_xls(BytesIO(file_content), required_columns)
if _callback:
th = get_table_handler()
SpacesTable.update(th, data)
resp = {"status": True}
else:
raise InvalidException(message)
logger.Logger.debug(f"{request.url.path} {resp}")
return resp