diff --git a/auditor/main.py b/auditor/main.py index d7d7636..350c8eb 100644 --- a/auditor/main.py +++ b/auditor/main.py @@ -98,7 +98,7 @@ ) # 包含路由 -app.include_router(auditor_router, prefix="/api/v1/auditor") +app.include_router(auditor_router, prefix="/api/v1") # 根路径路由 @app.get("/", tags=["系统接口"], summary="服务根路径") @@ -144,6 +144,32 @@ async def health_check(): "service": "auditor" } + +# 在 auditor/main.py 中添加全局异常处理 +from fastapi import Request +from fastapi.responses import JSONResponse + + + +# 在 auditor/main.py 的全局异常处理器中添加详细日志 +@app.exception_handler(Exception) +async def global_exception_handler(request: Request, exc: Exception): + """全局异常处理器""" + import traceback + logger.error(f"全局异常: {str(exc)}") + logger.error(f"请求URL: {request.url}") + logger.error(f"请求方法: {request.method}") + logger.error(f"异常堆栈: {traceback.format_exc()}") + + return JSONResponse( + status_code=500, + content={ + "code": 500, + "msg": f"服务器内部错误: {str(exc)}", + "success": False, + "data": None + } + ) # 支持直接运行 if __name__ == "__main__": import uvicorn diff --git a/auditor/schemas/data_schemas.py b/auditor/schemas/data_schemas.py index e483137..7a6d53b 100644 --- a/auditor/schemas/data_schemas.py +++ b/auditor/schemas/data_schemas.py @@ -1,14 +1,16 @@ -from pydantic import BaseModel -from typing import Optional, List, Union +from pydantic import BaseModel, ConfigDict +from typing import Optional, List, Union, Any +from datetime import datetime +#ok -# 原有接口的响应模型 +# ==================== 用户相关模型 ==================== class OriginalUserResponse(BaseModel): id: int name: str email: str created_at: Optional[str] = None -# 二次封装后的响应模型 + class EnhancedUserResponse(BaseModel): user_id: int user_name: str @@ -18,75 +20,71 @@ class EnhancedUserResponse(BaseModel): processed_at: str -# 出入库统计查询参数 +# ==================== 出入库统计相关模型 ==================== class StorageStatisticsQueryParams(BaseModel): """出入库统计查询参数""" - current: Optional[int] = 1 # 当前页 - size: Optional[int] = 10 # 每页数量 - startTime: Optional[str] = None # 开始时间(格式:YYYY-MM-DD HH:mm:ss) - endTime: Optional[str] = None # 结束时间(格式:YYYY-MM-DD HH:mm:ss) - recordStatus: Optional[int] = None # 记录状态:0-取刀,1-还刀,2-收刀,3-暂存,4-完成,5-违规还刀 - rankingType: Optional[int] = None # 排名类型:0-按数量排序,1-按金额排序 - order: Optional[int] = None # 排序顺序:0-从大到小(降序),1-从小到大(升序) + current: Optional[int] = 1 # 当前页 + size: Optional[int] = 10 # 每页数量 + startTime: Optional[str] = None # 开始时间(格式:YYYY-MM-DD HH:mm:ss) + endTime: Optional[str] = None # 结束时间(格式:YYYY-MM-DD HH:mm:ss) + recordStatus: Optional[int] = None # 记录状态:0-取刀,1-还刀,2-收刀,3-暂存,4-完成,5-违规还刀 + rankingType: Optional[int] = None # 排名类型:0-按数量排序,1-按金额排序 + order: Optional[int] = None # 排序顺序:0-从大到小(降序),1-从小到大(升序) -# 出入库记录模型 class StorageRecord(BaseModel): """出入库记录""" - account: Optional[str] = None # 用户名 - brandCode: Optional[str] = None # 品牌编码 - brandName: Optional[str] = None # 品牌名称 - cabinetCode: Optional[str] = None # 刀柜编码 - cabinetName: Optional[str] = None # 刀具柜名称 - createTime: Optional[str] = None # 创建时间 - cutterCode: Optional[str] = None # 刀具型号 - cutterId: Optional[int] = None # 耗材主键 - cutterType: Optional[str] = None # 刀具类型 - detailsCode: Optional[str] = None # 操作详情编码 - detailsName: Optional[str] = None # 操作详情名称 - factoryName: Optional[str] = None # 工厂名称 - id: Optional[int] = None # 主键id - name: Optional[str] = None # 用户名称 - oldPrice: Optional[float] = None # 历史单价 - operator: Optional[str] = None # 操作人 - price: Optional[float] = None # 单价 - quantity: Optional[int] = None # 数量 - remake: Optional[str] = None # 备注 - specification: Optional[str] = None # 规格 - status: Optional[int] = None # 业务状态 - stockLoc: Optional[str] = None # 库位号 - stockType: Optional[int] = None # 0:入库 1:出库 - updateTime: Optional[str] = None # 更新时间 - workshopName: Optional[str] = None # 车间名称 - - -# 出入库统计响应(分页) + account: Optional[str] = None # 用户名 + brandCode: Optional[str] = None # 品牌编码 + brandName: Optional[str] = None # 品牌名称 + cabinetCode: Optional[str] = None # 刀柜编码 + cabinetName: Optional[str] = None # 刀具柜名称 + createTime: Optional[str] = None # 创建时间 + cutterCode: Optional[str] = None # 刀具型号 + cutterId: Optional[int] = None # 耗材主键 + cutterType: Optional[str] = None # 刀具类型 + detailsCode: Optional[str] = None # 操作详情编码 + detailsName: Optional[str] = None # 操作详情名称 + factoryName: Optional[str] = None # 工厂名称 + id: Optional[int] = None # 主键id + name: Optional[str] = None # 用户名称 + oldPrice: Optional[float] = None # 历史单价 + operator: Optional[str] = None # 操作人 + price: Optional[float] = None # 单价 + quantity: Optional[int] = None # 数量 + remake: Optional[str] = None # 备注 + specification: Optional[str] = None # 规格 + status: Optional[int] = None # 业务状态 + stockLoc: Optional[str] = None # 库位号 + stockType: Optional[int] = None # 0:入库 1:出库 + updateTime: Optional[str] = None # 更新时间 + workshopName: Optional[str] = None # 车间名称 + + +class StorageStatisticsListData(BaseModel): + """出入库统计列表数据""" + current: int # 当前页 + size: int # 每页数量 + total: int # 总记录数 + pages: int # 总页数 + records: List[StorageRecord] # 记录列表 + + class StorageStatisticsResponse(BaseModel): """出入库统计响应""" code: int msg: str success: bool - data: Optional[dict] = None # 包含分页数据 + data: Optional[dict] = None # 包含分页数据 -# 出入库统计列表数据 -class StorageStatisticsListData(BaseModel): - """出入库统计列表数据""" - current: int # 当前页 - size: int # 每页数量 - total: int # 总记录数 - pages: int # 总页数 - records: List[StorageRecord] # 记录列表 - - -# 统计图表返回值 +# ==================== 图表统计相关模型 ==================== class ChartsData(BaseModel): """统计图表数据(全年取刀数量,全年取刀金额,刀具消耗数据)""" - titleList: List[str] = [] # 标题列表(如月份或刀具类型) - dataList: List[Union[str, int, float]] = [] # 数据列表(支持字符串、整数、浮点数) + titleList: List[str] = [] # 标题列表(如月份或刀具类型) + dataList: List[Union[str, int, float]] = [] # 数据列表(支持字符串、整数、浮点数) -# 统计图表响应 class ChartsResponse(BaseModel): """统计图表响应(全年取刀数量,全年取刀金额,刀具消耗响应)""" code: int @@ -95,90 +93,580 @@ class ChartsResponse(BaseModel): data: Optional[ChartsData] = None -# 总库存统计查询参数 +class ExtendedChartsData(BaseModel): + """扩展的图表数据结构 - 包含详细字段""" + dataList: List[Union[str, int, float]] = [] # 添加缺失的字段 + titleList: List[str] = [] # 添加缺失的字段 + # 扩展字段 - 使用 Optional 避免验证错误 + device_details: Optional[List['DeviceRankingDetail']] = None + employee_details: Optional[List['EmployeeRankingDetail']] = None + knife_model_details: Optional[List['KnifeModelRankingDetail']] = None + error_return_details: Optional[List['ErrorReturnRankingDetail']] = None + + +# ==================== 总库存统计相关模型 ==================== class TotalStockQueryParams(BaseModel): """总库存统计查询参数""" - statisticsType: Optional[str] = None # 统计类型 - brandName: Optional[str] = None # 品牌名称 - cabinetCode: Optional[str] = None # 刀柜编码 - cutterType: Optional[str] = None # 刀具类型 - stockStatus: Optional[int] = None # 库位状态 - current: Optional[int] = 1 # 当前页 - size: Optional[int] = 10 # 每页数量 + statisticsType: Optional[str] = None # 统计类型 + brandName: Optional[str] = None # 品牌名称 + cabinetCode: Optional[str] = None # 刀柜编码 + cutterType: Optional[str] = None # 刀具类型 + stockStatus: Optional[int] = None # 库位状态 + current: Optional[int] = 1 # 当前页 + size: Optional[int] = 10 # 每页数量 -# 库位详情模型 class StockLocationDetail(BaseModel): """取刀柜库位详情""" - id: Optional[int] = None # 主键id - brandCode: Optional[str] = None # 品牌编码 - brandName: Optional[str] = None # 品牌名称 - cabinetCode: Optional[str] = None # 刀柜编码 - cabinetName: Optional[str] = None # 刀具柜名称 - cutterCode: Optional[str] = None # 刀具型号 - cutterId: Optional[int] = None # 物料编码主键 - cutterType: Optional[str] = None # 刀具类型 - imageUrl: Optional[str] = None # 图片路径 - locCapacity: Optional[int] = None # 库位容量 - locPackQty: Optional[int] = None # 货道包装数量 - locSurplus: Optional[int] = None # 库位产品剩余[货道库存] - locType: Optional[int] = None # 库位类型[收刀柜: 0 取刀柜: 1] - materialCode: Optional[str] = None # 物料编码 - materialType: Optional[str] = None # 物料类型 - packQty: Optional[int] = None # 最小包装数量 - price: Optional[float] = None # 单价 - specification: Optional[str] = None # 规格 - stockLoc: Optional[str] = None # 库位号 - stockNum: Optional[int] = None # 当前库存数 - stockStatus: Optional[int] = None # 库位状态 - warningNum: Optional[int] = None # 警报数量 - warehouseInTime: Optional[str] = None # 入库时间 - updateTime: Optional[str] = None # 更新时间 - + id: Optional[int] = None # 主键id + brandCode: Optional[str] = None # 品牌编码 + brandName: Optional[str] = None # 品牌名称 + cabinetCode: Optional[str] = None # 刀柜编码 + cabinetName: Optional[str] = None # 刀具柜名称 + cutterCode: Optional[str] = None # 刀具型号 + cutterId: Optional[int] = None # 物料编码主键 + cutterType: Optional[str] = None # 刀具类型 + imageUrl: Optional[str] = None # 图片路径 + locCapacity: Optional[int] = None # 库位容量 + locPackQty: Optional[int] = None # 货道包装数量 + locSurplus: Optional[int] = None # 库位产品剩余[货道库存] + locType: Optional[int] = None # 库位类型[收刀柜: 0 取刀柜: 1] + materialCode: Optional[str] = None # 物料编码 + materialType: Optional[str] = None # 物料类型 + packQty: Optional[int] = None # 最小包装数量 + price: Optional[float] = None # 单价 + specification: Optional[str] = None # 规格 + stockLoc: Optional[str] = None # 库位号 + stockNum: Optional[int] = None # 当前库存数 + stockStatus: Optional[int] = None # 库位状态 + warningNum: Optional[int] = None # 警报数量 + warehouseInTime: Optional[str] = None # 入库时间 + updateTime: Optional[str] = None # 更新时间 + # 计算字段(前端展示用) - stockValue: Optional[float] = None # 库存价值(单价 * 剩余数量) - cabinetSide: Optional[str] = None # 柜子面 + stockValue: Optional[float] = None # 库存价值(单价 * 剩余数量) + cabinetSide: Optional[str] = None # 柜子面 -# 总库存统计响应 class TotalStockResponse(BaseModel): """总库存统计响应""" code: int msg: str success: bool - data: Optional[dict] = None # 包含分页数据或列表数据 + data: Optional[dict] = None # 包含分页数据或列表数据 -# 废刀回收统计 - 还刀信息详情 +# ==================== 废刀回收统计相关模型 ==================== class ReturnKnifeDetail(BaseModel): """还刀信息详情""" - id: Optional[int] = None # 取刀主键 - borrowStatus: Optional[int] = None # 还刀状态:0-修磨,1-报废,2-换线,3-错领 - borrowTime: Optional[str] = None # 还刀时间 - borrowUserName: Optional[str] = None # 还刀人 - brandName: Optional[str] = None # 品牌名称 - cutterCode: Optional[str] = None # 刀具型号 - cutterType: Optional[str] = None # 刀具类型 - lendTime: Optional[str] = None # 取刀时间 - lendUserName: Optional[str] = None # 借刀人 - recordStatus: Optional[int] = None # 记录状态:0-取刀,1-还刀,2-收刀,3-暂存 - specification: Optional[str] = None # 规格 - stockLoc: Optional[str] = None # 库位号 - - -# 废刀回收统计 - 还刀数据 + id: Optional[int] = None # 取刀主键 + borrowStatus: Optional[int] = None # 还刀状态:0-修磨,1-报废,2-换线,3-错领 + borrowTime: Optional[str] = None # 还刀时间 + borrowUserName: Optional[str] = None # 还刀人 + brandName: Optional[str] = None # 品牌名称 + cutterCode: Optional[str] = None # 刀具型号 + cutterType: Optional[str] = None # 刀具类型 + lendTime: Optional[str] = None # 取刀时间 + lendUserName: Optional[str] = None # 借刀人 + recordStatus: Optional[int] = None # 记录状态:0-取刀,1-还刀,2-收刀,3-暂存 + specification: Optional[str] = None # 规格 + stockLoc: Optional[str] = None # 库位号 + + class ReturnKnifeData(BaseModel): """收刀柜还刀数据""" - borrowStatus: Optional[str] = None # 还刀状态(字符串形式) - cabinetCode: Optional[str] = None # 刀柜编码 - recordStatus: Optional[int] = None # 记录状态:0-取刀,1-还刀,2-收刀,3-暂存 - list: Optional[List[ReturnKnifeDetail]] = [] # 还刀详情列表 + borrowStatus: Optional[str] = None # 还刀状态(字符串形式) + cabinetCode: Optional[str] = None # 刀柜编码 + recordStatus: Optional[int] = None # 记录状态:0-取刀,1-还刀,2-收刀,3-暂存 + list: Optional[List[ReturnKnifeDetail]] = [] # 还刀详情列表 -# 废刀回收统计响应 class WasteKnifeRecycleResponse(BaseModel): """废刀回收统计响应""" code: int msg: str success: bool - data: Optional[ReturnKnifeData] = None # 还刀数据 \ No newline at end of file + data: Optional[ReturnKnifeData] = None # 还刀数据 + + +# ==================== 排行接口模型 ==================== +class RankingBaseRequest(BaseModel): + """排行接口基础请求参数""" + startTime: Optional[str] = None + endTime: Optional[str] = None + order: Optional[int] = None + rankingType: Optional[int] = None + recordStatus: Optional[int] = None + + +class DeviceRankingDetail(BaseModel): + """设备用刀排行详细数据项""" + rank: int # 排名 + device_code: str # 设备编码 + device_name: str # 设备名称 + usage_count: int # 用刀次数 + total_amount: float # 总金额 + avg_usage_duration: float # 平均使用时长(小时) + usage_efficiency: float # 使用效率(百分比) + + +class EmployeeRankingDetail(BaseModel): + """员工领刀排行详细数据项 - 根据合并前版本修复""" + rank: int # 排名 + employee_name: str # 员工姓名 + department: str # 部门 + borrow_count: int # 领刀次数 + total_amount: float # 总金额 + avg_amount: float # 平均金额 + last_borrow_time: str # 最后领刀时间 + + +class KnifeModelRankingDetail(BaseModel): + """刀具型号排行详细数据项""" + rank: int # 排名 + model: str # 刀具型号 + knife_type: str # 刀具类型 + brand: str # 品牌名称 + usage_count: int # 使用次数 + total_amount: float # 总金额 + avg_lifespan: float # 平均寿命(小时) + popularity: float # 受欢迎度(百分比) + + +class ErrorReturnRankingDetail(BaseModel): + """异常还刀排行详细数据项""" + rank: int # 排名 + employee_name: str # 员工姓名 + department: str # 部门 + error_count: int # 异常次数 + error_type: str # 异常类型 + total_loss: float # 总损失 + last_error_time: str # 最后异常时间 + + +class RankingBaseResponse(BaseModel): + """排行接口基础响应""" + code: int + msg: str + success: bool + + +class DeviceRankingResponse(RankingBaseResponse): + """设备用刀排行响应""" + data: ExtendedChartsData + + +class KnifeModelRankingResponse(RankingBaseResponse): + """刀具型号排行响应""" + data: ExtendedChartsData + + +class EmployeeRankingResponse(RankingBaseResponse): + """员工领刀排行响应""" + data: ExtendedChartsData + + +class ErrorReturnRankingResponse(RankingBaseResponse): + """异常还刀排行响应""" + data: ExtendedChartsData + + +# ==================== 系统记录相关模型 ==================== + +# 合并自 auditor_record 模块的系统记录模型 +# 日期: 2025-11-15 +# 来源: auditor_record/schemas/data_schemas.py + +class ReplenishRecord(BaseModel): + """ + 补货记录模型 + """ + id: Optional[int] = None + brandName: Optional[str] = None # 品牌名称 + cabinetCode: Optional[str] = None # 刀柜编码 + createDept: Optional[int] = None # 创建部门 + createTime: Optional[str] = None # 创建时间 + createUser: Optional[int] = None # 创建人 + cutterCode: Optional[str] = None # 刀具型号 + cutterType: Optional[str] = None # 刀具类型 + detailsCode: Optional[str] = None # 操作详情 + isDeleted: Optional[int] = None # 是否已删除 + lendUserName: Optional[str] = None # 取出人 + logStatus: Optional[int] = None # 日志状态 + logType: Optional[str] = None # 日志类型 + materialCode: Optional[str] = None # 物料编码 + newPrice: Optional[float] = None # 新单价 + newStockNum: Optional[int] = None # 操作后库存数 + oldPrice: Optional[float] = None # 老单价 + oldStockNum: Optional[int] = None # 操作前库存数 + operator: Optional[str] = None # 操作人 + quantity: Optional[int] = None # 数量 + remake: Optional[str] = None # 备注 + specification: Optional[str] = None # 规格 + status: Optional[int] = None # 业务状态 + stockLoc: Optional[str] = None # 库位号 + storageUserName: Optional[str] = None # 暂存人 + tenantId: Optional[str] = None # 租户ID + updateTime: Optional[str] = None # 更新时间 + updateUser: Optional[int] = None # 更新人 + + model_config = ConfigDict(from_attributes=True) + + +class ReplenishRecordData(BaseModel): + """ + 补货记录分页数据 + """ + current: int + hitCount: bool + pages: int + records: List[ReplenishRecord] + searchCount: bool + size: int + total: int + + model_config = ConfigDict(from_attributes=True) + + +class ReplenishRecordResponse(BaseModel): + """ + 补货记录响应模型 + """ + code: int + data: ReplenishRecordData + msg: str + success: bool + + model_config = ConfigDict(from_attributes=True) + + +class ReplenishRecordListRequest(BaseModel): + """ + 补货记录列表请求参数 + """ + current: Optional[int] = None # 当前页 + endTime: Optional[str] = None # 结束时间 + order: Optional[int] = None # 顺序 0: 从大到小 1:从小到大 + rankingType: Optional[int] = None # 0: 数量 1: 金额 + recordStatus: Optional[int] = None # 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + size: Optional[int] = None # 每页的数量 + startTime: Optional[str] = None # 开始时间 + + model_config = ConfigDict(from_attributes=True) + + +class ExportReplenishRecordRequest(BaseModel): + """ + 导出补货记录请求参数 + """ + endTime: Optional[str] = None # 结束时间 + order: Optional[int] = None # 顺序 0: 从大到小 1:从小到大 + rankingType: Optional[int] = None # 0: 数量 1: 金额 + recordStatus: Optional[int] = None # 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + startTime: Optional[str] = None # 开始时间 + + model_config = ConfigDict(from_attributes=True) + + +# 领刀记录相关模型 +class LendRecord(BaseModel): + """ + 领刀记录模型 + """ + # 取刀人相关信息 + lendUser: int # 取刀人ID + lendUserName: str # 取刀人 + borrowUserName: Optional[str] = None # 还刀人 + + # 品牌信息 + brandName: str # 品牌名称 + brandCode: str # 品牌编码 + + # 刀具信息 + cutterType: str # 刀具类型 + cutterCode: str # 刀具型号 + specification: str # 规格 + materialCode: str # 物料编码 + price: float # 单价 + + # 位置信息 + cabinetCode: str # 刀柜编码 + lendStock: str # 借刀库位号 + borrowStock: Optional[str] = None # 还刀库位号 + + # 时间信息 + lendTime: str # 借刀时间 + borrowTime: Optional[str] = None # 还刀时间 + finalCollectTime: Optional[str] = None # 最终确认时间 + + # 状态信息 + recordStatus: int # 记录状态 (0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀) + borrowStatus: Optional[int] = None # 还刀状态 (0: 修磨 1: 报废 2: 换线 3: 错领) + finalCollectStatus: Optional[int] = None # 最终确认状态 (0: 通过 1: 未通过) + + # 备注和结果 + borrowRemarks: Optional[str] = None # 还刀备注 + finalCollectRemarks: Optional[str] = None # 最终确认结果 + collectStatus: Optional[str] = None # 管理员确认结果 + + model_config = ConfigDict(from_attributes=True) + + +class LendRecordData(BaseModel): + """ + 领刀记录分页数据 + """ + current: int + hitCount: bool + pages: int + records: List[LendRecord] + searchCount: bool + size: int + total: int + + model_config = ConfigDict(from_attributes=True) + + +class LendRecordResponse(BaseModel): + """ + 领刀记录响应模型 + """ + code: int + data: LendRecordData + msg: str + success: bool + + model_config = ConfigDict(from_attributes=True) + + +class LendRecordListRequest(BaseModel): + """ + 领刀记录列表请求参数 + """ + current: int = 1 + size: int = 20 + keyword: Optional[str] = None # 关键字搜索 + department: Optional[str] = None # 部门筛选 + startTime: Optional[str] = None # 开始时间 + endTime: Optional[str] = None # 结束时间 + order: Optional[int] = None # 顺序 0: 从大到小 1:从小到大 + rankingType: Optional[int] = None # 0: 数量 1: 金额 + recordStatus: Optional[int] = None # 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + + model_config = ConfigDict(from_attributes=True) + + +class ExportLendRecordRequest(BaseModel): + """ + 导出领刀记录请求参数 + """ + endTime: Optional[str] = None # 结束时间 + order: Optional[int] = None # 顺序 0: 从大到小 1:从小到大 + rankingType: Optional[int] = None # 0: 数量 1: 金额 + recordStatus: Optional[int] = None # 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + startTime: Optional[str] = None # 开始时间 + + model_config = ConfigDict(from_attributes=True) + + +# 公共暂存记录相关模型 +class StorageRecordModel(BaseModel): + """ + 公共暂存记录模型 + """ + id: Optional[int] = None # 主键ID + brandName: Optional[str] = None # 品牌名称 + cabinetCode: Optional[str] = None # 刀柜编码 + createDept: Optional[int] = None # 创建部门 + createTime: Optional[str] = None # 创建时间 + createUser: Optional[int] = None # 创建人 + cutterCode: Optional[str] = None # 刀具型号 + detailsCode: Optional[str] = None # 操作详情 + isDeleted: Optional[int] = None # 是否已删除 + lendUserName: Optional[str] = None # 取出人 + logStatus: Optional[int] = None # 日志状态 + materialCode: Optional[str] = None # 物料编码 + newPrice: Optional[float] = None # 新单价 + newStockNum: Optional[int] = None # 操作后库存数 + oldPrice: Optional[float] = None # 老单价 + oldStockNum: Optional[int] = None # 操作前库存数 + operator: Optional[str] = None # 操作人 + quantity: Optional[int] = None # 数量 + remake: Optional[str] = None # 备注 + specification: Optional[str] = None # 规格 + status: Optional[int] = None # 业务状态 + stockLoc: Optional[str] = None # 库位号 + storageUserName: Optional[str] = None # 暂存人 + tenantId: Optional[str] = None # 租户ID + updateTime: Optional[str] = None # 更新时间 + updateUser: Optional[int] = None # 更新人 + + model_config = ConfigDict(from_attributes=True) + + +class StorageRecordModelData(BaseModel): + """ + 公共暂存记录分页数据 + """ + current: int + hitCount: bool + pages: int + records: List[StorageRecordModel] + searchCount: bool + size: int + total: int + + model_config = ConfigDict(from_attributes=True) + + +class StorageRecordModelResponse(BaseModel): + """ + 公共暂存记录响应模型 + """ + code: int + data: StorageRecordModelData + msg: str + success: bool + + model_config = ConfigDict(from_attributes=True) + + +class StorageRecordListRequest(BaseModel): + """ + 公共暂存记录列表请求参数 + """ + current: Optional[int] = None # 当前页 + endTime: Optional[str] = None # 结束时间 + order: Optional[int] = None # 顺序 0: 从大到小 1:从小到大 + rankingType: Optional[int] = None # 0: 数量 1: 金额 + recordStatus: Optional[int] = None # 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + size: Optional[int] = None # 每页的数量 + startTime: Optional[str] = None # 开始时间 + + model_config = ConfigDict(from_attributes=True) + + +class ExportStorageRecordRequest(BaseModel): + """ + 导出公共暂存记录请求参数 + """ + endTime: Optional[str] = None # 结束时间 + order: Optional[int] = None # 顺序 0: 从大到小 1:从小到大 + rankingType: Optional[int] = None # 0: 数量 1: 金额 + recordStatus: Optional[int] = None # 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + startTime: Optional[str] = None # 开始时间 + + model_config = ConfigDict(from_attributes=True) + + +# 告警预警相关模型 +class AlarmWarning(BaseModel): + """ + 告警预警模型 + """ + id: int + locSurplus: int # 货道 + alarmLevel: int # 预警等级 + deviceType: str # 设备类型 + cabinetCode: str # 刀柜编码 + stockLoc: str # 库位号 + brandName: str # 品牌名称 + itemCode: str # 物品编码 + itemType: str # 物品类型 + currentStock: int # 当前库存 + thresholdValue: int # 阈值 + alarmMessage: str # 预警信息 + handleStatus: int # 处理状态 + createTime: str # 预警时间 + handleTime: Optional[str] = None # 处理时间 + handleUser: Optional[str] = None # 处理人 + handleRemark: Optional[str] = None # 处理备注 + + model_config = ConfigDict(from_attributes=True) + + +class AlarmWarningData(BaseModel): + """ + 告警预警分页数据 + """ + current: int + hitCount: bool + pages: int + records: List[AlarmWarning] + searchCount: bool + size: int + total: int + + model_config = ConfigDict(from_attributes=True) + + +class AlarmWarningResponse(BaseModel): + """ + 告警预警响应模型 + """ + code: int + data: AlarmWarningData + msg: str + success: bool + + model_config = ConfigDict(from_attributes=True) + + +class AlarmWarningListRequest(BaseModel): + """ + 告警预警列表请求参数 + """ + locSurplus: Optional[int] = None # 货道 + alarmLevel: Optional[int] = None # 预警等级 + deviceType: Optional[str] = None # 设备类型 + cabinetCode: Optional[str] = None # 刀柜编码 + brandName: Optional[str] = None # 品牌名称 + handleStatus: Optional[int] = None # 处理状态 + current: Optional[int] = None # 当前页 + size: Optional[int] = None # 每页数量 + + model_config = ConfigDict(from_attributes=True) + + +class ThresholdSetting(BaseModel): + """ + 阈值设置模型 + """ + locSurplus: int # 货道 + alarmThreshold: int # 预警阈值 + + model_config = ConfigDict(from_attributes=True) + + +class ThresholdSettingRequest(BaseModel): + """ + 阈值设置请求参数 + """ + locSurplus: int # 货道 + alarmThreshold: int # 预警阈值 + + model_config = ConfigDict(from_attributes=True) + + +class AlarmStatistics(BaseModel): + """ + 告警统计模型 + """ + level1Count: int # 安全库存量预警数量 + level2Count: int # 采集阈值存储量预警数量 + level3Count: int # 紧急补货存储量报警数量 + unhandledCount: int # 未处理预警总数 + + model_config = ConfigDict(from_attributes=True) + + +class AlarmStatisticsResponse(BaseModel): + """ + 告警统计响应模型 + """ + code: int + data: AlarmStatistics + msg: str + success: bool + + model_config = ConfigDict(from_attributes=True) \ No newline at end of file diff --git a/auditor/schemas/data_schemas_auditor.py b/auditor/schemas/data_schemas_auditor.py deleted file mode 100644 index 1dffa4b..0000000 --- a/auditor/schemas/data_schemas_auditor.py +++ /dev/null @@ -1,371 +0,0 @@ -from pydantic import BaseModel -from typing import List, Optional -from datetime import datetime - - -class LendRecord(BaseModel): - """ - 领刀记录模型 - """ - # 取刀人相关信息 - lendUser: int # 取刀人ID - lendUserName: str # 取刀人 - - # 品牌信息 - brandName: str # 品牌名称 - brandCode: str # 品牌编码 - - # 刀具信息 - cutterType: str # 刀具类型 - cutterCode: str # 刀具型号 - specification: str # 规格 - materialCode: str # 物料编码 - price: float # 单价 - - # 位置信息 - cabinetCode: str # 刀柜编码 - lendStock: str # 借刀库位号 - borrowStock: Optional[str] # 还刀库位号 - - # 时间信息 - lendTime: str # 借刀时间 - borrowTime: Optional[str] # 还刀时间 - finalCollectTime: Optional[str] # 最终确认时间 - - # 状态信息 - recordStatus: int # 记录状态 (0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀) - borrowStatus: Optional[int] # 还刀状态 (0: 修磨 1: 报废 2: 换线 3: 错领) - finalCollectStatus: Optional[int] # 最终确认状态 (0: 通过 1: 未通过) - - # 备注和结果 - borrowRemarks: Optional[str] # 还刀备注 - finalCollectRemarks: Optional[str] # 最终确认结果 - collectStatus: Optional[str] # 管理员确认结果 - - class Config: - orm_mode = True - - -class LendRecordData(BaseModel): - """ - 领刀记录分页数据 - """ - current: int - hitCount: bool - pages: int - records: List[LendRecord] - searchCount: bool - size: int - total: int - - class Config: - orm_mode = True - - -class LendRecordResponse(BaseModel): - """ - 领刀记录响应模型 - """ - code: int - data: LendRecordData - msg: str - success: bool - - class Config: - orm_mode = True - - -class LendRecordListRequest(BaseModel): - """ - 领刀记录列表请求参数 - """ - page: int = 1 - page_size: int = 20 - keyword: Optional[str] = None # 关键字搜索 - department: Optional[str] = None # 部门筛选 - start_date: Optional[str] = None # 开始日期 - end_date: Optional[str] = None # 结束日期 - order: Optional[int] = None # 顺序 0: 从大到小 1:从小到大 - rankingType: Optional[int] = None # 0: 数量 1: 金额 - recordStatus: Optional[int] = None # 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 - - class Config: - orm_mode = True - - -class ExportLendRecordRequest(BaseModel): - """ - 导出领刀记录请求参数 - """ - endTime: Optional[str] = None # 结束时间 - order: Optional[int] = None # 顺序 0: 从大到小 1:从小到大 - rankingType: Optional[int] = None # 0: 数量 1: 金额 - recordStatus: Optional[int] = None # 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 - startTime: Optional[str] = None # 开始时间 - - class Config: - orm_mode = True - - -class ItemDto(BaseModel): - """ - 刀柜货道项模型 - """ - locSurplus: Optional[int] # 货道刀具数量 - stockId: Optional[int] # 刀柜编码(根据文档描述,实际是stockId) - - class Config: - orm_mode = True - - -class RestockRequest(BaseModel): - """ - 补货请求模型 - """ - replenishDto: Optional[dict] = None # replenishDto 对象 - cabinetCode: Optional[str] = None # 刀柜编码 - itemDtoList: Optional[List[ItemDto]] = None # 补货信息列表 - - class Config: - orm_mode = True - - -class RestockData(BaseModel): - """ - 补货响应数据模型(取刀信息) - """ - # 根据响应说明,这里应该包含"取刀信息",暂时保持为空,可根据实际API响应添加字段 - pass - - -class RestockResponse(BaseModel): - """ - 补货响应模型 - """ - code: int - data: Optional[RestockData] - dataSuccess: Optional[bool] - msg: str - success: bool - - class Config: - orm_mode = True - - -class ReplenishRecord(BaseModel): - """ - 补货记录模型 - """ - brandName: Optional[str] = None # 品牌名称 - cabinetCode: Optional[str] = None # 刀柜编码 - createDept: Optional[int] = None # 创建部门 - createTime: Optional[str] = None # 创建时间 - createUser: Optional[int] = None # 创建人 - cutterCode: Optional[str] = None # 刀具型号 - cutterType: Optional[str] = None # 电池类型 - detailsCode: Optional[str] = None # 操作详情 - id: Optional[int] = None # 主键id - isDeleted: Optional[int] = None # 是否已删除 - lendUserName: Optional[str] = None # 取出人 - logStatus: Optional[int] = None # 日志类型 0:操作日志 1:公共暂存 2:补货 - logType: Optional[str] = None # 日志类型 - materialCode: Optional[str] = None # 物料编码 - newPrice: Optional[float] = None # 新单价 - newStockNum: Optional[int] = None # 操作后库存数 - oldPrice: Optional[float] = None # 老单价 - oldStockNum: Optional[int] = None # 操作前库存数 - operator: Optional[str] = None # 操作人 - quantity: Optional[int] = None # 数量 - remake: Optional[str] = None # 备注 - specification: Optional[str] = None # 规格 - status: Optional[int] = None # 业务状态 - stockLoc: Optional[str] = None # 库位号 - storageUserName: Optional[str] = None # 暂存人 - tenantId: Optional[str] = None # 租户ID - updateTime: Optional[str] = None # 更新时间 - updateUser: Optional[int] = None # 更新人 - - class Config: - orm_mode = True - - -class ReplenishRecordData(BaseModel): - """ - 补货记录分页数据 - """ - current: int - hitCount: bool - pages: int - records: List[ReplenishRecord] - searchCount: bool - size: int - total: int - - class Config: - orm_mode = True - - -class ReplenishRecordResponse(BaseModel): - """ - 补货记录响应模型 - """ - code: int - data: ReplenishRecordData - msg: str - success: bool - - class Config: - orm_mode = True - - -class ReplenishRecordListRequest(BaseModel): - """ - 补货记录列表请求参数 - """ - current: Optional[int] = None # 当前页 - endTime: Optional[str] = None # 结束时间 - order: Optional[int] = None # 顺序 0: 从大到小 1:从小到大 - rankingType: Optional[int] = None # 0: 数量 1: 金额 - recordStatus: Optional[int] = None # 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 - size: Optional[int] = None # 每页的数量 - startTime: Optional[str] = None # 开始时间 - - class Config: - orm_mode = True - - -class StorageRecord(ReplenishRecord): - """ - 公共暂存记录模型(与补货记录模型相同) - """ - pass - - -class StorageRecordData(BaseModel): - """ - 公共暂存记录分页数据 - """ - current: int - hitCount: bool - pages: int - records: List[StorageRecord] - searchCount: bool - size: int - total: int - - class Config: - orm_mode = True - - -class StorageRecordResponse(BaseModel): - """ - 公共暂存记录响应模型 - """ - code: int - data: StorageRecordData - msg: str - success: bool - - class Config: - orm_mode = True - - -class StorageRecordListRequest(BaseModel): - """ - 公共暂存记录列表请求参数 - """ - current: Optional[int] = None # 当前页 - endTime: Optional[str] = None # 结束时间 - order: Optional[int] = None # 顺序 0: 从大到小 1:从小到大 - rankingType: Optional[int] = None # 0: 数量 1: 金额 - recordStatus: Optional[int] = None # 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 - size: Optional[int] = None # 每页的数量 - startTime: Optional[str] = None # 开始时间 - - class Config: - orm_mode = True - - -class PersonalStorageInfo(BaseModel): - """ - 个人暂存柜信息 - """ - cabinetCode: Optional[str] = None # 刀柜编码 - id: Optional[str] = None # 通道号主键 - locPrefix: Optional[str] = None # 柜子ABCDE面 - locType: Optional[int] = None # 库位类型[收刀柜: 0 取刀柜: 1] - name: Optional[str] = None # 暂存用户名 - stockLoc: Optional[str] = None # 库位号 - storageCode: Optional[str] = None # 一次暂存编码 - storageType: Optional[int] = None # 暂存类型 0: 公共暂存 1: 个人暂存 - storageUser: Optional[int] = None # 暂存用户 - - class Config: - orm_mode = True - - -class PersonalStorageResponse(BaseModel): - """ - 个人暂存柜响应模型 - """ - code: int - data: Optional[List[PersonalStorageInfo]] - msg: str - success: bool - - class Config: - orm_mode = True - - -class MakeAlarmResponse(BaseModel): - """ - 设置取刀柜告警值响应模型 - """ - code: int - data: Optional[bool] - msg: str - success: bool - - class Config: - orm_mode = True - - -class CabinetAlarmResponse(BaseModel): - """ - 获取取刀柜告警值响应模型 - """ - code: int - data: Optional[int] - msg: str - success: bool - - class Config: - orm_mode = True - - -class SelectorOption(BaseModel): - """ - 选择器选项模型 - """ - label: Optional[str] = None # 标题 - value: Optional[str] = None # 值 - - class Config: - orm_mode = True - - -class CabinetSelectResponse(BaseModel): - """ - 获取刀柜信息下拉响应模型 - """ - code: int - data: Optional[List[SelectorOption]] - msg: str - success: bool - - class Config: - orm_mode = True - - - - diff --git a/auditor/services/api_client.py b/auditor/services/api_client.py index a4cd066..f9f3192 100644 --- a/auditor/services/api_client.py +++ b/auditor/services/api_client.py @@ -1,9 +1,9 @@ import requests -import requests import logging import os -from typing import Dict, Any, Optional - +from typing import Dict, Any, Optional, List +from urllib.parse import urljoin +#ok logger = logging.getLogger(__name__) @@ -13,7 +13,7 @@ class OriginalAPIClient: def __init__(self, base_url: str, api_key: Optional[str] = None, token_file: Optional[str] = None): """ 初始化API客户端 - + 参数: base_url: API基础URL api_key: API密钥(可选) @@ -41,7 +41,7 @@ def __init__(self, base_url: str, api_key: Optional[str] = None, token_file: Opt def _load_token_from_file(self, token_file: str) -> Optional[str]: """ 从文件读取Token - + 参数: token_file: Token文件路径 返回: @@ -54,7 +54,7 @@ def _load_token_from_file(self, token_file: str) -> Optional[str]: current_dir = os.path.dirname(os.path.abspath(__file__)) project_root = os.path.dirname(os.path.dirname(current_dir)) token_file = os.path.join(project_root, token_file) - + # 读取token文件 with open(token_file, 'r', encoding='utf-8') as f: token = f.read().strip() @@ -70,11 +70,11 @@ def _load_token_from_file(self, token_file: str) -> Optional[str]: except Exception as e: logger.error(f"读取Token文件失败: {e}") return None - + def update_token(self, token: str): """ 更新Token - + 参数: token: 新的Token字符串 """ @@ -106,7 +106,7 @@ def get_storage_statistics(self, params: Dict[str, Any]) -> Dict[str, Any]: 获取出入库统计数据 调用外部接口:/qw/knife/web/from/mes/record/stockList 请求类型:application/x-www-form-urlencoded - + 参数: - current: 当前页 - size: 每页数量 @@ -119,7 +119,7 @@ def get_storage_statistics(self, params: Dict[str, Any]) -> Dict[str, Any]: try: # 构建查询参数,过滤掉None值 query_params = {k: v for k, v in params.items() if v is not None} - + # 调用外部接口,使用 params 传递查询参数 response = self.session.get( f"{self.base_url}/qw/knife/web/from/mes/record/stockList", @@ -127,10 +127,10 @@ def get_storage_statistics(self, params: Dict[str, Any]) -> Dict[str, Any]: timeout=10 ) response.raise_for_status() - + # 获取外部接口返回的完整数据 external_data = response.json() - + # 提取需要的字段并返回 return { "code": external_data.get("code"), @@ -138,7 +138,7 @@ def get_storage_statistics(self, params: Dict[str, Any]) -> Dict[str, Any]: "success": external_data.get("success"), "data": external_data.get("data") } - + except requests.exceptions.RequestException as e: logger.error(f"获取出入库统计数据失败: {e}") return { @@ -154,7 +154,7 @@ def export_stock_record(self, params: Dict[str, Any]) -> Dict[str, Any]: 调用外部接口:/qw/knife/web/from/mes/record/exportStockRecord 请求方式:GET 请求数据类型:application/x-www-form-urlencoded - + 参数: - startTime: 开始时间 - endTime: 结束时间 @@ -165,7 +165,7 @@ def export_stock_record(self, params: Dict[str, Any]) -> Dict[str, Any]: try: # 构建查询参数,过滤掉None值 query_params = {k: v for k, v in params.items() if v is not None} - + # 调用外部导出接口 response = self.session.get( f"{self.base_url}/qw/knife/web/from/mes/record/exportStockRecord", @@ -173,10 +173,10 @@ def export_stock_record(self, params: Dict[str, Any]) -> Dict[str, Any]: timeout=10 ) response.raise_for_status() - + # 获取外部接口返回的完整数据 external_data = response.json() - + # 提取需要的字段并返回 return { "code": external_data.get("code"), @@ -184,7 +184,7 @@ def export_stock_record(self, params: Dict[str, Any]) -> Dict[str, Any]: "success": external_data.get("success"), "data": external_data.get("data") } - + except requests.exceptions.RequestException as e: logger.error(f"导出出入库记录失败: {e}") return { @@ -200,7 +200,7 @@ def get_charts_lend_by_year(self) -> Dict[str, Any]: 调用外部接口:/qw/knife/web/from/mes/statistics/chartsLendByYear 请求方式:GET 无需参数 - + 返回数据: - titleList: 月份标题列表(如:["1月", "2月", ..., "12月"]) - dataList: 对应的取刀数量列表 @@ -212,10 +212,10 @@ def get_charts_lend_by_year(self) -> Dict[str, Any]: timeout=10 ) response.raise_for_status() - + # 获取外部接口返回的数据 external_data = response.json() - + # 返回需要的字段 return { "code": external_data.get("code"), @@ -223,7 +223,7 @@ def get_charts_lend_by_year(self) -> Dict[str, Any]: "success": external_data.get("success"), "data": external_data.get("data") } - + except requests.exceptions.RequestException as e: logger.error(f"获取全年取刀数量统计失败: {e}") return { @@ -239,7 +239,7 @@ def get_charts_lend_price_by_year(self) -> Dict[str, Any]: 调用外部接口:/qw/knife/web/from/mes/statistics/chartsLendPriceByYear 请求方式:GET 无需参数 - + 返回数据: - titleList: 月份标题列表(如:["1月", "2月", ..., "12月"]) - dataList: 对应的取刀金额列表 @@ -251,10 +251,10 @@ def get_charts_lend_price_by_year(self) -> Dict[str, Any]: timeout=10 ) response.raise_for_status() - + # 获取外部接口返回的数据 external_data = response.json() - + # 返回需要的字段 return { "code": external_data.get("code"), @@ -262,7 +262,7 @@ def get_charts_lend_price_by_year(self) -> Dict[str, Any]: "success": external_data.get("success"), "data": external_data.get("data") } - + except requests.exceptions.RequestException as e: logger.error(f"获取全年取刀金额统计失败: {e}") return { @@ -278,7 +278,7 @@ def get_charts_accumulated(self) -> Dict[str, Any]: 调用外部接口:/qw/knife/web/from/mes/statistics/chartsAccumulated 请求方式:GET 无需参数 - + 返回数据: - titleList: 统计项标题列表(可能包含:累计使用次数、使用时长、平均寿命等) - dataList: 对应的数据列表 @@ -290,10 +290,10 @@ def get_charts_accumulated(self) -> Dict[str, Any]: timeout=10 ) response.raise_for_status() - + # 获取外部接口返回的数据 external_data = response.json() - + # 返回需要的字段 return { "code": external_data.get("code"), @@ -301,7 +301,7 @@ def get_charts_accumulated(self) -> Dict[str, Any]: "success": external_data.get("success"), "data": external_data.get("data") } - + except requests.exceptions.RequestException as e: logger.error(f"获取刀具消耗统计失败: {e}") return { @@ -316,7 +316,7 @@ def get_total_stock_list(self, params: Dict[str, Any]) -> Dict[str, Any]: 获取总库存统计列表(支持搜索和刷新) 调用外部接口:/qw/knife/web/from/mes/cabinetStock/stockLocTakeInfoById 请求方式:GET - + 参数: - statisticsType: 统计类型 - brandName: 品牌名称 @@ -325,10 +325,10 @@ def get_total_stock_list(self, params: Dict[str, Any]) -> Dict[str, Any]: - stockStatus: 库位状态 - current: 当前页 - size: 每页数量 - + 返回数据: - 库存列表数据,包含分页信息和详细记录 - + 注意: 由于外部接口是单个库位查询,这里假设外部系统也有列表接口。 如果外部接口路径不同,请调整 URL。 @@ -336,7 +336,7 @@ def get_total_stock_list(self, params: Dict[str, Any]) -> Dict[str, Any]: try: # 构建查询参数 query_params = {k: v for k, v in params.items() if v is not None} - + # 调用外部接口(这里假设有列表查询接口) # 如果外部系统没有列表接口,需要跟外部系统确认正确的接口地址 response = self.session.get( @@ -345,16 +345,16 @@ def get_total_stock_list(self, params: Dict[str, Any]) -> Dict[str, Any]: timeout=10 ) response.raise_for_status() - + # 获取外部接口返回的数据 external_data = response.json() - + # 如果外部返回的是单个对象,需要封装成列表格式 data = external_data.get("data") if data and not isinstance(data, list): # 如果是单个对象,转换为列表 data = [data] - + # 计算库存价值(单价 * 剩余数量) if data and isinstance(data, list): for item in data: @@ -362,7 +362,7 @@ def get_total_stock_list(self, params: Dict[str, Any]) -> Dict[str, Any]: price = item.get('price', 0) or 0 loc_surplus = item.get('locSurplus', 0) or 0 item['stockValue'] = round(price * loc_surplus, 2) - + # 返回需要的字段 return { "code": external_data.get("code"), @@ -370,7 +370,7 @@ def get_total_stock_list(self, params: Dict[str, Any]) -> Dict[str, Any]: "success": external_data.get("success"), "data": data } - + except requests.exceptions.RequestException as e: logger.error(f"获取总库存统计列表失败: {e}") return { @@ -385,10 +385,10 @@ def get_stock_location_by_id(self, stock_id: int) -> Dict[str, Any]: 获取取刀柜库位详情(单个) 调用外部接口:/qw/knife/web/from/mes/cabinetStock/stockLocTakeInfoById 请求方式:GET - + 参数: - stockId: 刀柜货道主键 - + 返回数据: - 单个库位的详细信息 """ @@ -400,17 +400,17 @@ def get_stock_location_by_id(self, stock_id: int) -> Dict[str, Any]: timeout=10 ) response.raise_for_status() - + # 获取外部接口返回的数据 external_data = response.json() - + # 计算库存价值 data = external_data.get("data") if data and isinstance(data, dict): price = data.get('price', 0) or 0 loc_surplus = data.get('locSurplus', 0) or 0 data['stockValue'] = round(price * loc_surplus, 2) - + # 返回需要的字段 return { "code": external_data.get("code"), @@ -418,7 +418,7 @@ def get_stock_location_by_id(self, stock_id: int) -> Dict[str, Any]: "success": external_data.get("success"), "data": data } - + except requests.exceptions.RequestException as e: logger.error(f"获取库位详情失败: {e}") return { @@ -433,12 +433,12 @@ def get_waste_knife_recycle_info(self, params: Dict[str, Any]) -> Dict[str, Any] 获取废刀回收统计信息(收刀柜还刀信息) 调用外部接口:/qw/knife/web/from/mes/lend/getLendByStock 请求方式:GET - + 参数: - borrowCode: 还刀码(可选) - cabinetCode: 刀柜编码(可选) - stockLoc: 刀柜库位号(可选) - + 返回数据: - 还刀数据,包含: * borrowStatus: 还刀状态 @@ -449,7 +449,7 @@ def get_waste_knife_recycle_info(self, params: Dict[str, Any]) -> Dict[str, Any] - 还刀人、借刀人 - 刀具信息:品牌、型号、类型、规格 - 时间信息:取刀时间、还刀时间 - + 业务场景: - 废刀回收统计:查看收刀柜中的还刀信息 - 统计报废刀具数量和信息 @@ -459,7 +459,7 @@ def get_waste_knife_recycle_info(self, params: Dict[str, Any]) -> Dict[str, Any] try: # 构建查询参数 query_params = {k: v for k, v in params.items() if v is not None} - + # 调用外部接口 response = self.session.get( f"{self.base_url}/qw/knife/web/from/mes/lend/getLendByStock", @@ -467,10 +467,10 @@ def get_waste_knife_recycle_info(self, params: Dict[str, Any]) -> Dict[str, Any] timeout=10 ) response.raise_for_status() - + # 获取外部接口返回的数据 external_data = response.json() - + # 返回需要的字段 return { "code": external_data.get("code"), @@ -478,7 +478,7 @@ def get_waste_knife_recycle_info(self, params: Dict[str, Any]) -> Dict[str, Any] "success": external_data.get("success"), "data": external_data.get("data") } - + except requests.exceptions.RequestException as e: logger.error(f"获取废刀回收统计信息失败: {e}") return { @@ -488,25 +488,803 @@ def get_waste_knife_recycle_info(self, params: Dict[str, Any]) -> Dict[str, Any] "data": None } + # ==================== 排行接口方法 ==================== + def get_ranking_data(self, endpoint: str, params: Dict[str, Any]) -> Dict[str, Any]: + """通用排行数据获取方法""" + try: + # 过滤None值参数 + query_params = {k: v for k, v in params.items() if v is not None} + + logger.info(f"调用外部API: {self.base_url}{endpoint}") + logger.info(f"请求参数: {query_params}") + + response = self.session.get( + f"{self.base_url}{endpoint}", + params=query_params, + timeout=10 + ) + response.raise_for_status() + + result = response.json() + logger.info(f"外部API响应: {result}") + return result + + except requests.exceptions.RequestException as e: + logger.error(f"获取排行数据失败 {endpoint}: {e}") + logger.info("由于外部API连接失败,返回模拟数据") + # 返回模拟数据用于测试 + return self._get_mock_ranking_data(endpoint, params) + + def _get_mock_ranking_data(self, endpoint: str, params: Dict[str, Any]) -> Dict[str, Any]: + """ + 生成模拟排行数据用于测试 + 当外部API不可用时提供基础数据 + """ + # 基础响应结构 + mock_data = { + "code": 200, + "msg": "模拟数据(外部API不可用)", + "success": True, + "data": { + "titleList": [], + "dataList": [] + } + } + + # 根据不同的端点提供不同的模拟数据 + if "chartsDeviceSanking" in endpoint: + # 设备用刀排行模拟数据 + mock_data["data"]["titleList"] = ["设备A", "设备B", "设备C", "设备D", "设备E"] + mock_data["data"]["dataList"] = [120, 95, 80, 65, 50] + elif "charts@tuttenbanking" in endpoint: + # 刀具型号排行模拟数据 + mock_data["data"]["titleList"] = ["型号A", "型号B", "型号C", "型号D", "型号E"] + mock_data["data"]["dataList"] = [1500, 1200, 900, 750, 600] + elif "chartslandHunting" in endpoint: + # 员工领刀排行模拟数据 + mock_data["data"]["titleList"] = ["员工A", "员工B", "员工C", "员工D", "员工E"] + mock_data["data"]["dataList"] = [45, 38, 32, 28, 25] + elif "dhatsErrorBorrow" in endpoint: + # 异常还刀排行模拟数据 + mock_data["data"]["titleList"] = ["异常类型A", "异常类型B", "异常类型C", "异常类型D"] + mock_data["data"]["dataList"] = [12, 8, 5, 3] + else: + # 默认模拟数据 + mock_data["data"]["titleList"] = ["项目1", "项目2", "项目3", "项目4", "项目5"] + mock_data["data"]["dataList"] = [100, 80, 60, 40, 20] + + return mock_data + # ==================== 排行接口方法 ==================== + # 这些是新增的排行接口,在合并版本中缺失 + + def get_device_ranking(self, params: Dict[str, Any]) -> Dict[str, Any]: + """ + 设备用刀排行 + 接口地址: /qw/knife/web/from/ms/statistics/chartsDeviceSanking + rankingType: 0.批量 1 查错 + """ + try: + # 构建查询参数,过滤掉None值 + query_params = {k: v for k, v in params.items() if v is not None} + + logger.info(f"调用设备用刀排行接口: {self.base_url}/qw/knife/web/from/ms/statistics/chartsDeviceSanking") + logger.info(f"请求参数: {query_params}") + + response = self.session.get( + f"{self.base_url}/qw/knife/web/from/ms/statistics/chartsDeviceSanking", + params=query_params, + timeout=10 + ) + response.raise_for_status() + + result = response.json() + logger.info(f"设备用刀排行响应: {result}") + return result + + except requests.exceptions.RequestException as e: + logger.error(f"获取设备用刀排行失败: {e}") + # 返回模拟数据用于测试 + return { + "code": 200, + "msg": "模拟数据(外部API不可用)", + "success": True, + "data": { + "titleList": ["设备A", "设备B", "设备C", "设备D", "设备E"], + "dataList": [120, 95, 80, 65, 50] + } + } + + def get_knife_model_ranking(self, params: Dict[str, Any]) -> Dict[str, Any]: + """ + 刀具型号排行 + 接口地址: /api/mifc/web/from/me/statistics/charts@tuttenbanking + rankingType: 0:数量 1:金额 + """ + try: + # 构建查询参数,过滤掉None值 + query_params = {k: v for k, v in params.items() if v is not None} + + logger.info(f"调用刀具型号排行接口: {self.base_url}/api/mifc/web/from/me/statistics/charts@tuttenbanking") + logger.info(f"请求参数: {query_params}") + + response = self.session.get( + f"{self.base_url}/api/mifc/web/from/me/statistics/charts@tuttenbanking", + params=query_params, + timeout=10 + ) + response.raise_for_status() + + result = response.json() + logger.info(f"刀具型号排行响应: {result}") + return result + + except requests.exceptions.RequestException as e: + logger.error(f"获取刀具型号排行失败: {e}") + # 返回模拟数据用于测试 + return { + "code": 200, + "msg": "模拟数据(外部API不可用)", + "success": True, + "data": { + "titleList": ["型号A", "型号B", "型号C", "型号D", "型号E"], + "dataList": [1500, 1200, 900, 750, 600] + } + } + + def get_employee_ranking(self, params: Dict[str, Any]) -> Dict[str, Any]: + """ + 员工领刀排行 + 接口地址: /go/kaife/web/from/mss/statistics/chartslandHunting + rankingType: 0:批量下拉量 (根据文档推测含义) + """ + try: + # 构建查询参数,过滤掉None值 + query_params = {k: v for k, v in params.items() if v is not None} + + logger.info(f"调用员工领刀排行接口: {self.base_url}/go/kaife/web/from/mss/statistics/chartslandHunting") + logger.info(f"请求参数: {query_params}") + + response = self.session.get( + f"{self.base_url}/go/kaife/web/from/mss/statistics/chartslandHunting", + params=query_params, + timeout=10 + ) + response.raise_for_status() + + result = response.json() + logger.info(f"员工领刀排行响应: {result}") + return result + + except requests.exceptions.RequestException as e: + logger.error(f"获取员工领刀排行失败: {e}") + # 返回模拟数据用于测试 + return { + "code": 200, + "msg": "模拟数据(外部API不可用)", + "success": True, + "data": { + "titleList": ["员工A", "员工B", "员工C", "员工D", "员工E"], + "dataList": [45, 38, 32, 28, 25] + } + } + + def get_error_return_ranking(self, params: Dict[str, Any]) -> Dict[str, Any]: + """ + 异常还刀排行 + 接口地址: /qw/knife/web/from/news/statsstics/dhatsErrorBorrow + rankingType: 0:批量 1:金额 + """ + try: + # 构建查询参数,过滤掉None值 + query_params = {k: v for k, v in params.items() if v is not None} + + logger.info(f"调用异常还刀排行接口: {self.base_url}/qw/knife/web/from/news/statsstics/dhatsErrorBorrow") + logger.info(f"请求参数: {query_params}") + + response = self.session.get( + f"{self.base_url}/qw/knife/web/from/news/statsstics/dhatsErrorBorrow", + params=query_params, + timeout=10 + ) + response.raise_for_status() + + result = response.json() + logger.info(f"异常还刀排行响应: {result}") + return result + + except requests.exceptions.RequestException as e: + logger.error(f"获取异常还刀排行失败: {e}") + # 返回模拟数据用于测试 + return { + "code": 200, + "msg": "模拟数据(外部API不可用)", + "success": True, + "data": { + "titleList": ["异常类型A", "异常类型B", "异常类型C", "异常类型D"], + "dataList": [12, 8, 5, 3] + } + } + + # ==================== 系统记录相关接口 ==================== + + # 合并自 auditor_record 模块的系统记录接口 + # 日期: 2025-11-15 + # 来源: auditor_record/services/api_client.py + + # 补货记录API客户端 + def get_replenish_records(self, + current: Optional[int] = None, + endTime: Optional[str] = None, + order: Optional[int] = None, + rankingType: Optional[int] = None, + recordStatus: Optional[int] = None, + size: Optional[int] = None, + startTime: Optional[str] = None) -> Dict[str, Any]: + """ + 获取补货记录列表 + + Args: + current: 当前页 + endTime: 结束时间 + order: 顺序 0: 从大到小 1:从小到大 + rankingType: 0: 数量 1: 金额 + recordStatus: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + size: 每页的数量 + startTime: 开始时间 + + Returns: + Dict: API响应结果 + """ + url = urljoin(self.base_url, "/qw/knife/web/from/mes/record/replenishList") + + params = {} + + # 添加可选参数 + if current is not None: + params["current"] = current + if endTime is not None: + params["endTime"] = endTime + if order is not None: + params["order"] = order + if rankingType is not None: + params["rankingType"] = rankingType + if recordStatus is not None: + params["recordStatus"] = recordStatus + if size is not None: + params["size"] = size + if startTime is not None: + params["startTime"] = startTime + + try: + response = self.session.get(url, params=params) + response.raise_for_status() + return response.json() + except requests.RequestException as e: + return { + "code": -1, + "msg": f"请求失败: {str(e)}", + "data": { + "current": 0, + "hitCount": False, + "pages": 0, + "records": [], + "searchCount": False, + "size": 0, + "total": 0 + }, + "success": False + } + + def export_replenish_records(self, + endTime: Optional[str] = None, + order: Optional[int] = None, + rankingType: Optional[int] = None, + recordStatus: Optional[int] = None, + startTime: Optional[str] = None) -> bytes: + """ + 导出补货记录 + + Args: + endTime: 结束时间 + order: 顺序 0: 从大到小 1:从小到大 + rankingType: 0: 数量 1: 金额 + recordStatus: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + startTime: 开始时间 + + Returns: + bytes: 导出的Excel文件内容 + """ + url = urljoin(self.base_url, "/qw/knife/web/from/mes/record/exportReplenishRecord") + + params = {} + + # 添加可选参数 + if endTime: + params["endTime"] = endTime + if order is not None: + params["order"] = order + if rankingType is not None: + params["rankingType"] = rankingType + if recordStatus is not None: + params["recordStatus"] = recordStatus + if startTime: + params["startTime"] = startTime + + try: + response = self.session.get(url, params=params) + response.raise_for_status() + return response.content + except requests.RequestException as e: + raise Exception(f"导出失败: {str(e)}") + + # 领刀记录API客户端 + def get_lend_records(self, + current: int = 1, + size: int = 20, + keyword: Optional[str] = None, + department: Optional[str] = None, + startTime: Optional[str] = None, + endTime: Optional[str] = None, + order: Optional[int] = None, + rankingType: Optional[int] = None, + recordStatus: Optional[int] = None) -> Dict[str, Any]: + """ + 获取领刀记录列表 + + Args: + current: 当前页码 + size: 每页数量 + keyword: 关键字搜索 + department: 部门筛选 + startTime: 开始时间 + endTime: 结束时间 + order: 顺序 0: 从大到小 1:从小到大 + rankingType: 0: 数量 1: 金额 + recordStatus: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + + Returns: + Dict: API响应结果 + """ + url = urljoin(self.base_url, "/qw/knife/web/from/mes/record/lendList") + + params = { + "current": current, + "size": size + } + + # 添加可选参数 + if keyword: + params["keyword"] = keyword + if department: + params["department"] = department + if startTime: + params["startTime"] = startTime + if endTime: + params["endTime"] = endTime + if order is not None: + params["order"] = order + if rankingType is not None: + params["rankingType"] = rankingType + if recordStatus is not None: + params["recordStatus"] = recordStatus + + try: + response = self.session.get(url, params=params) + response.raise_for_status() + return response.json() + except requests.RequestException as e: + return { + "code": -1, + "msg": f"请求失败: {str(e)}", + "data": { + "current": 0, + "hitCount": False, + "pages": 0, + "records": [], + "searchCount": False, + "size": 0, + "total": 0 + }, + "success": False + } + + def export_lend_records(self, + endTime: Optional[str] = None, + order: Optional[int] = None, + rankingType: Optional[int] = None, + recordStatus: Optional[int] = None, + startTime: Optional[str] = None) -> bytes: + """ + 导出领刀记录 + + Args: + endTime: 结束时间 + order: 顺序 0: 从大到小 1:从小到大 + rankingType: 0: 数量 1: 金额 + recordStatus: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + startTime: 开始时间 + + Returns: + bytes: 导出的文件内容 + """ + url = urljoin(self.base_url, "/qw/knife/web/from/mes/record/exportLendRecord") + + params = {} + + # 添加可选参数 + if endTime: + params["endTime"] = endTime + if order is not None: + params["order"] = order + if rankingType is not None: + params["rankingType"] = rankingType + if recordStatus is not None: + params["recordStatus"] = recordStatus + if startTime: + params["startTime"] = startTime + + try: + response = self.session.get(url, params=params) + response.raise_for_status() + return response.content + except requests.RequestException as e: + raise Exception(f"导出失败: {str(e)}") + + # 告警预警API客户端 + def list_alarm_warning(self, + locSurplus: Optional[int] = None, + alarmLevel: Optional[int] = None, + deviceType: Optional[str] = None, + cabinetCode: Optional[str] = None, + brandName: Optional[str] = None, + handleStatus: Optional[int] = None, + current: Optional[int] = None, + size: Optional[int] = None) -> Dict[str, Any]: + """ + 获取告警预警列表 + + Args: + locSurplus: 货道 + alarmLevel: 预警等级 + deviceType: 设备类型 + cabinetCode: 刀柜编码 + brandName: 品牌名称 + handleStatus: 处理状态 + current: 当前页 + size: 每页数量 + + Returns: + Dict: API响应结果 + """ + url = urljoin(self.base_url, "/qw/knife/web/from/mes/alarm/warning/list") + + params = {} + + # 添加可选参数 + if locSurplus is not None: + params["locSurplus"] = locSurplus + if alarmLevel is not None: + params["alarmLevel"] = alarmLevel + if deviceType is not None: + params["deviceType"] = deviceType + if cabinetCode is not None: + params["cabinetCode"] = cabinetCode + if brandName is not None: + params["brandName"] = brandName + if handleStatus is not None: + params["handleStatus"] = handleStatus + if current is not None: + params["current"] = current + if size is not None: + params["size"] = size + + try: + response = self.session.get(url, params=params) + response.raise_for_status() + return response.json() + except requests.RequestException as e: + return { + "code": -1, + "msg": f"请求失败: {str(e)}", + "data": { + "current": 0, + "hitCount": False, + "pages": 0, + "records": [], + "searchCount": False, + "size": 0, + "total": 0 + }, + "success": False + } + + def get_alarm_statistics(self) -> Dict[str, Any]: + """ + 获取告警统计信息 + + Returns: + Dict: API响应结果 + """ + url = urljoin(self.base_url, "/qw/knife/web/from/mes/alarm/warning/statistics") + + try: + response = self.session.get(url) + response.raise_for_status() + return response.json() + except requests.RequestException as e: + return { + "code": -1, + "msg": f"请求失败: {str(e)}", + "data": { + "level1Count": 0, + "level2Count": 0, + "level3Count": 0, + "unhandledCount": 0 + }, + "success": False + } + + def update_alarm_threshold(self, + locSurplus: int, + alarmThreshold: int) -> Dict[str, Any]: + """ + 更新告警阈值 + + Args: + locSurplus: 货道 + alarmThreshold: 告警阈值 + + Returns: + Dict: API响应结果 + """ + url = urljoin(self.base_url, "/qw/knife/web/from/mes/alarm/warning/threshold") + + data = { + "locSurplus": locSurplus, + "alarmThreshold": alarmThreshold + } + + try: + response = self.session.post(url, json=data) + response.raise_for_status() + return response.json() + except requests.RequestException as e: + return { + "code": -1, + "msg": f"请求失败: {str(e)}", + "data": None, + "success": False + } + + def handle_alarm_warning(self, + id: int, + handleStatus: int, + handleRemark: Optional[str] = None) -> Dict[str, Any]: + """ + 处理告警预警 + + Args: + id: 告警ID + handleStatus: 处理状态 (0: 未处理, 1: 已处理, 2: 已忽略) + handleRemark: 处理备注 + + Returns: + Dict: API响应结果 + """ + url = urljoin(self.base_url, f"/qw/knife/web/from/mes/alarm/warning/{id}/handle") + + data = { + "handleStatus": handleStatus + } + + if handleRemark is not None: + data["handleRemark"] = handleRemark + + try: + response = self.session.post(url, json=data) + response.raise_for_status() + return response.json() + except requests.RequestException as e: + return { + "code": -1, + "msg": f"请求失败: {str(e)}", + "data": None, + "success": False + } + + def batch_handle_alarm_warning(self, + ids: List[int], + handleStatus: int, + handleRemark: Optional[str] = None) -> Dict[str, Any]: + """ + 批量处理告警预警 + + Args: + ids: 告警ID列表 + handleStatus: 处理状态 (0: 未处理, 1: 已处理, 2: 已忽略) + handleRemark: 处理备注 + + Returns: + Dict: API响应结果 + """ + url = urljoin(self.base_url, "/qw/knife/web/from/mes/alarm/warning/batch/handle") + + data = { + "ids": ids, + "handleStatus": handleStatus + } + + if handleRemark is not None: + data["handleRemark"] = handleRemark + + try: + response = self.session.post(url, json=data) + response.raise_for_status() + return response.json() + except requests.RequestException as e: + return { + "code": -1, + "msg": f"请求失败: {str(e)}", + "data": None, + "success": False + } + + def export_alarm_warning(self, + locSurplus: Optional[int] = None, + alarmLevel: Optional[int] = None, + deviceType: Optional[str] = None, + cabinetCode: Optional[str] = None, + brandName: Optional[str] = None, + handleStatus: Optional[int] = None) -> bytes: + """ + 导出告警预警 + + Args: + locSurplus: 货道 + alarmLevel: 预警等级 + deviceType: 设备类型 + cabinetCode: 刀柜编码 + brandName: 品牌名称 + handleStatus: 处理状态 + + Returns: + bytes: 导出的文件内容 + """ + url = urljoin(self.base_url, "/qw/knife/web/from/mes/alarm/warning/export") + + params = {} + + # 添加可选参数 + if locSurplus is not None: + params["locSurplus"] = locSurplus + if alarmLevel is not None: + params["alarmLevel"] = alarmLevel + if deviceType is not None: + params["deviceType"] = deviceType + if cabinetCode is not None: + params["cabinetCode"] = cabinetCode + if brandName is not None: + params["brandName"] = brandName + if handleStatus is not None: + params["handleStatus"] = handleStatus + + try: + response = self.session.get(url, params=params) + response.raise_for_status() + return response.content + except requests.RequestException as e: + raise Exception(f"导出失败: {str(e)}") + + # 公共暂存记录API客户端 + def get_storage_records(self, + current: Optional[int] = None, + endTime: Optional[str] = None, + order: Optional[int] = None, + rankingType: Optional[int] = None, + recordStatus: Optional[int] = None, + size: Optional[int] = None, + startTime: Optional[str] = None) -> Dict[str, Any]: + """ + 获取公共暂存记录列表 + + Args: + current: 当前页 + endTime: 结束时间 + order: 顺序 0: 从大到小 1:从小到大 + rankingType: 0: 数量 1: 金额 + recordStatus: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + size: 每页的数量 + startTime: 开始时间 + + Returns: + Dict: API响应结果 + """ + url = urljoin(self.base_url, "/qw/knife/web/from/mes/record/storageList") + + params = {} + + # 添加可选参数 + if current is not None: + params["current"] = current + if endTime is not None: + params["endTime"] = endTime + if order is not None: + params["order"] = order + if rankingType is not None: + params["rankingType"] = rankingType + if recordStatus is not None: + params["recordStatus"] = recordStatus + if size is not None: + params["size"] = size + if startTime is not None: + params["startTime"] = startTime + + try: + response = self.session.get(url, params=params) + response.raise_for_status() + return response.json() + except requests.RequestException as e: + return { + "code": -1, + "msg": f"请求失败: {str(e)}", + "data": { + "current": 0, + "hitCount": False, + "pages": 0, + "records": [], + "searchCount": False, + "size": 0, + "total": 0 + }, + "success": False + } + + def export_storage_records(self, + endTime: Optional[str] = None, + order: Optional[int] = None, + rankingType: Optional[int] = None, + recordStatus: Optional[int] = None, + startTime: Optional[str] = None) -> bytes: + """ + 导出公共暂存记录 + + Args: + endTime: 结束时间 + order: 顺序 0: 从大到小 1:从小到大 + rankingType: 0: 数量 1: 金额 + recordStatus: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + startTime: 开始时间 + + Returns: + bytes: 导出的文件内容 + """ + url = urljoin(self.base_url, "/qw/knife/web/from/mes/record/exportStorageRecord") + + params = {} + + # 添加可选参数 + if endTime: + params["endTime"] = endTime + if order is not None: + params["order"] = order + if rankingType is not None: + params["rankingType"] = rankingType + if recordStatus is not None: + params["recordStatus"] = recordStatus + if startTime: + params["startTime"] = startTime + + try: + response = self.session.get(url, params=params) + response.raise_for_status() + return response.content + except requests.RequestException as e: + raise Exception(f"导出失败: {str(e)}") + -# 初始化API客户端 -# 使用配置文件的设置 -try: - from config.config import settings - - # 优先使用环境变量ORIGINAL_API_KEY - # 其次尝试从token.txt文件读取 - api_key = settings.ORIGINAL_API_KEY if settings.ORIGINAL_API_KEY else None - - original_api_client = OriginalAPIClient( - base_url=settings.ORIGINAL_API_BASE_URL, - api_key=api_key, - token_file=settings.TOKEN_FILE_PATH - ) -except ImportError: - # 如果无法导入配置,使用默认配置 - original_api_client = OriginalAPIClient( - base_url="http://39.98.115.114:8983", - api_key=None, - token_file="token.txt" - ) \ No newline at end of file +# 初始化API客户端 - 使用 master 分支的配置,保留 token_file 支持 +original_api_client = OriginalAPIClient( + base_url="http://39.98.115.114:8983", + api_key=None, + token_file="token.txt" +) \ No newline at end of file diff --git a/auditor/services/api_client_auditor.py b/auditor/services/api_client_auditor.py deleted file mode 100644 index 6393414..0000000 --- a/auditor/services/api_client_auditor.py +++ /dev/null @@ -1,384 +0,0 @@ -import requests -from typing import Dict, Any, Optional, List -from urllib.parse import urljoin -import json - - -class LendRecordAPIClient: - """ - 领刀记录API客户端 - """ - - def __init__(self, base_url: str = "http://localhost:8080"): - """ - 初始化API客户端 - - Args: - base_url: API基础URL - """ - self.base_url = base_url - self.session = requests.Session() - - def get_lend_records(self, - page: int = 1, - page_size: int = 20, - keyword: Optional[str] = None, - department: Optional[str] = None, - start_date: Optional[str] = None, - end_date: Optional[str] = None, - order: Optional[int] = None, - rankingType: Optional[int] = None, - recordStatus: Optional[int] = None) -> Dict[str, Any]: - """ - 获取领刀记录列表 - - Args: - page: 页码 - page_size: 每页数量 - keyword: 关键字搜索 - department: 部门筛选 - start_date: 开始时间 - end_date: 结束时间 - order: 顺序 0: 从大到小 1:从小到大 - rankingType: 0: 数量 1: 金额 - recordStatus: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 - - Returns: - Dict: API响应结果 - """ - url = urljoin(self.base_url, "/qw/knife/web/from/mes/record/lendList") - - params = { - "current": page, - "size": page_size - } - - # 添加可选参数 - if keyword: - params["keyword"] = keyword - if department: - params["department"] = department - if start_date: - params["startTime"] = start_date - if end_date: - params["endTime"] = end_date - if order is not None: - params["order"] = order - if rankingType is not None: - params["rankingType"] = rankingType - if recordStatus is not None: - params["recordStatus"] = recordStatus - - try: - response = self.session.get(url, params=params) - response.raise_for_status() - return response.json() - except requests.RequestException as e: - return { - "code": -1, - "msg": f"请求失败: {str(e)}", - "data": { - "current": 0, - "hitCount": False, - "pages": 0, - "records": [], - "searchCount": False, - "size": 0, - "total": 0 - }, - "success": False - } - - def export_lend_records(self, - endTime: Optional[str] = None, - order: Optional[int] = None, - rankingType: Optional[int] = None, - recordStatus: Optional[int] = None, - startTime: Optional[str] = None) -> bytes: - """ - 导出领刀记录 - - Args: - endTime: 结束时间 - order: 顺序 0: 从大到小 1:从小到大 - rankingType: 0: 数量 1: 金额 - recordStatus: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 - startTime: 开始时间 - - Returns: - bytes: 导出的文件内容 - """ - url = urljoin(self.base_url, "/qw/knife/web/from/mes/record/exportLendRecord") - - params = {} - - # 添加可选参数 - if endTime: - params["endTime"] = endTime - if order is not None: - params["order"] = order - if rankingType is not None: - params["rankingType"] = rankingType - if recordStatus is not None: - params["recordStatus"] = recordStatus - if startTime: - params["startTime"] = startTime - - try: - response = self.session.get(url, params=params) - response.raise_for_status() - return response.content - except requests.RequestException as e: - raise Exception(f"导出失败: {str(e)}") - - def restock_cabinet(self, - cabinetCode: Optional[str] = None, - itemDtoList: Optional[List[Dict[str, Any]]] = None, - replenishDto: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: - """ - 刀柜补货 - - Args: - replenishDto: replenishDto 对象 - cabinetCode: 刀柜编码 - itemDtoList: 补货信息列表 - - Returns: - Dict: API响应结果 - """ - url = urljoin(self.base_url, "/qw/knife/app/from/mes/cabinet/changeBan") - - # 构造请求数据 - data = {} - if replenishDto is not None: - data["replenishDto"] = replenishDto - if cabinetCode is not None: - data["cabinetCode"] = cabinetCode - if itemDtoList is not None: - data["itemDtoList"] = itemDtoList - - try: - response = self.session.post(url, json=data) - response.raise_for_status() - return response.json() - except requests.RequestException as e: - return { - "code": -1, - "msg": f"补货失败: {str(e)}", - "data": None, - "success": False, - "dataSuccess": False - } - - def get_replenish_records(self, - current: Optional[int] = None, - endTime: Optional[str] = None, - order: Optional[int] = None, - rankingType: Optional[int] = None, - recordStatus: Optional[int] = None, - size: Optional[int] = None, - startTime: Optional[str] = None) -> Dict[str, Any]: - """ - 获取补货记录列表 - - Args: - current: 当前页 - endTime: 结束时间 - order: 顺序 0: 从大到小 1:从小到大 - rankingType: 0: 数量 1: 金额 - recordStatus: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 - size: 每页的数量 - startTime: 开始时间 - - Returns: - Dict: API响应结果 - """ - url = urljoin(self.base_url, "/qw/knife/web/from/mes/record/replenishList") - - params = {} - - # 添加可选参数 - if current is not None: - params["current"] = current - if endTime is not None: - params["endTime"] = endTime - if order is not None: - params["order"] = order - if rankingType is not None: - params["rankingType"] = rankingType - if recordStatus is not None: - params["recordStatus"] = recordStatus - if size is not None: - params["size"] = size - if startTime is not None: - params["startTime"] = startTime - - try: - response = self.session.get(url, params=params) - response.raise_for_status() - return response.json() - except requests.RequestException as e: - return { - "code": -1, - "msg": f"请求失败: {str(e)}", - "data": { - "current": 0, - "hitCount": False, - "pages": 0, - "records": [], - "searchCount": False, - "size": 0, - "total": 0 - }, - "success": False - } - - def get_storage_records(self, - current: Optional[int] = None, - endTime: Optional[str] = None, - order: Optional[int] = None, - rankingType: Optional[int] = None, - recordStatus: Optional[int] = None, - size: Optional[int] = None, - startTime: Optional[str] = None) -> Dict[str, Any]: - """ - 获取公共暂存记录列表 - - Args: - current: 当前页 - endTime: 结束时间 - order: 顺序 0: 从大到小 1:从小到大 - rankingType: 0: 数量 1: 金额 - recordStatus: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 - size: 每页的数量 - startTime: 开始时间 - - Returns: - Dict: API响应结果 - """ - url = urljoin(self.base_url, "/qw/knife/web/from/mes/record/storageList") - - params = {} - - # 添加可选参数 - if current is not None: - params["current"] = current - if endTime is not None: - params["endTime"] = endTime - if order is not None: - params["order"] = order - if rankingType is not None: - params["rankingType"] = rankingType - if recordStatus is not None: - params["recordStatus"] = recordStatus - if size is not None: - params["size"] = size - if startTime is not None: - params["startTime"] = startTime - - try: - response = self.session.get(url, params=params) - response.raise_for_status() - return response.json() - except requests.RequestException as e: - return { - "code": -1, - "msg": f"请求失败: {str(e)}", - "data": { - "current": 0, - "hitCount": False, - "pages": 0, - "records": [], - "searchCount": False, - "size": 0, - "total": 0 - }, - "success": False - } - - def get_personal_storage(self, cabinetCode: Optional[str] = None) -> Dict[str, Any]: - """ - 获取个人暂存柜信息 - - Args: - cabinetCode: 刀柜编码 - - Returns: - Dict: API响应结果 - """ - url = urljoin(self.base_url, "/qw/knife/app/from/mes/cabinet/personalStorage") - - params = {} - if cabinetCode is not None: - params["cabinetCode"] = cabinetCode - - try: - response = self.session.get(url, params=params) - response.raise_for_status() - return response.json() - except requests.RequestException as e: - return { - "code": -1, - "msg": f"请求失败: {str(e)}", - "data": None, - "success": False - } - - def set_make_alarm(self, cabinetCode: Optional[str] = None, alarmValue: Optional[int] = None) -> Dict[str, Any]: - """ - 设置取刀柜告警值 - - Args: - cabinetCode: 刀柜编码 - alarmValue: 告警值 - - Returns: - Dict: API响应结果 - """ - url = urljoin(self.base_url, "/qw/knife/web/from/mes/cabinet/makeAlarm") - - params = {} - if cabinetCode is not None: - params["cabinetCode"] = cabinetCode - if alarmValue is not None: - params["alarmValue"] = alarmValue - - try: - response = self.session.get(url, params=params) - response.raise_for_status() - return response.json() - except requests.RequestException as e: - return { - "code": -1, - "msg": f"请求失败: {str(e)}", - "data": False, - "success": False - } - - def get_make_alarm(self, cabinetCode: Optional[str] = None) -> Dict[str, Any]: - """ - 获取取刀柜告警值 - - Args: - cabinetCode: 刀柜编码 - - Returns: - Dict: API响应结果 - """ - url = urljoin(self.base_url, "/qw/knife/web/from/mes/cabinet/getMakeAlarm") - - params = {} - if cabinetCode is not None: - params["cabinetCode"] = cabinetCode - - try: - response = self.session.get(url, params=params) - response.raise_for_status() - return response.json() - except requests.RequestException as e: - return { - "code": -1, - "msg": f"请求失败: {str(e)}", - "data": None, - "success": False - } \ No newline at end of file diff --git "a/record\351\203\250\345\210\206\346\216\245\345\217\243.txt" "b/record\351\203\250\345\210\206\346\216\245\345\217\243.txt" index b58cbd4..7709ff5 100644 --- "a/record\351\203\250\345\210\206\346\216\245\345\217\243.txt" +++ "b/record\351\203\250\345\210\206\346\216\245\345\217\243.txt" @@ -1,19 +1,26 @@ # 接口地址说明: -# 1. 获取领刀记录列表: GET /teamleader_lend_record/list -# 2. 导出领刀记录: GET /teamleader_lend_record/export -# 3. 刀柜补货: POST /teamleader_lend_record/restock -# 4. 获取补货记录列表: GET /teamleader_lend_record/replenish_list -# 5. 获取公共暂存记录列表: GET /teamleader_lend_record/storage_list -# 6. 获取个人暂存柜信息: GET /teamleader_lend_record/personal_storage -# 7. 设置取刀柜告警值: GET /teamleader_lend_record/make_alarm -# 8. 获取取刀柜告警值: GET /teamleader_lend_record/get_make_alarm - +# 1. 获取补货记录列表: GET /auditor_record/replenish_list +# 2. 导出补货记录: GET /auditor_record/export_replenish +# 3. 获取领刀记录列表: GET /auditor_record/list +# 4. 导出领刀记录: GET /auditor_record/export +# 5. 获取公共暂存记录列表: GET /auditor_record/storage_list +# 6. 导出公共暂存记录: GET /auditor_record/export_storage +# 7. 获取告警预警列表: GET /auditor_record/alarm_list +# 8. 获取告警统计信息: GET /auditor_record/alarm_statistics +# 9. 更新告警阈值: POST /auditor_record/alarm_threshold +# 10. 处理告警预警: POST /auditor_record/handle_alarm/{alarm_id} +# 11. 批量处理告警预警: POST /auditor_record/batch_handle_alarm +# 12. 导出告警预警: GET /auditor_record/export_alarm # 接口地址说明: -# 1. 获取领刀记录列表: GET /auditor_lend_record/list -# 2. 导出领刀记录: GET /auditor_lend_record/export -# 3. 刀柜补货: POST /auditor_lend_record/restock -# 4. 获取补货记录列表: GET /auditor_lend_record/replenish_list -# 5. 获取公共暂存记录列表: GET /auditor_lend_record/storage_list -# 6. 获取个人暂存柜信息: GET /auditor_lend_record/personal_storage -# 7. 设置取刀柜告警值: GET /auditor_lend_record/make_alarm -# 8. 获取取刀柜告警值: GET /auditor_lend_record/get_make_alarm \ No newline at end of file +# 1. 获取补货记录列表: GET /teamleader_record/replenish_list +# 2. 导出补货记录: GET /teamleader_record/export_replenish +# 3. 获取领刀记录列表: GET /teamleader_record/list +# 4. 导出领刀记录: GET /teamleader_record/export +# 5. 获取公共暂存记录列表: GET /teamleader_record/storage_list +# 6. 导出公共暂存记录: GET /teamleader_record/export_storage +# 7. 获取告警预警列表: GET /teamleader_record/alarm_list +# 8. 获取告警统计信息: GET /teamleader_record/alarm_statistics +# 9. 更新告警阈值: POST /teamleader_record/alarm_threshold +# 10. 处理告警预警: POST /teamleader_record/handle_alarm/{alarm_id} +# 11. 批量处理告警预警: POST /teamleader_record/batch_handle_alarm +# 12. 导出告警预警: GET /teamleader_record/export_alarm \ No newline at end of file diff --git a/routers/auditor_router.py b/routers/auditor_router.py index b591672..11b9592 100644 --- a/routers/auditor_router.py +++ b/routers/auditor_router.py @@ -1,33 +1,70 @@ -from fastapi import APIRouter, HTTPException, Query -from typing import Optional +from fastapi import APIRouter, HTTPException, Query, Response, Body +from typing import Optional, List +import logging +import traceback +import json + +# 配置日志 +logger = logging.getLogger(__name__) # 导入所需的模块 from auditor.services.api_client import original_api_client as api_client from auditor.schemas.data_schemas import ( - StorageStatisticsResponse, - ChartsResponse, + StorageStatisticsResponse, + ChartsResponse, TotalStockResponse, - WasteKnifeRecycleResponse + WasteKnifeRecycleResponse, + DeviceRankingResponse, + KnifeModelRankingResponse, + EmployeeRankingResponse, + ErrorReturnRankingResponse, + # 系统记录相关模型 + LendRecordResponse, + ExportLendRecordRequest, + ReplenishRecordResponse, + StorageRecordModelResponse, + AlarmWarningResponse, + AlarmStatisticsResponse, + ThresholdSettingRequest, + ExportReplenishRecordRequest, + ExportStorageRecordRequest ) -router = APIRouter() +router = APIRouter( + prefix="/auditor", + tags=["Auditor-审计员"], + responses={404: {"description": "Not found"}} +) +# 测试接口 +@router.get("/test", tags=["测试"]) +async def test_connection(): + """测试路由连接""" + return { + "message": "审计员接口连接正常", + "status": "success", + "timestamp": "2024-11-11" + } + +# ==================== 出入库统计接口 ==================== @router.get("/storage-statistics", response_model=StorageStatisticsResponse, tags=["出入库统计"]) async def get_storage_statistics( - current: Optional[int] = Query(1, ge=1, description="当前页"), - size: Optional[int] = Query(10, ge=1, le=100, description="每页数量"), - start_time: Optional[str] = Query(None, alias="startTime", description="开始时间(YYYY-MM-DD HH:mm:ss)"), - end_time: Optional[str] = Query(None, alias="endTime", description="结束时间(YYYY-MM-DD HH:mm:ss)"), - record_status: Optional[int] = Query(None, alias="recordStatus", description="记录状态:0-取刀,1-还刀,2-收刀,3-暂存,4-完成,5-违规还刀"), - ranking_type: Optional[int] = Query(None, alias="rankingType", description="排名类型:0-按数量排序,1-按金额排序"), - order: Optional[int] = Query(None, description="排序顺序:0-从大到小(降序),1-从小到大(升序)") + current: Optional[int] = Query(1, ge=1, description="当前页"), + size: Optional[int] = Query(10, ge=1, le=100, description="每页数量"), + start_time: Optional[str] = Query(None, alias="startTime", description="开始时间(YYYY-MM-DD HH:mm:ss)"), + end_time: Optional[str] = Query(None, alias="endTime", description="结束时间(YYYY-MM-DD HH:mm:ss)"), + record_status: Optional[int] = Query(None, alias="recordStatus", + description="记录状态:0-取刀,1-还刀,2-收刀,3-暂存,4-完成,5-违规还刀"), + ranking_type: Optional[int] = Query(None, alias="rankingType", + description="排名类型:0-按数量排序,1-按金额排序"), + order: Optional[int] = Query(None, description="排序顺序:0-从大到小(降序),1-从小到大(升序)") ): """ 获取出入库统计数据 - + 功能:查询刀具的出入库记录统计信息 - + 参数说明: - current: 当前页码,默认1 - size: 每页数量,默认10 @@ -46,7 +83,7 @@ async def get_storage_statistics( - order: 排序顺序 * 0: 从大到小(降序) * 1: 从小到大(升序) - + 返回数据: - code: 状态码 - msg: 返回消息 @@ -69,30 +106,34 @@ async def get_storage_statistics( "rankingType": ranking_type, "order": order } - + # 调用API客户端方法获取数据 result = api_client.get_storage_statistics(params) - + return result - + except Exception as e: + logger.error(f"获取出入库统计数据失败: {str(e)}") + logger.error(traceback.format_exc()) raise HTTPException(status_code=500, detail=f"获取出入库统计数据失败: {str(e)}") @router.get("/export-stock-record", response_model=StorageStatisticsResponse, tags=["出入库统计"]) async def export_stock_record( - start_time: Optional[str] = Query(None, alias="startTime", description="开始时间(YYYY-MM-DD HH:mm:ss)"), - end_time: Optional[str] = Query(None, alias="endTime", description="结束时间(YYYY-MM-DD HH:mm:ss)"), - record_status: Optional[int] = Query(None, alias="recordStatus", description="记录状态:0-取刀,1-还刀,2-收刀,3-暂存,4-完成,5-违规还刀"), - ranking_type: Optional[int] = Query(None, alias="rankingType", description="排名类型:0-按数量排序,1-按金额排序"), - order: Optional[int] = Query(None, description="排序顺序:0-从大到小(降序),1-从小到大(升序)") + start_time: Optional[str] = Query(None, alias="startTime", description="开始时间(YYYY-MM-DD HH:mm:ss)"), + end_time: Optional[str] = Query(None, alias="endTime", description="结束时间(YYYY-MM-DD HH:mm:ss)"), + record_status: Optional[int] = Query(None, alias="recordStatus", + description="记录状态:0-取刀,1-还刀,2-收刀,3-暂存,4-完成,5-违规还刀"), + ranking_type: Optional[int] = Query(None, alias="rankingType", + description="排名类型:0-按数量排序,1-按金额排序"), + order: Optional[int] = Query(None, description="排序顺序:0-从大到小(降序),1-从小到大(升序)") ): """ 导出刀具耗材数据(出入库记录) - + 功能:导出刀具的出入库记录数据,返回JSON格式 封装外部接口:/qw/knife/web/from/mes/record/exportStockRecord - + 参数说明: - startTime: 开始时间 - endTime: 结束时间 @@ -109,7 +150,7 @@ async def export_stock_record( - order: 排序顺序 * 0: 从大到小(降序) * 1: 从小到大(升序) - + 返回数据: - code: 状态码 - msg: 返回消息 @@ -153,27 +194,30 @@ async def export_stock_record( "rankingType": ranking_type, "order": order } - + # 调用API客户端方法获取导出数据 result = api_client.export_stock_record(params) - + return result - + except Exception as e: + logger.error(f"导出出入库记录失败: {str(e)}") + logger.error(traceback.format_exc()) raise HTTPException(status_code=500, detail=f"导出出入库记录失败: {str(e)}") +# ==================== 统计图表接口 ==================== @router.get("/charts-lend-by-year", response_model=ChartsResponse, tags=["统计图表"]) async def get_charts_lend_by_year(): """ 获取全年取刀数量统计 - + 功能:查询全年(12个月)的取刀数量统计数据 封装外部接口:/qw/knife/web/from/mes/statistics/chartsLendByYear - + 请求参数: - 无 - + 返回数据: - code: 状态码 - msg: 返回消息 @@ -181,12 +225,12 @@ async def get_charts_lend_by_year(): - data: 统计数据 * titleList: 月份标题列表 ["1月", "2月", ..., "12月"] * dataList: 对应的取刀数量列表 [150, 200, 180, ...] - + 使用场景: - 显示全年取刀数量趋势图 - 查找取刀高峰月份(前端通过 Math.max(dataList) 计算) - 对比各月取刀数量 - + 示例响应: ```json { @@ -205,6 +249,8 @@ async def get_charts_lend_by_year(): result = api_client.get_charts_lend_by_year() return result except Exception as e: + logger.error(f"获取全年取刀数量统计失败: {str(e)}") + logger.error(traceback.format_exc()) raise HTTPException(status_code=500, detail=f"获取全年取刀数量统计失败: {str(e)}") @@ -212,13 +258,13 @@ async def get_charts_lend_by_year(): async def get_charts_lend_price_by_year(): """ 获取全年取刀金额统计 - + 功能:查询全年(12个月)的取刀金额统计数据 封装外部接口:/qw/knife/web/from/mes/statistics/chartsLendPriceByYear - + 请求参数: - 无 - + 返回数据: - code: 状态码 - msg: 返回消息 @@ -226,13 +272,13 @@ async def get_charts_lend_price_by_year(): - data: 统计数据 * titleList: 月份标题列表 ["1月", "2月", ..., "12月"] * dataList: 对应的取刀金额列表 [15000.50, 20000.00, ...] - + 使用场景: - 显示全年取刀金额趋势图 - 查找金额高峰月份(前端通过 Math.max(dataList) 计算) - 对比各月取刀成本 - 计算全年总金额(前端通过 dataList.reduce((a,b)=>a+b) 计算) - + 示例响应: ```json { @@ -251,6 +297,8 @@ async def get_charts_lend_price_by_year(): result = api_client.get_charts_lend_price_by_year() return result except Exception as e: + logger.error(f"获取全年取刀金额统计失败: {str(e)}") + logger.error(traceback.format_exc()) raise HTTPException(status_code=500, detail=f"获取全年取刀金额统计失败: {str(e)}") @@ -258,13 +306,13 @@ async def get_charts_lend_price_by_year(): async def get_charts_accumulated(): """ 获取刀具消耗统计 - + 功能:查询刀具的消耗统计数据,支持按刀具类型或统计指标展示 封装外部接口:/qw/knife/web/from/mes/statistics/chartsAccumulated - + 请求参数: - 无 - + 返回数据: - code: 状态码 - msg: 返回消息 @@ -272,13 +320,13 @@ async def get_charts_accumulated(): - data: 统计数据 * titleList: 统计项标题列表(刀具类型或统计指标) * dataList: 对应的数据列表 - + 数据展示方式: - + 方式1 - 按刀具类型统计: - titleList 包含:车刀片、铣刀、钻头、镑刀等刀具类型 - dataList 包含:各类刀具的累计消耗数量或使用次数 - + 方式2 - 按统计指标展示: - titleList 包含: * 累计使用次数:刀具被使用的总次数 @@ -287,13 +335,13 @@ async def get_charts_accumulated(): * 更换次数:刀具被更换的次数 * 使用效率:刀具使用效率百分比 - dataList 包含:各指标对应的数值 - + 使用场景: - 显示刀具消耗情况(按类型分类) - 统计刀具使用效率(按指标分类) - 分析刀具消耗趋势 - 为刀具采购提供数据支持 - + 示例响应 1 - 按刀具类型统计: ```json { @@ -306,7 +354,7 @@ async def get_charts_accumulated(): } } ``` - + 示例响应 2 - 按统计指标展示: ```json { @@ -325,7 +373,7 @@ async def get_charts_accumulated(): } } ``` - + 注意:请先调用接口查看实际返回的数据结构,以确定 titleList 和 dataList 的具体内容和展示方式。 """ try: @@ -333,25 +381,28 @@ async def get_charts_accumulated(): result = api_client.get_charts_accumulated() return result except Exception as e: + logger.error(f"获取刀具消耗统计失败: {str(e)}") + logger.error(traceback.format_exc()) raise HTTPException(status_code=500, detail=f"获取刀具消耗统计失败: {str(e)}") +# ==================== 总库存统计接口 ==================== @router.get("/total-stock", response_model=TotalStockResponse, tags=["总库存统计"]) async def get_total_stock( - statistics_type: Optional[str] = Query(None, alias="statisticsType", description="统计类型"), - brand_name: Optional[str] = Query(None, alias="brandName", description="品牌名称"), - cabinet_code: Optional[str] = Query(None, alias="cabinetCode", description="刀柜编码"), - cutter_type: Optional[str] = Query(None, alias="cutterType", description="刀具类型"), - stock_status: Optional[int] = Query(None, alias="stockStatus", description="库位状态"), - current: Optional[int] = Query(1, ge=1, description="当前页"), - size: Optional[int] = Query(10, ge=1, le=100, description="每页数量") + statistics_type: Optional[str] = Query(None, alias="statisticsType", description="统计类型"), + brand_name: Optional[str] = Query(None, alias="brandName", description="品牌名称"), + cabinet_code: Optional[str] = Query(None, alias="cabinetCode", description="刀柜编码"), + cutter_type: Optional[str] = Query(None, alias="cutterType", description="刀具类型"), + stock_status: Optional[int] = Query(None, alias="stockStatus", description="库位状态"), + current: Optional[int] = Query(1, ge=1, description="当前页"), + size: Optional[int] = Query(10, ge=1, le=100, description="每页数量") ): """ 获取总库存统计列表(支持搜索和刷新) - + 功能:查询刀柜库存统计数据,支持多条件搜索和列表刷新 封装外部接口:/qw/knife/web/from/mes/cabinetStock/stockLocTakeInfoList - + 请求参数: - statisticsType: 统计类型(可选) - brandName: 品牌名称(可选) @@ -360,7 +411,7 @@ async def get_total_stock( - stockStatus: 库位状态(可选) - current: 当前页,默认1 - size: 每页数量,默认10 - + 返回数据: - code: 状态码 - msg: 返回消息 @@ -382,22 +433,22 @@ async def get_total_stock( * warehouseInTime: 入库时间 * updateTime: 更新时间 * 及其他字段... - + 使用场景: 1. **搜索功能**:根据统计类型、品牌名称、刀柜编码、刀具类型、库位状态进行查询 2. **刷新功能**:不带搜索条件,获取所有库存数据 3. **分页显示**:支持分页查询,适合前端表格展示 - + 示例请求 1 - 搜索(根据品牌名称和刀具类型): ``` GET /api/v1/auditor/total-stock?brandName=三菱&cutterType=车刀片¤t=1&size=10 ``` - + 示例请求 2 - 刷新(获取所有数据): ``` GET /api/v1/auditor/total-stock?current=1&size=20 ``` - + 示例响应: ```json { @@ -440,7 +491,7 @@ async def get_total_stock( ] } ``` - + 注意: 1. 库存价值 (stockValue) 由后端自动计算:price * locSurplus 2. 如果外部接口路径不同,请联系外部系统提供正确的列表查询接口 @@ -457,13 +508,15 @@ async def get_total_stock( "current": current, "size": size } - + # 调用API客户端方法获取数据 result = api_client.get_total_stock_list(params) - + return result - + except Exception as e: + logger.error(f"获取总库存统计列表失败: {str(e)}") + logger.error(traceback.format_exc()) raise HTTPException(status_code=500, detail=f"获取总库存统计列表失败: {str(e)}") @@ -471,28 +524,28 @@ async def get_total_stock( async def get_stock_location_detail(stock_id: int): """ 获取单个库位详情 - + 功能:根据库位主键查询单个库位的详细信息 封装外部接口:/qw/knife/web/from/mes/cabinetStock/stockLocTakeInfoById - + 请求参数: - stock_id: 刀柜货道主键(路径参数) - + 返回数据: - code: 状态码 - msg: 返回消息 - success: 是否成功 - data: 库位详细信息(包含所有字段) - + 使用场景: - 查看单个库位的完整信息 - 点击表格行查看详情 - + 示例请求: ``` GET /api/v1/auditor/stock-location/123 ``` - + 示例响应: ```json { @@ -529,30 +582,33 @@ async def get_stock_location_detail(stock_id: int): try: # 调用API客户端方法获取数据 result = api_client.get_stock_location_by_id(stock_id) - + return result - + except Exception as e: + logger.error(f"获取库位详情失败: {str(e)}") + logger.error(traceback.format_exc()) raise HTTPException(status_code=500, detail=f"获取库位详情失败: {str(e)}") +# ==================== 废刀回收统计接口 ==================== @router.get("/waste-knife-recycle", response_model=WasteKnifeRecycleResponse, tags=["废刀回收统计"]) async def get_waste_knife_recycle( - borrow_code: Optional[str] = Query(None, alias="borrowCode", description="还刀码"), - cabinet_code: Optional[str] = Query(None, alias="cabinetCode", description="刀柜编码"), - stock_loc: Optional[str] = Query(None, alias="stockLoc", description="刀柜库位号") + borrow_code: Optional[str] = Query(None, alias="borrowCode", description="还刀码"), + cabinet_code: Optional[str] = Query(None, alias="cabinetCode", description="刀柜编码"), + stock_loc: Optional[str] = Query(None, alias="stockLoc", description="刀柜库位号") ): """ 获取废刀回收统计信息(收刀柜还刀信息) - + 功能:查询收刀柜中的还刀信息,用于废刀回收统计和管理 封装外部接口:/qw/knife/web/from/mes/lend/getLendByStock - + 请求参数(全部可选): - borrowCode: 还刀码 - cabinetCode: 刀柜编码 - stockLoc: 刀柜库位号 - + 返回数据: - code: 状态码 - msg: 返回消息 @@ -574,23 +630,23 @@ async def get_waste_knife_recycle( - recordStatus: 记录状态 - specification: 规格 - stockLoc: 库位号 - + 使用场景: 1. **废刀回收统计**:查看所有报废刀具(borrowStatus=1) 2. **修磨管理**:查看需要修磨的刀具(borrowStatus=0) 3. **错领追踪**:查询错领刀具情况(borrowStatus=3) 4. **换线管理**:统计需要换线的刀具(borrowStatus=2) - + 示例请求 1 - 查询指定刀柜的还刀信息: ``` GET /api/v1/auditor/waste-knife-recycle?cabinetCode=CB001 ``` - + 示例请求 2 - 查询指定库位的还刀信息: ``` GET /api/v1/auditor/waste-knife-recycle?cabinetCode=CB001&stockLoc=A01-01 ``` - + 示例响应: ```json { @@ -634,7 +690,7 @@ async def get_waste_knife_recycle( } } ``` - + 前端处理建议: - 根据 borrowStatus 分类统计: * 0-修磨:需要修磨的刀具数量 @@ -651,11 +707,739 @@ async def get_waste_knife_recycle( "cabinetCode": cabinet_code, "stockLoc": stock_loc } - + # 调用API客户端方法获取数据 result = api_client.get_waste_knife_recycle_info(params) - + return result - + except Exception as e: + logger.error(f"获取废刀回收统计信息失败: {str(e)}") + logger.error(traceback.format_exc()) raise HTTPException(status_code=500, detail=f"获取废刀回收统计信息失败: {str(e)}") + + +# ==================== 排行接口 ==================== +@router.get("/device-ranking", response_model=DeviceRankingResponse, tags=["设备排行"]) +async def get_device_ranking( + start_time: Optional[str] = Query(None, alias="startTime", description="开始时间"), + end_time: Optional[str] = Query(None, alias="endTime", description="结束时间"), + order: Optional[int] = Query(None, description="顺序 0:从大到小 1:从小到大"), + ranking_type: Optional[int] = Query(None, alias="rankingType", description="0:批量 1:查错"), + record_status: Optional[int] = Query(None, alias="recordStatus", description="记录状态") +): + """ + 设备用刀排行 + + 功能:获取设备用刀排行数据,按使用次数或金额排序 + 封装外部接口:/ou/knife/web/from/ms/statistics/chartsDeviceSanking + + 参数说明: + - startTime: 开始时间(可选) + - endTime: 结束时间(可选) + - order: 排序顺序(0:从大到小 1:从小到大) + - rankingType: 排行类型(0:批量 1:查错) + - recordStatus: 记录状态(可选) + + 返回数据: + - code: 状态码 + - msg: 返回消息 + - success: 是否成功 + - data: 排行数据 + * titleList: 设备名称列表 + * dataList: 对应的排行数据 + * device_details: 设备详细信息(可选) + - rank: 排名 + - device_code: 设备编码 + - device_name: 设备名称 + - usage_count: 用刀次数 + - total_amount: 总金额 + - avg_usage_duration: 平均使用时长 + - usage_efficiency: 使用效率 + """ + try: + logger.info(f"调用设备用刀排行接口,参数: {locals()}") + params = { + "startTime": start_time, + "endTime": end_time, + "order": order, + "rankingType": ranking_type, + "recordStatus": record_status + } + + result = api_client.get_device_ranking(params) + logger.info(f"设备用刀排行返回结果: {result}") + return result + + except Exception as e: + logger.error(f"获取设备用刀排行失败: {str(e)}") + logger.error(traceback.format_exc()) + raise HTTPException(status_code=500, detail=f"获取设备用刀排行失败: {str(e)}") + + +@router.get("/knife-model-ranking", response_model=KnifeModelRankingResponse, tags=["刀具排行"]) +async def get_knife_model_ranking( + start_time: Optional[str] = Query(None, alias="startTime", description="开始时间"), + end_time: Optional[str] = Query(None, alias="endTime", description="结束时间"), + order: Optional[int] = Query(None, description="顺序 0:从大到小 1:从小到大"), + ranking_type: Optional[int] = Query(None, alias="rankingType", description="0:数量 1:金额"), + record_status: Optional[int] = Query(None, alias="recordStatus", description="记录状态") +): + """ + 刀具型号排行 + + 功能:获取刀具型号排行数据,按使用数量或金额排序 + 封装外部接口:/api/mifc/web/from/me/statistics/charts@tuttenbanking + + 参数说明: + - startTime: 开始时间(可选) + - endTime: 结束时间(可选) + - order: 排序顺序(0:从大到小 1:从小到大) + - rankingType: 排行类型(0:数量 1:金额) + - recordStatus: 记录状态(可选) + + 返回数据: + - code: 状态码 + - msg: 返回消息 + - success: 是否成功 + - data: 排行数据 + * titleList: 刀具型号列表 + * dataList: 对应的排行数据 + * knife_model_details: 刀具型号详细信息(可选) + - rank: 排名 + - model: 刀具型号 + - knife_type: 刀具类型 + - brand: 品牌名称 + - usage_count: 使用次数 + - total_amount: 总金额 + - avg_lifespan: 平均寿命 + - popularity: 受欢迎度 + """ + try: + params = { + "startTime": start_time, + "endTime": end_time, + "order": order, + "rankingType": ranking_type, + "recordStatus": record_status + } + + return api_client.get_knife_model_ranking(params) + + except Exception as e: + logger.error(f"获取刀具型号排行失败: {str(e)}") + logger.error(traceback.format_exc()) + raise HTTPException(status_code=500, detail=f"获取刀具型号排行失败: {str(e)}") + + +@router.get("/employee-ranking", response_model=EmployeeRankingResponse, tags=["员工排行"]) +async def get_employee_ranking( + start_time: Optional[str] = Query(None, alias="startTime", description="开始时间"), + end_time: Optional[str] = Query(None, alias="endTime", description="结束时间"), + order: Optional[int] = Query(None, description="顺序 0:从大到小 1:从小到大"), + ranking_type: Optional[int] = Query(None, alias="rankingType", description="排行类型"), + record_status: Optional[int] = Query(None, alias="recordStatus", description="记录状态") +): + """ + 员工领刀排行 + + 功能:获取员工领刀排行数据,按领刀次数或金额排序 + 封装外部接口:/go/kaife/web/from/mss/statistics/chartslandHunting + + 参数说明: + - startTime: 开始时间(可选) + - endTime: 结束时间(可选) + - order: 排序顺序(0:从大到小 1:从小到大) + - rankingType: 排行类型 + - recordStatus: 记录状态(可选) + + 返回数据: + - code: 状态码 + - msg: 返回消息 + - success: 是否成功 + - data: 排行数据 + * titleList: 员工姓名列表 + * dataList: 对应的排行数据 + * employee_details: 员工详细信息(可选) + - rank: 排名 + - employee_name: 员工姓名 + - department: 部门 + - borrow_count: 领刀次数 + - total_amount: 总金额 + - avg_amount: 平均金额 + - last_borrow_time: 最后领刀时间 + """ + try: + params = { + "startTime": start_time, + "endTime": end_time, + "order": order, + "rankingType": ranking_type, + "recordStatus": record_status + } + + return api_client.get_employee_ranking(params) + + except Exception as e: + logger.error(f"获取员工领刀排行失败: {str(e)}") + logger.error(traceback.format_exc()) + raise HTTPException(status_code=500, detail=f"获取员工领刀排行失败: {str(e)}") + + +@router.get("/error-return-ranking", response_model=ErrorReturnRankingResponse, tags=["异常排行"]) +async def get_error_return_ranking( + start_time: Optional[str] = Query(None, alias="startTime", description="开始时间"), + end_time: Optional[str] = Query(None, alias="endTime", description="结束时间"), + order: Optional[int] = Query(None, description="顺序 0:从大到小 1:从小到大"), + ranking_type: Optional[int] = Query(None, alias="rankingType", description="0:批量 1:金额"), + record_status: Optional[int] = Query(None, alias="recordStatus", description="记录状态") +): + """ + 异常还刀排行 + + 功能:获取异常还刀排行数据,按异常次数或损失金额排序 + 封装外部接口:/ou/knife/web/from/news/statsstics/dhatsErrorBorrow + + 参数说明: + - startTime: 开始时间(可选) + - endTime: 结束时间(可选) + - order: 排序顺序(0:从大到小 1:从小到大) + - rankingType: 排行类型(0:批量 1:金额) + - recordStatus: 记录状态(可选) + + 返回数据: + - code: 状态码 + - msg: 返回消息 + - success: 是否成功 + - data: 排行数据 + * titleList: 异常类型或员工列表 + * dataList: 对应的排行数据 + * error_return_details: 异常还刀详细信息(可选) + - rank: 排名 + - employee_name: 员工姓名 + - department: 部门 + - error_count: 异常次数 + - error_type: 异常类型 + - total_loss: 总损失 + - last_error_time: 最后异常时间 + """ + try: + params = { + "startTime": start_time, + "endTime": end_time, + "order": order, + "rankingType": ranking_type, + "recordStatus": record_status + } + + return api_client.get_error_return_ranking(params) + + except Exception as e: + logger.error(f"获取异常还刀排行失败: {str(e)}") + logger.error(traceback.format_exc()) + raise HTTPException(status_code=500, detail=f"获取异常还刀排行失败: {str(e)}") + + +# ==================== 系统记录相关接口 ==================== + +# 合并自 auditor_record 模块的系统记录接口 +# 日期: 2025-11-15 + +# 领刀记录相关路由 +@router.get("/list", response_model=LendRecordResponse, tags=["领刀记录"]) +async def get_lend_record_list( + current: int = Query(1, ge=1, description="页码"), + size: int = Query(20, ge=1, le=100, description="每页数量"), + keyword: Optional[str] = Query(None, description="关键字搜索"), + department: Optional[str] = Query(None, description="部门筛选"), + startTime: Optional[str] = Query(None, description="开始时间"), + endTime: Optional[str] = Query(None, description="结束时间"), + order: Optional[int] = Query(None, description="顺序 0: 从大到小 1:从小到大"), + rankingType: Optional[int] = Query(None, description="0: 数量 1: 金额"), + recordStatus: Optional[int] = Query(None, description="0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀") +): + """ + 获取领刀记录列表 + + Args: + current: 页码,默认为1 + size: 每页数量,默认为20,最大100 + keyword: 关键字搜索 + department: 部门筛选 + startTime: 开始时间 + endTime: 结束时间 + order: 顺序 0: 从大到小 1:从小到大 + rankingType: 0: 数量 1: 金额 + recordStatus: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + + Returns: + LendRecordResponse: 领刀记录列表响应 + """ + result = api_client.get_lend_records( + current=current, + size=size, + keyword=keyword, + department=department, + startTime=startTime, + endTime=endTime, + order=order, + rankingType=rankingType, + recordStatus=recordStatus + ) + + return result + + +@router.get("/export", tags=["领刀记录"]) +async def export_lend_records( + endTime: Optional[str] = Query(None, description="结束时间"), + order: Optional[int] = Query(None, description="顺序 0: 从大到小 1:从小到大"), + rankingType: Optional[int] = Query(None, description="0: 数量 1: 金额"), + recordStatus: Optional[int] = Query(None, description="0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀"), + startTime: Optional[str] = Query(None, description="开始时间") +): + """ + 导出领刀记录 + + Args: + endTime: 结束时间 + order: 顺序 0: 从大到小 1:从小到大 + rankingType: 0: 数量 1: 金额 + recordStatus: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + startTime: 开始时间 + + Returns: + Response: 包含导出文件的响应 + """ + try: + file_content = api_client.export_lend_records( + endTime=endTime, + order=order, + rankingType=rankingType, + recordStatus=recordStatus, + startTime=startTime + ) + + return Response( + content=file_content, + media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + headers={ + "Content-Disposition": "attachment; filename=lend_records.xlsx" + } + ) + except Exception as e: + return Response( + content=json.dumps({ + "code": -1, + "msg": f"导出失败: {str(e)}", + "data": None, + "success": False + }, ensure_ascii=False), + media_type="application/json" + ) + + +# 告警预警相关路由 +@router.get("/alarm_list", response_model=AlarmWarningResponse, tags=["告警预警"]) +async def list_alarm_warning( + loc_surplus: Optional[int] = Query(None, description="货道"), + alarm_level: Optional[int] = Query(None, description="预警等级"), + device_type: Optional[str] = Query(None, description="设备类型"), + cabinet_code: Optional[str] = Query(None, description="刀柜编码"), + brand_name: Optional[str] = Query(None, description="品牌名称"), + handle_status: Optional[int] = Query(None, description="处理状态"), + current: Optional[int] = Query(None, description="当前页"), + size: Optional[int] = Query(None, description="每页数量") +): + """ + 获取告警预警列表 + + Args: + loc_surplus: 货道 + alarm_level: 预警等级 + device_type: 设备类型 + cabinet_code: 刀柜编码 + brand_name: 品牌名称 + handle_status: 处理状态 + current: 当前页 + size: 每页数量 + + Returns: + AlarmWarningResponse: 告警预警列表响应 + """ + result = api_client.list_alarm_warning( + locSurplus=loc_surplus, + alarmLevel=alarm_level, + deviceType=device_type, + cabinetCode=cabinet_code, + brandName=brand_name, + handleStatus=handle_status, + current=current, + size=size + ) + + return result + + +@router.get("/alarm_statistics", response_model=AlarmStatisticsResponse, tags=["告警预警"]) +async def get_alarm_statistics(): + """ + 获取告警统计信息 + + Returns: + AlarmStatisticsResponse: 告警统计信息响应 + """ + result = api_client.get_alarm_statistics() + + return result + + +@router.post("/alarm_threshold", tags=["告警预警"]) +async def update_alarm_threshold( + request: ThresholdSettingRequest = Body(..., description="阈值设置请求参数") +): + """ + 更新告警阈值 + + Args: + request: 阈值设置请求参数 + + Returns: + Response: 更新结果响应 + """ + result = api_client.update_alarm_threshold( + locSurplus=request.locSurplus, + alarmThreshold=request.alarmThreshold + ) + + return result + + +@router.post("/handle_alarm/{alarm_id}", tags=["告警预警"]) +async def handle_alarm_warning( + alarm_id: int, + handle_status: int = Body(..., description="处理状态"), + handle_remark: Optional[str] = Body(None, description="处理备注") +): + """ + 处理告警预警 + + Args: + alarm_id: 告警ID + handle_status: 处理状态 (0: 未处理, 1: 已处理, 2: 已忽略) + handle_remark: 处理备注 + + Returns: + Response: 处理结果响应 + """ + result = api_client.handle_alarm_warning( + id=alarm_id, + handleStatus=handle_status, + handleRemark=handle_remark + ) + + return result + + +@router.post("/batch_handle_alarm", tags=["告警预警"]) +async def batch_handle_alarm_warning( + ids: List[int] = Body(..., description="告警ID列表"), + handle_status: int = Body(..., description="处理状态"), + handle_remark: Optional[str] = Body(None, description="处理备注") +): + """ + 批量处理告警预警 + + Args: + ids: 告警ID列表 + handle_status: 处理状态 (0: 未处理, 1: 已处理, 2: 已忽略) + handle_remark: 处理备注 + + Returns: + Response: 处理结果响应 + """ + result = api_client.batch_handle_alarm_warning( + ids=ids, + handleStatus=handle_status, + handleRemark=handle_remark + ) + + return result + + +@router.get("/export_alarm", tags=["告警预警"]) +async def export_alarm_warning( + loc_surplus: Optional[int] = Query(None, description="货道"), + alarm_level: Optional[int] = Query(None, description="预警等级"), + device_type: Optional[str] = Query(None, description="设备类型"), + cabinet_code: Optional[str] = Query(None, description="刀柜编码"), + brand_name: Optional[str] = Query(None, description="品牌名称"), + handle_status: Optional[int] = Query(None, description="处理状态") +): + """ + 导出告警预警 + + Args: + loc_surplus: 货道 + alarm_level: 预警等级 + device_type: 设备类型 + cabinet_code: 刀柜编码 + brand_name: 品牌名称 + handle_status: 处理状态 + + Returns: + Response: 包含导出文件的响应 + """ + try: + file_content = api_client.export_alarm_warning( + locSurplus=loc_surplus, + alarmLevel=alarm_level, + deviceType=device_type, + cabinetCode=cabinet_code, + brandName=brand_name, + handleStatus=handle_status + ) + + return Response( + content=file_content, + media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + headers={ + "Content-Disposition": "attachment; filename=alarm_warnings.xlsx" + } + ) + except Exception as e: + return Response( + content=json.dumps({ + "code": -1, + "msg": f"导出失败: {str(e)}", + "data": None, + "success": False + }, ensure_ascii=False), + media_type="application/json" + ) + + +# 补货记录相关路由 +@router.get("/replenish_list", response_model=ReplenishRecordResponse, tags=["补货记录"]) +async def get_replenish_record_list( + current: Optional[int] = Query(None, description="当前页"), + end_time: Optional[str] = Query(None, description="结束时间"), + order: Optional[int] = Query(None, description="顺序 0: 从大到小 1:从小到大"), + ranking_type: Optional[int] = Query(None, description="0: 数量 1: 金额"), + record_status: Optional[int] = Query(None, description="0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀"), + size: Optional[int] = Query(None, description="每页的数量"), + start_time: Optional[str] = Query(None, description="开始时间") +): + """ + 获取补货记录列表 + + Args: + current: 当前页 + end_time: 结束时间 + order: 顺序 0: 从大到小 1:从小到大 + ranking_type: 0: 数量 1: 金额 + record_status: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + size: 每页的数量 + start_time: 开始时间 + + Returns: + ReplenishRecordResponse: 补货记录列表响应,包含以下字段: + - lendUserName: 取出人 + - storageUserName: 暂存人 + - brandName: 品牌名称 + - cutterType: 电池类型 + - cutterCode: 刀具型号 + - specification: 规格 + - quantity: 数量 + - oldPrice: 老单价 + - newPrice: 新单价 + - oldStockNum: 操作前库存数 + - newStockNum: 操作后库存数 + - stockLoc: 库位号 + - logType: 补货类型 + - status: 业务状态 + - cabinetCode: 刀柜编码 + - createTime: 创建时间 + - operator: 操作人 + - materialCode: 物料编码 + - detailsCode: 操作详情 + - remake: 备注 + - createDept: 创建部门 + - createUser: 创建人 + - updateUser: 更新人 + - tenantId: 租户ID + - isDeleted: 是否已删除 + """ + result = api_client.get_replenish_records( + current=current, + endTime=end_time, + order=order, + rankingType=ranking_type, + recordStatus=record_status, + size=size, + startTime=start_time + ) + + return result + + +@router.get("/export_replenish", tags=["补货记录"]) +async def export_replenish_records( + end_time: Optional[str] = Query(None, description="结束时间"), + order: Optional[int] = Query(None, description="顺序 0: 从大到小 1:从小到大"), + ranking_type: Optional[int] = Query(None, description="0: 数量 1: 金额"), + record_status: Optional[int] = Query(None, description="0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀"), + start_time: Optional[str] = Query(None, description="开始时间") +): + """ + 导出补货记录 + + Args: + end_time: 结束时间 + order: 顺序 0: 从大到小 1:从小到大 + ranking_type: 0: 数量 1: 金额 + record_status: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + start_time: 开始时间 + + Returns: + Response: 包含导出文件的响应,Excel文件包含以下字段: + - lendUserName: 取出人 + - storageUserName: 暂存人 + - brandName: 品牌名称 + - cutterType: 电池类型 + - cutterCode: 刀具型号 + - specification: 规格 + - quantity: 数量 + - oldPrice: 老单价 + - newPrice: 新单价 + - oldStockNum: 操作前库存数 + - newStockNum: 操作后库存数 + - stockLoc: 库位号 + - logType: 补货类型 + - status: 业务状态 + - cabinetCode: 刀柜编码 + - createTime: 创建时间 + - operator: 操作人 + - materialCode: 物料编码 + - detailsCode: 操作详情 + - remake: 备注 + - createDept: 创建部门 + - createUser: 创建人 + - updateUser: 更新人 + - tenantId: 租户ID + - isDeleted: 是否已删除 + """ + try: + file_content = api_client.export_replenish_records( + endTime=end_time, + order=order, + rankingType=ranking_type, + recordStatus=record_status, + startTime=start_time + ) + + return Response( + content=file_content, + media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + headers={ + "Content-Disposition": "attachment; filename=replenish_records.xlsx" + } + ) + except Exception as e: + return Response( + content=json.dumps({ + "code": -1, + "msg": f"导出失败: {str(e)}", + "data": None, + "success": False + }, ensure_ascii=False), + media_type="application/json" + ) + + +# 公共暂存记录相关路由 +@router.get("/storage_list", response_model=StorageRecordModelResponse, tags=["公共暂存记录"]) +async def get_storage_record_list( + current: Optional[int] = Query(None, description="当前页"), + end_time: Optional[str] = Query(None, description="结束时间"), + order: Optional[int] = Query(None, description="顺序 0: 从大到小 1:从小到大"), + ranking_type: Optional[int] = Query(None, description="0: 数量 1: 金额"), + record_status: Optional[int] = Query(None, description="0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀"), + size: Optional[int] = Query(None, description="每页的数量"), + start_time: Optional[str] = Query(None, description="开始时间") +): + """ + 获取公共暂存记录列表 + + Args: + current: 当前页 + end_time: 结束时间 + order: 顺序 0: 从大到小 1:从小到大 + ranking_type: 0: 数量 1: 金额 + record_status: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + size: 每页的数量 + start_time: 开始时间 + + Returns: + StorageRecordModelResponse: 公共暂存记录列表响应 + """ + result = api_client.get_storage_records( + current=current, + endTime=end_time, + order=order, + rankingType=ranking_type, + recordStatus=record_status, + size=size, + startTime=start_time + ) + + return result + + +@router.get("/export_storage", tags=["公共暂存记录"]) +async def export_storage_records( + end_time: Optional[str] = Query(None, description="结束时间"), + order: Optional[int] = Query(None, description="顺序 0: 从大到小 1:从小到大"), + ranking_type: Optional[int] = Query(None, description="0: 数量 1: 金额"), + record_status: Optional[int] = Query(None, description="0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀"), + start_time: Optional[str] = Query(None, description="开始时间") +): + """ + 导出公共暂存记录 + + Args: + end_time: 结束时间 + order: 顺序 0: 从大到小 1:从小到大 + ranking_type: 0: 数量 1: 金额 + record_status: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + start_time: 开始时间 + + Returns: + Response: 包含导出文件的响应 + """ + try: + file_content = api_client.export_storage_records( + endTime=end_time, + order=order, + rankingType=ranking_type, + recordStatus=record_status, + startTime=start_time + ) + + return Response( + content=file_content, + media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + headers={ + "Content-Disposition": "attachment; filename=storage_records.xlsx" + } + ) + except Exception as e: + return Response( + content=json.dumps({ + "code": -1, + "msg": f"导出失败: {str(e)}", + "data": None, + "success": False + }, ensure_ascii=False), + media_type="application/json" + ) \ No newline at end of file diff --git a/routers/lend_record_router.py b/routers/lend_record_router.py deleted file mode 100644 index 59ee3ab..0000000 --- a/routers/lend_record_router.py +++ /dev/null @@ -1,502 +0,0 @@ -from fastapi import APIRouter, Depends, Query, Response, Body -from typing import Optional, List -from teamleader_lend_record.schemas.data_schemas import ( - LendRecordResponse, - LendRecordListRequest, - ExportLendRecordRequest, - RestockRequest, - RestockResponse, - ReplenishRecordResponse, - ReplenishRecordListRequest, - StorageRecordResponse, - StorageRecordListRequest, - PersonalStorageResponse, - MakeAlarmResponse, - CabinetAlarmResponse -) -from teamleader_lend_record.services.api_client import api_client as teamleader_api_client -from auditor_lend_record.services.api_client import api_client as auditor_api_client -import json - -# 创建主路由 -router = APIRouter(prefix="", tags=["领刀记录"]) - -# 团队长路由函数 -@router.get("/teamleader_lend_record/list", response_model=LendRecordResponse) -async def get_teamleader_lend_record_list( - page: int = Query(1, ge=1, description="页码"), - page_size: int = Query(20, ge=1, le=100, description="每页数量"), - keyword: Optional[str] = Query(None, description="关键字搜索"), - department: Optional[str] = Query(None, description="部门筛选"), - start_date: Optional[str] = Query(None, description="开始时间"), - end_date: Optional[str] = Query(None, description="结束时间"), - order: Optional[int] = Query(None, description="顺序 0: 从大到小 1:从小到大"), - ranking_type: Optional[int] = Query(None, description="0: 数量 1: 金额"), - record_status: Optional[int] = Query(None, description="0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀") -): - """ - 获取领刀记录列表 - - Args: - page: 页码,默认为1 - page_size: 每页数量,默认为20,最大100 - keyword: 关键字搜索 - department: 部门筛选 - start_date: 开始时间 - end_date: 结束时间 - order: 顺序 0: 从大到小 1:从小到大 - ranking_type: 0: 数量 1: 金额 - record_status: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 - - Returns: - LendRecordResponse: 领刀记录列表响应 - """ - result = teamleader_api_client.get_lend_records( - page=page, - page_size=page_size, - keyword=keyword, - department=department, - start_date=start_date, - end_date=end_date, - order=order, - rankingType=ranking_type, - recordStatus=record_status - ) - - return result - -@router.get("/teamleader_lend_record/export") -async def export_teamleader_lend_records( - end_time: Optional[str] = Query(None, description="结束时间"), - order: Optional[int] = Query(None, description="顺序 0: 从大到小 1:从小到大"), - ranking_type: Optional[int] = Query(None, description="0: 数量 1: 金额"), - record_status: Optional[int] = Query(None, description="0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀"), - start_time: Optional[str] = Query(None, description="开始时间") -): - """ - 导出领刀记录 - - Args: - end_time: 结束时间 - order: 顺序 0: 从大到小 1:从小到大 - ranking_type: 0: 数量 1: 金额 - record_status: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 - start_time: 开始时间 - - Returns: - Response: 包含导出文件的响应 - """ - try: - file_content = teamleader_api_client.export_lend_records( - endTime=end_time, - order=order, - rankingType=ranking_type, - recordStatus=record_status, - startTime=start_time - ) - - return Response( - content=file_content, - media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - headers={ - "Content-Disposition": "attachment; filename=lend_records.xlsx" - } - ) - except Exception as e: - return Response( - content=json.dumps({ - "code": -1, - "msg": f"导出失败: {str(e)}", - "data": None, - "success": False - }, ensure_ascii=False), - media_type="application/json" - ) - -@router.post("/teamleader_lend_record/restock", response_model=RestockResponse) -async def teamleader_restock_cabinet( - request: RestockRequest = Body(None, description="补货请求参数") -): - """ - 刀柜补货接口 - - Args: - request: 补货请求参数 - - Returns: - RestockResponse: 补货结果响应 - """ - result = teamleader_api_client.restock_cabinet( - cabinetCode=request.cabinetCode, - itemDtoList=[item.dict() for item in request.itemDtoList] if request.itemDtoList else None, - replenishDto=request.replenishDto - ) - - return result - -@router.get("/teamleader_lend_record/replenish_list", response_model=ReplenishRecordResponse) -async def get_teamleader_replenish_record_list( - current: Optional[int] = Query(None, description="当前页"), - end_time: Optional[str] = Query(None, description="结束时间"), - order: Optional[int] = Query(None, description="顺序 0: 从大到小 1:从小到大"), - ranking_type: Optional[int] = Query(None, description="0: 数量 1: 金额"), - record_status: Optional[int] = Query(None, description="0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀"), - size: Optional[int] = Query(None, description="每页的数量"), - start_time: Optional[str] = Query(None, description="开始时间") -): - """ - 获取补货记录列表 - - Args: - current: 当前页 - end_time: 结束时间 - order: 顺序 0: 从大到小 1:从小到大 - ranking_type: 0: 数量 1: 金额 - record_status: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 - size: 每页的数量 - start_time: 开始时间 - - Returns: - ReplenishRecordResponse: 补货记录列表响应 - """ - result = teamleader_api_client.get_replenish_records( - current=current, - endTime=end_time, - order=order, - rankingType=ranking_type, - recordStatus=record_status, - size=size, - startTime=start_time - ) - - return result - -@router.get("/teamleader_lend_record/storage_list", response_model=StorageRecordResponse) -async def get_teamleader_storage_record_list( - current: Optional[int] = Query(None, description="当前页"), - end_time: Optional[str] = Query(None, description="结束时间"), - order: Optional[int] = Query(None, description="顺序 0: 从大到小 1:从小到大"), - ranking_type: Optional[int] = Query(None, description="0: 数量 1: 金额"), - record_status: Optional[int] = Query(None, description="0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀"), - size: Optional[int] = Query(None, description="每页的数量"), - start_time: Optional[str] = Query(None, description="开始时间") -): - """ - 获取公共暂存记录列表 - - Args: - current: 当前页 - end_time: 结束时间 - order: 顺序 0: 从大到小 1:从小到大 - ranking_type: 0: 数量 1: 金额 - record_status: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 - size: 每页的数量 - start_time: 开始时间 - - Returns: - StorageRecordResponse: 公共暂存记录列表响应 - """ - result = teamleader_api_client.get_storage_records( - current=current, - endTime=end_time, - order=order, - rankingType=ranking_type, - recordStatus=record_status, - size=size, - startTime=start_time - ) - - return result - -@router.get("/teamleader_lend_record/personal_storage", response_model=PersonalStorageResponse) -async def get_teamleader_personal_storage( - cabinet_code: Optional[str] = Query(None, description="刀柜编码") -): - """ - 获取个人暂存柜信息 - - Args: - cabinet_code: 刀柜编码 - - Returns: - PersonalStorageResponse: 个人暂存柜信息响应 - """ - result = teamleader_api_client.get_personal_storage(cabinetCode=cabinet_code) - - return result - -@router.get("/teamleader_lend_record/make_alarm", response_model=MakeAlarmResponse) -async def set_teamleader_make_alarm( - cabinet_code: Optional[str] = Query(None, description="刀柜编码"), - alarm_value: Optional[int] = Query(None, description="告警值") -): - """ - 设置取刀柜告警值 - - Args: - cabinet_code: 刀柜编码 - alarm_value: 告警值 - - Returns: - MakeAlarmResponse: 设置告警值响应 - """ - result = teamleader_api_client.set_make_alarm(cabinetCode=cabinet_code, alarmValue=alarm_value) - - return result - -@router.get("/teamleader_lend_record/get_make_alarm", response_model=CabinetAlarmResponse) -async def get_teamleader_make_alarm( - cabinet_code: Optional[str] = Query(None, description="刀柜编码") -): - """ - 获取取刀柜告警值 - - Args: - cabinet_code: 刀柜编码 - - Returns: - CabinetAlarmResponse: 获取告警值响应 - """ - result = teamleader_api_client.get_make_alarm(cabinetCode=cabinet_code) - - return result - -# 审核员路由函数 -@router.get("/auditor_lend_record/list", response_model=LendRecordResponse) -async def get_auditor_lend_record_list( - page: int = Query(1, ge=1, description="页码"), - page_size: int = Query(20, ge=1, le=100, description="每页数量"), - keyword: Optional[str] = Query(None, description="关键字搜索"), - department: Optional[str] = Query(None, description="部门筛选"), - start_date: Optional[str] = Query(None, description="开始时间"), - end_date: Optional[str] = Query(None, description="结束时间"), - order: Optional[int] = Query(None, description="顺序 0: 从大到小 1:从小到大"), - ranking_type: Optional[int] = Query(None, description="0: 数量 1: 金额"), - record_status: Optional[int] = Query(None, description="0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀") -): - """ - 获取领刀记录列表 - - Args: - page: 页码,默认为1 - page_size: 每页数量,默认为20,最大100 - keyword: 关键字搜索 - department: 部门筛选 - start_date: 开始时间 - end_date: 结束时间 - order: 顺序 0: 从大到小 1:从小到大 - ranking_type: 0: 数量 1: 金额 - record_status: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 - - Returns: - LendRecordResponse: 领刀记录列表响应 - """ - result = auditor_api_client.get_lend_records( - page=page, - page_size=page_size, - keyword=keyword, - department=department, - start_date=start_date, - end_date=end_date, - order=order, - rankingType=ranking_type, - recordStatus=record_status - ) - - return result - -@router.get("/auditor_lend_record/export") -async def export_auditor_lend_records( - end_time: Optional[str] = Query(None, description="结束时间"), - order: Optional[int] = Query(None, description="顺序 0: 从大到小 1:从小到大"), - ranking_type: Optional[int] = Query(None, description="0: 数量 1: 金额"), - record_status: Optional[int] = Query(None, description="0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀"), - start_time: Optional[str] = Query(None, description="开始时间") -): - """ - 导出领刀记录 - - Args: - end_time: 结束时间 - order: 顺序 0: 从大到小 1:从小到大 - ranking_type: 0: 数量 1: 金额 - record_status: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 - start_time: 开始时间 - - Returns: - Response: 包含导出文件的响应 - """ - try: - file_content = auditor_api_client.export_lend_records( - endTime=end_time, - order=order, - rankingType=ranking_type, - recordStatus=record_status, - startTime=start_time - ) - - return Response( - content=file_content, - media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - headers={ - "Content-Disposition": "attachment; filename=lend_records.xlsx" - } - ) - except Exception as e: - return Response( - content=json.dumps({ - "code": -1, - "msg": f"导出失败: {str(e)}", - "data": None, - "success": False - }, ensure_ascii=False), - media_type="application/json" - ) - -@router.post("/auditor_lend_record/restock", response_model=RestockResponse) -async def auditor_restock_cabinet( - request: RestockRequest = Body(None, description="补货请求参数") -): - """ - 刀柜补货接口 - - Args: - request: 补货请求参数 - - Returns: - RestockResponse: 补货结果响应 - """ - result = auditor_api_client.restock_cabinet( - cabinetCode=request.cabinetCode, - itemDtoList=[item.dict() for item in request.itemDtoList] if request.itemDtoList else None, - replenishDto=request.replenishDto - ) - - return result - -@router.get("/auditor_lend_record/replenish_list", response_model=ReplenishRecordResponse) -async def get_auditor_replenish_record_list( - current: Optional[int] = Query(None, description="当前页"), - end_time: Optional[str] = Query(None, description="结束时间"), - order: Optional[int] = Query(None, description="顺序 0: 从大到小 1:从小到大"), - ranking_type: Optional[int] = Query(None, description="0: 数量 1: 金额"), - record_status: Optional[int] = Query(None, description="0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀"), - size: Optional[int] = Query(None, description="每页的数量"), - start_time: Optional[str] = Query(None, description="开始时间") -): - """ - 获取补货记录列表 - - Args: - current: 当前页 - end_time: 结束时间 - order: 顺序 0: 从大到小 1:从小到大 - ranking_type: 0: 数量 1: 金额 - record_status: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 - size: 每页的数量 - start_time: 开始时间 - - Returns: - ReplenishRecordResponse: 补货记录列表响应 - """ - result = auditor_api_client.get_replenish_records( - current=current, - endTime=end_time, - order=order, - rankingType=ranking_type, - recordStatus=record_status, - size=size, - startTime=start_time - ) - - return result - -@router.get("/auditor_lend_record/storage_list", response_model=StorageRecordResponse) -async def get_auditor_storage_record_list( - current: Optional[int] = Query(None, description="当前页"), - end_time: Optional[str] = Query(None, description="结束时间"), - order: Optional[int] = Query(None, description="顺序 0: 从大到小 1:从小到大"), - ranking_type: Optional[int] = Query(None, description="0: 数量 1: 金额"), - record_status: Optional[int] = Query(None, description="0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀"), - size: Optional[int] = Query(None, description="每页的数量"), - start_time: Optional[str] = Query(None, description="开始时间") -): - """ - 获取公共暂存记录列表 - - Args: - current: 当前页 - end_time: 结束时间 - order: 顺序 0: 从大到小 1:从小到大 - ranking_type: 0: 数量 1: 金额 - record_status: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 - size: 每页的数量 - start_time: 开始时间 - - Returns: - StorageRecordResponse: 公共暂存记录列表响应 - """ - result = auditor_api_client.get_storage_records( - current=current, - endTime=end_time, - order=order, - rankingType=ranking_type, - recordStatus=record_status, - size=size, - startTime=start_time - ) - - return result - -@router.get("/auditor_lend_record/personal_storage", response_model=PersonalStorageResponse) -async def get_auditor_personal_storage( - cabinet_code: Optional[str] = Query(None, description="刀柜编码") -): - """ - 获取个人暂存柜信息 - - Args: - cabinet_code: 刀柜编码 - - Returns: - PersonalStorageResponse: 个人暂存柜信息响应 - """ - result = auditor_api_client.get_personal_storage(cabinetCode=cabinet_code) - - return result - -@router.get("/auditor_lend_record/make_alarm", response_model=MakeAlarmResponse) -async def set_auditor_make_alarm( - cabinet_code: Optional[str] = Query(None, description="刀柜编码"), - alarm_value: Optional[int] = Query(None, description="告警值") -): - """ - 设置取刀柜告警值 - - Args: - cabinet_code: 刀柜编码 - alarm_value: 告警值 - - Returns: - MakeAlarmResponse: 设置告警值响应 - """ - result = auditor_api_client.set_make_alarm(cabinetCode=cabinet_code, alarmValue=alarm_value) - - return result - -@router.get("/auditor_lend_record/get_make_alarm", response_model=CabinetAlarmResponse) -async def get_auditor_make_alarm( - cabinet_code: Optional[str] = Query(None, description="刀柜编码") -): - """ - 获取取刀柜告警值 - - Args: - cabinet_code: 刀柜编码 - - Returns: - CabinetAlarmResponse: 获取告警值响应 - """ - result = auditor_api_client.get_make_alarm(cabinetCode=cabinet_code) - - return result \ No newline at end of file diff --git a/routers/operator_router.py b/routers/operator_router.py index 4d7a671..4343547 100644 --- a/routers/operator_router.py +++ b/routers/operator_router.py @@ -187,7 +187,7 @@ async def create_lend_record( """ try: # 调用API客户端方法创建借出记录 - result = api_client.create_lend_record(lend_record.dict()) + result = api_client.create_lend_record(lend_record.model_dump()) return result except Exception as e: @@ -204,7 +204,7 @@ async def batch_return_knife_heads( # 模拟当前用户信息 current_user = {"employeeCode": "zhangsan"} # 调用API客户端方法处理批量归还 - result = api_client.process_batch_return(request.dict()) + result = api_client.process_batch_return(request.model_dump()) return result except Exception as e: @@ -221,7 +221,7 @@ async def temp_store_batch_return_knife_heads( # 模拟当前用户信息 current_user = {"employeeCode": "zhangsan"} # 调用API客户端方法处理暂存刀头批量归还 - result = api_client.process_temp_store_batch_return(request.dict()) + result = api_client.process_temp_store_batch_return(request.model_dump()) return result except Exception as e: @@ -239,7 +239,7 @@ async def update_borrow_record( # 模拟当前用户信息 current_user = {"employeeCode": "zhangsan"} # 调用API客户端方法更新借出记录 - result = api_client.update_borrow_record_service(borrow_id, request.dict(), current_user) + result = api_client.update_borrow_record_service(borrow_id, request.model_dump(), current_user) return result except Exception as e: @@ -256,7 +256,7 @@ async def return_knife_head( # 模拟当前用户信息 current_user = {"employeeCode": "zhangsan"} # 调用API客户端方法处理归还 - result = api_client.process_return_service(request.dict(), current_user) + result = api_client.process_return_service(request.model_dump(), current_user) return result except Exception as e: @@ -274,7 +274,7 @@ async def temp_store_knife_head( # 使用请求中的借出人编码作为当前用户 current_user = {"employeeCode": request.borrowerCode} # 调用API客户端方法处理暂存 - result = api_client.process_temp_store_service(request.dict(), current_user) + result = api_client.process_temp_store_service(request.model_dump(), current_user) return result except Exception as e: @@ -347,7 +347,7 @@ async def create_handle_lend_record( """ try: # 调用API客户端方法创建刀柄借出记录 - result = api_client.create_handle_lend_record(handle_record.dict()) + result = api_client.create_handle_lend_record(handle_record.model_dump()) return result except Exception as e: @@ -365,7 +365,7 @@ async def update_handle_lend_record( # 模拟当前用户信息 current_user = {"employeeCode": "zhangsan"} # 调用API客户端方法更新刀柄借出记录 - result = api_client.update_handle_lend_record(handle_id, request.dict(), current_user) + result = api_client.update_handle_lend_record(handle_id, request.model_dump(), current_user) return result except Exception as e: diff --git a/routers/teamleader_router.py b/routers/teamleader_router.py index e6e274f..bad1c04 100644 --- a/routers/teamleader_router.py +++ b/routers/teamleader_router.py @@ -1,13 +1,14 @@ -from fastapi import APIRouter, Query, HTTPException -from typing import Optional +from fastapi import APIRouter, Query, HTTPException, Response, Body +from typing import Optional, List import sys import os +import json # 将项目根目录添加到Python路径 sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from teamleader.schemas.data_schemas import ( - CutterQueryParams, + CutterQueryParams, CutterQueryResponse, CreateCutterRequest, CreateCutterResponse, @@ -27,7 +28,22 @@ StockTakeQueryParams, StockTakeQueryResponse, PreBatchPlugResponse, - OnPreBatchPlugResponse + OnPreBatchPlugResponse, + LendRecordResponse, + ReplenishRecordResponse, + StorageRecordResponse, + AlarmWarningResponse, + AlarmStatisticsResponse, + ThresholdSettingRequest, + # 新增统计接口模型 + StorageStatisticsQueryParams, + StorageStatisticsResponse, + ExportStockRecordQueryParams, + ChartsResponse, + TotalStockQueryParams, + TotalStockResponse, + StockLocationDetailResponse, + WasteKnifeRecycleResponse ) from teamleader.services.api_client import TeamLeaderAPIClient from config.config import settings @@ -49,7 +65,7 @@ summary="刀具耗材分页查询", description=""" 查询刀具耗材信息,支持多维度筛选和分页。 - + **支持的查询条件:** - 品牌名称:模糊查询 - 刀具柜名称:模糊查询 @@ -58,7 +74,7 @@ - 刀具类型:模糊查询 - 刀具型号:模糊查询 - 价格区间:最低价格和最高价格 - + **注意:** - 所有查询条件均为可选,不传递任何条件时返回全部数据 - 价格区间可以只传最低价或最高价,也可以同时传递 @@ -66,16 +82,16 @@ """ ) async def query_cutters( - brandName: Optional[str] = Query(None, description="品牌名称"), - cabinetName: Optional[str] = Query(None, description="刀具柜名称"), - createTime: Optional[str] = Query(None, description="创建时间(格式:YYYY-MM-DD HH:mm:ss)"), - createUser: Optional[int] = Query(None, description="创建人 ID"), - cutterType: Optional[str] = Query(None, description="刀具类型"), - cutterCode: Optional[str] = Query(None, description="刀具型号"), - minPrice: Optional[float] = Query(None, description="最低价格", ge=0), - maxPrice: Optional[float] = Query(None, description="最高价格", ge=0), - current: int = Query(1, description="当前页码", ge=1), - size: int = Query(10, description="每页数量", ge=1, le=100) + brandName: Optional[str] = Query(None, description="品牌名称"), + cabinetName: Optional[str] = Query(None, description="刀具柜名称"), + createTime: Optional[str] = Query(None, description="创建时间(格式:YYYY-MM-DD HH:mm:ss)"), + createUser: Optional[int] = Query(None, description="创建人 ID"), + cutterType: Optional[str] = Query(None, description="刀具类型"), + cutterCode: Optional[str] = Query(None, description="刀具型号"), + minPrice: Optional[float] = Query(None, description="最低价格", ge=0), + maxPrice: Optional[float] = Query(None, description="最高价格", ge=0), + current: int = Query(1, description="当前页码", ge=1), + size: int = Query(10, description="每页数量", ge=1, le=100) ): """ 刀具耗材分页查询接口 @@ -83,7 +99,7 @@ async def query_cutters( # 验证价格区间 if minPrice is not None and maxPrice is not None and minPrice > maxPrice: raise HTTPException(status_code=400, detail="最低价格不能大于最高价格") - + # 构建查询参数 query_params = CutterQueryParams( brandName=brandName, @@ -97,20 +113,20 @@ async def query_cutters( current=current, size=size ) - + # 调用原始API try: - result = api_client.get_cutter_list(query_params.dict(exclude_none=False)) - + result = api_client.get_cutter_list(query_params.model_dump(exclude_none=False)) + # 检查响应状态 if not result.get("success"): raise HTTPException( status_code=500, detail=result.get("msg", "查询失败") ) - + return result - + except HTTPException: raise except Exception as e: @@ -127,16 +143,16 @@ async def query_cutters( """ ) async def search_cutters( - brandName: Optional[str] = Query(None, description="品牌名称"), - cabinetName: Optional[str] = Query(None, description="刀具柜名称"), - createTime: Optional[str] = Query(None, description="创建时间(格式:YYYY-MM-DD HH:mm:ss)"), - createUser: Optional[int] = Query(None, description="创建人 ID"), - cutterType: Optional[str] = Query(None, description="刀具类型"), - cutterCode: Optional[str] = Query(None, description="刀具型号"), - minPrice: Optional[float] = Query(None, description="最低价格", ge=0), - maxPrice: Optional[float] = Query(None, description="最高价格", ge=0), - current: int = Query(1, description="当前页码", ge=1), - size: int = Query(10, description="每页数量", ge=1, le=100) + brandName: Optional[str] = Query(None, description="品牌名称"), + cabinetName: Optional[str] = Query(None, description="刀具柜名称"), + createTime: Optional[str] = Query(None, description="创建时间(格式:YYYY-MM-DD HH:mm:ss)"), + createUser: Optional[int] = Query(None, description="创建人 ID"), + cutterType: Optional[str] = Query(None, description="刀具类型"), + cutterCode: Optional[str] = Query(None, description="刀具型号"), + minPrice: Optional[float] = Query(None, description="最低价格", ge=0), + maxPrice: Optional[float] = Query(None, description="最高价格", ge=0), + current: int = Query(1, description="当前页码", ge=1), + size: int = Query(10, description="每页数量", ge=1, le=100) ): """ 刀具耗材搜索接口(复用查询逻辑) @@ -162,18 +178,18 @@ async def search_cutters( summary="新增刀具耗材", description=""" 新增刀具耗材信息。 - + **必填字段:** - 品牌名称(brandName) - 刀具柜名称(cabinetName) - 刀具型号(cutterCode) - 单价(price) - 创建人(createUser) - + **可选字段:** - 刀头图片列表(imageUrlList) - 其他扩展字段(品牌编码、刀具类型、规格等) - + **注意:** - 单价必须大于0 - 图片列表为数组格式,每个图片包含name、newFilename、url字段 @@ -186,20 +202,20 @@ async def create_cutter(request: CreateCutterRequest): # 验证价格 if request.price <= 0: raise HTTPException(status_code=400, detail="单价必须大于0") - + # 调用原始API try: - result = api_client.create_cutter(request.dict()) - + result = api_client.create_cutter(request.model_dump()) + # 检查响应状态 if not result.get("success"): raise HTTPException( status_code=500, detail=result.get("msg", "新增失败") ) - + return result - + except HTTPException: raise except Exception as e: @@ -212,10 +228,10 @@ async def create_cutter(request: CreateCutterRequest): summary="修改刀具耗材", description=""" 修改指定刀具耗材的信息。 - + **必填字段:** - 刀具ID(cutter_id,通过URL路径传递) - + **可修改字段:** - 品牌名称(brandName) - 刀具柜名称(cabinetName) @@ -224,7 +240,7 @@ async def create_cutter(request: CreateCutterRequest): - 更新人(updateUser) - 刀头图片列表(imageUrlList) - 其他扩展字段 - + **注意:** - 只传递需要修改的字段,未传递的字段将保持原值 - 如果传递了price字段,必须大于0 @@ -238,24 +254,24 @@ async def update_cutter(cutter_id: int, request: UpdateCutterRequest): # 验证价格(如果传递了价格字段) if request.price is not None and request.price <= 0: raise HTTPException(status_code=400, detail="单价必须大于0") - + # 将URL路径中的ID赋值给请求体 request.id = cutter_id - + # 调用原始API try: # 只传递非空字段 - result = api_client.update_cutter(request.dict(exclude_none=True)) - + result = api_client.update_cutter(request.model_dump(exclude_none=True)) + # 检查响应状态 if not result.get("success"): raise HTTPException( status_code=500, detail=result.get("msg", "修改失败") ) - + return result - + except HTTPException: raise except Exception as e: @@ -268,14 +284,14 @@ async def update_cutter(cutter_id: int, request: UpdateCutterRequest): summary="批量删除刀具耗材", description=""" 批量删除刀具耗材记录。 - + **参数说明:** - ids: 要删除的刀具ID集合,多个ID用逗号分割 - + **使用场景:** - 删除单条记录:ids=123 - 批量删除:ids=123,456,789 - + **注意:** - 删除操作不可逆,请谨慎操作 - 建议在删除前进行二次确认 @@ -283,7 +299,7 @@ async def update_cutter(cutter_id: int, request: UpdateCutterRequest): """ ) async def delete_cutters( - ids: str = Query(..., description="要删除的刀具ID集合,多个ID用逗号分割(例如:1,2,3)") + ids: str = Query(..., description="要删除的刀具ID集合,多个ID用逗号分割(例如:1,2,3)") ): """ 批量删除刀具耗材接口 @@ -291,28 +307,28 @@ async def delete_cutters( # 验证ids参数 if not ids or ids.strip() == "": raise HTTPException(status_code=400, detail="删除ID不能为空") - + # 验证ids格式(应该是数字和逗号的组合) import re if not re.match(r'^\d+(,\d+)*$', ids.strip()): raise HTTPException( - status_code=400, + status_code=400, detail="ID格式不正确,应为数字或用逗号分隔的数字(例如:1,2,3)" ) - + # 调用原始API try: result = api_client.delete_cutters(ids) - + # 检查响应状态 if not result.get("success"): raise HTTPException( status_code=500, detail=result.get("msg", "删除失败") ) - + return result - + except HTTPException: raise except Exception as e: @@ -327,7 +343,7 @@ async def delete_cutters( summary="品牌信息分页查询", description=""" 查询刀具品牌信息,支持多维度筛选和分页。 - + **支持的查询条件:** - 品牌编码:模糊查询 - 品牌名称:模糊查询 @@ -336,7 +352,7 @@ async def delete_cutters( - 业务状态:精确匹配 - 创建人:精确匹配 - 创建时间范围:开始时间和结束时间 - + **注意:** - 所有查询条件均为可选,不传递任何条件时返回全部数据 - 时间范围可以只传开始时间或结束时间,也可以同时传递 @@ -345,16 +361,16 @@ async def delete_cutters( """ ) async def query_brands( - brandCode: Optional[str] = Query(None, description="品牌编码"), - brandName: Optional[str] = Query(None, description="品牌名称"), - corporateName: Optional[str] = Query(None, description="公司名称"), - supplierName: Optional[str] = Query(None, description="供应商名称"), - status: Optional[int] = Query(None, description="业务状态"), - createUser: Optional[int] = Query(None, description="创建人 ID"), - startTime: Optional[str] = Query(None, description="创建开始时间(格式:YYYY-MM-DD HH:mm:ss)"), - endTime: Optional[str] = Query(None, description="创建结束时间(格式:YYYY-MM-DD HH:mm:ss)"), - current: int = Query(1, description="当前页码", ge=1), - size: int = Query(10, description="每页数量", ge=1, le=100) + brandCode: Optional[str] = Query(None, description="品牌编码"), + brandName: Optional[str] = Query(None, description="品牌名称"), + corporateName: Optional[str] = Query(None, description="公司名称"), + supplierName: Optional[str] = Query(None, description="供应商名称"), + status: Optional[int] = Query(None, description="业务状态"), + createUser: Optional[int] = Query(None, description="创建人 ID"), + startTime: Optional[str] = Query(None, description="创建开始时间(格式:YYYY-MM-DD HH:mm:ss)"), + endTime: Optional[str] = Query(None, description="创建结束时间(格式:YYYY-MM-DD HH:mm:ss)"), + current: int = Query(1, description="当前页码", ge=1), + size: int = Query(10, description="每页数量", ge=1, le=100) ): """ 品牌信息分页查询接口 @@ -364,7 +380,7 @@ async def query_brands( # 简单验证:开始时间不能大于结束时间 if startTime > endTime: raise HTTPException(status_code=400, detail="开始时间不能大于结束时间") - + # 构建查询参数 query_params = BrandQueryParams( brandCode=brandCode, @@ -378,20 +394,20 @@ async def query_brands( current=current, size=size ) - + # 调用原始API try: - result = api_client.get_brand_list(query_params.dict(exclude_none=False)) - + result = api_client.get_brand_list(query_params.model_dump(exclude_none=False)) + # 检查响应状态 if not result.get("success"): raise HTTPException( status_code=500, detail=result.get("msg", "查询失败") ) - + return result - + except HTTPException: raise except Exception as e: @@ -404,7 +420,7 @@ async def query_brands( summary="新增品牌信息", description=""" 新增刀具品牌信息。 - + **必填字段:** - 品牌编码(brandCode) - 品牌名称(brandName) @@ -412,11 +428,11 @@ async def query_brands( - 供应商名称(supplierName) - 供应商联系人(supplierUser) - 联系方式(phone) - + **可选字段:** - 创建部门(createDept) - 业务状态(status) - + **注意:** - 新增时不需要传递id字段 - 联系方式建议使用手机号码格式 @@ -429,24 +445,24 @@ async def create_brand(request: SubmitBrandRequest): # 验证联系方式格式(简单验证) if request.phone and len(request.phone.strip()) == 0: raise HTTPException(status_code=400, detail="联系方式不能为空") - + # 新增时不应该有id if request.id is not None: raise HTTPException(status_code=400, detail="新增品牌时不应该传递id字段") - + # 调用原始API try: - result = api_client.submit_brand(request.dict()) - + result = api_client.submit_brand(request.model_dump()) + # 检查响应状态 if not result.get("success"): raise HTTPException( status_code=500, detail=result.get("msg", "新增失败") ) - + return result - + except HTTPException: raise except Exception as e: @@ -459,7 +475,7 @@ async def create_brand(request: SubmitBrandRequest): summary="修改品牌信息", description=""" 修改指定品牌的信息。 - + **必填字段:** - 品牌 ID(brand_id,通过URL路径传递) - 品牌编码(brandCode) @@ -468,11 +484,11 @@ async def create_brand(request: SubmitBrandRequest): - 供应商名称(supplierName) - 供应商联系人(supplierUser) - 联系方式(phone) - + **可选字段:** - 创建部门(createDept) - 业务状态(status) - + **注意:** - 修改时必须提供品牌ID - 所有字段都需要传递,即使不修改也要传递原值 @@ -485,23 +501,23 @@ async def update_brand(brand_id: int, request: SubmitBrandRequest): # 验证联系方式格式(简单验证) if request.phone and len(request.phone.strip()) == 0: raise HTTPException(status_code=400, detail="联系方式不能为空") - + # 将URL路径中的ID赋值给请求体 request.id = brand_id - + # 调用原始API try: - result = api_client.submit_brand(request.dict()) - + result = api_client.submit_brand(request.model_dump()) + # 检查响应状态 if not result.get("success"): raise HTTPException( status_code=500, detail=result.get("msg", "修改失败") ) - + return result - + except HTTPException: raise except Exception as e: @@ -514,14 +530,14 @@ async def update_brand(brand_id: int, request: SubmitBrandRequest): summary="批量删除品牌信息", description=""" 批量删除品牌信息记录。 - + **参数说明:** - ids: 要删除的品牌ID集合,多个ID用逗号分割 - + **使用场景:** - 删除单条记录:ids=123 - 批量删除:ids=123,456,789 - + **注意:** - 删除操作不可逆,请谨慎操作 - 建议在删除前进行二次确认 @@ -530,7 +546,7 @@ async def update_brand(brand_id: int, request: SubmitBrandRequest): """ ) async def delete_brands( - ids: str = Query(..., description="要删除的品牌ID集合,多个ID用逗号分割(例如:1,2,3)") + ids: str = Query(..., description="要删除的品牌ID集合,多个ID用逗号分割(例如:1,2,3)") ): """ 批量删除品牌信息接口 @@ -538,28 +554,28 @@ async def delete_brands( # 验证ids参数 if not ids or ids.strip() == "": raise HTTPException(status_code=400, detail="删除ID不能为空") - + # 验证ids格式(应该是数字和逗号的组合) import re if not re.match(r'^\d+(,\d+)*$', ids.strip()): raise HTTPException( - status_code=400, + status_code=400, detail="ID格式不正确,应为数字或用逗号分隔的数字(例如:1,2,3)" ) - + # 调用原始API try: result = api_client.delete_brands(ids) - + # 检查响应状态 if not result.get("success"): raise HTTPException( status_code=500, detail=result.get("msg", "删除失败") ) - + return result - + except HTTPException: raise except Exception as e: @@ -574,14 +590,14 @@ async def delete_brands( summary="收刀柜信息查询", description=""" 查询收刀柜货道信息,支持多维度筛选。 - + **支持的查询条件:** - 刀柜编码:精确匹配 - 库位号:精确匹配 - 柜子面:ABCDE面 - 库位状态:精确匹配 - 绑定状态:0-非禁用 1-禁用 - + **展示字段:** - 库位号(stockLoc) - 柜子面(locPrefix) @@ -593,20 +609,20 @@ async def delete_brands( - 绑定状态(isBan) - 绑定刀具型号(cutterCode) - 最近更新时间(warehouseInTime) - + **注意:** - 所有查询条件均为可选,不传递任何条件时返回全部收刀柜数据 - 此接口同时支持搜索和刷新功能 """ ) async def query_stock_put_cabinets( - cabinetCode: Optional[str] = Query(None, description="刀柜编码"), - stockLoc: Optional[str] = Query(None, description="库位号"), - locPrefix: Optional[str] = Query(None, description="柜子面(ABCDE)"), - stockStatus: Optional[int] = Query(None, description="库位状态"), - isBan: Optional[str] = Query(None, description="绑定状态(0:非禁用 1:禁用)"), - borrowStatus: Optional[int] = Query(None, description="还刀状态(0:修磨 1:报废 2:换线 3:错领)"), - storageType: Optional[int] = Query(None, description="暂存类型(0:公共暂存 1:个人暂存 2:扩展取刀)") + cabinetCode: Optional[str] = Query(None, description="刀柜编码"), + stockLoc: Optional[str] = Query(None, description="库位号"), + locPrefix: Optional[str] = Query(None, description="柜子面(ABCDE)"), + stockStatus: Optional[int] = Query(None, description="库位状态"), + isBan: Optional[str] = Query(None, description="绑定状态(0:非禁用 1:禁用)"), + borrowStatus: Optional[int] = Query(None, description="还刀状态(0:修磨 1:报废 2:换线 3:错领)"), + storageType: Optional[int] = Query(None, description="暂存类型(0:公共暂存 1:个人暂存 2:扩展取刀)") ): """ 收刀柜信息查询接口 @@ -621,20 +637,20 @@ async def query_stock_put_cabinets( borrowStatus=borrowStatus, storageType=storageType ) - + # 调用原始API try: - result = api_client.get_stock_put_list(query_params.dict(exclude_none=False)) - + result = api_client.get_stock_put_list(query_params.model_dump(exclude_none=False)) + # 检查响应状态 if not result.get("success"): raise HTTPException( status_code=500, detail=result.get("msg", "查询失败") ) - + return result - + except HTTPException: raise except Exception as e: @@ -647,14 +663,14 @@ async def query_stock_put_cabinets( summary="货道解绑耗材(清空刀具数量)", description=""" 解绑指定货道的刀具耗材,清空刀具数量。 - + **参数说明:** - stock_id: 刀柜货道主键(通过URL路径传递) - + **操作效果:** - 清空该货道的刀具绑定关系 - 重置刀具数量为0 - + **注意:** - 解绑操作不可逆,请谨慎操作 - 建议在解绑前进行二次确认 @@ -668,16 +684,16 @@ async def unbind_stock_cutter(stock_id: int): # 调用原始API try: result = api_client.unbind_stock_cutter(stock_id) - + # 检查响应状态 if not result.get("success"): raise HTTPException( status_code=500, detail=result.get("msg", "解绑失败") ) - + return result - + except HTTPException: raise except Exception as e: @@ -690,15 +706,15 @@ async def unbind_stock_cutter(stock_id: int): summary="货道禁用/启用库位", description=""" 禁用或启用指定货道的库位。 - + **参数说明:** - stock_id: 刀柜货道主键(通过URL路径传递) - is_ban: 禁用状态(0-启用 1-禁用) - + **操作效果:** - 禁用:该货道不能进行取刀/还刀操作 - 启用:恢复该货道的正常使用 - + **注意:** - 禁用后不影响已有数据,只是暂时停用 - 启用后可以继续使用 @@ -706,8 +722,8 @@ async def unbind_stock_cutter(stock_id: int): """ ) async def change_stock_ban_status( - stock_id: int, - is_ban: int = Query(..., description="禁用状态(0:启用 1:禁用)", ge=0, le=1) + stock_id: int, + is_ban: int = Query(..., description="禁用状态(0:启用 1:禁用)", ge=0, le=1) ): """ 货道禁用/启用接口 @@ -715,16 +731,16 @@ async def change_stock_ban_status( # 调用原始API try: result = api_client.change_stock_ban_status(stock_id, is_ban) - + # 检查响应状态 if not result.get("success"): raise HTTPException( status_code=500, detail=result.get("msg", "操作失败") ) - + return result - + except HTTPException: raise except Exception as e: @@ -737,12 +753,12 @@ async def change_stock_ban_status( summary="货道统计数据查询", description=""" 查询收刀柜货道的统计数据。 - + **支持的查询条件:** - 刀柜编码:精确匹配 - 柜子面:ABCDE面 - 库位类型:收刀柜(0) 或 取刀柜(1),默认0 - + **返回统计数据:** - 货道总数(totalNum) - 禁用数量(disableNum) @@ -750,12 +766,12 @@ async def change_stock_ban_status( - 占用数量(workNum) - 库存告警值(makeAlarm) - 总库存金额(totalAmount,扩展字段) - + **使用场景:** - - 收刀柜货道管理页面的“货道统计”按钮 + - 收刀柜货道管理页面的"货道统计"按钮 - 可按刀柜编码和柜子面进行筛选 - 默认查询收刀柜的统计数据 - + **注意:** - 所有查询条件均为可选 - 不传递任何条件时返回所有收刀柜的统计数据 @@ -763,9 +779,9 @@ async def change_stock_ban_status( """ ) async def get_stock_statistics( - cabinetCode: Optional[str] = Query(None, description="刀柜编码"), - locPrefix: Optional[str] = Query(None, description="柜子面(ABCDE)"), - locType: Optional[int] = Query(0, description="库位类型(收刀柜:0 取刀柜:1)") + cabinetCode: Optional[str] = Query(None, description="刀柜编码"), + locPrefix: Optional[str] = Query(None, description="柜子面(ABCDE)"), + locType: Optional[int] = Query(0, description="库位类型(收刀柜:0 取刀柜:1)") ): """ 货道统计数据查询接口 @@ -776,20 +792,20 @@ async def get_stock_statistics( locPrefix=locPrefix, locType=locType ) - + # 调用原始API try: - result = api_client.get_stock_statistical_num(query_params.dict(exclude_none=False)) - + result = api_client.get_stock_statistical_num(query_params.model_dump(exclude_none=False)) + # 检查响应状态 if not result.get("success"): raise HTTPException( status_code=500, detail=result.get("msg", "查询失败") ) - + return result - + except HTTPException: raise except Exception as e: @@ -804,7 +820,7 @@ async def get_stock_statistics( summary="取刀柜信息查询", description=""" 查询取刀柜货道信息,支持多维度筛选。 - + **支持的查询条件:** - 品牌编码:精确匹配 - 刀柜编码:精确匹配 @@ -812,7 +828,7 @@ async def get_stock_statistics( - 刀具类型:精确匹配 - 柜子面:ABCDE面 - 库位号:精确匹配 - + **展示字段:** - 品牌名称(brandName) - 刀具型号(cutterCode) @@ -826,22 +842,22 @@ async def get_stock_statistics( - 暂存类型(storageType) - 绑定状态(isBan) - 刀柜编码(cabinetCode) - + **注意:** - 所有查询条件均为可选,不传递任何条件时返回全部取刀柜数据 - 此接口同时支持搜索和刷新功能 """ ) async def query_stock_take_cabinets( - brandCode: Optional[str] = Query(None, description="品牌编码"), - cabinetCode: Optional[str] = Query(None, description="刀柜编码"), - cutterCode: Optional[str] = Query(None, description="刀具型号"), - cutterType: Optional[str] = Query(None, description="刀具类型"), - locPrefix: Optional[str] = Query(None, description="柜子面(ABCDE)"), - stockLoc: Optional[str] = Query(None, description="库位号"), - cutterOrBrand: Optional[str] = Query(None, description="耗材型号或品牌"), - materialCode: Optional[str] = Query(None, description="物料编码"), - specification: Optional[str] = Query(None, description="规格") + brandCode: Optional[str] = Query(None, description="品牌编码"), + cabinetCode: Optional[str] = Query(None, description="刀柜编码"), + cutterCode: Optional[str] = Query(None, description="刀具型号"), + cutterType: Optional[str] = Query(None, description="刀具类型"), + locPrefix: Optional[str] = Query(None, description="柜子面(ABCDE)"), + stockLoc: Optional[str] = Query(None, description="库位号"), + cutterOrBrand: Optional[str] = Query(None, description="耗材型号或品牌"), + materialCode: Optional[str] = Query(None, description="物料编码"), + specification: Optional[str] = Query(None, description="规格") ): """ 取刀柜信息查询接口 @@ -858,20 +874,20 @@ async def query_stock_take_cabinets( materialCode=materialCode, specification=specification ) - + # 调用原始API try: - result = api_client.get_stock_take_list(query_params.dict(exclude_none=False)) - + result = api_client.get_stock_take_list(query_params.model_dump(exclude_none=False)) + # 检查响应状态 if not result.get("success"): raise HTTPException( status_code=500, detail=result.get("msg", "查询失败") ) - + return result - + except HTTPException: raise except Exception as e: @@ -884,14 +900,14 @@ async def query_stock_take_cabinets( summary="取刀柜货道解绑耗材", description=""" 解绑指定取刀柜货道的刀具耗材,清空刀具数量。 - + **参数说明:** - stock_id: 刀柜货道主键(通过URL路径传递) - + **操作效果:** - 清空该货道的刀具绑定关系 - 重置刀具数量为0 - + **注意:** - 解绑操作不可逆,请谨慎操作 - 建议在解绑前进行二次确认 @@ -906,16 +922,16 @@ async def unbind_stock_take_cutter(stock_id: int): # 调用原始API(与收刀柜使用相同API) try: result = api_client.unbind_stock_cutter(stock_id) - + # 检查响应状态 if not result.get("success"): raise HTTPException( status_code=500, detail=result.get("msg", "解绑失败") ) - + return result - + except HTTPException: raise except Exception as e: @@ -928,15 +944,15 @@ async def unbind_stock_take_cutter(stock_id: int): summary="取刀柜货道禁用/启用库位", description=""" 禁用或启用指定取刀柜货道的库位。 - + **参数说明:** - stock_id: 刀柜货道主键(通过URL路径传递) - is_ban: 禁用状态(0-启用 1-禁用) - + **操作效果:** - 禁用:该货道不能进行取刀/还刀操作 - 启用:恢复该货道的正常使用 - + **注意:** - 禁用后不影响已有数据,只是暂时停用 - 启用后可以继续使用 @@ -945,8 +961,8 @@ async def unbind_stock_take_cutter(stock_id: int): """ ) async def change_stock_take_ban_status( - stock_id: int, - is_ban: int = Query(..., description="禁用状态(0:启用 1:禁用)", ge=0, le=1) + stock_id: int, + is_ban: int = Query(..., description="禁用状态(0:启用 1:禁用)", ge=0, le=1) ): """ 取刀柜货道禁用/启用接口(复用收刀柜禁用/启用接口) @@ -954,16 +970,16 @@ async def change_stock_take_ban_status( # 调用原始API(与收刀柜使用相同API) try: result = api_client.change_stock_ban_status(stock_id, is_ban) - + # 检查响应状态 if not result.get("success"): raise HTTPException( status_code=500, detail=result.get("msg", "操作失败") ) - + return result - + except HTTPException: raise except Exception as e: @@ -978,21 +994,21 @@ async def change_stock_take_ban_status( summary="预补刀查询(检查是否可以补刀)", description=""" 批量预补刀查询,获取指定刀柜中哪些货道可以补刀。 - + **参数说明:** - cabinet_code: 刀柜编码(通过URL路径传递) - + **返回数据:** - successStock: 可以补刀的货道列表 - 包含:刀柜编码、库位号、货道容量、补货前数量、补货后数量 - errorStock: 不能补刀的货道列表 - 包含:刀柜编码、库位号、失败原因 - + **使用场景:** - - 点击“预补刀”按钮时调用 + - 点击"预补刀"按钮时调用 - 在实际补刀前检查哪些货道需要补充 - 展示补刀预览信息给用户确认 - + **注意:** - 此接口仅查询,不会实际补刀 - 需要用户确认后再调用补刀接口 @@ -1006,16 +1022,16 @@ async def pre_batch_plug(cabinet_code: str): # 调用原始API try: result = api_client.pre_batch_plug(cabinet_code) - + # 检查响应状态 if not result.get("success"): raise HTTPException( status_code=500, detail=result.get("msg", "查询失败") ) - + return result - + except HTTPException: raise except Exception as e: @@ -1028,20 +1044,20 @@ async def pre_batch_plug(cabinet_code: str): summary="批量一键补刀", description=""" 执行批量一键补刀操作,对指定刀柜中需要补充的货道进行补刀。 - + **参数说明:** - cabinet_code: 刀柜编码(通过URL路径传递) - + **操作效果:** - 对所有可补充的货道进行补刀 - 补充到货道的最大容量 - 自动更新库存数量 - + **使用场景:** - - 点击“补刀”按钮时调用 + - 点击"补刀"按钮时调用 - 在预补刀查询后用户确认执行 - 定期维护时批量补充刀具 - + **注意:** - 此操作不可逆,请谨慎操作 - 建议先调用预补刀接口查看预览 @@ -1056,17 +1072,916 @@ async def on_pre_batch_plug(cabinet_code: str): # 调用原始API try: result = api_client.on_pre_batch_plug(cabinet_code) - + # 检查响应状态 if not result.get("success"): raise HTTPException( status_code=500, detail=result.get("msg", "补刀失败") ) - + return result - + except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=f"服务器错误: {str(e)}") + + +# ==================== 统计接口(修正版) ==================== + +@router.get( + "/storage-statistics", + response_model=StorageStatisticsResponse, + summary="出入库统计列表", + description=""" + 获取刀具的出入库记录统计信息。 + + **功能:** 查询刀具的出入库记录统计信息 + + **参数说明:** + - current: 当前页码,默认1 + - size: 每页数量,默认10 + - startTime: 开始时间(格式:YYYY-MM-DD HH:mm:ss) + - endTime: 结束时间(格式:YYYY-MM-DD HH:mm:ss) + - recordStatus: 记录状态 + * 0: 取刀 + * 1: 还刀 + * 2: 收刀 + * 3: 暂存 + * 4: 完成 + * 5: 违规还刀 + - rankingType: 排名类型 + * 0: 按数量排序 + * 1: 按金额排序 + - order: 排序顺序 + * 0: 从大到小(降序) + * 1: 从小到大(升序) + + **返回数据:** + - code: 状态码 + - msg: 返回消息 + - success: 是否成功 + - data: 分页数据 + * current: 当前页 + * size: 每页数量 + * total: 总记录数 + * pages: 总页数 + * records: 记录列表 + """ +) +async def get_storage_statistics( + current: Optional[int] = Query(1, ge=1, description="当前页"), + size: Optional[int] = Query(10, ge=1, le=100, description="每页数量"), + start_time: Optional[str] = Query(None, alias="startTime", description="开始时间(YYYY-MM-DD HH:mm:ss)"), + end_time: Optional[str] = Query(None, alias="endTime", description="结束时间(YYYY-MM-DD HH:mm:ss)"), + record_status: Optional[int] = Query(None, alias="recordStatus", + description="记录状态:0-取刀,1-还刀,2-收刀,3-暂存,4-完成,5-违规还刀"), + ranking_type: Optional[int] = Query(None, alias="rankingType", + description="排名类型:0-按数量排序,1-按金额排序"), + order: Optional[int] = Query(None, description="排序顺序:0-从大到小(降序),1-从小到大(升序)") +): + """ + 出入库统计列表接口 + """ + try: + # 构建查询参数 + params = { + "current": current, + "size": size, + "startTime": start_time, + "endTime": end_time, + "recordStatus": record_status, + "rankingType": ranking_type, + "order": order + } + + # 调用API客户端方法获取数据 + result = api_client.get_storage_statistics(params) + + return result + + except Exception as e: + raise HTTPException(status_code=500, detail=f"获取出入库统计数据失败: {str(e)}") + + +@router.get( + "/export-stock-record", + response_model=StorageStatisticsResponse, + summary="出入库记录导出", + description=""" + 导出刀具耗材数据(出入库记录)。 + + **功能:** 导出刀具的出入库记录数据,返回JSON格式 + + **参数说明:** + - startTime: 开始时间 + - endTime: 结束时间 + - recordStatus: 记录状态 + * 0: 取刀 + * 1: 还刀 + * 2: 收刀 + * 3: 暂存 + * 4: 完成 + * 5: 违规还刀 + - rankingType: 排名类型 + * 0: 按数量排序 + * 1: 按金额排序 + - order: 排序顺序 + * 0: 从大到小(降序) + * 1: 从小到大(升序) + + **返回数据:** + - code: 状态码 + - msg: 返回消息 + - success: 是否成功 + - data: 导出数据 + * records: 记录列表(包含完整的出入库信息) + - account: 用户名 + - name: 用户名称 + - brandName: 品牌名称 + - brandCode: 品牌编码 + - cutterType: 刀具类型 + - cutterCode: 刀具型号 + - specification: 规格 + - quantity: 数量 + - price: 单价 + - oldPrice: 历史单价 + - stockLoc: 库位号 + - stockType: 库存类型(0:入库 1:出库) + - status: 业务状态 + - cabinetName: 刀具柜名称 + - cabinetCode: 刀具柜编码 + - factoryName: 工厂名称 + - workshopName: 车间名称 + - operator: 操作人 + - createTime: 创建时间 + - updateTime: 更新时间 + - detailsName: 操作详情 + - remake: 备注 + - 及其他字段... + """ +) +async def export_stock_record( + start_time: Optional[str] = Query(None, alias="startTime", description="开始时间(YYYY-MM-DD HH:mm:ss)"), + end_time: Optional[str] = Query(None, alias="endTime", description="结束时间(YYYY-MM-DD HH:mm:ss)"), + record_status: Optional[int] = Query(None, alias="recordStatus", + description="记录状态:0-取刀,1-还刀,2-收刀,3-暂存,4-完成,5-违规还刀"), + ranking_type: Optional[int] = Query(None, alias="rankingType", + description="排名类型:0-按数量排序,1-按金额排序"), + order: Optional[int] = Query(None, description="排序顺序:0-从大到小(降序),1-从小到大(升序)") +): + """ + 出入库记录导出接口 + """ + try: + # 构建查询参数 + params = { + "startTime": start_time, + "endTime": end_time, + "recordStatus": record_status, + "rankingType": ranking_type, + "order": order + } + + # 调用API客户端方法获取导出数据 + result = api_client.export_stock_record(params) + + return result + + except Exception as e: + raise HTTPException(status_code=500, detail=f"导出出入库记录失败: {str(e)}") + + +@router.get( + "/charts-accumulated", + response_model=ChartsResponse, + summary="刀具消耗统计", + description=""" + 获取刀具的消耗统计数据。 + + **功能:** 查询刀具的消耗统计数据,支持按刀具类型或统计指标展示 + + **请求参数:** 无 + + **返回数据:** + - code: 状态码 + - msg: 返回消息 + - success: 是否成功 + - data: 统计数据 + * titleList: 统计项标题列表(刀具类型或统计指标) + * dataList: 对应的数据列表 + + **数据展示方式:** + + 方式1 - 按刀具类型统计: + - titleList 包含:车刀片、铣刀、钻头、镑刀等刀具类型 + - dataList 包含:各类刀具的累计消耗数量或使用次数 + + 方式2 - 按统计指标展示: + - titleList 包含: + * 累计使用次数:刀具被使用的总次数 + * 累计使用时长:刀具累计工作时间 + * 平均寿命:刀具平均使用寿命 + * 更换次数:刀具被更换的次数 + * 使用效率:刀具使用效率百分比 + - dataList 包含:各指标对应的数值 + """ +) +async def get_charts_accumulated(): + """ + 刀具消耗统计接口 + """ + try: + # 调用API客户端方法获取数据 + result = api_client.get_charts_accumulated() + return result + except Exception as e: + raise HTTPException(status_code=500, detail=f"获取刀具消耗统计失败: {str(e)}") + + +@router.get( + "/total-stock", + response_model=TotalStockResponse, + summary="总库存统计列表", + description=""" + 获取刀柜库存统计数据,支持多条件搜索和列表刷新。 + + **功能:** 查询刀柜库存统计数据,支持多条件搜索和列表刷新 + + **请求参数:** + - statisticsType: 统计类型(可选) + - brandName: 品牌名称(可选) + - cabinetCode: 刀柜编码(可选) + - cutterType: 刀具类型(可选) + - stockStatus: 库位状态(可选) + - current: 当前页,默认1 + - size: 每页数量,默认10 + + **返回数据:** + - code: 状态码 + - msg: 返回消息 + - success: 是否成功 + - data: 库存列表数据(数组格式) + * id: 主键id + * cabinetCode: 刀柜编码 + * stockLoc: 库位号 + * brandName: 品牌名称 + * cutterCode: 刀具型号 + * cutterType: 刀具类型 + * specification: 规格 + * locCapacity: 库位容量 + * locSurplus: 剩余数量(货道库存) + * stockStatus: 库位状态 + * price: 单价 + * stockValue: 库存价值(后端计算:单价 * 剩余数量) + * cabinetSide: 柜子面 + * warehouseInTime: 入库时间 + * updateTime: 更新时间 + + **使用场景:** + 1. **搜索功能**:根据统计类型、品牌名称、刀柜编码、刀具类型、库位状态进行查询 + 2. **刷新功能**:不带搜索条件,获取所有库存数据 + 3. **分页显示**:支持分页查询,适合前端表格展示 + """ +) +async def get_total_stock( + statistics_type: Optional[str] = Query(None, alias="statisticsType", description="统计类型"), + brand_name: Optional[str] = Query(None, alias="brandName", description="品牌名称"), + cabinet_code: Optional[str] = Query(None, alias="cabinetCode", description="刀柜编码"), + cutter_type: Optional[str] = Query(None, alias="cutterType", description="刀具类型"), + stock_status: Optional[int] = Query(None, alias="stockStatus", description="库位状态"), + current: Optional[int] = Query(1, ge=1, description="当前页"), + size: Optional[int] = Query(10, ge=1, le=100, description="每页数量") +): + """ + 总库存统计列表接口 + """ + try: + # 构建查询参数 + params = { + "statisticsType": statistics_type, + "brandName": brand_name, + "cabinetCode": cabinet_code, + "cutterType": cutter_type, + "stockStatus": stock_status, + "current": current, + "size": size + } + + # 调用API客户端方法获取数据 + result = api_client.get_total_stock_list(params) + + return result + + except Exception as e: + raise HTTPException(status_code=500, detail=f"获取总库存统计列表失败: {str(e)}") + + +@router.get( + "/stock-location/{stock_id}", + response_model=StockLocationDetailResponse, + summary="总库存统计详情", + description=""" + 获取单个库位的详细信息(总库存统计的一部分)。 + + **功能:** 根据库位主键查询单个库位的详细信息 + + **请求参数:** + - stock_id: 刀柜货道主键(路径参数) + + **返回数据:** + - code: 状态码 + - msg: 返回消息 + - success: 是否成功 + - data: 库位详细信息(包含所有字段) + + **使用场景:** + - 查看单个库位的完整信息 + - 点击表格行查看详情 + """ +) +async def get_stock_location_detail(stock_id: int): + """ + 总库存统计详情接口(单个库位详情) + """ + try: + # 调用API客户端方法获取数据 + result = api_client.get_stock_location_by_id(stock_id) + + return result + + except Exception as e: + raise HTTPException(status_code=500, detail=f"获取库位详情失败: {str(e)}") + + +@router.get( + "/waste-knife-recycle", + response_model=WasteKnifeRecycleResponse, + summary="废刀回收统计", + description=""" + 获取废刀回收统计信息(收刀柜还刀信息)。 + + **功能:** 查询收刀柜中的还刀信息,用于废刀回收统计和管理 + + **请求参数(全部可选):** + - borrowCode: 还刀码 + - cabinetCode: 刀柜编码 + - stockLoc: 刀柜库位号 + + **返回数据:** + - code: 状态码 + - msg: 返回消息 + - success: 是否成功 + - data: 还刀数据 + * borrowStatus: 还刀状态(字符串) + * cabinetCode: 刀柜编码 + * recordStatus: 记录状态:0-取刀,1-还刀,2-收刀,3-暂存 + * list: 还刀详情列表 + - id: 取刀主键 + - borrowStatus: 还刀状态:0-修磨,1-报废,2-换线,3-错领 + - borrowTime: 还刀时间 + - borrowUserName: 还刀人 + - brandName: 品牌名称 + - cutterCode: 刀具型号 + - cutterType: 刀具类型 + - lendTime: 取刀时间 + - lendUserName: 借刀人 + - recordStatus: 记录状态 + - specification: 规格 + - stockLoc: 库位号 + + **使用场景:** + 1. **废刀回收统计**:查看所有报废刀具(borrowStatus=1) + 2. **修磨管理**:查看需要修磨的刀具(borrowStatus=0) + 3. **错领追踪**:查询错领刀具情况(borrowStatus=3) + 4. **换线管理**:统计需要换线的刀具(borrowStatus=2) + """ +) +async def get_waste_knife_recycle( + borrow_code: Optional[str] = Query(None, alias="borrowCode", description="还刀码"), + cabinet_code: Optional[str] = Query(None, alias="cabinetCode", description="刀柜编码"), + stock_loc: Optional[str] = Query(None, alias="stockLoc", description="刀柜库位号") +): + """ + 废刀回收统计接口 + """ + try: + # 构建查询参数 + params = { + "borrowCode": borrow_code, + "cabinetCode": cabinet_code, + "stockLoc": stock_loc + } + + # 调用API客户端方法获取数据 + result = api_client.get_waste_knife_recycle_info(params) + + return result + + except Exception as e: + raise HTTPException(status_code=500, detail=f"获取废刀回收统计信息失败: {str(e)}") + + +# ==================== 合并自 系统记录 模块的接口 ==================== +# 合并日期: 2025-11-15 +# 来源: teamleader_record/lend_record_router.py + +# 领刀记录相关路由 (班组长) +@router.get("/list", response_model=LendRecordResponse, tags=["班组长记录"]) +async def get_lend_record_list( + current: int = Query(1, ge=1, description="页码"), + size: int = Query(20, ge=1, le=100, description="每页数量"), + keyword: Optional[str] = Query(None, description="关键字搜索"), + department: Optional[str] = Query(None, description="部门筛选"), + startTime: Optional[str] = Query(None, description="开始时间"), + endTime: Optional[str] = Query(None, description="结束时间"), + order: Optional[int] = Query(None, description="顺序 0: 从大到小 1:从小到大"), + rankingType: Optional[int] = Query(None, description="0: 数量 1: 金额"), + recordStatus: Optional[int] = Query(None, description="0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀") +): + """ + 获取领刀记录列表 (班组长) + + Args: + current: 页码,默认为1 + size: 每页数量,默认为20,最大100 + keyword: 关键字搜索 + department: 部门筛选 + startTime: 开始时间 + endTime: 结束时间 + order: 顺序 0: 从大到小 1:从小到大 + rankingType: 0: 数量 1: 金额 + recordStatus: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + + Returns: + LendRecordResponse: 领刀记录列表响应 + """ + result = api_client.get_lend_records( + current=current, + size=size, + keyword=keyword, + department=department, + startTime=startTime, + endTime=endTime, + order=order, + rankingType=rankingType, + recordStatus=recordStatus + ) + + return result + + +@router.get("/export", tags=["班组长记录"]) +async def export_lend_records( + endTime: Optional[str] = Query(None, description="结束时间"), + order: Optional[int] = Query(None, description="顺序 0: 从大到小 1:从小到大"), + rankingType: Optional[int] = Query(None, description="0: 数量 1: 金额"), + recordStatus: Optional[int] = Query(None, description="0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀"), + startTime: Optional[str] = Query(None, description="开始时间") +): + """ + 导出领刀记录 (班组长) + + Args: + endTime: 结束时间 + order: 顺序 0: 从大到小 1:从小到大 + rankingType: 0: 数量 1: 金额 + recordStatus: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + startTime: 开始时间 + + Returns: + Response: 包含导出文件的响应 + """ + try: + file_content = api_client.export_lend_records( + endTime=endTime, + order=order, + rankingType=rankingType, + recordStatus=recordStatus, + startTime=startTime + ) + + return Response( + content=file_content, + media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + headers={ + "Content-Disposition": "attachment; filename=lend_records.xlsx" + } + ) + except Exception as e: + return Response( + content=json.dumps({ + "code": -1, + "msg": f"导出失败: {str(e)}", + "data": None, + "success": False + }, ensure_ascii=False), + media_type="application/json" + ) + + +# 告警预警相关路由 (班组长) +@router.get("/alarm_list", response_model=AlarmWarningResponse, tags=["班组长记录"]) +async def list_alarm_warning( + loc_surplus: Optional[int] = Query(None, description="货道"), + alarm_level: Optional[int] = Query(None, description="预警等级"), + device_type: Optional[str] = Query(None, description="设备类型"), + cabinet_code: Optional[str] = Query(None, description="刀柜编码"), + brand_name: Optional[str] = Query(None, description="品牌名称"), + handle_status: Optional[int] = Query(None, description="处理状态"), + current: Optional[int] = Query(None, description="当前页"), + size: Optional[int] = Query(None, description="每页数量") +): + """ + 获取告警预警列表 (班组长) + + Args: + loc_surplus: 货道 + alarm_level: 预警等级 + device_type: 设备类型 + cabinet_code: 刀柜编码 + brand_name: 品牌名称 + handle_status: 处理状态 + current: 当前页 + size: 每页数量 + + Returns: + AlarmWarningResponse: 告警预警列表响应 + """ + result = api_client.list_alarm_warning( + locSurplus=loc_surplus, + alarmLevel=alarm_level, + deviceType=device_type, + cabinetCode=cabinet_code, + brandName=brand_name, + handleStatus=handle_status, + current=current, + size=size + ) + + return result + + +@router.get("/alarm_statistics", response_model=AlarmStatisticsResponse, tags=["班组长记录"]) +async def get_alarm_statistics(): + """ + 获取告警统计信息 (班组长) + + Returns: + AlarmStatisticsResponse: 告警统计信息响应 + """ + result = api_client.get_alarm_statistics() + + return result + + +@router.post("/alarm_threshold", tags=["班组长记录"]) +async def update_alarm_threshold( + request: ThresholdSettingRequest = Body(..., description="阈值设置请求参数") +): + """ + 更新告警阈值 (班组长) + + Args: + request: 阈值设置请求参数 + + Returns: + Response: 更新结果响应 + """ + result = api_client.update_alarm_threshold( + locSurplus=request.locSurplus, + alarmThreshold=request.alarmThreshold + ) + + return result + + +@router.post("/handle_alarm/{alarm_id}", tags=["班组长记录"]) +async def handle_alarm_warning( + alarm_id: int, + handle_status: int = Body(..., description="处理状态"), + handle_remark: Optional[str] = Body(None, description="处理备注") +): + """ + 处理告警预警 (班组长) + + Args: + alarm_id: 告警ID + handle_status: 处理状态 (0: 未处理, 1: 已处理, 2: 已忽略) + handle_remark: 处理备注 + + Returns: + Response: 处理结果响应 + """ + result = api_client.handle_alarm_warning( + id=alarm_id, + handleStatus=handle_status, + handleRemark=handle_remark + ) + + return result + + +@router.post("/batch_handle_alarm", tags=["班组长记录"]) +async def batch_handle_alarm_warning( + ids: List[int] = Body(..., description="告警ID列表"), + handle_status: int = Body(..., description="处理状态"), + handle_remark: Optional[str] = Body(None, description="处理备注") +): + """ + 批量处理告警预警 (班组长) + + Args: + ids: 告警ID列表 + handle_status: 处理状态 (0: 未处理, 1: 已处理, 2: 已忽略) + handle_remark: 处理备注 + + Returns: + Response: 处理结果响应 + """ + result = api_client.batch_handle_alarm_warning( + ids=ids, + handleStatus=handle_status, + handleRemark=handle_remark + ) + + return result + + +@router.get("/export_alarm", tags=["班组长记录"]) +async def export_alarm_warning( + loc_surplus: Optional[int] = Query(None, description="货道"), + alarm_level: Optional[int] = Query(None, description="预警等级"), + device_type: Optional[str] = Query(None, description="设备类型"), + cabinet_code: Optional[str] = Query(None, description="刀柜编码"), + brand_name: Optional[str] = Query(None, description="品牌名称"), + handle_status: Optional[int] = Query(None, description="处理状态") +): + """ + 导出告警预警 (班组长) + + Args: + loc_surplus: 货道 + alarm_level: 预警等级 + device_type: 设备类型 + cabinet_code: 刀柜编码 + brand_name: 品牌名称 + handle_status: 处理状态 + + Returns: + Response: 包含导出文件的响应 + """ + try: + file_content = api_client.export_alarm_warning( + locSurplus=loc_surplus, + alarmLevel=alarm_level, + deviceType=device_type, + cabinetCode=cabinet_code, + brandName=brand_name, + handleStatus=handle_status + ) + + return Response( + content=file_content, + media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + headers={ + "Content-Disposition": "attachment; filename=alarm_warnings.xlsx" + } + ) + except Exception as e: + return Response( + content=json.dumps({ + "code": -1, + "msg": f"导出失败: {str(e)}", + "data": None, + "success": False + }, ensure_ascii=False), + media_type="application/json" + ) + + +# 补货记录相关路由 (班组长) +@router.get("/replenish_list", response_model=ReplenishRecordResponse, tags=["班组长记录"]) +async def get_replenish_record_list( + current: Optional[int] = Query(None, description="当前页"), + end_time: Optional[str] = Query(None, description="结束时间"), + order: Optional[int] = Query(None, description="顺序 0: 从大到小 1:从小到大"), + ranking_type: Optional[int] = Query(None, description="0: 数量 1: 金额"), + record_status: Optional[int] = Query(None, description="0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀"), + size: Optional[int] = Query(None, description="每页的数量"), + start_time: Optional[str] = Query(None, description="开始时间") +): + """ + 获取补货记录列表 (班组长) + + Args: + current: 当前页 + end_time: 结束时间 + order: 顺序 0: 从大到小 1:从小到大 + ranking_type: 0: 数量 1: 金额 + record_status: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + size: 每页的数量 + start_time: 开始时间 + + Returns: + ReplenishRecordResponse: 补货记录列表响应,包含以下字段: + - lendUserName: 取出人 + - storageUserName: 暂存人 + - brandName: 品牌名称 + - cutterType: 电池类型 + - cutterCode: 刀具型号 + - specification: 规格 + - quantity: 数量 + - oldPrice: 老单价 + - newPrice: 新单价 + - oldStockNum: 操作前库存数 + - newStockNum: 操作后库存数 + - stockLoc: 库位号 + - logType: 补货类型 + - status: 业务状态 + - cabinetCode: 刀柜编码 + - createTime: 创建时间 + - operator: 操作人 + - materialCode: 物料编码 + - detailsCode: 操作详情 + - remake: 备注 + - createDept: 创建部门 + - createUser: 创建人 + - updateUser: 更新人 + - tenantId: 租户ID + - isDeleted: 是否已删除 + """ + result = api_client.get_replenish_records( + current=current, + endTime=end_time, + order=order, + rankingType=ranking_type, + recordStatus=record_status, + size=size, + startTime=start_time + ) + + return result + + +@router.get("/export_replenish", tags=["班组长记录"]) +async def export_replenish_records( + end_time: Optional[str] = Query(None, description="结束时间"), + order: Optional[int] = Query(None, description="顺序 0: 从大到小 1:从小到大"), + ranking_type: Optional[int] = Query(None, description="0: 数量 1: 金额"), + record_status: Optional[int] = Query(None, description="0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀"), + start_time: Optional[str] = Query(None, description="开始时间") +): + """ + 导出补货记录 (班组长) + + Args: + end_time: 结束时间 + order: 顺序 0: 从大到小 1:从小到大 + ranking_type: 0: 数量 1: 金额 + record_status: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + start_time: 开始时间 + + Returns: + Response: 包含导出文件的响应,Excel文件包含以下字段: + - lendUserName: 取出人 + - storageUserName: 暂存人 + - brandName: 品牌名称 + - cutterType: 电池类型 + - cutterCode: 刀具型号 + - specification: 规格 + - quantity: 数量 + - oldPrice: 老单价 + - newPrice: 新单价 + - oldStockNum: 操作前库存数 + - newStockNum: 操作后库存数 + - stockLoc: 库位号 + - logType: 补货类型 + - status: 业务状态 + - cabinetCode: 刀柜编码 + - createTime: 创建时间 + - operator: 操作人 + - materialCode: 物料编码 + - detailsCode: 操作详情 + - remake: 备注 + - createDept: 创建部门 + - createUser: 创建人 + - updateUser: 更新人 + - tenantId: 租户ID + - isDeleted: 是否已删除 + """ + try: + file_content = api_client.export_replenish_records( + endTime=end_time, + order=order, + rankingType=ranking_type, + recordStatus=record_status, + startTime=start_time + ) + + return Response( + content=file_content, + media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + headers={ + "Content-Disposition": "attachment; filename=replenish_records.xlsx" + } + ) + except Exception as e: + return Response( + content=json.dumps({ + "code": -1, + "msg": f"导出失败: {str(e)}", + "data": None, + "success": False + }, ensure_ascii=False), + media_type="application/json" + ) + + +# 公共暂存记录相关路由 (班组长) +@router.get("/storage_list", response_model=StorageRecordResponse, tags=["班组长记录"]) +async def get_storage_record_list( + current: Optional[int] = Query(None, description="当前页"), + end_time: Optional[str] = Query(None, description="结束时间"), + order: Optional[int] = Query(None, description="顺序 0: 从大到小 1:从小到大"), + ranking_type: Optional[int] = Query(None, description="0: 数量 1: 金额"), + record_status: Optional[int] = Query(None, description="0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀"), + size: Optional[int] = Query(None, description="每页的数量"), + start_time: Optional[str] = Query(None, description="开始时间") +): + """ + 获取公共暂存记录列表 (班组长) + + Args: + current: 当前页 + end_time: 结束时间 + order: 顺序 0: 从大到小 1:从小到大 + ranking_type: 0: 数量 1: 金额 + record_status: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + size: 每页的数量 + start_time: 开始时间 + + Returns: + StorageRecordResponse: 公共暂存记录列表响应 + """ + result = api_client.get_storage_records( + current=current, + endTime=end_time, + order=order, + rankingType=ranking_type, + recordStatus=record_status, + size=size, + startTime=start_time + ) + + return result + + +@router.get("/export_storage", tags=["班组长记录"]) +async def export_storage_records( + end_time: Optional[str] = Query(None, description="结束时间"), + order: Optional[int] = Query(None, description="顺序 0: 从大到小 1:从小到大"), + ranking_type: Optional[int] = Query(None, description="0: 数量 1: 金额"), + record_status: Optional[int] = Query(None, description="0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀"), + start_time: Optional[str] = Query(None, description="开始时间") +): + """ + 导出公共暂存记录 (班组长) + + Args: + end_time: 结束时间 + order: 顺序 0: 从大到小 1:从小到大 + ranking_type: 0: 数量 1: 金额 + record_status: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + start_time: 开始时间 + + Returns: + Response: 包含导出文件的响应 + """ + try: + file_content = api_client.export_storage_records( + endTime=end_time, + order=order, + rankingType=ranking_type, + recordStatus=record_status, + startTime=start_time + ) + + return Response( + content=file_content, + media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + headers={ + "Content-Disposition": "attachment; filename=storage_records.xlsx" + } + ) + except Exception as e: + return Response( + content=json.dumps({ + "code": -1, + "msg": f"导出失败: {str(e)}", + "data": None, + "success": False + }, ensure_ascii=False), + media_type="application/json" + ) \ No newline at end of file diff --git a/teamleader/schemas/data_schemas.py b/teamleader/schemas/data_schemas.py index 423a209..8a5f5c9 100644 --- a/teamleader/schemas/data_schemas.py +++ b/teamleader/schemas/data_schemas.py @@ -1,5 +1,6 @@ -from pydantic import BaseModel -from typing import Optional, List +from pydantic import BaseModel, ConfigDict +from typing import Optional, List, Union, Any +from datetime import datetime # ==================== 刀具管理相关模型 ==================== @@ -7,79 +8,79 @@ # 刀具查询参数模型 class CutterQueryParams(BaseModel): """刀具耗材分页查询参数""" - brandName: Optional[str] = None # 品牌名称 - cabinetName: Optional[str] = None # 刀具柜名称 - createTime: Optional[str] = None # 创建时间 - createUser: Optional[int] = None # 创建人 - cutterType: Optional[str] = None # 刀具类型 - cutterCode: Optional[str] = None # 刀具型号 - minPrice: Optional[float] = None # 最低价格 - maxPrice: Optional[float] = None # 最高价格 - current: Optional[int] = 1 # 当前页 - size: Optional[int] = 10 # 每页数量 + brandName: Optional[str] = None # 品牌名称 + cabinetName: Optional[str] = None # 刀具柜名称 + createTime: Optional[str] = None # 创建时间 + createUser: Optional[int] = None # 创建人 + cutterType: Optional[str] = None # 刀具类型 + cutterCode: Optional[str] = None # 刀具型号 + minPrice: Optional[float] = None # 最低价格 + maxPrice: Optional[float] = None # 最高价格 + current: Optional[int] = 1 # 当前页 + size: Optional[int] = 10 # 每页数量 # 文件信息模型 class FileInfo(BaseModel): """文件信息""" - name: Optional[str] = None # 文件名 - newFilename: Optional[str] = None # 新文件名 - url: Optional[str] = None # 文件路径 + name: Optional[str] = None # 文件名 + newFilename: Optional[str] = None # 新文件名 + url: Optional[str] = None # 文件路径 # 刀柜耗材数量模型 class CabinetCutterInfo(BaseModel): """刀柜耗材数量""" - cabinetCode: Optional[str] = None # 刀柜编码 - cabinetName: Optional[str] = None # 刀具柜名称 - cutterId: Optional[int] = None # 耗材主键 - locSurplus: Optional[int] = None # 库位产品剩余[货道库存] - stockLoc: Optional[str] = None # 库位号 + cabinetCode: Optional[str] = None # 刀柜编码 + cabinetName: Optional[str] = None # 刀具柜名称 + cutterId: Optional[int] = None # 耗材主键 + locSurplus: Optional[int] = None # 库位产品剩余[货道库存] + stockLoc: Optional[str] = None # 库位号 # 刀具耗材详细信息模型 class CutterDetail(BaseModel): """刀具耗材详细信息""" - id: Optional[int] = None # 主键id - brandCode: Optional[str] = None # 品牌编码 - brandName: Optional[str] = None # 品牌名称 + id: Optional[int] = None # 主键id + brandCode: Optional[str] = None # 品牌编码 + brandName: Optional[str] = None # 品牌名称 cabinetList: Optional[List[CabinetCutterInfo]] = [] # 刀柜信息 - createDept: Optional[int] = None # 创建部门 - createTime: Optional[str] = None # 创建时间 - createUser: Optional[int] = None # 创建人 - cutterCode: Optional[str] = None # 刀具型号 - cutterType: Optional[str] = None # 刀具类型 - imageUrl: Optional[str] = None # 图片路径 + createDept: Optional[int] = None # 创建部门 + createTime: Optional[str] = None # 创建时间 + createUser: Optional[int] = None # 创建人 + cutterCode: Optional[str] = None # 刀具型号 + cutterType: Optional[str] = None # 刀具类型 + imageUrl: Optional[str] = None # 图片路径 imageUrlList: Optional[List[FileInfo]] = [] # 耗材图片集合 - inventoryWarning: Optional[int] = None # 库存警告 - isDeleted: Optional[int] = None # 是否已删除 - isUniqueCode: Optional[int] = None # 是否一刀一码(0:否 1:是) - materialCode: Optional[str] = None # 物料编码 - materialType: Optional[str] = None # 物料类型 - numberLife: Optional[int] = None # 寿命次数 - packQty: Optional[int] = None # 最小包装数量 - packUnit: Optional[str] = None # 最小包装单位 - price: Optional[float] = None # 单价 - specification: Optional[str] = None # 规格 - status: Optional[int] = None # 业务状态 - stockNum: Optional[int] = None # 当前库存数 - tenantId: Optional[str] = None # 租户ID - timeLife: Optional[int] = None # 寿命小时 - updateTime: Optional[str] = None # 更新时间 - updateUser: Optional[int] = None # 更新人 - version: Optional[int] = None # 版本号 + inventoryWarning: Optional[int] = None # 库存警告 + isDeleted: Optional[int] = None # 是否已删除 + isUniqueCode: Optional[int] = None # 是否一刀一码(0:否 1:是) + materialCode: Optional[str] = None # 物料编码 + materialType: Optional[str] = None # 物料类型 + numberLife: Optional[int] = None # 寿命次数 + packQty: Optional[int] = None # 最小包装数量 + packUnit: Optional[str] = None # 最小包装单位 + price: Optional[float] = None # 单价 + specification: Optional[str] = None # 规格 + status: Optional[int] = None # 业务状态 + stockNum: Optional[int] = None # 当前库存数 + tenantId: Optional[str] = None # 租户ID + timeLife: Optional[int] = None # 寿命小时 + updateTime: Optional[str] = None # 更新时间 + updateUser: Optional[int] = None # 更新人 + version: Optional[int] = None # 版本号 # 刀具分页数据模型 class CutterPageData(BaseModel): """刀具分页数据""" - current: Optional[int] = None # 当前页 - size: Optional[int] = None # 每页数量 - total: Optional[int] = None # 总记录数 - pages: Optional[int] = None # 总页数 - records: Optional[List[CutterDetail]] = [] # 记录列表 - searchCount: Optional[bool] = None # 是否进行count查询 - hitCount: Optional[bool] = None # 是否命中count缓存 + current: Optional[int] = None # 当前页 + size: Optional[int] = None # 每页数量 + total: Optional[int] = None # 总记录数 + pages: Optional[int] = None # 总页数 + records: Optional[List[CutterDetail]] = [] # 记录列表 + searchCount: Optional[bool] = None # 是否进行count查询 + hitCount: Optional[bool] = None # 是否命中count缓存 # 刀具查询响应模型 @@ -88,7 +89,7 @@ class CutterQueryResponse(BaseModel): code: int msg: str success: bool - data: Optional[CutterPageData] = None # 分页数据 + data: Optional[CutterPageData] = None # 分页数据 # ==================== 新增刀具耗材相关模型 ==================== @@ -96,25 +97,25 @@ class CutterQueryResponse(BaseModel): # 新增刀具耗材请求模型 class CreateCutterRequest(BaseModel): """新增刀具耗材请求""" - brandName: str # 品牌名称(必填) - cabinetName: str # 刀具柜名称(必填) - cutterCode: str # 刀具型号(必填) - price: float # 单价(必填) - createUser: int # 创建人(必填) + brandName: str # 品牌名称(必填) + cabinetName: str # 刀具柜名称(必填) + cutterCode: str # 刀具型号(必填) + price: float # 单价(必填) + createUser: int # 创建人(必填) imageUrlList: Optional[List[FileInfo]] = [] # 刀头图片列表(可选) - + # 其他可选字段 - brandCode: Optional[str] = None # 品牌编码 - cutterType: Optional[str] = None # 刀具类型 - specification: Optional[str] = None # 规格 - materialCode: Optional[str] = None # 物料编码 - materialType: Optional[str] = None # 物料类型 - packQty: Optional[int] = None # 最小包装数量 - packUnit: Optional[str] = None # 最小包装单位 - inventoryWarning: Optional[int] = None # 库存警告 - numberLife: Optional[int] = None # 寿命次数 - timeLife: Optional[int] = None # 寿命小时 - isUniqueCode: Optional[int] = 0 # 是否一刀一码(0:否 1:是) + brandCode: Optional[str] = None # 品牌编码 + cutterType: Optional[str] = None # 刀具类型 + specification: Optional[str] = None # 规格 + materialCode: Optional[str] = None # 物料编码 + materialType: Optional[str] = None # 物料类型 + packQty: Optional[int] = None # 最小包装数量 + packUnit: Optional[str] = None # 最小包装单位 + inventoryWarning: Optional[int] = None # 库存警告 + numberLife: Optional[int] = None # 寿命次数 + timeLife: Optional[int] = None # 寿命小时 + isUniqueCode: Optional[int] = 0 # 是否一刀一码(0:否 1:是) # 新增刀具耗材响应模型 @@ -123,33 +124,33 @@ class CreateCutterResponse(BaseModel): code: int msg: str success: bool - data: Optional[CutterDetail] = None # 新增成功后返回的刀具详情 + data: Optional[CutterDetail] = None # 新增成功后返回的刀具详情 # 修改刀具耗材请求模型 class UpdateCutterRequest(BaseModel): """修改刀具耗材请求""" - id: int # 刀具ID(必填,用于标识要修改的记录) - brandName: Optional[str] = None # 品牌名称 - cabinetName: Optional[str] = None # 刀具柜名称 - cutterCode: Optional[str] = None # 刀具型号 - price: Optional[float] = None # 单价 - updateUser: Optional[int] = None # 更新人 + id: int # 刀具ID(必填,用于标识要修改的记录) + brandName: Optional[str] = None # 品牌名称 + cabinetName: Optional[str] = None # 刀具柜名称 + cutterCode: Optional[str] = None # 刀具型号 + price: Optional[float] = None # 单价 + updateUser: Optional[int] = None # 更新人 imageUrlList: Optional[List[FileInfo]] = [] # 刀头图片列表 - + # 其他可选字段 - brandCode: Optional[str] = None # 品牌编码 - cutterType: Optional[str] = None # 刀具类型 - specification: Optional[str] = None # 规格 - materialCode: Optional[str] = None # 物料编码 - materialType: Optional[str] = None # 物料类型 - packQty: Optional[int] = None # 最小包装数量 - packUnit: Optional[str] = None # 最小包装单位 - inventoryWarning: Optional[int] = None # 库存警告 - numberLife: Optional[int] = None # 寿命次数 - timeLife: Optional[int] = None # 寿命小时 - isUniqueCode: Optional[int] = None # 是否一刀一码(0:否 1:是) - status: Optional[int] = None # 业务状态 + brandCode: Optional[str] = None # 品牌编码 + cutterType: Optional[str] = None # 刀具类型 + specification: Optional[str] = None # 规格 + materialCode: Optional[str] = None # 物料编码 + materialType: Optional[str] = None # 物料类型 + packQty: Optional[int] = None # 最小包装数量 + packUnit: Optional[str] = None # 最小包装单位 + inventoryWarning: Optional[int] = None # 库存警告 + numberLife: Optional[int] = None # 寿命次数 + timeLife: Optional[int] = None # 寿命小时 + isUniqueCode: Optional[int] = None # 是否一刀一码(0:否 1:是) + status: Optional[int] = None # 业务状态 # 修改刀具耗材响应模型 @@ -158,7 +159,7 @@ class UpdateCutterResponse(BaseModel): code: int msg: str success: bool - data: Optional[CutterDetail] = None # 修改成功后返回的刀具详情 + data: Optional[CutterDetail] = None # 修改成功后返回的刀具详情 # ==================== 删除刀具耗材相关模型 ==================== @@ -169,7 +170,7 @@ class DeleteCutterResponse(BaseModel): code: int msg: str success: bool - data: Optional[bool] = None # 删除是否成功 + data: Optional[bool] = None # 删除是否成功 # ==================== 品牌管理相关模型 ==================== @@ -177,48 +178,48 @@ class DeleteCutterResponse(BaseModel): # 品牌信息查询参数模型 class BrandQueryParams(BaseModel): """品牌信息分页查询参数""" - brandCode: Optional[str] = None # 品牌编码 - brandName: Optional[str] = None # 品牌名称 - corporateName: Optional[str] = None # 公司名称 - supplierName: Optional[str] = None # 供应商名称 - status: Optional[int] = None # 业务状态 - createUser: Optional[int] = None # 创建人 - startTime: Optional[str] = None # 创建开始时间 - endTime: Optional[str] = None # 创建结束时间 - current: Optional[int] = 1 # 当前页 - size: Optional[int] = 10 # 每页数量 + brandCode: Optional[str] = None # 品牌编码 + brandName: Optional[str] = None # 品牌名称 + corporateName: Optional[str] = None # 公司名称 + supplierName: Optional[str] = None # 供应商名称 + status: Optional[int] = None # 业务状态 + createUser: Optional[int] = None # 创建人 + startTime: Optional[str] = None # 创建开始时间 + endTime: Optional[str] = None # 创建结束时间 + current: Optional[int] = 1 # 当前页 + size: Optional[int] = 10 # 每页数量 # 品牌信息详细模型 class BrandDetail(BaseModel): """品牌信息详细信息""" - id: Optional[int] = None # 主键id - brandCode: Optional[str] = None # 品牌编码 - brandName: Optional[str] = None # 品牌名称 - corporateName: Optional[str] = None # 公司名称 - supplierName: Optional[str] = None # 供应商名称 - supplierUser: Optional[str] = None # 供应商联系人 - phone: Optional[str] = None # 联系方式 - status: Optional[int] = None # 业务状态 - createUser: Optional[int] = None # 创建人 - createDept: Optional[int] = None # 创建部门 - createTime: Optional[str] = None # 创建时间 - updateUser: Optional[int] = None # 更新人 - updateTime: Optional[str] = None # 更新时间 - isDeleted: Optional[int] = None # 是否已删除 - tenantId: Optional[str] = None # 租户ID + id: Optional[int] = None # 主键id + brandCode: Optional[str] = None # 品牌编码 + brandName: Optional[str] = None # 品牌名称 + corporateName: Optional[str] = None # 公司名称 + supplierName: Optional[str] = None # 供应商名称 + supplierUser: Optional[str] = None # 供应商联系人 + phone: Optional[str] = None # 联系方式 + status: Optional[int] = None # 业务状态 + createUser: Optional[int] = None # 创建人 + createDept: Optional[int] = None # 创建部门 + createTime: Optional[str] = None # 创建时间 + updateUser: Optional[int] = None # 更新人 + updateTime: Optional[str] = None # 更新时间 + isDeleted: Optional[int] = None # 是否已删除 + tenantId: Optional[str] = None # 租户ID # 品牌分页数据模型 class BrandPageData(BaseModel): """品牌分页数据""" - current: Optional[int] = None # 当前页 - size: Optional[int] = None # 每页数量 - total: Optional[int] = None # 总记录数 - pages: Optional[int] = None # 总页数 - records: Optional[List[BrandDetail]] = [] # 记录列表 - searchCount: Optional[bool] = None # 是否进行count查询 - hitCount: Optional[bool] = None # 是否命中count缓存 + current: Optional[int] = None # 当前页 + size: Optional[int] = None # 每页数量 + total: Optional[int] = None # 总记录数 + pages: Optional[int] = None # 总页数 + records: Optional[List[BrandDetail]] = [] # 记录列表 + searchCount: Optional[bool] = None # 是否进行count查询 + hitCount: Optional[bool] = None # 是否命中count缓存 # 品牌查询响应模型 @@ -227,26 +228,26 @@ class BrandQueryResponse(BaseModel): code: int msg: str success: bool - data: Optional[BrandPageData] = None # 分页数据 + data: Optional[BrandPageData] = None # 分页数据 # 新增或修改品牌信息请求模型 class SubmitBrandRequest(BaseModel): """新增或修改品牌信息请求""" - id: Optional[int] = None # 主键id(修改时必填,新增时不填) - brandCode: str # 品牌编码(必填) - brandName: str # 品牌名称(必填) - corporateName: str # 公司名称(必填) - supplierName: str # 供应商名称(必填) - supplierUser: str # 供应商联系人(必填) - phone: str # 联系方式(必填) - createDept: Optional[int] = None # 创建部门 - status: Optional[int] = None # 业务状态 - + id: Optional[int] = None # 主键id(修改时必填,新增时不填) + brandCode: str # 品牌编码(必填) + brandName: str # 品牌名称(必填) + corporateName: str # 公司名称(必填) + supplierName: str # 供应商名称(必填) + supplierUser: str # 供应商联系人(必填) + phone: str # 联系方式(必填) + createDept: Optional[int] = None # 创建部门 + status: Optional[int] = None # 业务状态 + # 其他可选字段 - createUser: Optional[int] = None # 创建人 - updateUser: Optional[int] = None # 更新人 - tenantId: Optional[str] = None # 租户ID + createUser: Optional[int] = None # 创建人 + updateUser: Optional[int] = None # 更新人 + tenantId: Optional[str] = None # 租户ID # 新增或修改品牌信息响应模型 @@ -255,7 +256,7 @@ class SubmitBrandResponse(BaseModel): code: int msg: str success: bool - data: Optional[bool] = None # 操作是否成功 + data: Optional[bool] = None # 操作是否成功 # 删除品牌信息响应模型 @@ -264,7 +265,7 @@ class DeleteBrandResponse(BaseModel): code: int msg: str success: bool - data: Optional[bool] = None # 删除是否成功 + data: Optional[bool] = None # 删除是否成功 # ==================== 收刀柜管理相关模型 ==================== @@ -272,48 +273,48 @@ class DeleteBrandResponse(BaseModel): # 收刀柜查询参数模型 class StockPutQueryParams(BaseModel): """收刀柜信息查询参数""" - cabinetCode: Optional[str] = None # 刀柜编码 - stockLoc: Optional[str] = None # 库位号 - locPrefix: Optional[str] = None # 柜子ABCDE面 - stockStatus: Optional[int] = None # 库位状态 - isBan: Optional[str] = None # 绑定状态(是否禁用 0:非禁用 1:禁用) - borrowStatus: Optional[int] = None # 还刀状态(0:修磨 1:报废 2:换线 3:错领) - storageType: Optional[int] = None # 暂存类型(0:公共暂存 1:个人暂存 2:扩展取刀) + cabinetCode: Optional[str] = None # 刀柜编码 + stockLoc: Optional[str] = None # 库位号 + locPrefix: Optional[str] = None # 柜子ABCDE面 + stockStatus: Optional[int] = None # 库位状态 + isBan: Optional[str] = None # 绑定状态(是否禁用 0:非禁用 1:禁用) + borrowStatus: Optional[int] = None # 还刀状态(0:修磨 1:报废 2:换线 3:错领) + storageType: Optional[int] = None # 暂存类型(0:公共暂存 1:个人暂存 2:扩展取刀) # 收刀柜信息详细模型 class StockPutDetail(BaseModel): """收刀柜信息详细数据""" - id: Optional[str] = None # 通道号主键 - stockLoc: Optional[str] = None # 库位号 - locPrefix: Optional[str] = None # 柜子ABCDE面 - cabinetCode: Optional[str] = None # 刀柜编码 - locCapacity: Optional[int] = None # 货道容量(库位容量) - locSurplus: Optional[int] = None # 剩余数量(库位产品剩余) - packQty: Optional[int] = None # 包装数量(最小包装数量) - stockStatus: Optional[int] = None # 库位状态 - isBan: Optional[str] = None # 绑定状态(是否禁用 0:非禁用 1:禁用) - cutterCode: Optional[str] = None # 绑定刀具型号 - warehouseInTime: Optional[str] = None # 最近更新时间(入库时间) - + id: Optional[str] = None # 通道号主键 + stockLoc: Optional[str] = None # 库位号 + locPrefix: Optional[str] = None # 柜子ABCDE面 + cabinetCode: Optional[str] = None # 刀柜编码 + locCapacity: Optional[int] = None # 货道容量(库位容量) + locSurplus: Optional[int] = None # 剩余数量(库位产品剩余) + packQty: Optional[int] = None # 包装数量(最小包装数量) + stockStatus: Optional[int] = None # 库位状态 + isBan: Optional[str] = None # 绑定状态(是否禁用 0:非禁用 1:禁用) + cutterCode: Optional[str] = None # 绑定刀具型号 + warehouseInTime: Optional[str] = None # 最近更新时间(入库时间) + # 其他字段 - brandCode: Optional[str] = None # 品牌编码 - brandName: Optional[str] = None # 品牌名称 - cutterId: Optional[int] = None # 耗材主键 - cutterType: Optional[str] = None # 刀具类型 - materialCode: Optional[str] = None # 物料编码 - materialType: Optional[str] = None # 物料类型 - specification: Optional[str] = None # 规格 - price: Optional[float] = None # 单价 - locType: Optional[int] = None # 库位类型(收刀柜:0 取刀柜:1) - warningNum: Optional[int] = None # 警报数量 - storageType: Optional[int] = None # 暂存类型 - + brandCode: Optional[str] = None # 品牌编码 + brandName: Optional[str] = None # 品牌名称 + cutterId: Optional[int] = None # 耗材主键 + cutterType: Optional[str] = None # 刀具类型 + materialCode: Optional[str] = None # 物料编码 + materialType: Optional[str] = None # 物料类型 + specification: Optional[str] = None # 规格 + price: Optional[float] = None # 单价 + locType: Optional[int] = None # 库位类型(收刀柜:0 取刀柜:1) + warningNum: Optional[int] = None # 警报数量 + storageType: Optional[int] = None # 暂存类型 + # 还刀相关信息 - borrowCode: Optional[str] = None # 还刀编码 - borrowStatus: Optional[int] = None # 还刀状态(0:修磨 1:报废 2:换线 3:错领) - account: Optional[str] = None # 还刀人账号 - name: Optional[str] = None # 还刀人名称 + borrowCode: Optional[str] = None # 还刀编码 + borrowStatus: Optional[int] = None # 还刀状态(0:修磨 1:报废 2:换线 3:错领) + account: Optional[str] = None # 还刀人账号 + name: Optional[str] = None # 还刀人名称 # 收刀柜查询响应模型 @@ -322,7 +323,7 @@ class StockPutQueryResponse(BaseModel): code: int msg: str success: bool - data: Optional[List[StockPutDetail]] = [] # 收刀柜信息列表 + data: Optional[List[StockPutDetail]] = [] # 收刀柜信息列表 # 货道操作响应模型(解绑、禁用/启用) @@ -331,26 +332,26 @@ class StockOperationResponse(BaseModel): code: int msg: str success: bool - data: Optional[bool] = None # 操作是否成功 + data: Optional[bool] = None # 操作是否成功 # 货道数量统计数据模型 class StockStatisticalData(BaseModel): """货道数量统计数据""" - totalNum: Optional[int] = None # 货道总数 - disableNum: Optional[int] = None # 禁用数量 - freeNum: Optional[int] = None # 空闲数量(未使用) - workNum: Optional[int] = None # 占用数量(已使用) - makeAlarm: Optional[int] = None # 库存告警值(取刀柜库存告警值) - totalAmount: Optional[float] = None # 总库存金额(扩展字段) + totalNum: Optional[int] = None # 货道总数 + disableNum: Optional[int] = None # 禁用数量 + freeNum: Optional[int] = None # 空闲数量(未使用) + workNum: Optional[int] = None # 占用数量(已使用) + makeAlarm: Optional[int] = None # 库存告警值(取刀柜库存告警值) + totalAmount: Optional[float] = None # 总库存金额(扩展字段) # 货道统计查询参数模型 class StockStatisticalQueryParams(BaseModel): """货道统计查询参数""" - cabinetCode: Optional[str] = None # 刀柜编码 - locPrefix: Optional[str] = None # 柜子ABCDE面 - locType: Optional[int] = 0 # 库位类型(收刀柜:0 取刀柜:1,默认0) + cabinetCode: Optional[str] = None # 刀柜编码 + locPrefix: Optional[str] = None # 柜子ABCDE面 + locType: Optional[int] = 0 # 库位类型(收刀柜:0 取刀柜:1,默认0) # 货道统计响应模型 @@ -367,51 +368,51 @@ class StockStatisticalResponse(BaseModel): # 取刀柜查询参数模型 class StockTakeQueryParams(BaseModel): """取刀柜信息查询参数""" - brandCode: Optional[str] = None # 品牌编码 - cabinetCode: Optional[str] = None # 刀柜编码 - cutterCode: Optional[str] = None # 刀具型号 - cutterType: Optional[str] = None # 刀具类型 - locPrefix: Optional[str] = None # 柜子ABCDE面 - stockLoc: Optional[str] = None # 库位号 - cutterOrBrand: Optional[str] = None # 耗材型号或品牌 - materialCode: Optional[str] = None # 物料编码 - specification: Optional[str] = None # 规格 + brandCode: Optional[str] = None # 品牌编码 + cabinetCode: Optional[str] = None # 刀柜编码 + cutterCode: Optional[str] = None # 刀具型号 + cutterType: Optional[str] = None # 刀具类型 + locPrefix: Optional[str] = None # 柜子ABCDE面 + stockLoc: Optional[str] = None # 库位号 + cutterOrBrand: Optional[str] = None # 耗材型号或品牌 + materialCode: Optional[str] = None # 物料编码 + specification: Optional[str] = None # 规格 # 取刀柜信息详细模型 class StockTakeDetail(BaseModel): """取刀柜信息详细数据""" - id: Optional[str] = None # 通道号主键 - brandName: Optional[str] = None # 品牌名称 - brandCode: Optional[str] = None # 品牌编码 - cutterCode: Optional[str] = None # 刀具型号 - cutterType: Optional[str] = None # 刀具类型 - stockLoc: Optional[str] = None # 库位号 - locPrefix: Optional[str] = None # 柜子ABCDE面 - price: Optional[float] = None # 单价(元) - locCapacity: Optional[int] = None # 货道容量 - locSurplus: Optional[int] = None # 剩余数量(库位产品剩余) - stockStatus: Optional[int] = None # 货道状态(库位状态) - storageType: Optional[int] = None # 暂存类型(扩展字段) - isBan: Optional[str] = None # 绑定状态(是否禁用 0:非禁用 1:禁用) - cabinetCode: Optional[str] = None # 刀柜编码 - + id: Optional[str] = None # 通道号主键 + brandName: Optional[str] = None # 品牌名称 + brandCode: Optional[str] = None # 品牌编码 + cutterCode: Optional[str] = None # 刀具型号 + cutterType: Optional[str] = None # 刀具类型 + stockLoc: Optional[str] = None # 库位号 + locPrefix: Optional[str] = None # 柜子ABCDE面 + price: Optional[float] = None # 单价(元) + locCapacity: Optional[int] = None # 货道容量 + locSurplus: Optional[int] = None # 剩余数量(库位产品剩余) + stockStatus: Optional[int] = None # 货道状态(库位状态) + storageType: Optional[int] = None # 暂存类型(扩展字段) + isBan: Optional[str] = None # 绑定状态(是否禁用 0:非禁用 1:禁用) + cabinetCode: Optional[str] = None # 刀柜编码 + # 其他字段 - cabinetName: Optional[str] = None # 刀具柜名称 - cutterId: Optional[int] = None # 物料编码主键 - materialCode: Optional[str] = None # 物料编码 - materialType: Optional[str] = None # 物料类型 - specification: Optional[str] = None # 规格 - imageUrl: Optional[str] = None # 图片路径 - locType: Optional[int] = None # 库位类型(收刀柜:0 取刀柜:1) - packQty: Optional[int] = None # 最小包装数量 - stockNum: Optional[int] = None # 当前库存数 - warningNum: Optional[int] = None # 警报数量 - inventoryWarning: Optional[int] = None # 库存警告 - warehouseInTime: Optional[str] = None # 入库时间 - awayQty: Optional[int] = None # 磨损数量 - numberLife: Optional[int] = None # 寿命次数 - timeLife: Optional[int] = None # 寿命小时 + cabinetName: Optional[str] = None # 刀具柜名称 + cutterId: Optional[int] = None # 物料编码主键 + materialCode: Optional[str] = None # 物料编码 + materialType: Optional[str] = None # 物料类型 + specification: Optional[str] = None # 规格 + imageUrl: Optional[str] = None # 图片路径 + locType: Optional[int] = None # 库位类型(收刀柜:0 取刀柜:1) + packQty: Optional[int] = None # 最小包装数量 + stockNum: Optional[int] = None # 当前库存数 + warningNum: Optional[int] = None # 警报数量 + inventoryWarning: Optional[int] = None # 库存警告 + warehouseInTime: Optional[str] = None # 入库时间 + awayQty: Optional[int] = None # 磨损数量 + numberLife: Optional[int] = None # 寿命次数 + timeLife: Optional[int] = None # 寿命小时 # 取刀柜查询响应模型 @@ -420,7 +421,7 @@ class StockTakeQueryResponse(BaseModel): code: int msg: str success: bool - data: Optional[List[StockTakeDetail]] = [] # 取刀柜信息列表 + data: Optional[List[StockTakeDetail]] = [] # 取刀柜信息列表 # ==================== 补刀相关模型 ==================== @@ -428,19 +429,19 @@ class StockTakeQueryResponse(BaseModel): # 货道补货信息模型 class StockPlugInfo(BaseModel): """货道补货信息""" - cabinetCode: Optional[str] = None # 刀柜编码 - stockLoc: Optional[str] = None # 库位号 - locCapacity: Optional[int] = None # 货道容量 - locSurplus: Optional[int] = None # 补货前数量 - plugNum: Optional[int] = None # 补货后数量 - massage: Optional[str] = None # 原因(错误原因或成功信息) + cabinetCode: Optional[str] = None # 刀柜编码 + stockLoc: Optional[str] = None # 库位号 + locCapacity: Optional[int] = None # 货道容量 + locSurplus: Optional[int] = None # 补货前数量 + plugNum: Optional[int] = None # 补货后数量 + massage: Optional[str] = None # 原因(错误原因或成功信息) # 预补刀查询结果数据模型 class PreBatchPlugData(BaseModel): """预补刀查询结果数据""" - successStock: Optional[List[StockPlugInfo]] = [] # 补刀成功的货道列表 - errorStock: Optional[List[StockPlugInfo]] = [] # 补刀失败的货道列表 + successStock: Optional[List[StockPlugInfo]] = [] # 补刀成功的货道列表 + errorStock: Optional[List[StockPlugInfo]] = [] # 补刀失败的货道列表 # 预补刀查询响应模型 @@ -449,7 +450,7 @@ class PreBatchPlugResponse(BaseModel): code: int msg: str success: bool - data: Optional[PreBatchPlugData] = None # 预补刀查询结果 + data: Optional[PreBatchPlugData] = None # 预补刀查询结果 # 批量补刀响应模型 @@ -458,4 +459,581 @@ class OnPreBatchPlugResponse(BaseModel): code: int msg: str success: bool - data: Optional[bool] = None # 补刀是否成功 + data: Optional[bool] = None # 补刀是否成功 + + +# ==================== 统计接口模型(修正版) ==================== + +# ==================== 出入库统计相关模型 ==================== +class StorageStatisticsQueryParams(BaseModel): + """出入库统计查询参数""" + current: Optional[int] = 1 # 当前页 + size: Optional[int] = 10 # 每页数量 + startTime: Optional[str] = None # 开始时间(格式:YYYY-MM-DD HH:mm:ss) + endTime: Optional[str] = None # 结束时间(格式:YYYY-MM-DD HH:mm:ss) + recordStatus: Optional[int] = None # 记录状态:0-取刀,1-还刀,2-收刀,3-暂存,4-完成,5-违规还刀 + rankingType: Optional[int] = None # 排名类型:0-按数量排序,1-按金额排序 + order: Optional[int] = None # 排序顺序:0-从大到小(降序),1-从小到大(升序) + + +class ExportStockRecordQueryParams(BaseModel): + """出入库记录导出查询参数""" + startTime: Optional[str] = None # 开始时间(格式:YYYY-MM-DD HH:mm:ss) + endTime: Optional[str] = None # 结束时间(格式:YYYY-MM-DD HH:mm:ss) + recordStatus: Optional[int] = None # 记录状态:0-取刀,1-还刀,2-收刀,3-暂存,4-完成,5-违规还刀 + rankingType: Optional[int] = None # 排名类型:0-按数量排序,1-按金额排序 + order: Optional[int] = None # 排序顺序:0-从大到小(降序),1-从小到大(升序) + + +class StorageRecord(BaseModel): + """出入库记录""" + account: Optional[str] = None # 用户名 + brandCode: Optional[str] = None # 品牌编码 + brandName: Optional[str] = None # 品牌名称 + cabinetCode: Optional[str] = None # 刀柜编码 + cabinetName: Optional[str] = None # 刀具柜名称 + createTime: Optional[str] = None # 创建时间 + cutterCode: Optional[str] = None # 刀具型号 + cutterId: Optional[int] = None # 耗材主键 + cutterType: Optional[str] = None # 刀具类型 + detailsCode: Optional[str] = None # 操作详情编码 + detailsName: Optional[str] = None # 操作详情名称 + factoryName: Optional[str] = None # 工厂名称 + id: Optional[int] = None # 主键id + name: Optional[str] = None # 用户名称 + oldPrice: Optional[float] = None # 历史单价 + operator: Optional[str] = None # 操作人 + price: Optional[float] = None # 单价 + quantity: Optional[int] = None # 数量 + remake: Optional[str] = None # 备注 + specification: Optional[str] = None # 规格 + status: Optional[int] = None # 业务状态 + stockLoc: Optional[str] = None # 库位号 + stockType: Optional[int] = None # 0:入库 1:出库 + updateTime: Optional[str] = None # 更新时间 + workshopName: Optional[str] = None # 车间名称 + + +class StorageStatisticsListData(BaseModel): + """出入库统计列表数据""" + current: int # 当前页 + size: int # 每页数量 + total: int # 总记录数 + pages: int # 总页数 + records: List[StorageRecord] # 记录列表 + + +class StorageStatisticsResponse(BaseModel): + """出入库统计响应""" + code: int + msg: str + success: bool + data: Optional[dict] = None # 包含分页数据 + + +# ==================== 图表统计相关模型 ==================== +class ChartsData(BaseModel): + """统计图表数据(全年取刀数量,全年取刀金额,刀具消耗数据)""" + titleList: List[str] = [] # 标题列表(如月份或刀具类型) + dataList: List[Union[str, int, float]] = [] # 数据列表(支持字符串、整数、浮点数) + + +class ChartsResponse(BaseModel): + """统计图表响应(全年取刀数量,全年取刀金额,刀具消耗响应)""" + code: int + msg: str + success: bool + data: Optional[ChartsData] = None + + +# ==================== 总库存统计相关模型 ==================== +class TotalStockQueryParams(BaseModel): + """总库存统计查询参数""" + statisticsType: Optional[str] = None # 统计类型 + brandName: Optional[str] = None # 品牌名称 + cabinetCode: Optional[str] = None # 刀柜编码 + cutterType: Optional[str] = None # 刀具类型 + stockStatus: Optional[int] = None # 库位状态 + current: Optional[int] = 1 # 当前页 + size: Optional[int] = 10 # 每页数量 + + +class StockLocationDetail(BaseModel): + """取刀柜库位详情""" + id: Optional[int] = None # 主键id + brandCode: Optional[str] = None # 品牌编码 + brandName: Optional[str] = None # 品牌名称 + cabinetCode: Optional[str] = None # 刀柜编码 + cabinetName: Optional[str] = None # 刀具柜名称 + cutterCode: Optional[str] = None # 刀具型号 + cutterId: Optional[int] = None # 物料编码主键 + cutterType: Optional[str] = None # 刀具类型 + imageUrl: Optional[str] = None # 图片路径 + locCapacity: Optional[int] = None # 库位容量 + locPackQty: Optional[int] = None # 货道包装数量 + locSurplus: Optional[int] = None # 库位产品剩余[货道库存] + locType: Optional[int] = None # 库位类型[收刀柜: 0 取刀柜: 1] + materialCode: Optional[str] = None # 物料编码 + materialType: Optional[str] = None # 物料类型 + packQty: Optional[int] = None # 最小包装数量 + price: Optional[float] = None # 单价 + specification: Optional[str] = None # 规格 + stockLoc: Optional[str] = None # 库位号 + stockNum: Optional[int] = None # 当前库存数 + stockStatus: Optional[int] = None # 库位状态 + warningNum: Optional[int] = None # 警报数量 + warehouseInTime: Optional[str] = None # 入库时间 + updateTime: Optional[str] = None # 更新时间 + + # 计算字段(前端展示用) + stockValue: Optional[float] = None # 库存价值(单价 * 剩余数量) + cabinetSide: Optional[str] = None # 柜子面 + + +class TotalStockResponse(BaseModel): + """总库存统计响应""" + code: int + msg: str + success: bool + data: Optional[dict] = None # 包含分页数据或列表数据 + + +class StockLocationDetailResponse(BaseModel): + """库位详情响应""" + code: int + msg: str + success: bool + data: Optional[StockLocationDetail] = None # 库位详情数据 + + +# ==================== 废刀回收统计相关模型 ==================== +class ReturnKnifeDetail(BaseModel): + """还刀信息详情""" + id: Optional[int] = None # 取刀主键 + borrowStatus: Optional[int] = None # 还刀状态:0-修磨,1-报废,2-换线,3-错领 + borrowTime: Optional[str] = None # 还刀时间 + borrowUserName: Optional[str] = None # 还刀人 + brandName: Optional[str] = None # 品牌名称 + cutterCode: Optional[str] = None # 刀具型号 + cutterType: Optional[str] = None # 刀具类型 + lendTime: Optional[str] = None # 取刀时间 + lendUserName: Optional[str] = None # 借刀人 + recordStatus: Optional[int] = None # 记录状态:0-取刀,1-还刀,2-收刀,3-暂存 + specification: Optional[str] = None # 规格 + stockLoc: Optional[str] = None # 库位号 + + +class ReturnKnifeData(BaseModel): + """收刀柜还刀数据""" + borrowStatus: Optional[str] = None # 还刀状态(字符串形式) + cabinetCode: Optional[str] = None # 刀柜编码 + recordStatus: Optional[int] = None # 记录状态:0-取刀,1-还刀,2-收刀,3-暂存 + list: Optional[List[ReturnKnifeDetail]] = [] # 还刀详情列表 + + +class WasteKnifeRecycleResponse(BaseModel): + """废刀回收统计响应""" + code: int + msg: str + success: bool + data: Optional[ReturnKnifeData] = None # 还刀数据 + + +# ==================== 合并自 系统记录 模块的模型 ==================== +# 合并日期: 2025-11-15 +# 来源: teamleader_record/data_schemas.py + +# 补货记录相关模型 (班组长) +class ReplenishRecord(BaseModel): + """ + 补货记录模型 (班组长) + """ + model_config = ConfigDict(from_attributes=True) + + id: Optional[int] = None + brandName: Optional[str] = None # 品牌名称 + cabinetCode: Optional[str] = None # 刀柜编码 + createDept: Optional[int] = None # 创建部门 + createTime: Optional[str] = None # 创建时间 + createUser: Optional[int] = None # 创建人 + cutterCode: Optional[str] = None # 刀具型号 + cutterType: Optional[str] = None # 刀具类型 + detailsCode: Optional[str] = None # 操作详情 + isDeleted: Optional[int] = None # 是否已删除 + lendUserName: Optional[str] = None # 取出人 + logStatus: Optional[int] = None # 日志状态 + logType: Optional[str] = None # 日志类型 + materialCode: Optional[str] = None # 物料编码 + newPrice: Optional[float] = None # 新单价 + newStockNum: Optional[int] = None # 操作后库存数 + oldPrice: Optional[float] = None # 老单价 + oldStockNum: Optional[int] = None # 操作前库存数 + operator: Optional[str] = None # 操作人 + quantity: Optional[int] = None # 数量 + remake: Optional[str] = None # 备注 + specification: Optional[str] = None # 规格 + status: Optional[int] = None # 业务状态 + stockLoc: Optional[str] = None # 库位号 + storageUserName: Optional[str] = None # 暂存人 + tenantId: Optional[str] = None # 租户ID + updateTime: Optional[str] = None # 更新时间 + updateUser: Optional[int] = None # 更新人 + + +class ReplenishRecordData(BaseModel): + """ + 补货记录分页数据 (班组长) + """ + model_config = ConfigDict(from_attributes=True) + + current: int + hitCount: bool + pages: int + records: List[ReplenishRecord] + searchCount: bool + size: int + total: int + + +class ReplenishRecordResponse(BaseModel): + """ + 补货记录响应模型 (班组长) + """ + model_config = ConfigDict(from_attributes=True) + + code: int + data: ReplenishRecordData + msg: str + success: bool + + +class ReplenishRecordListRequest(BaseModel): + """ + 补货记录列表请求参数 (班组长) + """ + model_config = ConfigDict(from_attributes=True) + + current: Optional[int] = None # 当前页 + endTime: Optional[str] = None # 结束时间 + order: Optional[int] = None # 顺序 0: 从大到小 1:从小到大 + rankingType: Optional[int] = None # 0: 数量 1: 金额 + recordStatus: Optional[int] = None # 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + size: Optional[int] = None # 每页的数量 + startTime: Optional[str] = None # 开始时间 + + +class ExportReplenishRecordRequest(BaseModel): + """ + 导出补货记录请求参数 (班组长) + """ + model_config = ConfigDict(from_attributes=True) + + endTime: Optional[str] = None # 结束时间 + order: Optional[int] = None # 顺序 0: 从大到小 1:从小到大 + rankingType: Optional[int] = None # 0: 数量 1: 金额 + recordStatus: Optional[int] = None # 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + startTime: Optional[str] = None # 开始时间 + + +# 领刀记录相关模型 (班组长) +class LendRecord(BaseModel): + """ + 领刀记录模型 (班组长) + """ + model_config = ConfigDict(from_attributes=True) + + # 取刀人相关信息 + lendUser: int # 取刀人ID + lendUserName: str # 取刀人 + borrowUserName: Optional[str] # 还刀人 + + # 品牌信息 + brandName: str # 品牌名称 + brandCode: str # 品牌编码 + + # 刀具信息 + cutterType: str # 刀具类型 + cutterCode: str # 刀具型号 + specification: str # 规格 + materialCode: str # 物料编码 + price: float # 单价 + + # 位置信息 + cabinetCode: str # 刀柜编码 + lendStock: str # 借刀库位号 + borrowStock: Optional[str] # 还刀库位号 + + # 时间信息 + lendTime: str # 借刀时间 + borrowTime: Optional[str] # 还刀时间 + finalCollectTime: Optional[str] # 最终确认时间 + + # 状态信息 + recordStatus: int # 记录状态 (0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀) + borrowStatus: Optional[int] # 还刀状态 (0: 修磨 1: 报废 2: 换线 3: 错领) + finalCollectStatus: Optional[int] # 最终确认状态 (0: 通过 1: 未通过) + + # 备注和结果 + borrowRemarks: Optional[str] # 还刀备注 + finalCollectRemarks: Optional[str] # 最终确认结果 + collectStatus: Optional[str] # 管理员确认结果 + + +class LendRecordData(BaseModel): + """ + 领刀记录分页数据 (班组长) + """ + model_config = ConfigDict(from_attributes=True) + + current: int + hitCount: bool + pages: int + records: List[LendRecord] + searchCount: bool + size: int + total: int + + +class LendRecordResponse(BaseModel): + """ + 领刀记录响应模型 (班组长) + """ + model_config = ConfigDict(from_attributes=True) + + code: int + data: LendRecordData + msg: str + success: bool + + +class LendRecordListRequest(BaseModel): + """ + 领刀记录列表请求参数 (班组长) + """ + model_config = ConfigDict(from_attributes=True) + + current: int = 1 + size: int = 20 + keyword: Optional[str] = None # 关键字搜索 + department: Optional[str] = None # 部门筛选 + startTime: Optional[str] = None # 开始时间 + endTime: Optional[str] = None # 结束时间 + order: Optional[int] = None # 顺序 0: 从大到小 1:从小到大 + rankingType: Optional[int] = None # 0: 数量 1: 金额 + recordStatus: Optional[int] = None # 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + + +class ExportLendRecordRequest(BaseModel): + """ + 导出领刀记录请求参数 (班组长) + """ + model_config = ConfigDict(from_attributes=True) + + endTime: Optional[str] = None # 结束时间 + order: Optional[int] = None # 顺序 0: 从大到小 1:从小到大 + rankingType: Optional[int] = None # 0: 数量 1: 金额 + recordStatus: Optional[int] = None # 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + startTime: Optional[str] = None # 开始时间 + + +# 公共暂存记录相关模型 (班组长) +class StorageRecord(BaseModel): + """ + 公共暂存记录模型 (班组长) + """ + model_config = ConfigDict(from_attributes=True) + + id: Optional[int] = None # 主键ID + brandName: Optional[str] = None # 品牌名称 + cabinetCode: Optional[str] = None # 刀柜编码 + createDept: Optional[int] = None # 创建部门 + createTime: Optional[str] = None # 创建时间 + createUser: Optional[int] = None # 创建人 + cutterCode: Optional[str] = None # 刀具型号 + detailsCode: Optional[str] = None # 操作详情 + isDeleted: Optional[int] = None # 是否已删除 + lendUserName: Optional[str] = None # 取出人 + logStatus: Optional[int] = None # 日志状态 + materialCode: Optional[str] = None # 物料编码 + newPrice: Optional[float] = None # 新单价 + newStockNum: Optional[int] = None # 操作后库存数 + oldPrice: Optional[float] = None # 老单价 + oldStockNum: Optional[int] = None # 操作前库存数 + operator: Optional[str] = None # 操作人 + quantity: Optional[int] = None # 数量 + remake: Optional[str] = None # 备注 + specification: Optional[str] = None # 规格 + status: Optional[int] = None # 业务状态 + stockLoc: Optional[str] = None # 库位号 + storageUserName: Optional[str] = None # 暂存人 + tenantId: Optional[str] = None # 租户ID + updateTime: Optional[str] = None # 更新时间 + updateUser: Optional[int] = None # 更新人 + + +class StorageRecordData(BaseModel): + """ + 公共暂存记录分页数据 (班组长) + """ + model_config = ConfigDict(from_attributes=True) + + current: int + hitCount: bool + pages: int + records: List[StorageRecord] + searchCount: bool + size: int + total: int + + +class StorageRecordResponse(BaseModel): + """ + 公共暂存记录响应模型 (班组长) + """ + model_config = ConfigDict(from_attributes=True) + + code: int + data: StorageRecordData + msg: str + success: bool + + +class StorageRecordListRequest(BaseModel): + """ + 公共暂存记录列表请求参数 (班组长) + """ + model_config = ConfigDict(from_attributes=True) + + current: Optional[int] = None # 当前页 + endTime: Optional[str] = None # 结束时间 + order: Optional[int] = None # 顺序 0: 从大到小 1:从小到大 + rankingType: Optional[int] = None # 0: 数量 1: 金额 + recordStatus: Optional[int] = None # 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + size: Optional[int] = None # 每页的数量 + startTime: Optional[str] = None # 开始时间 + + +class ExportStorageRecordRequest(BaseModel): + """ + 导出公共暂存记录请求参数 (班组长) + """ + model_config = ConfigDict(from_attributes=True) + + endTime: Optional[str] = None # 结束时间 + order: Optional[int] = None # 顺序 0: 从大到小 1:从小到大 + rankingType: Optional[int] = None # 0: 数量 1: 金额 + recordStatus: Optional[int] = None # 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + startTime: Optional[str] = None # 开始时间 + + +# 告警预警相关模型 (班组长) +class AlarmWarning(BaseModel): + """ + 告警预警模型 (班组长) + """ + model_config = ConfigDict(from_attributes=True) + + id: int + locSurplus: int # 货道 + alarmLevel: int # 预警等级 + deviceType: str # 设备类型 + cabinetCode: str # 刀柜编码 + stockLoc: str # 库位号 + brandName: str # 品牌名称 + itemCode: str # 物品编码 + itemType: str # 物品类型 + currentStock: int # 当前库存 + thresholdValue: int # 阈值 + alarmMessage: str # 预警信息 + handleStatus: int # 处理状态 + createTime: str # 预警时间 + handleTime: Optional[str] = None # 处理时间 + handleUser: Optional[str] = None # 处理人 + handleRemark: Optional[str] = None # 处理备注 + + +class AlarmWarningData(BaseModel): + """ + 告警预警分页数据 (班组长) + """ + model_config = ConfigDict(from_attributes=True) + + current: int + hitCount: bool + pages: int + records: List[AlarmWarning] + searchCount: bool + size: int + total: int + + +class AlarmWarningResponse(BaseModel): + """ + 告警预警响应模型 (班组长) + """ + model_config = ConfigDict(from_attributes=True) + + code: int + data: AlarmWarningData + msg: str + success: bool + + +class AlarmWarningListRequest(BaseModel): + """ + 告警预警列表请求参数 (班组长) + """ + model_config = ConfigDict(from_attributes=True) + + locSurplus: Optional[int] = None # 货道 + alarmLevel: Optional[int] = None # 预警等级 + deviceType: Optional[str] = None # 设备类型 + cabinetCode: Optional[str] = None # 刀柜编码 + brandName: Optional[str] = None # 品牌名称 + handleStatus: Optional[int] = None # 处理状态 + current: Optional[int] = None # 当前页 + size: Optional[int] = None # 每页数量 + + +class ThresholdSetting(BaseModel): + """ + 阈值设置模型 (班组长) + """ + model_config = ConfigDict(from_attributes=True) + + locSurplus: int # 货道 + alarmThreshold: int # 预警阈值 + + +class ThresholdSettingRequest(BaseModel): + """ + 阈值设置请求参数 (班组长) + """ + model_config = ConfigDict(from_attributes=True) + + locSurplus: int # 货道 + alarmThreshold: int # 预警阈值 + + +class AlarmStatistics(BaseModel): + """ + 告警统计模型 (班组长) + """ + model_config = ConfigDict(from_attributes=True) + + level1Count: int # 安全库存量预警数量 + level2Count: int # 采集阈值存储量预警数量 + level3Count: int # 紧急补货存储量报警数量 + unhandledCount: int # 未处理预警总数 + + +class AlarmStatisticsResponse(BaseModel): + """ + 告警统计响应模型 (班组长) + """ + model_config = ConfigDict(from_attributes=True) + + code: int + data: AlarmStatistics + msg: str + success: bool \ No newline at end of file diff --git a/teamleader/schemas/data_schemas_record.py b/teamleader/schemas/data_schemas_record.py deleted file mode 100644 index 1dffa4b..0000000 --- a/teamleader/schemas/data_schemas_record.py +++ /dev/null @@ -1,371 +0,0 @@ -from pydantic import BaseModel -from typing import List, Optional -from datetime import datetime - - -class LendRecord(BaseModel): - """ - 领刀记录模型 - """ - # 取刀人相关信息 - lendUser: int # 取刀人ID - lendUserName: str # 取刀人 - - # 品牌信息 - brandName: str # 品牌名称 - brandCode: str # 品牌编码 - - # 刀具信息 - cutterType: str # 刀具类型 - cutterCode: str # 刀具型号 - specification: str # 规格 - materialCode: str # 物料编码 - price: float # 单价 - - # 位置信息 - cabinetCode: str # 刀柜编码 - lendStock: str # 借刀库位号 - borrowStock: Optional[str] # 还刀库位号 - - # 时间信息 - lendTime: str # 借刀时间 - borrowTime: Optional[str] # 还刀时间 - finalCollectTime: Optional[str] # 最终确认时间 - - # 状态信息 - recordStatus: int # 记录状态 (0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀) - borrowStatus: Optional[int] # 还刀状态 (0: 修磨 1: 报废 2: 换线 3: 错领) - finalCollectStatus: Optional[int] # 最终确认状态 (0: 通过 1: 未通过) - - # 备注和结果 - borrowRemarks: Optional[str] # 还刀备注 - finalCollectRemarks: Optional[str] # 最终确认结果 - collectStatus: Optional[str] # 管理员确认结果 - - class Config: - orm_mode = True - - -class LendRecordData(BaseModel): - """ - 领刀记录分页数据 - """ - current: int - hitCount: bool - pages: int - records: List[LendRecord] - searchCount: bool - size: int - total: int - - class Config: - orm_mode = True - - -class LendRecordResponse(BaseModel): - """ - 领刀记录响应模型 - """ - code: int - data: LendRecordData - msg: str - success: bool - - class Config: - orm_mode = True - - -class LendRecordListRequest(BaseModel): - """ - 领刀记录列表请求参数 - """ - page: int = 1 - page_size: int = 20 - keyword: Optional[str] = None # 关键字搜索 - department: Optional[str] = None # 部门筛选 - start_date: Optional[str] = None # 开始日期 - end_date: Optional[str] = None # 结束日期 - order: Optional[int] = None # 顺序 0: 从大到小 1:从小到大 - rankingType: Optional[int] = None # 0: 数量 1: 金额 - recordStatus: Optional[int] = None # 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 - - class Config: - orm_mode = True - - -class ExportLendRecordRequest(BaseModel): - """ - 导出领刀记录请求参数 - """ - endTime: Optional[str] = None # 结束时间 - order: Optional[int] = None # 顺序 0: 从大到小 1:从小到大 - rankingType: Optional[int] = None # 0: 数量 1: 金额 - recordStatus: Optional[int] = None # 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 - startTime: Optional[str] = None # 开始时间 - - class Config: - orm_mode = True - - -class ItemDto(BaseModel): - """ - 刀柜货道项模型 - """ - locSurplus: Optional[int] # 货道刀具数量 - stockId: Optional[int] # 刀柜编码(根据文档描述,实际是stockId) - - class Config: - orm_mode = True - - -class RestockRequest(BaseModel): - """ - 补货请求模型 - """ - replenishDto: Optional[dict] = None # replenishDto 对象 - cabinetCode: Optional[str] = None # 刀柜编码 - itemDtoList: Optional[List[ItemDto]] = None # 补货信息列表 - - class Config: - orm_mode = True - - -class RestockData(BaseModel): - """ - 补货响应数据模型(取刀信息) - """ - # 根据响应说明,这里应该包含"取刀信息",暂时保持为空,可根据实际API响应添加字段 - pass - - -class RestockResponse(BaseModel): - """ - 补货响应模型 - """ - code: int - data: Optional[RestockData] - dataSuccess: Optional[bool] - msg: str - success: bool - - class Config: - orm_mode = True - - -class ReplenishRecord(BaseModel): - """ - 补货记录模型 - """ - brandName: Optional[str] = None # 品牌名称 - cabinetCode: Optional[str] = None # 刀柜编码 - createDept: Optional[int] = None # 创建部门 - createTime: Optional[str] = None # 创建时间 - createUser: Optional[int] = None # 创建人 - cutterCode: Optional[str] = None # 刀具型号 - cutterType: Optional[str] = None # 电池类型 - detailsCode: Optional[str] = None # 操作详情 - id: Optional[int] = None # 主键id - isDeleted: Optional[int] = None # 是否已删除 - lendUserName: Optional[str] = None # 取出人 - logStatus: Optional[int] = None # 日志类型 0:操作日志 1:公共暂存 2:补货 - logType: Optional[str] = None # 日志类型 - materialCode: Optional[str] = None # 物料编码 - newPrice: Optional[float] = None # 新单价 - newStockNum: Optional[int] = None # 操作后库存数 - oldPrice: Optional[float] = None # 老单价 - oldStockNum: Optional[int] = None # 操作前库存数 - operator: Optional[str] = None # 操作人 - quantity: Optional[int] = None # 数量 - remake: Optional[str] = None # 备注 - specification: Optional[str] = None # 规格 - status: Optional[int] = None # 业务状态 - stockLoc: Optional[str] = None # 库位号 - storageUserName: Optional[str] = None # 暂存人 - tenantId: Optional[str] = None # 租户ID - updateTime: Optional[str] = None # 更新时间 - updateUser: Optional[int] = None # 更新人 - - class Config: - orm_mode = True - - -class ReplenishRecordData(BaseModel): - """ - 补货记录分页数据 - """ - current: int - hitCount: bool - pages: int - records: List[ReplenishRecord] - searchCount: bool - size: int - total: int - - class Config: - orm_mode = True - - -class ReplenishRecordResponse(BaseModel): - """ - 补货记录响应模型 - """ - code: int - data: ReplenishRecordData - msg: str - success: bool - - class Config: - orm_mode = True - - -class ReplenishRecordListRequest(BaseModel): - """ - 补货记录列表请求参数 - """ - current: Optional[int] = None # 当前页 - endTime: Optional[str] = None # 结束时间 - order: Optional[int] = None # 顺序 0: 从大到小 1:从小到大 - rankingType: Optional[int] = None # 0: 数量 1: 金额 - recordStatus: Optional[int] = None # 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 - size: Optional[int] = None # 每页的数量 - startTime: Optional[str] = None # 开始时间 - - class Config: - orm_mode = True - - -class StorageRecord(ReplenishRecord): - """ - 公共暂存记录模型(与补货记录模型相同) - """ - pass - - -class StorageRecordData(BaseModel): - """ - 公共暂存记录分页数据 - """ - current: int - hitCount: bool - pages: int - records: List[StorageRecord] - searchCount: bool - size: int - total: int - - class Config: - orm_mode = True - - -class StorageRecordResponse(BaseModel): - """ - 公共暂存记录响应模型 - """ - code: int - data: StorageRecordData - msg: str - success: bool - - class Config: - orm_mode = True - - -class StorageRecordListRequest(BaseModel): - """ - 公共暂存记录列表请求参数 - """ - current: Optional[int] = None # 当前页 - endTime: Optional[str] = None # 结束时间 - order: Optional[int] = None # 顺序 0: 从大到小 1:从小到大 - rankingType: Optional[int] = None # 0: 数量 1: 金额 - recordStatus: Optional[int] = None # 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 - size: Optional[int] = None # 每页的数量 - startTime: Optional[str] = None # 开始时间 - - class Config: - orm_mode = True - - -class PersonalStorageInfo(BaseModel): - """ - 个人暂存柜信息 - """ - cabinetCode: Optional[str] = None # 刀柜编码 - id: Optional[str] = None # 通道号主键 - locPrefix: Optional[str] = None # 柜子ABCDE面 - locType: Optional[int] = None # 库位类型[收刀柜: 0 取刀柜: 1] - name: Optional[str] = None # 暂存用户名 - stockLoc: Optional[str] = None # 库位号 - storageCode: Optional[str] = None # 一次暂存编码 - storageType: Optional[int] = None # 暂存类型 0: 公共暂存 1: 个人暂存 - storageUser: Optional[int] = None # 暂存用户 - - class Config: - orm_mode = True - - -class PersonalStorageResponse(BaseModel): - """ - 个人暂存柜响应模型 - """ - code: int - data: Optional[List[PersonalStorageInfo]] - msg: str - success: bool - - class Config: - orm_mode = True - - -class MakeAlarmResponse(BaseModel): - """ - 设置取刀柜告警值响应模型 - """ - code: int - data: Optional[bool] - msg: str - success: bool - - class Config: - orm_mode = True - - -class CabinetAlarmResponse(BaseModel): - """ - 获取取刀柜告警值响应模型 - """ - code: int - data: Optional[int] - msg: str - success: bool - - class Config: - orm_mode = True - - -class SelectorOption(BaseModel): - """ - 选择器选项模型 - """ - label: Optional[str] = None # 标题 - value: Optional[str] = None # 值 - - class Config: - orm_mode = True - - -class CabinetSelectResponse(BaseModel): - """ - 获取刀柜信息下拉响应模型 - """ - code: int - data: Optional[List[SelectorOption]] - msg: str - success: bool - - class Config: - orm_mode = True - - - - diff --git a/teamleader/services/api_client.py b/teamleader/services/api_client.py index eca8e3e..b0cf987 100644 --- a/teamleader/services/api_client.py +++ b/teamleader/services/api_client.py @@ -1,8 +1,8 @@ import requests import logging import os -from typing import Dict, Any, Optional -import requests +from typing import Dict, Any, Optional, List +from urllib.parse import urljoin logger = logging.getLogger(__name__) @@ -13,7 +13,7 @@ class TeamLeaderAPIClient: def __init__(self, base_url: str, api_key: Optional[str] = None, token_file: Optional[str] = None): """ 初始化API客户端 - + 参数: base_url: API基础URL api_key: API密钥(可选) @@ -41,7 +41,7 @@ def __init__(self, base_url: str, api_key: Optional[str] = None, token_file: Opt def _load_token_from_file(self, token_file: str) -> Optional[str]: """ 从文件读取Token - + 参数: token_file: Token文件路径 返回: @@ -54,7 +54,7 @@ def _load_token_from_file(self, token_file: str) -> Optional[str]: current_dir = os.path.dirname(os.path.abspath(__file__)) project_root = os.path.dirname(os.path.dirname(current_dir)) token_file = os.path.join(project_root, token_file) - + # 读取token文件 with open(token_file, 'r', encoding='utf-8') as f: token = f.read().strip() @@ -70,11 +70,11 @@ def _load_token_from_file(self, token_file: str) -> Optional[str]: except Exception as e: logger.error(f"读取Token文件失败: {e}") return None - + def update_token(self, token: str): """ 更新Token - + 参数: token: 新的Token字符串 """ @@ -86,7 +86,7 @@ def get_cutter_list(self, params: Optional[Dict] = None) -> Dict[str, Any]: 分页查询刀具耗材信息 接口地址: /qw/knife/web/from/mes/cutter/list 请求方式: GET - + 参数: params: 查询参数,包括: - brandName: 品牌名称 @@ -103,39 +103,39 @@ def get_cutter_list(self, params: Optional[Dict] = None) -> Dict[str, Any]: 包含分页数据的响应 """ url = f"{self.base_url}/qw/knife/web/from/mes/cutter/list" - + try: # 构建请求参数,过滤掉None值 request_params = {} - + if params: # 基本查询参数 if params.get("brandName"): request_params["brandName"] = params["brandName"] - + if params.get("cutterType"): request_params["cutterType"] = params["cutterType"] - + if params.get("cutterCode"): request_params["cutterCode"] = params["cutterCode"] - + if params.get("createTime"): request_params["createTime"] = params["createTime"] - + if params.get("createUser") is not None: request_params["createUser"] = params["createUser"] - + # 处理刀具柜名称:需要通过cabinetList传递 if params.get("cabinetName"): request_params["cabinetList[0].cabinetName"] = params["cabinetName"] - + # 价格区间筛选:前端的minPrice和maxPrice,后端通过price参数过滤 # 注:这里可能需要根据实际接口调整,如果接口支持区间查询 # 如果原始接口不支持价格区间,则需要在返回后进行过滤 if params.get("minPrice") is not None or params.get("maxPrice") is not None: # 标记需要进行价格过滤 pass - + # 分页参数 request_params["current"] = params.get("current", 1) request_params["size"] = params.get("size", 10) @@ -143,21 +143,21 @@ def get_cutter_list(self, params: Optional[Dict] = None) -> Dict[str, Any]: # 默认分页参数 request_params["current"] = 1 request_params["size"] = 10 - + logger.info(f"请求刀具列表,参数: {request_params}") - + # 发起GET请求 response = self.session.get(url, params=request_params, timeout=10) response.raise_for_status() - + result = response.json() - + # 如果有价格区间筛选,对结果进行过滤 if params and (params.get("minPrice") is not None or params.get("maxPrice") is not None): if result.get("success") and result.get("data") and result["data"].get("records"): min_price = params.get("minPrice") max_price = params.get("maxPrice") - + # 过滤记录 filtered_records = [] for record in result["data"]["records"]: @@ -169,16 +169,16 @@ def get_cutter_list(self, params: Optional[Dict] = None) -> Dict[str, Any]: if max_price is not None and price > max_price: continue filtered_records.append(record) - + # 更新结果 result["data"]["records"] = filtered_records result["data"]["total"] = len(filtered_records) # 重新计算总页数 size = result["data"].get("size", 10) result["data"]["pages"] = (len(filtered_records) + size - 1) // size if size > 0 else 0 - + return result - + except requests.exceptions.RequestException as e: logger.error(f"获取刀具列表失败: {e}") return { @@ -202,7 +202,7 @@ def create_cutter(self, cutter_data: Dict[str, Any]) -> Dict[str, Any]: 接口地址: /qw/knife/web/from/mes/cutter/saveCutter 请求方式: POST 请求数据类型: application/json - + 参数: cutter_data: 刀具耗材数据,包括: - brandName: 品牌名称(必填) @@ -216,24 +216,24 @@ def create_cutter(self, cutter_data: Dict[str, Any]) -> Dict[str, Any]: 新增结果,包含新增成功后的刀具详情 """ url = f"{self.base_url}/qw/knife/web/from/mes/cutter/saveCutter" - + try: # 构建请求体,处理cabinetList字段 request_body = {} - + # 必填字段 if cutter_data.get("brandName"): request_body["brandName"] = cutter_data["brandName"] - + if cutter_data.get("cutterCode"): request_body["cutterCode"] = cutter_data["cutterCode"] - + if cutter_data.get("price") is not None: request_body["price"] = cutter_data["price"] - + if cutter_data.get("createUser") is not None: request_body["createUser"] = cutter_data["createUser"] - + # 处理刀具柜信息:将cabinetName转换为cabinetList数组 if cutter_data.get("cabinetName"): request_body["cabinetList"] = [{ @@ -243,11 +243,11 @@ def create_cutter(self, cutter_data: Dict[str, Any]) -> Dict[str, Any]: "locSurplus": cutter_data.get("locSurplus", 0), "stockLoc": cutter_data.get("stockLoc", "") }] - + # 处理图片列表 if cutter_data.get("imageUrlList"): request_body["imageUrlList"] = cutter_data["imageUrlList"] - + # 其他可选字段 optional_fields = [ "brandCode", "cutterType", "specification", "materialCode", @@ -255,22 +255,22 @@ def create_cutter(self, cutter_data: Dict[str, Any]) -> Dict[str, Any]: "numberLife", "timeLife", "isUniqueCode", "imageUrl", "createDept", "status", "stockNum", "tenantId" ] - + for field in optional_fields: if cutter_data.get(field) is not None: request_body[field] = cutter_data[field] - + logger.info(f"新增刀具耗材,请求体: {request_body}") - + # 发起POST请求,使用JSON格式 response = self.session.post(url, json=request_body, timeout=10) response.raise_for_status() - + result = response.json() logger.info(f"新增刀具耗材成功: {result}") - + return result - + except requests.exceptions.RequestException as e: logger.error(f"新增刀具耗材失败: {e}") return { @@ -294,7 +294,7 @@ def update_cutter(self, cutter_data: Dict[str, Any]) -> Dict[str, Any]: 接口地址: /qw/knife/web/from/mes/cutter/updateCutter 请求方式: POST 请求数据类型: application/json - + 参数: cutter_data: 刀具耗材数据,包括: - id: 刀具ID(必填,用于标识要修改的记录) @@ -309,11 +309,11 @@ def update_cutter(self, cutter_data: Dict[str, Any]) -> Dict[str, Any]: 修改结果,包含修改成功后的刀具详情 """ url = f"{self.base_url}/qw/knife/web/from/mes/cutter/updateCutter" - + try: # 构建请求体 request_body = {} - + # 必填字段:id if cutter_data.get("id") is None: return { @@ -323,20 +323,20 @@ def update_cutter(self, cutter_data: Dict[str, Any]) -> Dict[str, Any]: "data": None } request_body["id"] = cutter_data["id"] - + # 可选字段 - 基本信息 if cutter_data.get("brandName") is not None: request_body["brandName"] = cutter_data["brandName"] - + if cutter_data.get("cutterCode") is not None: request_body["cutterCode"] = cutter_data["cutterCode"] - + if cutter_data.get("price") is not None: request_body["price"] = cutter_data["price"] - + if cutter_data.get("updateUser") is not None: request_body["updateUser"] = cutter_data["updateUser"] - + # 处理刀具柜信息:将cabinetName转换为cabinetList数组 if cutter_data.get("cabinetName") is not None: request_body["cabinetList"] = [{ @@ -346,35 +346,35 @@ def update_cutter(self, cutter_data: Dict[str, Any]) -> Dict[str, Any]: "locSurplus": cutter_data.get("locSurplus"), "stockLoc": cutter_data.get("stockLoc", "") }] - + # 处理图片列表 if cutter_data.get("imageUrlList") is not None: request_body["imageUrlList"] = cutter_data["imageUrlList"] - + # 其他可选字段 optional_fields = [ "brandCode", "cutterType", "specification", "materialCode", "materialType", "packQty", "packUnit", "inventoryWarning", "numberLife", "timeLife", "isUniqueCode", "imageUrl", - "createDept", "createUser", "createTime", "status", + "createDept", "createUser", "createTime", "status", "stockNum", "tenantId", "version" ] - + for field in optional_fields: if cutter_data.get(field) is not None: request_body[field] = cutter_data[field] - + logger.info(f"修改刀具耗材,请求体: {request_body}") - + # 发起POST请求,使用JSON格式 response = self.session.post(url, json=request_body, timeout=10) response.raise_for_status() - + result = response.json() logger.info(f"修改刀具耗材成功: {result}") - + return result - + except requests.exceptions.RequestException as e: logger.error(f"修改刀具耗材失败: {e}") return { @@ -398,14 +398,14 @@ def delete_cutters(self, ids: str) -> Dict[str, Any]: 接口地址: /qw/knife/web/from/mes/cutter/delete 请求方式: POST 请求数据类型: application/json - + 参数: ids: 主键集合,逗号分割(例如:"1,2,3" 或 "1") 返回: 删除结果,成功返回true """ url = f"{self.base_url}/qw/knife/web/from/mes/cutter/delete" - + try: # 验证ids参数 if not ids or ids.strip() == "": @@ -415,23 +415,23 @@ def delete_cutters(self, ids: str) -> Dict[str, Any]: "success": False, "data": False } - + # 构建请求参数(使用query参数) params = { "ids": ids } - + logger.info(f"删除刀具耗材,参数: {params}") - + # 发起POST请求,使用params传递query参数 response = self.session.post(url, params=params, timeout=10) response.raise_for_status() - + result = response.json() logger.info(f"删除刀具耗材成功: {result}") - + return result - + except requests.exceptions.RequestException as e: logger.error(f"删除刀具耗材失败: {e}") return { @@ -454,7 +454,7 @@ def get_brand_list(self, params: Optional[Dict] = None) -> Dict[str, Any]: 分页查询品牌信息 接口地址: /qw/knife/web/from/mes/cutter/pageListBrand 请求方式: GET - + 参数: params: 查询参数,包括: - brandCode: 品牌编码 @@ -471,38 +471,38 @@ def get_brand_list(self, params: Optional[Dict] = None) -> Dict[str, Any]: 包含分页数据的响应 """ url = f"{self.base_url}/qw/knife/web/from/mes/cutter/pageListBrand" - + try: # 构建请求参数,过滤掉None值 request_params = {} - + if params: # 基本查询参数 if params.get("brandCode"): request_params["brandCode"] = params["brandCode"] - + if params.get("brandName"): request_params["brandName"] = params["brandName"] - + if params.get("corporateName"): request_params["corporateName"] = params["corporateName"] - + if params.get("supplierName"): request_params["supplierName"] = params["supplierName"] - + if params.get("status") is not None: request_params["status"] = params["status"] - + if params.get("createUser") is not None: request_params["createUser"] = params["createUser"] - + # 时间范围查询 if params.get("startTime"): request_params["startTime"] = params["startTime"] - + if params.get("endTime"): request_params["endTime"] = params["endTime"] - + # 分页参数 request_params["current"] = params.get("current", 1) request_params["size"] = params.get("size", 10) @@ -510,17 +510,17 @@ def get_brand_list(self, params: Optional[Dict] = None) -> Dict[str, Any]: # 默认分页参数 request_params["current"] = 1 request_params["size"] = 10 - + logger.info(f"请求品牌列表,参数: {request_params}") - + # 发起GET请求 response = self.session.get(url, params=request_params, timeout=10) response.raise_for_status() - + result = response.json() - + return result - + except requests.exceptions.RequestException as e: logger.error(f"获取品牌列表失败: {e}") return { @@ -544,7 +544,7 @@ def submit_brand(self, brand_data: Dict[str, Any]) -> Dict[str, Any]: 接口地址: /qw/knife/web/from/mes/cutter/submitBrand 请求方式: POST 请求数据类型: application/json - + 参数: brand_data: 品牌信息数据,包括: - id: 主键id(修改时必填,新增时不填) @@ -560,59 +560,59 @@ def submit_brand(self, brand_data: Dict[str, Any]) -> Dict[str, Any]: 操作结果,成功返回true """ url = f"{self.base_url}/qw/knife/web/from/mes/cutter/submitBrand" - + try: # 构建请求体 request_body = {} - + # 必填字段 if brand_data.get("brandCode"): request_body["brandCode"] = brand_data["brandCode"] - + if brand_data.get("brandName"): request_body["brandName"] = brand_data["brandName"] - + if brand_data.get("corporateName"): request_body["corporateName"] = brand_data["corporateName"] - + if brand_data.get("supplierName"): request_body["supplierName"] = brand_data["supplierName"] - + if brand_data.get("supplierUser"): request_body["supplierUser"] = brand_data["supplierUser"] - + if brand_data.get("phone"): request_body["phone"] = brand_data["phone"] - + # 可选字段 if brand_data.get("id") is not None: request_body["id"] = brand_data["id"] - + if brand_data.get("createDept") is not None: request_body["createDept"] = brand_data["createDept"] - + if brand_data.get("status") is not None: request_body["status"] = brand_data["status"] - + # 其他可选字段 optional_fields = ["createUser", "updateUser", "tenantId"] for field in optional_fields: if brand_data.get(field) is not None: request_body[field] = brand_data[field] - + # 判断是新增还是修改 operation = "修改" if brand_data.get("id") else "新增" logger.info(f"{operation}品牌信息,请求体: {request_body}") - + # 发起POST请求,使用JSON格式 response = self.session.post(url, json=request_body, timeout=10) response.raise_for_status() - + result = response.json() logger.info(f"{operation}品牌信息成功: {result}") - + return result - + except requests.exceptions.RequestException as e: logger.error(f"提交品牌信息失败: {e}") return { @@ -636,14 +636,14 @@ def delete_brands(self, ids: str) -> Dict[str, Any]: 接口地址: /qw/knife/web/from/mes/cutter/delBrand 请求方式: POST 请求数据类型: application/json - + 参数: ids: 主键集合,逗号分割(例如:"1,2,3" 或 "1") 返回: 删除结果,成功返回true """ url = f"{self.base_url}/qw/knife/web/from/mes/cutter/delBrand" - + try: # 验证ids参数 if not ids or ids.strip() == "": @@ -653,23 +653,23 @@ def delete_brands(self, ids: str) -> Dict[str, Any]: "success": False, "data": False } - + # 构建请求参数(使用query参数) params = { "ids": ids } - + logger.info(f"删除品牌信息,参数: {params}") - + # 发起POST请求,使用params传递query参数 response = self.session.post(url, params=params, timeout=10) response.raise_for_status() - + result = response.json() logger.info(f"删除品牌信息成功: {result}") - + return result - + except requests.exceptions.RequestException as e: logger.error(f"删除品牌信息失败: {e}") return { @@ -692,7 +692,7 @@ def get_stock_put_list(self, params: Optional[Dict] = None) -> Dict[str, Any]: 获取收刀柜信息 接口地址: /qw/knife/app/from/mes/cabinet/stockPutList 请求方式: GET - + 参数: params: 查询参数,包括: - cabinetCode: 刀柜编码 @@ -706,44 +706,44 @@ def get_stock_put_list(self, params: Optional[Dict] = None) -> Dict[str, Any]: 收刀柜信息列表 """ url = f"{self.base_url}/qw/knife/app/from/mes/cabinet/stockPutList" - + try: # 构建请求参数,过滤掉None值 request_params = {} - + if params: # 基本查询参数 if params.get("cabinetCode"): request_params["cabinetCode"] = params["cabinetCode"] - + if params.get("stockLoc"): request_params["stockLoc"] = params["stockLoc"] - + if params.get("locPrefix"): request_params["locPrefix"] = params["locPrefix"] - + if params.get("stockStatus") is not None: request_params["stockStatus"] = params["stockStatus"] - + if params.get("isBan") is not None: request_params["isBan"] = params["isBan"] - + if params.get("borrowStatus") is not None: request_params["borrowStatus"] = params["borrowStatus"] - + if params.get("storageType") is not None: request_params["storageType"] = params["storageType"] - + logger.info(f"请求收刀柜信息列表,参数: {request_params}") - + # 发起GET请求 response = self.session.get(url, params=request_params, timeout=10) response.raise_for_status() - + result = response.json() - + return result - + except requests.exceptions.RequestException as e: logger.error(f"获取收刀柜信息列表失败: {e}") return { @@ -767,14 +767,14 @@ def unbind_stock_cutter(self, stock_id: int) -> Dict[str, Any]: 接口地址: /qw/knife/web/from/mes/cabinetStock/stockUnBindCutter 请求方式: POST 请求数据类型: application/json - + 参数: stock_id: 刀柜货道主键 返回: 解绑结果,成功返回true """ url = f"{self.base_url}/qw/knife/web/from/mes/cabinetStock/stockUnBindCutter" - + try: # 验证stock_id参数 if stock_id is None: @@ -784,23 +784,23 @@ def unbind_stock_cutter(self, stock_id: int) -> Dict[str, Any]: "success": False, "data": False } - + # 构建请求参数(使用query参数) params = { "stockId": stock_id } - + logger.info(f"解绑货道耗材,参数: {params}") - + # 发起POST请求,使用params传递query参数 response = self.session.post(url, params=params, timeout=10) response.raise_for_status() - + result = response.json() logger.info(f"解绑货道耗材成功: {result}") - + return result - + except requests.exceptions.RequestException as e: logger.error(f"解绑货道耗材失败: {e}") return { @@ -824,7 +824,7 @@ def change_stock_ban_status(self, stock_id: int, is_ban: int) -> Dict[str, Any]: 接口地址: /qw/knife/web/from/mes/cabinetStock/changeBan 请求方式: POST 请求数据类型: application/json - + 参数: stock_id: 刀柜货道主键 is_ban: 0-非禁用(启用) 1-禁用 @@ -832,7 +832,7 @@ def change_stock_ban_status(self, stock_id: int, is_ban: int) -> Dict[str, Any]: 操作结果,成功返回true """ url = f"{self.base_url}/qw/knife/web/from/mes/cabinetStock/changeBan" - + try: # 验证参数 if stock_id is None: @@ -842,7 +842,7 @@ def change_stock_ban_status(self, stock_id: int, is_ban: int) -> Dict[str, Any]: "success": False, "data": False } - + if is_ban not in [0, 1]: return { "code": 400, @@ -850,25 +850,25 @@ def change_stock_ban_status(self, stock_id: int, is_ban: int) -> Dict[str, Any]: "success": False, "data": False } - + # 构建请求参数(使用query参数) params = { "stockId": stock_id, "isBan": is_ban } - + operation = "禁用" if is_ban == 1 else "启用" logger.info(f"{operation}货道库位,参数: {params}") - + # 发起POST请求,使用params传递query参数 response = self.session.post(url, params=params, timeout=10) response.raise_for_status() - + result = response.json() logger.info(f"{operation}货道库位成功: {result}") - + return result - + except requests.exceptions.RequestException as e: logger.error(f"修改货道禁用状态失败: {e}") return { @@ -891,7 +891,7 @@ def get_stock_statistical_num(self, params: Optional[Dict] = None) -> Dict[str, 获取货道统计数据 接口地址: /qw/knife/app/from/mes/cabinet/stockStatisticalNum 请求方式: GET - + 参数: params: 查询参数,包括: - cabinetCode: 刀柜编码 @@ -906,20 +906,20 @@ def get_stock_statistical_num(self, params: Optional[Dict] = None) -> Dict[str, - makeAlarm: 库存告警值 """ url = f"{self.base_url}/qw/knife/app/from/mes/cabinet/stockStatisticalNum" - + try: # 构建请求参数,过滤掉None值 request_params = {} - + if params: # 刀柜编码 if params.get("cabinetCode"): request_params["cabinetCode"] = params["cabinetCode"] - + # 柜子面 if params.get("locPrefix"): request_params["locPrefix"] = params["locPrefix"] - + # 库位类型(默认0-收刀柜) if params.get("locType") is not None: request_params["locType"] = params["locType"] @@ -929,18 +929,18 @@ def get_stock_statistical_num(self, params: Optional[Dict] = None) -> Dict[str, else: # 如果没有传递参数,默认查询收刀柜 request_params["locType"] = 0 - + logger.info(f"请求货道统计数据,参数: {request_params}") - + # 发起GET请求 response = self.session.get(url, params=request_params, timeout=10) response.raise_for_status() - + result = response.json() logger.info(f"获取货道统计数据成功: {result}") - + return result - + except requests.exceptions.RequestException as e: logger.error(f"获取货道统计数据失败: {e}") return { @@ -975,7 +975,7 @@ def get_stock_take_list(self, params: Optional[Dict] = None) -> Dict[str, Any]: 获取取刀柜信息 接口地址: /qw/knife/app/from/mes/cabinet/stockTakeList 请求方式: GET - + 参数: params: 查询参数,包括: - brandCode: 品牌编码 @@ -991,50 +991,50 @@ def get_stock_take_list(self, params: Optional[Dict] = None) -> Dict[str, Any]: 取刀柜信息列表 """ url = f"{self.base_url}/qw/knife/app/from/mes/cabinet/stockTakeList" - + try: # 构建请求参数,过滤掉None值 request_params = {} - + if params: # 基本查询参数 if params.get("brandCode"): request_params["brandCode"] = params["brandCode"] - + if params.get("cabinetCode"): request_params["cabinetCode"] = params["cabinetCode"] - + if params.get("cutterCode"): request_params["cutterCode"] = params["cutterCode"] - + if params.get("cutterType"): request_params["cutterType"] = params["cutterType"] - + if params.get("locPrefix"): request_params["locPrefix"] = params["locPrefix"] - + if params.get("stockLoc"): request_params["stockLoc"] = params["stockLoc"] - + if params.get("cutterOrBrand"): request_params["cutterOrBrand"] = params["cutterOrBrand"] - + if params.get("materialCode"): request_params["materialCode"] = params["materialCode"] - + if params.get("specification"): request_params["specification"] = params["specification"] - + logger.info(f"请求取刀柜信息列表,参数: {request_params}") - + # 发起GET请求 response = self.session.get(url, params=request_params, timeout=10) response.raise_for_status() - + result = response.json() - + return result - + except requests.exceptions.RequestException as e: logger.error(f"获取取刀柜信息列表失败: {e}") return { @@ -1058,7 +1058,7 @@ def pre_batch_plug(self, cabinet_code: str) -> Dict[str, Any]: 接口地址: /qw/knife/web/from/mes/cabinetStock/preBatchPlug 请求方式: POST 请求数据类型: application/json - + 参数: cabinet_code: 刀柜编码 返回: @@ -1067,7 +1067,7 @@ def pre_batch_plug(self, cabinet_code: str) -> Dict[str, Any]: - errorStock: 不能补刀的货道列表 """ url = f"{self.base_url}/qw/knife/web/from/mes/cabinetStock/preBatchPlug" - + try: # 验证cabinet_code参数 if not cabinet_code or cabinet_code.strip() == "": @@ -1080,23 +1080,23 @@ def pre_batch_plug(self, cabinet_code: str) -> Dict[str, Any]: "errorStock": [] } } - + # 构建请求参数(使用query参数) params = { "cabinetCode": cabinet_code } - + logger.info(f"预补刀查询,参数: {params}") - + # 发起POST请求,使用params传递query参数 response = self.session.post(url, params=params, timeout=10) response.raise_for_status() - + result = response.json() logger.info(f"预补刀查询成功: {result}") - + return result - + except requests.exceptions.RequestException as e: logger.error(f"预补刀查询失败: {e}") return { @@ -1126,14 +1126,14 @@ def on_pre_batch_plug(self, cabinet_code: str) -> Dict[str, Any]: 接口地址: /qw/knife/web/from/mes/cabinetStock/onPreBatchPlug 请求方式: POST 请求数据类型: application/json - + 参数: cabinet_code: 刀柜编码 返回: 补刀结果,成功返回true """ url = f"{self.base_url}/qw/knife/web/from/mes/cabinetStock/onPreBatchPlug" - + try: # 验证cabinet_code参数 if not cabinet_code or cabinet_code.strip() == "": @@ -1143,23 +1143,23 @@ def on_pre_batch_plug(self, cabinet_code: str) -> Dict[str, Any]: "success": False, "data": False } - + # 构建请求参数(使用query参数) params = { "cabinetCode": cabinet_code } - + logger.info(f"批量补刀,参数: {params}") - + # 发起POST请求,使用params传递query参数 response = self.session.post(url, params=params, timeout=10) response.raise_for_status() - + result = response.json() logger.info(f"批量补刀成功: {result}") - + return result - + except requests.exceptions.RequestException as e: logger.error(f"批量补刀失败: {e}") return { @@ -1177,16 +1177,949 @@ def on_pre_batch_plug(self, cabinet_code: str) -> Dict[str, Any]: "data": False } + # ==================== 统计接口方法(修正版) ==================== + + def get_storage_statistics(self, params: Dict[str, Any]) -> Dict[str, Any]: + """ + 获取出入库统计数据 + 调用外部接口:/qw/knife/web/from/mes/record/stockList + 请求类型:application/x-www-form-urlencoded + + 参数: + - current: 当前页 + - size: 每页数量 + - startTime: 开始时间 + - endTime: 结束时间 + - recordStatus: 记录状态(0:取刀 1:还刀 2:收刀 3:暂存 4:完成 5:违规还刀) + - rankingType: 排名类型(0:数量 1:金额) + - order: 排序顺序(0:从大到小 1:从小到大) + """ + try: + # 构建查询参数,过滤掉None值 + query_params = {k: v for k, v in params.items() if v is not None} + + # 调用外部接口,使用 params 传递查询参数 + response = self.session.get( + f"{self.base_url}/qw/knife/web/from/mes/record/stockList", + params=query_params, + timeout=10 + ) + response.raise_for_status() + + # 获取外部接口返回的完整数据 + external_data = response.json() + + # 提取需要的字段并返回 + return { + "code": external_data.get("code"), + "msg": external_data.get("msg"), + "success": external_data.get("success"), + "data": external_data.get("data") + } + + except requests.exceptions.RequestException as e: + logger.error(f"获取出入库统计数据失败: {e}") + return { + "code": 500, + "msg": f"调用外部接口失败: {str(e)}", + "success": False, + "data": None + } + + def export_stock_record(self, params: Dict[str, Any]) -> Dict[str, Any]: + """ + 导出刀具耗材数据(出入库记录) + 调用外部接口:/qw/knife/web/from/mes/record/exportStockRecord + 请求方式:GET + 请求数据类型:application/x-www-form-urlencoded + + 参数: + - startTime: 开始时间 + - endTime: 结束时间 + - recordStatus: 记录状态(0:取刀 1:还刀 2:收刀 3:暂存 4:完成 5:违规还刀) + - rankingType: 排名类型(0:数量 1:金额) + - order: 排序顺序(0:从大到小 1:从小到大) + """ + try: + # 构建查询参数,过滤掉None值 + query_params = {k: v for k, v in params.items() if v is not None} + + # 调用外部导出接口 + response = self.session.get( + f"{self.base_url}/qw/knife/web/from/mes/record/exportStockRecord", + params=query_params, + timeout=10 + ) + response.raise_for_status() + + # 获取外部接口返回的完整数据 + external_data = response.json() + + # 提取需要的字段并返回 + return { + "code": external_data.get("code"), + "msg": external_data.get("msg"), + "success": external_data.get("success"), + "data": external_data.get("data") + } + + except requests.exceptions.RequestException as e: + logger.error(f"导出出入库记录失败: {e}") + return { + "code": 500, + "msg": f"调用外部导出接口失败: {str(e)}", + "success": False, + "data": None + } + + def get_charts_accumulated(self) -> Dict[str, Any]: + """ + 获取刀具消耗统计 + 调用外部接口:/qw/knife/web/from/mes/statistics/chartsAccumulated + 请求方式:GET + 无需参数 + + 返回数据: + - titleList: 统计项标题列表(可能包含:累计使用次数、使用时长、平均寿命等) + - dataList: 对应的数据列表 + """ + try: + # 调用外部接口 + response = self.session.get( + f"{self.base_url}/qw/knife/web/from/mes/statistics/chartsAccumulated", + timeout=10 + ) + response.raise_for_status() + + # 获取外部接口返回的数据 + external_data = response.json() + + # 返回需要的字段 + return { + "code": external_data.get("code"), + "msg": external_data.get("msg"), + "success": external_data.get("success"), + "data": external_data.get("data") + } + + except requests.exceptions.RequestException as e: + logger.error(f"获取刀具消耗统计失败: {e}") + return { + "code": 500, + "msg": f"调用外部接口失败: {str(e)}", + "success": False, + "data": None + } + + def get_total_stock_list(self, params: Dict[str, Any]) -> Dict[str, Any]: + """ + 获取总库存统计列表(支持搜索和刷新) + 调用外部接口:/qw/knife/web/from/mes/cabinetStock/stockLocTakeInfoList + 请求方式:GET + + 参数: + - statisticsType: 统计类型 + - brandName: 品牌名称 + - cabinetCode: 刀柜编码 + - cutterType: 刀具类型 + - stockStatus: 库位状态 + - current: 当前页 + - size: 每页数量 + + 返回数据: + - 库存列表数据,包含分页信息和详细记录 + """ + try: + # 构建查询参数 + query_params = {k: v for k, v in params.items() if v is not None} + + # 调用外部接口 + response = self.session.get( + f"{self.base_url}/qw/knife/web/from/mes/cabinetStock/stockLocTakeInfoList", + params=query_params, + timeout=10 + ) + response.raise_for_status() + + # 获取外部接口返回的数据 + external_data = response.json() + + # 如果外部返回的是单个对象,需要封装成列表格式 + data = external_data.get("data") + if data and not isinstance(data, list): + # 如果是单个对象,转换为列表 + data = [data] + + # 计算库存价值(单价 * 剩余数量) + if data and isinstance(data, list): + for item in data: + if isinstance(item, dict): + price = item.get('price', 0) or 0 + loc_surplus = item.get('locSurplus', 0) or 0 + item['stockValue'] = round(price * loc_surplus, 2) + + # 返回需要的字段 + return { + "code": external_data.get("code"), + "msg": external_data.get("msg"), + "success": external_data.get("success"), + "data": data + } + + except requests.exceptions.RequestException as e: + logger.error(f"获取总库存统计列表失败: {e}") + return { + "code": 500, + "msg": f"调用外部接口失败: {str(e)}", + "success": False, + "data": None + } + + def get_stock_location_by_id(self, stock_id: int) -> Dict[str, Any]: + """ + 获取取刀柜库位详情(单个) + 调用外部接口:/qw/knife/web/from/mes/cabinetStock/stockLocTakeInfoById + 请求方式:GET + + 参数: + - stockId: 刀柜货道主键 + + 返回数据: + - 单个库位的详细信息 + """ + try: + # 调用外部接口 + response = self.session.get( + f"{self.base_url}/qw/knife/web/from/mes/cabinetStock/stockLocTakeInfoById", + params={"stockId": stock_id}, + timeout=10 + ) + response.raise_for_status() + + # 获取外部接口返回的数据 + external_data = response.json() + + # 计算库存价值 + data = external_data.get("data") + if data and isinstance(data, dict): + price = data.get('price', 0) or 0 + loc_surplus = data.get('locSurplus', 0) or 0 + data['stockValue'] = round(price * loc_surplus, 2) + + # 返回需要的字段 + return { + "code": external_data.get("code"), + "msg": external_data.get("msg"), + "success": external_data.get("success"), + "data": data + } + + except requests.exceptions.RequestException as e: + logger.error(f"获取库位详情失败: {e}") + return { + "code": 500, + "msg": f"调用外部接口失败: {str(e)}", + "success": False, + "data": None + } + + def get_waste_knife_recycle_info(self, params: Dict[str, Any]) -> Dict[str, Any]: + """ + 获取废刀回收统计信息(收刀柜还刀信息) + 调用外部接口:/qw/knife/web/from/mes/lend/getLendByStock + 请求方式:GET + + 参数: + - borrowCode: 还刀码(可选) + - cabinetCode: 刀柜编码(可选) + - stockLoc: 刀柜库位号(可选) + + 返回数据: + - 还刀数据,包含: + * borrowStatus: 还刀状态 + * cabinetCode: 刀柜编码 + * recordStatus: 记录状态 + * list: 还刀详情列表,包含: + - 还刀状态:0-修磨,1-报废,2-换线,3-错领 + - 还刀人、借刀人 + - 刀具信息:品牌、型号、类型、规格 + - 时间信息:取刀时间、还刀时间 + """ + try: + # 构建查询参数 + query_params = {k: v for k, v in params.items() if v is not None} + + # 调用外部接口 + response = self.session.get( + f"{self.base_url}/qw/knife/web/from/mes/lend/getLendByStock", + params=query_params, + timeout=10 + ) + response.raise_for_status() + + # 获取外部接口返回的数据 + external_data = response.json() + + # 返回需要的字段 + return { + "code": external_data.get("code"), + "msg": external_data.get("msg"), + "success": external_data.get("success"), + "data": external_data.get("data") + } + + except requests.exceptions.RequestException as e: + logger.error(f"获取废刀回收统计信息失败: {e}") + return { + "code": 500, + "msg": f"调用外部接口失败: {str(e)}", + "success": False, + "data": None + } + + # ==================== 合并自 系统记录 模块的API客户端 ==================== + # 合并日期: 2025-11-15 + # 来源: teamleader_record/api_client.py + + # 补货记录API客户端 (班组长) + def get_replenish_records(self, + current: Optional[int] = None, + endTime: Optional[str] = None, + order: Optional[int] = None, + rankingType: Optional[int] = None, + recordStatus: Optional[int] = None, + size: Optional[int] = None, + startTime: Optional[str] = None) -> Dict[str, Any]: + """ + 获取补货记录列表 (班组长) + + Args: + current: 当前页 + endTime: 结束时间 + order: 顺序 0: 从大到小 1:从小到大 + rankingType: 0: 数量 1: 金额 + recordStatus: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + size: 每页的数量 + startTime: 开始时间 + + Returns: + Dict: API响应结果,包含以下字段: + - code: 响应码 + - msg: 响应消息 + - success: 是否成功 + - data: 补货记录数据,包含以下子字段: + - current: 当前页码 + - pages: 总页数 + - records: 记录列表,每条记录包含以下字段: + - lendUserName: 取出人 + - storageUserName: 暂存人 + - brandName: 品牌名称 + - cutterType: 电池类型 + - cutterCode: 刀具型号 + - specification: 规格 + - quantity: 数量 + - oldPrice: 老单价 + - newPrice: 新单价 + - oldStockNum: 操作前库存数 + - newStockNum: 操作后库存数 + - stockLoc: 库位号 + - logType: 补货类型 + - status: 业务状态 + - cabinetCode: 刀柜编码 + - createTime: 创建时间 + - operator: 操作人 + - materialCode: 物料编码 + - detailsCode: 操作详情 + - remake: 备注 + - createDept: 创建部门 + - createUser: 创建人 + - updateUser: 更新人 + - tenantId: 租户ID + - isDeleted: 是否已删除 + - size: 每页记录数 + - total: 总记录数 + """ + url = urljoin(self.base_url, "/qw/knife/web/from/mes/record/replenishList") + + params = {} + + # 添加可选参数 + if current is not None: + params["current"] = current + if endTime is not None: + params["endTime"] = endTime + if order is not None: + params["order"] = order + if rankingType is not None: + params["rankingType"] = rankingType + if recordStatus is not None: + params["recordStatus"] = recordStatus + if size is not None: + params["size"] = size + if startTime is not None: + params["startTime"] = startTime + + try: + response = self.session.get(url, params=params) + response.raise_for_status() + return response.json() + except requests.RequestException as e: + return { + "code": -1, + "msg": f"请求失败: {str(e)}", + "data": { + "current": 0, + "hitCount": False, + "pages": 0, + "records": [], + "searchCount": False, + "size": 0, + "total": 0 + }, + "success": False + } + + def export_replenish_records(self, + endTime: Optional[str] = None, + order: Optional[int] = None, + rankingType: Optional[int] = None, + recordStatus: Optional[int] = None, + startTime: Optional[str] = None) -> bytes: + """ + 导出补货记录 (班组长) + + Args: + endTime: 结束时间 + order: 顺序 0: 从大到小 1:从小到大 + rankingType: 0: 数量 1: 金额 + recordStatus: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + startTime: 开始时间 + + Returns: + bytes: 导出的Excel文件内容,包含以下字段: + - lendUserName: 取出人 + - storageUserName: 暂存人 + - brandName: 品牌名称 + - cutterType: 电池类型 + - cutterCode: 刀具型号 + - specification: 规格 + - quantity: 数量 + - oldPrice: 老单价 + - newPrice: 新单价 + - oldStockNum: 操作前库存数 + - newStockNum: 操作后库存数 + - stockLoc: 库位号 + - logType: 补货类型 + - status: 业务状态 + - cabinetCode: 刀柜编码 + - createTime: 创建时间 + - operator: 操作人 + - materialCode: 物料编码 + - detailsCode: 操作详情 + - remake: 备注 + - createDept: 创建部门 + - createUser: 创建人 + - updateUser: 更新人 + - tenantId: 租户ID + - isDeleted: 是否已删除 + """ + url = urljoin(self.base_url, "/qw/knife/web/from/mes/record/exportReplenishRecord") + + params = {} + + # 添加可选参数 + if endTime: + params["endTime"] = endTime + if order is not None: + params["order"] = order + if rankingType is not None: + params["rankingType"] = rankingType + if recordStatus is not None: + params["recordStatus"] = recordStatus + if startTime: + params["startTime"] = startTime + + try: + response = self.session.get(url, params=params) + response.raise_for_status() + return response.content + except requests.RequestException as e: + raise Exception(f"导出失败: {str(e)}") + + # 领刀记录API客户端 (班组长) + def get_lend_records(self, + current: int = 1, + size: int = 20, + keyword: Optional[str] = None, + department: Optional[str] = None, + startTime: Optional[str] = None, + endTime: Optional[str] = None, + order: Optional[int] = None, + rankingType: Optional[int] = None, + recordStatus: Optional[int] = None) -> Dict[str, Any]: + """ + 获取领刀记录列表 (班组长) + + Args: + current: 当前页码 + size: 每页数量 + keyword: 关键字搜索 + department: 部门筛选 + startTime: 开始时间 + endTime: 结束时间 + order: 顺序 0: 从大到小 1:从小到大 + rankingType: 0: 数量 1: 金额 + recordStatus: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + + Returns: + Dict: API响应结果 + """ + url = urljoin(self.base_url, "/qw/knife/web/from/mes/record/lendList") + + params = { + "current": current, + "size": size + } + + # 添加可选参数 + if keyword: + params["keyword"] = keyword + if department: + params["department"] = department + if startTime: + params["startTime"] = startTime + if endTime: + params["endTime"] = endTime + if order is not None: + params["order"] = order + if rankingType is not None: + params["rankingType"] = rankingType + if recordStatus is not None: + params["recordStatus"] = recordStatus + + try: + response = self.session.get(url, params=params) + response.raise_for_status() + return response.json() + except requests.RequestException as e: + return { + "code": -1, + "msg": f"请求失败: {str(e)}", + "data": { + "current": 0, + "hitCount": False, + "pages": 0, + "records": [], + "searchCount": False, + "size": 0, + "total": 0 + }, + "success": False + } + + def export_lend_records(self, + endTime: Optional[str] = None, + order: Optional[int] = None, + rankingType: Optional[int] = None, + recordStatus: Optional[int] = None, + startTime: Optional[str] = None) -> bytes: + """ + 导出领刀记录 (班组长) + + Args: + endTime: 结束时间 + order: 顺序 0: 从大到小 1:从小到大 + rankingType: 0: 数量 1: 金额 + recordStatus: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + startTime: 开始时间 + + Returns: + bytes: 导出的文件内容 + """ + url = urljoin(self.base_url, "/qw/knife/web/from/mes/record/exportLendRecord") + + params = {} + + # 添加可选参数 + if endTime: + params["endTime"] = endTime + if order is not None: + params["order"] = order + if rankingType is not None: + params["rankingType"] = rankingType + if recordStatus is not None: + params["recordStatus"] = recordStatus + if startTime: + params["startTime"] = startTime + + try: + response = self.session.get(url, params=params) + response.raise_for_status() + return response.content + except requests.RequestException as e: + raise Exception(f"导出失败: {str(e)}") + + # 告警预警API客户端 (班组长) + def list_alarm_warning(self, + locSurplus: Optional[int] = None, + alarmLevel: Optional[int] = None, + deviceType: Optional[str] = None, + cabinetCode: Optional[str] = None, + brandName: Optional[str] = None, + handleStatus: Optional[int] = None, + current: Optional[int] = None, + size: Optional[int] = None) -> Dict[str, Any]: + """ + 获取告警预警列表 (班组长) + + Args: + locSurplus: 货道 + alarmLevel: 预警等级 + deviceType: 设备类型 + cabinetCode: 刀柜编码 + brandName: 品牌名称 + handleStatus: 处理状态 + current: 当前页 + size: 每页数量 + + Returns: + Dict: API响应结果 + """ + url = urljoin(self.base_url, "/qw/knife/web/from/mes/alarm/warning/list") + + params = {} + + # 添加可选参数 + if locSurplus is not None: + params["locSurplus"] = locSurplus + if alarmLevel is not None: + params["alarmLevel"] = alarmLevel + if deviceType is not None: + params["deviceType"] = deviceType + if cabinetCode is not None: + params["cabinetCode"] = cabinetCode + if brandName is not None: + params["brandName"] = brandName + if handleStatus is not None: + params["handleStatus"] = handleStatus + if current is not None: + params["current"] = current + if size is not None: + params["size"] = size + + try: + response = self.session.get(url, params=params) + response.raise_for_status() + return response.json() + except requests.RequestException as e: + return { + "code": -1, + "msg": f"请求失败: {str(e)}", + "data": { + "current": 0, + "hitCount": False, + "pages": 0, + "records": [], + "searchCount": False, + "size": 0, + "total": 0 + }, + "success": False + } + + def get_alarm_statistics(self) -> Dict[str, Any]: + """ + 获取告警统计信息 (班组长) + + Returns: + Dict: API响应结果 + """ + url = urljoin(self.base_url, "/qw/knife/web/from/mes/alarm/warning/statistics") + + try: + response = self.session.get(url) + response.raise_for_status() + return response.json() + except requests.RequestException as e: + return { + "code": -1, + "msg": f"请求失败: {str(e)}", + "data": { + "level1Count": 0, + "level2Count": 0, + "level3Count": 0, + "unhandledCount": 0 + }, + "success": False + } + + def update_alarm_threshold(self, + locSurplus: int, + alarmThreshold: int) -> Dict[str, Any]: + """ + 更新告警阈值 (班组长) + + Args: + locSurplus: 货道 + alarmThreshold: 告警阈值 + + Returns: + Dict: API响应结果 + """ + url = urljoin(self.base_url, "/qw/knife/web/from/mes/alarm/warning/threshold") + + data = { + "locSurplus": locSurplus, + "alarmThreshold": alarmThreshold + } + + try: + response = self.session.post(url, json=data) + response.raise_for_status() + return response.json() + except requests.RequestException as e: + return { + "code": -1, + "msg": f"请求失败: {str(e)}", + "data": None, + "success": False + } + + def handle_alarm_warning(self, + id: int, + handleStatus: int, + handleRemark: Optional[str] = None) -> Dict[str, Any]: + """ + 处理告警预警 (班组长) + + Args: + id: 告警ID + handleStatus: 处理状态 (0: 未处理, 1: 已处理, 2: 已忽略) + handleRemark: 处理备注 + + Returns: + Dict: API响应结果 + """ + url = urljoin(self.base_url, f"/qw/knife/web/from/mes/alarm/warning/{id}/handle") + + data = { + "handleStatus": handleStatus + } + + if handleRemark is not None: + data["handleRemark"] = handleRemark + + try: + response = self.session.post(url, json=data) + response.raise_for_status() + return response.json() + except requests.RequestException as e: + return { + "code": -1, + "msg": f"请求失败: {str(e)}", + "data": None, + "success": False + } + + def batch_handle_alarm_warning(self, + ids: List[int], + handleStatus: int, + handleRemark: Optional[str] = None) -> Dict[str, Any]: + """ + 批量处理告警预警 (班组长) + + Args: + ids: 告警ID列表 + handleStatus: 处理状态 (0: 未处理, 1: 已处理, 2: 已忽略) + handleRemark: 处理备注 + + Returns: + Dict: API响应结果 + """ + url = urljoin(self.base_url, "/qw/knife/web/from/mes/alarm/warning/batch/handle") + + data = { + "ids": ids, + "handleStatus": handleStatus + } + + if handleRemark is not None: + data["handleRemark"] = handleRemark + + try: + response = self.session.post(url, json=data) + response.raise_for_status() + return response.json() + except requests.RequestException as e: + return { + "code": -1, + "msg": f"请求失败: {str(e)}", + "data": None, + "success": False + } + + def export_alarm_warning(self, + locSurplus: Optional[int] = None, + alarmLevel: Optional[int] = None, + deviceType: Optional[str] = None, + cabinetCode: Optional[str] = None, + brandName: Optional[str] = None, + handleStatus: Optional[int] = None) -> bytes: + """ + 导出告警预警 (班组长) + + Args: + locSurplus: 货道 + alarmLevel: 预警等级 + deviceType: 设备类型 + cabinetCode: 刀柜编码 + brandName: 品牌名称 + handleStatus: 处理状态 + + Returns: + bytes: 导出的文件内容 + """ + url = urljoin(self.base_url, "/qw/knife/web/from/mes/alarm/warning/export") + + params = {} + + # 添加可选参数 + if locSurplus is not None: + params["locSurplus"] = locSurplus + if alarmLevel is not None: + params["alarmLevel"] = alarmLevel + if deviceType is not None: + params["deviceType"] = deviceType + if cabinetCode is not None: + params["cabinetCode"] = cabinetCode + if brandName is not None: + params["brandName"] = brandName + if handleStatus is not None: + params["handleStatus"] = handleStatus + + try: + response = self.session.get(url, params=params) + response.raise_for_status() + return response.content + except requests.RequestException as e: + raise Exception(f"导出失败: {str(e)}") + + # 公共暂存记录API客户端 (班组长) + def get_storage_records(self, + current: Optional[int] = None, + endTime: Optional[str] = None, + order: Optional[int] = None, + rankingType: Optional[int] = None, + recordStatus: Optional[int] = None, + size: Optional[int] = None, + startTime: Optional[str] = None) -> Dict[str, Any]: + """ + 获取公共暂存记录列表 (班组长) + + Args: + current: 当前页 + endTime: 结束时间 + order: 顺序 0: 从大到小 1:从小到大 + rankingType: 0: 数量 1: 金额 + recordStatus: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + size: 每页的数量 + startTime: 开始时间 + + Returns: + Dict: API响应结果 + """ + url = urljoin(self.base_url, "/qw/knife/web/from/mes/record/storageList") + + params = {} + + # 添加可选参数 + if current is not None: + params["current"] = current + if endTime is not None: + params["endTime"] = endTime + if order is not None: + params["order"] = order + if rankingType is not None: + params["rankingType"] = rankingType + if recordStatus is not None: + params["recordStatus"] = recordStatus + if size is not None: + params["size"] = size + if startTime is not None: + params["startTime"] = startTime + + try: + response = self.session.get(url, params=params) + response.raise_for_status() + return response.json() + except requests.RequestException as e: + return { + "code": -1, + "msg": f"请求失败: {str(e)}", + "data": { + "current": 0, + "hitCount": False, + "pages": 0, + "records": [], + "searchCount": False, + "size": 0, + "total": 0 + }, + "success": False + } + + def export_storage_records(self, + endTime: Optional[str] = None, + order: Optional[int] = None, + rankingType: Optional[int] = None, + recordStatus: Optional[int] = None, + startTime: Optional[str] = None) -> bytes: + """ + 导出公共暂存记录 (班组长) + + Args: + endTime: 结束时间 + order: 顺序 0: 从大到小 1:从小到大 + rankingType: 0: 数量 1: 金额 + recordStatus: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 + startTime: 开始时间 + + Returns: + bytes: 导出的文件内容 + """ + url = urljoin(self.base_url, "/qw/knife/web/from/mes/record/exportStorageRecord") + + params = {} + + # 添加可选参数 + if endTime: + params["endTime"] = endTime + if order is not None: + params["order"] = order + if rankingType is not None: + params["rankingType"] = rankingType + if recordStatus is not None: + params["recordStatus"] = recordStatus + if startTime: + params["startTime"] = startTime + + try: + response = self.session.get(url, params=params) + response.raise_for_status() + return response.content + except requests.RequestException as e: + raise Exception(f"导出失败: {str(e)}") + # 初始化API客户端 # 使用配置文件的设置 try: from config.config import settings - + # 优先使用环境变量ORIGINAL_API_KEY # 其次尝试从token.txt文件读取 api_key = settings.ORIGINAL_API_KEY if settings.ORIGINAL_API_KEY else None - + teamleader_api_client = TeamLeaderAPIClient( base_url=settings.ORIGINAL_API_BASE_URL, api_key=api_key, @@ -1195,7 +2128,7 @@ def on_pre_batch_plug(self, cabinet_code: str) -> Dict[str, Any]: except ImportError: # 如果无法导入配置,使用默认配置 teamleader_api_client = TeamLeaderAPIClient( - base_url="https://your-api-domain.com", # 请替换为你的真实API地址 + base_url="http://39.38.115.114:8983", # 请替换为你的真实API地址 api_key=None, token_file="token.txt" ) \ No newline at end of file diff --git a/teamleader/services/api_client_record.py b/teamleader/services/api_client_record.py deleted file mode 100644 index 6393414..0000000 --- a/teamleader/services/api_client_record.py +++ /dev/null @@ -1,384 +0,0 @@ -import requests -from typing import Dict, Any, Optional, List -from urllib.parse import urljoin -import json - - -class LendRecordAPIClient: - """ - 领刀记录API客户端 - """ - - def __init__(self, base_url: str = "http://localhost:8080"): - """ - 初始化API客户端 - - Args: - base_url: API基础URL - """ - self.base_url = base_url - self.session = requests.Session() - - def get_lend_records(self, - page: int = 1, - page_size: int = 20, - keyword: Optional[str] = None, - department: Optional[str] = None, - start_date: Optional[str] = None, - end_date: Optional[str] = None, - order: Optional[int] = None, - rankingType: Optional[int] = None, - recordStatus: Optional[int] = None) -> Dict[str, Any]: - """ - 获取领刀记录列表 - - Args: - page: 页码 - page_size: 每页数量 - keyword: 关键字搜索 - department: 部门筛选 - start_date: 开始时间 - end_date: 结束时间 - order: 顺序 0: 从大到小 1:从小到大 - rankingType: 0: 数量 1: 金额 - recordStatus: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 - - Returns: - Dict: API响应结果 - """ - url = urljoin(self.base_url, "/qw/knife/web/from/mes/record/lendList") - - params = { - "current": page, - "size": page_size - } - - # 添加可选参数 - if keyword: - params["keyword"] = keyword - if department: - params["department"] = department - if start_date: - params["startTime"] = start_date - if end_date: - params["endTime"] = end_date - if order is not None: - params["order"] = order - if rankingType is not None: - params["rankingType"] = rankingType - if recordStatus is not None: - params["recordStatus"] = recordStatus - - try: - response = self.session.get(url, params=params) - response.raise_for_status() - return response.json() - except requests.RequestException as e: - return { - "code": -1, - "msg": f"请求失败: {str(e)}", - "data": { - "current": 0, - "hitCount": False, - "pages": 0, - "records": [], - "searchCount": False, - "size": 0, - "total": 0 - }, - "success": False - } - - def export_lend_records(self, - endTime: Optional[str] = None, - order: Optional[int] = None, - rankingType: Optional[int] = None, - recordStatus: Optional[int] = None, - startTime: Optional[str] = None) -> bytes: - """ - 导出领刀记录 - - Args: - endTime: 结束时间 - order: 顺序 0: 从大到小 1:从小到大 - rankingType: 0: 数量 1: 金额 - recordStatus: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 - startTime: 开始时间 - - Returns: - bytes: 导出的文件内容 - """ - url = urljoin(self.base_url, "/qw/knife/web/from/mes/record/exportLendRecord") - - params = {} - - # 添加可选参数 - if endTime: - params["endTime"] = endTime - if order is not None: - params["order"] = order - if rankingType is not None: - params["rankingType"] = rankingType - if recordStatus is not None: - params["recordStatus"] = recordStatus - if startTime: - params["startTime"] = startTime - - try: - response = self.session.get(url, params=params) - response.raise_for_status() - return response.content - except requests.RequestException as e: - raise Exception(f"导出失败: {str(e)}") - - def restock_cabinet(self, - cabinetCode: Optional[str] = None, - itemDtoList: Optional[List[Dict[str, Any]]] = None, - replenishDto: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: - """ - 刀柜补货 - - Args: - replenishDto: replenishDto 对象 - cabinetCode: 刀柜编码 - itemDtoList: 补货信息列表 - - Returns: - Dict: API响应结果 - """ - url = urljoin(self.base_url, "/qw/knife/app/from/mes/cabinet/changeBan") - - # 构造请求数据 - data = {} - if replenishDto is not None: - data["replenishDto"] = replenishDto - if cabinetCode is not None: - data["cabinetCode"] = cabinetCode - if itemDtoList is not None: - data["itemDtoList"] = itemDtoList - - try: - response = self.session.post(url, json=data) - response.raise_for_status() - return response.json() - except requests.RequestException as e: - return { - "code": -1, - "msg": f"补货失败: {str(e)}", - "data": None, - "success": False, - "dataSuccess": False - } - - def get_replenish_records(self, - current: Optional[int] = None, - endTime: Optional[str] = None, - order: Optional[int] = None, - rankingType: Optional[int] = None, - recordStatus: Optional[int] = None, - size: Optional[int] = None, - startTime: Optional[str] = None) -> Dict[str, Any]: - """ - 获取补货记录列表 - - Args: - current: 当前页 - endTime: 结束时间 - order: 顺序 0: 从大到小 1:从小到大 - rankingType: 0: 数量 1: 金额 - recordStatus: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 - size: 每页的数量 - startTime: 开始时间 - - Returns: - Dict: API响应结果 - """ - url = urljoin(self.base_url, "/qw/knife/web/from/mes/record/replenishList") - - params = {} - - # 添加可选参数 - if current is not None: - params["current"] = current - if endTime is not None: - params["endTime"] = endTime - if order is not None: - params["order"] = order - if rankingType is not None: - params["rankingType"] = rankingType - if recordStatus is not None: - params["recordStatus"] = recordStatus - if size is not None: - params["size"] = size - if startTime is not None: - params["startTime"] = startTime - - try: - response = self.session.get(url, params=params) - response.raise_for_status() - return response.json() - except requests.RequestException as e: - return { - "code": -1, - "msg": f"请求失败: {str(e)}", - "data": { - "current": 0, - "hitCount": False, - "pages": 0, - "records": [], - "searchCount": False, - "size": 0, - "total": 0 - }, - "success": False - } - - def get_storage_records(self, - current: Optional[int] = None, - endTime: Optional[str] = None, - order: Optional[int] = None, - rankingType: Optional[int] = None, - recordStatus: Optional[int] = None, - size: Optional[int] = None, - startTime: Optional[str] = None) -> Dict[str, Any]: - """ - 获取公共暂存记录列表 - - Args: - current: 当前页 - endTime: 结束时间 - order: 顺序 0: 从大到小 1:从小到大 - rankingType: 0: 数量 1: 金额 - recordStatus: 0: 取刀 1: 还刀 2: 收刀 3: 暂存 4: 完成 5:违规还刀 - size: 每页的数量 - startTime: 开始时间 - - Returns: - Dict: API响应结果 - """ - url = urljoin(self.base_url, "/qw/knife/web/from/mes/record/storageList") - - params = {} - - # 添加可选参数 - if current is not None: - params["current"] = current - if endTime is not None: - params["endTime"] = endTime - if order is not None: - params["order"] = order - if rankingType is not None: - params["rankingType"] = rankingType - if recordStatus is not None: - params["recordStatus"] = recordStatus - if size is not None: - params["size"] = size - if startTime is not None: - params["startTime"] = startTime - - try: - response = self.session.get(url, params=params) - response.raise_for_status() - return response.json() - except requests.RequestException as e: - return { - "code": -1, - "msg": f"请求失败: {str(e)}", - "data": { - "current": 0, - "hitCount": False, - "pages": 0, - "records": [], - "searchCount": False, - "size": 0, - "total": 0 - }, - "success": False - } - - def get_personal_storage(self, cabinetCode: Optional[str] = None) -> Dict[str, Any]: - """ - 获取个人暂存柜信息 - - Args: - cabinetCode: 刀柜编码 - - Returns: - Dict: API响应结果 - """ - url = urljoin(self.base_url, "/qw/knife/app/from/mes/cabinet/personalStorage") - - params = {} - if cabinetCode is not None: - params["cabinetCode"] = cabinetCode - - try: - response = self.session.get(url, params=params) - response.raise_for_status() - return response.json() - except requests.RequestException as e: - return { - "code": -1, - "msg": f"请求失败: {str(e)}", - "data": None, - "success": False - } - - def set_make_alarm(self, cabinetCode: Optional[str] = None, alarmValue: Optional[int] = None) -> Dict[str, Any]: - """ - 设置取刀柜告警值 - - Args: - cabinetCode: 刀柜编码 - alarmValue: 告警值 - - Returns: - Dict: API响应结果 - """ - url = urljoin(self.base_url, "/qw/knife/web/from/mes/cabinet/makeAlarm") - - params = {} - if cabinetCode is not None: - params["cabinetCode"] = cabinetCode - if alarmValue is not None: - params["alarmValue"] = alarmValue - - try: - response = self.session.get(url, params=params) - response.raise_for_status() - return response.json() - except requests.RequestException as e: - return { - "code": -1, - "msg": f"请求失败: {str(e)}", - "data": False, - "success": False - } - - def get_make_alarm(self, cabinetCode: Optional[str] = None) -> Dict[str, Any]: - """ - 获取取刀柜告警值 - - Args: - cabinetCode: 刀柜编码 - - Returns: - Dict: API响应结果 - """ - url = urljoin(self.base_url, "/qw/knife/web/from/mes/cabinet/getMakeAlarm") - - params = {} - if cabinetCode is not None: - params["cabinetCode"] = cabinetCode - - try: - response = self.session.get(url, params=params) - response.raise_for_status() - return response.json() - except requests.RequestException as e: - return { - "code": -1, - "msg": f"请求失败: {str(e)}", - "data": None, - "success": False - } \ No newline at end of file diff --git a/testapi.py b/testapi.py new file mode 100644 index 0000000..3c689b0 --- /dev/null +++ b/testapi.py @@ -0,0 +1,168 @@ +import requests +import json +from datetime import datetime, timedelta + + +def test_knife_statistics_api(): + # 服务器基础地址 + base_url = "http://39.98.115.114:8983" + + # 接口地址 + api_url = f"{base_url}/qw/knife/web/from/mes/statistics/chartsDeviceRanking" + + # 从token.txt读取token + with open('token.txt', 'r', encoding='utf-8') as f: + token_ = f.read().strip() + + # 设置请求头 + headers = { + 'Authorization': f'Bearer {token_}', + 'Content-Type': 'application/x-www-form-urlencoded', + 'Accept': '*/*' + } + + # 设置查询参数 - 这里设置一些示例参数,你可以根据需要调整 + params = { + 'startTime': '2025-01-01 00:00:00', # 开始时间 + 'endTime': '2025-12-31 23:59:59', # 结束时间 + 'order': 0, # 0:从大到小 1:从小到大 + 'rankingType': 0, # 0:数量 1:金额 + 'recordStatus': 0 # 0:取刀 1:还刀 2:收刀 3:暂存 4:完成 5:违规还刀 + } + + try: + # 发送GET请求 + response = requests.get( + url=api_url, + headers=headers, + params=params, + timeout=30 # 设置超时时间 + ) + + print(f"状态码: {response.status_code}") + print(f"响应头: {dict(response.headers)}") + + # 尝试解析JSON响应 + try: + json_data = response.json() + print("响应数据 (JSON格式):") + print(json.dumps(json_data, indent=2, ensure_ascii=False)) + + # 保存响应到文件以便后续分析 + with open('api_response.json', 'w', encoding='utf-8') as f: + json.dump(json_data, f, indent=2, ensure_ascii=False) + print("\n响应数据已保存到 api_response.json 文件") + + except json.JSONDecodeError: + print("响应不是有效的JSON格式") + print("原始响应内容:") + print(response.text) + + except requests.exceptions.RequestException as e: + print(f"请求失败: {e}") + except Exception as e: + print(f"发生错误: {e}") + + +def test_with_different_parameters(): + """测试不同参数组合的响应""" + base_url = "http://39.98.115.114:8983" + api_url = f"{base_url}/qw/knife/web/from/mes/statistics/chartsDeviceRanking" + + with open('token.txt', 'r', encoding='utf-8') as f: + token_ = f.read().strip() + + headers = { + 'Authorization': f'Bearer {token_}', + 'Content-Type': 'application/x-www-form-urlencoded', + 'Accept': '*/*' + } + + # 不同的参数组合测试 + test_cases = [ + { + 'name': '数量排名-从大到小', + 'params': { + 'startTime': '2024-01-01 00:00:00', + 'endTime': '2024-12-31 23:59:59', + 'order': 0, + 'rankingType': 0, + 'recordStatus': 0 + } + }, + { + 'name': '金额排名-从小到大', + 'params': { + 'startTime': '2024-01-01 00:00:00', + 'endTime': '2024-12-31 23:59:59', + 'order': 1, + 'rankingType': 1, + 'recordStatus': 1 + } + }, + { + 'name': '仅时间范围', + 'params': { + 'startTime': '2024-06-01 00:00:00', + 'endTime': '2024-06-30 23:59:59' + } + } + ] + + for test_case in test_cases: + print(f"\n{'=' * 50}") + print(f"测试用例: {test_case['name']}") + print(f"参数: {test_case['params']}") + print('=' * 50) + + try: + response = requests.get( + url=api_url, + headers=headers, + params=test_case['params'], + timeout=30 + ) + + print(f"状态码: {response.status_code}") + + if response.status_code == 200: + try: + json_data = response.json() + print("响应数据结构:") + print(f"数据类型: {type(json_data)}") + if isinstance(json_data, dict): + print(f"包含字段: {list(json_data.keys())}") + elif isinstance(json_data, list): + print(f"数组长度: {len(json_data)}") + if len(json_data) > 0: + print(f"数组元素类型: {type(json_data[0])}") + if isinstance(json_data[0], dict): + print(f"第一个元素的字段: {list(json_data[0].keys())}") + + # 保存到单独的文件 + filename = f"response_{test_case['name']}.json" + with open(filename, 'w', encoding='utf-8') as f: + json.dump(json_data, f, indent=2, ensure_ascii=False) + print(f"响应已保存到: {filename}") + + except json.JSONDecodeError: + print("响应不是有效的JSON格式") + print(f"原始响应: {response.text[:200]}...") # 只显示前200个字符 + else: + print(f"请求失败,状态码: {response.status_code}") + print(f"错误信息: {response.text}") + + except Exception as e: + print(f"请求异常: {e}") + + +if __name__ == "__main__": + print("开始测试刀具统计API...") + + # 执行基本测试 + print("\n1. 基本API测试:") + test_knife_statistics_api() + + # 执行多参数测试 + print("\n2. 多参数组合测试:") + test_with_different_parameters() \ No newline at end of file diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 0000000..75d7d1e --- /dev/null +++ b/utils/__init__.py @@ -0,0 +1,4 @@ +"""Utils工具包""" +from .token_manager import TokenManager, refresh_token + +__all__ = ['TokenManager', 'refresh_token'] diff --git a/utils/token_manager.py b/utils/token_manager.py new file mode 100644 index 0000000..e4781f7 --- /dev/null +++ b/utils/token_manager.py @@ -0,0 +1,195 @@ +""" +Token管理工具 +用于登录获取新Token并自动保存到token.txt文件 +""" +import requests +import logging +import os +from typing import Dict, Any, Optional + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + + +class TokenManager: + """Token管理器 - 负责登录和Token刷新""" + + def __init__(self): + self.login_url = "https://www.weiliansmartcabinet.com/api/blade-auth/oauth/token" + self.project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + self.token_file = os.path.join(self.project_root, "token.txt") + + def login(self, username: str, password: str) -> Optional[str]: + """ + 使用账号密码登录获取Token + + 参数: + username: 用户名 + password: MD5加密后的密码 + + 返回: + Token字符串,失败返回None + """ + # 请求参数 + data = { + "username": username, + "password": password, + "grantTypeInfo": "web", # 使用web方式登录,而不是applet + "grant_type": "captcha" + } + + # 请求头 + headers = { + "Content-Type": "application/x-www-form-urlencoded", + "Authorization": "Basic c2FiZXI6c2FiZXJfc2VjcmV0" + } + + try: + logger.info(f"正在为用户 {username} 登录...") + + # 发送登录请求 + response = requests.post( + self.login_url, + data=data, + headers=headers, + timeout=10 + ) + response.raise_for_status() + + # 解析响应 + result = response.json() + + # 打印完整响应用于调试 + logger.info(f"响应状态码: {response.status_code}") + logger.info(f"响应内容: {result}") + + # 检查响应格式:直接返回access_token,没有success字段 + token = result.get("access_token") + if token: + logger.info("✅ 登录成功!") + logger.info(f"Token有效期: {result.get('expires_in')} 秒") + return token + else: + # 如果没有access_token,尝试检查是否有错误信息 + error_msg = result.get('msg') or result.get('error_description') or '未知错误' + logger.error(f"❌ 登录失败: {error_msg}") + logger.error(f"完整响应: {result}") + return None + + except requests.exceptions.RequestException as e: + logger.error(f"❌ 登录请求失败: {e}") + return None + except Exception as e: + logger.error(f"❌ 登录过程出错: {e}") + return None + + def save_token(self, token: str) -> bool: + """ + 保存Token到token.txt文件 + + 参数: + token: Token字符串 + + 返回: + True表示保存成功,False表示失败 + """ + try: + with open(self.token_file, 'w', encoding='utf-8') as f: + f.write(token) + logger.info(f"✅ Token已保存到: {self.token_file}") + return True + except Exception as e: + logger.error(f"❌ 保存Token失败: {e}") + return False + + def refresh_token(self, username: str, password: str) -> bool: + """ + 刷新Token(登录并保存) + + 参数: + username: 用户名 + password: MD5加密后的密码 + + 返回: + True表示刷新成功,False表示失败 + """ + logger.info("========== Token刷新开始 ==========") + + # 登录获取新Token + token = self.login(username, password) + if not token: + logger.error("❌ Token刷新失败") + return False + + # 保存Token + if self.save_token(token): + logger.info("✅ Token刷新成功!") + logger.info("===================================") + return True + else: + logger.error("❌ Token刷新失败") + return False + + def get_current_token(self) -> Optional[str]: + """ + 读取当前保存的Token + + 返回: + Token字符串,失败返回None + """ + try: + if os.path.exists(self.token_file): + with open(self.token_file, 'r', encoding='utf-8') as f: + token = f.read().strip() + return token if token else None + else: + logger.warning(f"Token文件不存在: {self.token_file}") + return None + except Exception as e: + logger.error(f"读取Token失败: {e}") + return None + + +# 便捷函数 +def refresh_token(username: str = "李敖", password: str = "e52ca69c7b3f5c50483cf9c821e77157"): + """ + 快捷刷新Token函数 + + 参数: + username: 用户名,默认"李敖" + password: MD5密码,默认"e52ca69c7b3f5c50483cf9c821e77157" + """ + manager = TokenManager() + return manager.refresh_token(username, password) + + +# 主程序 - 可以直接运行此脚本刷新Token +if __name__ == "__main__": + import sys + + print("\n" + "="*50) + print(" Token 刷新工具") + print("="*50 + "\n") + + # 检查是否提供了命令行参数 + if len(sys.argv) >= 3: + username = sys.argv[1] + password = sys.argv[2] + print(f"使用提供的账号: {username}") + else: + # 使用默认账号 + username = "李敖" + password = "e52ca69c7b3f5c50483cf9c821e77157" + print(f"使用默认账号: {username}") + + print() + + # 执行刷新 + success = refresh_token(username, password) + + if success: + print("\n✅ Token刷新完成!可以重启服务使用新Token。\n") + sys.exit(0) + else: + print("\n❌ Token刷新失败!请检查账号密码或网络连接。\n") + sys.exit(1) diff --git "a/\345\256\241\350\256\241\345\221\230\346\216\245\345\217\243\346\265\213\350\257\225\346\214\207\345\215\227.md" "b/\345\256\241\350\256\241\345\221\230\346\216\245\345\217\243\346\265\213\350\257\225\346\214\207\345\215\227.md" new file mode 100644 index 0000000..806ddbd --- /dev/null +++ "b/\345\256\241\350\256\241\345\221\230\346\216\245\345\217\243\346\265\213\350\257\225\346\214\207\345\215\227.md" @@ -0,0 +1,587 @@ +# 审计员接口测试指南 + +## 📋 目录 +1. [环境准备](#环境准备) +2. [Token获取与更新](#token获取与更新) +3. [启动审计员服务](#启动审计员服务) +4. [Postman接口测试](#postman接口测试) +5. [常见问题](#常见问题) + +--- + +## 环境准备 + +### 1. 安装Python依赖 + +```bash +cd /Users/linhe/PycharmProjects/KnifeInterface + +# 安装所有依赖 +pip3 install requests fastapi uvicorn pydantic python-dotenv +``` + +### 2. 验证安装 + +```bash +python3 -c "import fastapi, uvicorn, requests; print('✅ 依赖安装成功')" +``` + +--- + +## Token获取与更新 + +### 方式1:使用Token管理工具(推荐) + +#### 快速刷新Token + +```bash +cd /Users/linhe/PycharmProjects/KnifeInterface + +# 使用默认账号密码刷新Token +python3 utils/token_manager.py +``` + +**预期输出**: +``` +================================================== + Token 刷新工具 +================================================== + +使用默认账号: 李敖 + +INFO:__main__:========== Token刷新开始 ========== +INFO:__main__:正在为用户 李敖 登录... +INFO:__main__:响应状态码: 200 +INFO:__main__:✅ 登录成功! +INFO:__main__:Token有效期: 3600 秒 +INFO:__main__:✅ Token已保存到: /Users/linhe/PycharmProjects/KnifeInterface/token.txt +INFO:__main__:✅ Token刷新成功! + +✅ Token刷新完成!可以重启服务使用新Token。 +``` + +#### 使用自定义账号 + +```bash +# 格式:python3 utils/token_manager.py "用户名" "MD5加密的密码" +python3 utils/token_manager.py "李敖" "e52ca69c7b3f5c50483cf9c821e77157" +``` + +### 方式2:手动验证Token + +#### 检查Token文件 + +```bash +# 查看Token文件是否存在 +ls -lh token.txt + +# 查看Token前100个字符 +head -c 100 token.txt +``` + +#### 测试Token有效性 + +```bash +# 使用curl测试Token是否有效 +TOKEN=$(cat token.txt) +curl -s -X GET "http://39.98.115.114:8983/qw/knife/web/from/mes/statistics/chartsLendByYear" \ + -H "Blade-Auth: Bearer $TOKEN" | python3 -m json.tool +``` + +**成功响应示例**: +```json +{ + "code": 200, + "success": true, + "data": { + "titleList": ["1月", "2月", "3月", ...], + "dataList": ["0", "0", "0", ...] + } +} +``` + +**失败响应(Token过期)**: +```json +{ + "code": 401, + "success": false, + "msg": "请求未授权" +} +``` + +--- + +## 启动审计员服务 + +### 1. 启动服务 + +```bash +cd /Users/linhe/PycharmProjects/KnifeInterface + +# 启动审计员服务(端口8003) +python3 -m auditor.main +``` + +**预期输出**: +``` +2025-11-14 15:00:00 - auditor.main - INFO - ========== 审计员服务启动 ========== +2025-11-14 15:00:00 - auditor.main - INFO - API Base URL: http://39.98.115.114:8983 +2025-11-14 15:00:00 - auditor.main - INFO - ✅ Token已加载(前50字符): Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9... +2025-11-14 15:00:00 - auditor.main - INFO - ===================================== +INFO: Will watch for changes in these directories: ['/Users/linhe/PycharmProjects/KnifeInterface'] +INFO: Uvicorn running on http://0.0.0.0:8003 (Press CTRL+C to quit) +INFO: Started reloader process [xxxxx] using StatReload +INFO: Started server process [xxxxx] +INFO: Waiting for application startup. +INFO: Application startup complete. +``` + +### 2. 后台启动(可选) + +```bash +# 后台启动并将日志输出到文件 +nohup python3 -m auditor.main > auditor.log 2>&1 & + +# 查看日志 +tail -f auditor.log + +# 停止服务 +pkill -f "auditor.main" +``` + +### 3. 验证服务状态 + +#### 方法1:检查端口 + +```bash +# 检查8003端口是否被占用 +lsof -i :8003 +``` + +**预期输出**: +``` +COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME +Python 99904 linhe 4u IPv4 xxxxx 0t0 TCP *:8003 (LISTEN) +``` + +#### 方法2:健康检查 + +```bash +curl http://localhost:8003/health +``` + +**预期响应**: +```json +{ + "status": "healthy", + "service": "auditor" +} +``` + +#### 方法3:访问API文档 + +在浏览器打开:`http://localhost:8003/docs` + +--- + +## Postman接口测试 + +### 环境配置 + +#### 1. 创建环境变量 + +在Postman中创建新环境:`刀具管理系统-审计员` + +添加以下变量: + +| 变量名 | 值 | 说明 | +|--------|-----|------| +| `base_url` | `http://localhost:8003/api/v1/auditor` | 审计员服务基础URL | +| `external_api` | `http://39.98.115.114:8983` | 外部API地址 | +| `token` | `从token.txt复制` | Bearer Token(可选) | + +### 测试用例 + +#### 1. 系统接口测试 + +##### 1.1 健康检查 + +- **请求方式**: `GET` +- **URL**: `http://localhost:8003/health` +- **Headers**: 无 +- **预期结果**: +```json +{ + "status": "healthy", + "service": "auditor" +} +``` + +##### 1.2 服务根路径 + +- **请求方式**: `GET` +- **URL**: `http://localhost:8003/` +- **Headers**: 无 +- **预期结果**: +```json +{ + "message": "审计员接口服务已启动", + "status": "success", + "role": "auditor", + "permissions": "只读权限" +} +``` + +--- + +#### 2. 统计图表接口 + +##### 2.1 全年取刀数量统计 + +- **请求方式**: `GET` +- **URL**: `{{base_url}}/charts-lend-by-year` +- **完整URL**: `http://localhost:8003/api/v1/auditor/charts-lend-by-year` +- **Headers**: 无 +- **参数**: 无 +- **预期结果**: +```json +{ + "code": 200, + "msg": "操作成功", + "success": true, + "data": { + "titleList": ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月"], + "dataList": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "1.00", "5.00"] + } +} +``` + +##### 2.2 全年取刀金额统计 + +- **请求方式**: `GET` +- **URL**: `{{base_url}}/charts-lend-price-by-year` +- **完整URL**: `http://localhost:8003/api/v1/auditor/charts-lend-price-by-year` +- **Headers**: 无 +- **参数**: 无 +- **预期结果**: +```json +{ + "code": 200, + "success": true, + "data": { + "titleList": ["1月", "2月", ..., "12月"], + "dataList": [15000.50, 20000.00, ...] + } +} +``` + +##### 2.3 刀具消耗统计 + +- **请求方式**: `GET` +- **URL**: `{{base_url}}/charts-accumulated` +- **完整URL**: `http://localhost:8003/api/v1/auditor/charts-accumulated` +- **Headers**: 无 +- **参数**: 无 + +--- + +#### 3. 出入库统计接口 + +##### 3.1 出入库统计查询(分页) + +- **请求方式**: `GET` +- **URL**: `{{base_url}}/storage-statistics` +- **完整URL**: `http://localhost:8003/api/v1/auditor/storage-statistics` +- **Headers**: 无 +- **Query参数**: + +| 参数名 | 类型 | 必填 | 说明 | 示例值 | +|--------|------|------|------|--------| +| current | int | 否 | 当前页 | 1 | +| size | int | 否 | 每页数量 | 10 | +| startTime | string | 否 | 开始时间 | 2024-01-01 00:00:00 | +| endTime | string | 否 | 结束时间 | 2024-12-31 23:59:59 | +| recordStatus | int | 否 | 记录状态:0-取刀,1-还刀,2-收刀,3-暂存,4-完成,5-违规还刀 | 0 | +| rankingType | int | 否 | 排名类型:0-按数量,1-按金额 | 0 | +| order | int | 否 | 排序:0-降序,1-升序 | 0 | + +**测试示例1**(基础查询): +``` +GET http://localhost:8003/api/v1/auditor/storage-statistics?current=1&size=10 +``` + +**测试示例2**(带筛选条件): +``` +GET http://localhost:8003/api/v1/auditor/storage-statistics?current=1&size=10&recordStatus=0&order=0 +``` + +##### 3.2 导出出入库记录 + +- **请求方式**: `GET` +- **URL**: `{{base_url}}/export-stock-record` +- **完整URL**: `http://localhost:8003/api/v1/auditor/export-stock-record` +- **Query参数**: 同3.1(除了current和size) + +--- + +#### 4. 总库存统计接口 + +##### 4.1 总库存列表查询 + +- **请求方式**: `GET` +- **URL**: `{{base_url}}/total-stock` +- **完整URL**: `http://localhost:8003/api/v1/auditor/total-stock` +- **Query参数**: + +| 参数名 | 类型 | 必填 | 说明 | 示例值 | +|--------|------|------|------|--------| +| current | int | 否 | 当前页 | 1 | +| size | int | 否 | 每页数量 | 10 | +| statisticsType | string | 否 | 统计类型 | - | +| brandName | string | 否 | 品牌名称 | 三菱 | +| cabinetCode | string | 否 | 刀柜编码 | CB001 | +| cutterType | string | 否 | 刀具类型 | 车刀片 | +| stockStatus | int | 否 | 库位状态 | 1 | + +**测试示例1**(查询所有): +``` +GET http://localhost:8003/api/v1/auditor/total-stock?current=1&size=20 +``` + +**测试示例2**(按品牌筛选): +``` +GET http://localhost:8003/api/v1/auditor/total-stock?brandName=三菱&cutterType=车刀片¤t=1&size=10 +``` + +##### 4.2 单个库位详情 + +- **请求方式**: `GET` +- **URL**: `{{base_url}}/stock-location/{stock_id}` +- **完整URL**: `http://localhost:8003/api/v1/auditor/stock-location/123` +- **路径参数**: + - `stock_id`: 库位主键ID(整数) + +**测试示例**: +``` +GET http://localhost:8003/api/v1/auditor/stock-location/123 +``` + +--- + +#### 5. 废刀回收统计接口 + +##### 5.1 废刀回收信息查询 + +- **请求方式**: `GET` +- **URL**: `{{base_url}}/waste-knife-recycle` +- **完整URL**: `http://localhost:8003/api/v1/auditor/waste-knife-recycle` +- **Query参数**: + +| 参数名 | 类型 | 必填 | 说明 | 示例值 | +|--------|------|------|------|--------| +| borrowCode | string | 否 | 还刀码 | - | +| cabinetCode | string | 否 | 刀柜编码 | CB001 | +| stockLoc | string | 否 | 库位号 | A01-01 | + +**测试示例**: +``` +GET http://localhost:8003/api/v1/auditor/waste-knife-recycle?cabinetCode=CB001&stockLoc=A01-01 +``` + +--- + +### Postman Collection结构建议 + +``` +审计员接口测试/ +├── 系统接口/ +│ ├── 健康检查 +│ └── 服务根路径 +├── 统计图表/ +│ ├── 全年取刀数量统计 +│ ├── 全年取刀金额统计 +│ └── 刀具消耗统计 +├── 出入库统计/ +│ ├── 出入库统计查询 +│ └── 导出出入库记录 +├── 总库存统计/ +│ ├── 总库存列表查询 +│ └── 单个库位详情 +└── 废刀回收统计/ + └── 废刀回收信息查询 +``` + +--- + +## 常见问题 + +### Q1: Token过期怎么办? + +**问题现象**: +```json +{ + "code": 401, + "msg": "请求未授权" +} +``` + +**解决方案**: +```bash +# 重新获取Token +python3 utils/token_manager.py + +# 重启服务 +pkill -f "auditor.main" +python3 -m auditor.main +``` + +--- + +### Q2: 服务启动失败 + +**问题1**: `ModuleNotFoundError: No module named 'fastapi'` + +**解决方案**: +```bash +pip3 install fastapi uvicorn pydantic requests python-dotenv +``` + +**问题2**: 端口8003已被占用 + +**解决方案**: +```bash +# 查找占用进程 +lsof -i :8003 + +# 杀死进程 +kill -9 +``` + +--- + +### Q3: 接口返回500错误 + +**可能原因**: +1. Token未正确加载 +2. 外部API地址错误 +3. 外部API服务不可用 + +**排查步骤**: + +1. 检查Token是否有效: +```bash +TOKEN=$(cat token.txt) +curl -s -X GET "http://39.98.115.114:8983/qw/knife/web/from/mes/statistics/chartsLendByYear" \ + -H "Blade-Auth: Bearer $TOKEN" +``` + +2. 检查服务日志: +```bash +tail -f auditor.log # 如果使用后台启动 +``` + +3. 重启服务: +```bash +pkill -f "auditor.main" +python3 -m auditor.main +``` + +--- + +### Q4: Postman测试无响应 + +**检查清单**: +- [ ] 服务是否正常启动? `lsof -i :8003` +- [ ] URL是否正确?检查端口号和路径 +- [ ] 网络是否可达? `curl http://localhost:8003/health` +- [ ] 防火墙是否阻止? + +--- + +### Q5: 如何查看完整的API文档? + +**方法1**: 访问Swagger UI +``` +浏览器打开:http://localhost:8003/docs +``` + +**方法2**: 访问ReDoc +``` +浏览器打开:http://localhost:8003/redoc +``` + +--- + +## 快速测试脚本 + +### 一键测试所有接口 + +创建文件 `test_all.sh`: + +```bash +#!/bin/bash + +BASE_URL="http://localhost:8003/api/v1/auditor" + +echo "========== 审计员接口测试 ==========" +echo "" + +echo "1. 测试健康检查..." +curl -s http://localhost:8003/health | python3 -m json.tool +echo "" + +echo "2. 测试全年取刀数量统计..." +curl -s $BASE_URL/charts-lend-by-year | python3 -m json.tool | head -20 +echo "" + +echo "3. 测试出入库统计..." +curl -s "$BASE_URL/storage-statistics?current=1&size=5" | python3 -m json.tool | head -30 +echo "" + +echo "4. 测试总库存统计..." +curl -s "$BASE_URL/total-stock?current=1&size=5" | python3 -m json.tool | head -30 +echo "" + +echo "========== 测试完成 ==========" +``` + +运行测试: +```bash +chmod +x test_all.sh +./test_all.sh +``` + +--- + +## 附录 + +### A. Token有效期说明 + +- **有效期**: 3600秒(1小时) +- **过期时间**: 从登录成功开始计算 +- **刷新建议**: 建议在Token过期前10分钟刷新 + +### B. 外部API配置 + +- **API地址**: http://39.98.115.114:8983 +- **认证方式**: Blade-Auth Header +- **Header格式**: `Blade-Auth: Bearer ` + +### C. 服务端口分配 + +| 服务 | 端口 | 说明 | +|------|------|------| +| 审计员服务 | 8003 | 本测试涉及的服务 | +| 班组长服务 | 8002 | 其他角色服务 | +| 管理员服务 | 8000 | 其他角色服务 | +| 操作员服务 | 8001 | 其他角色服务 | + +--- + +## 更新日志 + +- **2025-11-14**: 初始版本创建 + - 添加Token获取与更新指南 + - 添加服务启动说明 + - 添加完整Postman测试用例 + - 添加常见问题解决方案