725 lines
26 KiB
Python
725 lines
26 KiB
Python
|
#!/usr/bin/env python
|
|||
|
# -*- coding: utf-8 -*-
|
|||
|
|
|||
|
"""
|
|||
|
动作点管理API路由
|
|||
|
实现动作点和库位的管理功能
|
|||
|
"""
|
|||
|
|
|||
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
|||
|
from sqlalchemy.orm import Session
|
|||
|
from typing import Any, Dict, Optional
|
|||
|
|
|||
|
from data.session import get_db
|
|||
|
from services.operate_point_service import OperatePointService
|
|||
|
from routes.model.base import ApiResponse
|
|||
|
from routes.model.operate_point_model import (
|
|||
|
# OperatePointListRequest, OperatePointListResponse,
|
|||
|
StorageLocationListRequest, StorageLocationListResponse,
|
|||
|
StorageLocationStatusUpdateRequest, StorageLocationStatusUpdateResponse,
|
|||
|
BatchStorageLocationStatusUpdateRequest, BatchStorageLocationStatusUpdateResponse,
|
|||
|
StorageLocationActionEnum,
|
|||
|
# StorageAreaTypeEnum
|
|||
|
ExtendedPropertyCreateRequest, ExtendedPropertyCreateResponse,
|
|||
|
ExtendedPropertyListRequest, ExtendedPropertyListResponse,
|
|||
|
ExtendedPropertyDeleteResponse,
|
|||
|
StorageLocationDetailResponse, StorageLocationEditRequest, StorageLocationEditResponse,
|
|||
|
StorageLocationLogListRequest, StorageLocationLogListResponse,
|
|||
|
)
|
|||
|
from routes.common_api import format_response, error_response
|
|||
|
from utils.logger import get_logger
|
|||
|
from data.models import OperatePointLayer
|
|||
|
from data.models import OperatePoint # Added missing import
|
|||
|
|
|||
|
# 创建路由
|
|||
|
router = APIRouter(prefix="/api/vwed-operate-point", tags=["动作点管理"])
|
|||
|
|
|||
|
# 设置日志
|
|||
|
logger = get_logger("app.operate_point_api")
|
|||
|
|
|||
|
|
|||
|
def get_action_descriptions():
|
|||
|
"""获取操作类型的说明文档"""
|
|||
|
descriptions = {
|
|||
|
StorageLocationActionEnum.OCCUPY: "占用库位",
|
|||
|
StorageLocationActionEnum.RELEASE: "释放库位",
|
|||
|
StorageLocationActionEnum.LOCK: "锁定库位(需要提供锁定者)",
|
|||
|
StorageLocationActionEnum.UNLOCK: "解锁库位",
|
|||
|
StorageLocationActionEnum.ENABLE: "启用库位",
|
|||
|
StorageLocationActionEnum.DISABLE: "禁用库位",
|
|||
|
StorageLocationActionEnum.SET_EMPTY_TRAY: "设置为空托盘",
|
|||
|
StorageLocationActionEnum.CLEAR_EMPTY_TRAY: "清除空托盘状态"
|
|||
|
}
|
|||
|
return descriptions
|
|||
|
|
|||
|
|
|||
|
# 标准API响应格式
|
|||
|
def api_response(code: int = 200, message: str = "操作成功", data: Any = None) -> Dict[str, Any]:
|
|||
|
"""
|
|||
|
标准API响应格式
|
|||
|
|
|||
|
Args:
|
|||
|
code: 状态码
|
|||
|
message: 响应消息
|
|||
|
data: 响应数据
|
|||
|
|
|||
|
Returns:
|
|||
|
Dict[str, Any]: 格式化的响应数据
|
|||
|
"""
|
|||
|
return {
|
|||
|
"code": code,
|
|||
|
"message": message,
|
|||
|
"data": data
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
@router.get("/list", response_model=ApiResponse[StorageLocationListResponse])
|
|||
|
async def get_storage_location_list(
|
|||
|
scene_id: Optional[str] = Query(None, description="场景ID"),
|
|||
|
storage_area_id: Optional[str] = Query(None, description="库区ID"),
|
|||
|
station_name: Optional[str] = Query(None, description="站点名称(支持模糊搜索)"),
|
|||
|
storage_location_name: Optional[str] = Query(None, description="库位名称(支持模糊搜索)"),
|
|||
|
layer_name: Optional[str] = Query(None, description="层名称(支持模糊搜索)"),
|
|||
|
is_disabled: Optional[bool] = Query(None, description="是否禁用"),
|
|||
|
is_occupied: Optional[bool] = Query(None, description="是否占用"),
|
|||
|
is_locked: Optional[bool] = Query(None, description="是否锁定"),
|
|||
|
is_empty_tray: Optional[bool] = Query(None, description="是否空托盘"),
|
|||
|
include_operate_point_info: bool = Query(True, description="是否包含动作点信息"),
|
|||
|
include_extended_fields: bool = Query(True, description="是否包含扩展字段"),
|
|||
|
page: int = Query(1, ge=1, description="页码"),
|
|||
|
page_size: int = Query(20, ge=1, le=100, description="每页数量"),
|
|||
|
db: Session = Depends(get_db)
|
|||
|
):
|
|||
|
"""
|
|||
|
获取库位列表
|
|||
|
|
|||
|
库位基于动作点分层(OperatePointLayer),每一层对应一个库位。
|
|||
|
|
|||
|
支持多种筛选条件:
|
|||
|
- 场景ID:根据场景筛选库位
|
|||
|
- 库区ID:根据库区筛选库位
|
|||
|
- 站点名称:支持模糊搜索
|
|||
|
- 库位名称:支持模糊搜索
|
|||
|
- 层名称:支持模糊搜索
|
|||
|
- 是否禁用:筛选禁用/启用的库位
|
|||
|
- 是否占用:筛选已占用/空闲的库位
|
|||
|
- 是否锁定:筛选锁定/解锁的库位
|
|||
|
- 是否空托盘:筛选空托盘/非空托盘的库位
|
|||
|
- 是否包含动作点信息:控制返回数据是否包含所属动作点的详细信息
|
|||
|
- 是否包含扩展字段:控制返回数据是否包含自定义扩展字段
|
|||
|
|
|||
|
返回数据包含:
|
|||
|
- 库位基本信息(ID、层索引、层名称等)
|
|||
|
- 库位状态(是否占用、锁定、禁用、空托盘等)
|
|||
|
- 货物信息(货物内容、重量、体积等)
|
|||
|
- 库位规格(最大承重、最大体积、层高等)
|
|||
|
- 动作点信息(如果启用include_operate_point_info)
|
|||
|
- 扩展字段(如果启用include_extended_fields)
|
|||
|
- 统计信息(总数、各种状态的数量、使用率等)
|
|||
|
|
|||
|
Args:
|
|||
|
scene_id: 场景ID
|
|||
|
storage_area_id: 库区ID
|
|||
|
station_name: 站点名称
|
|||
|
storage_location_name: 库位名称
|
|||
|
layer_name: 层名称
|
|||
|
is_disabled: 是否禁用
|
|||
|
is_occupied: 是否占用
|
|||
|
is_locked: 是否锁定
|
|||
|
is_empty_tray: 是否空托盘
|
|||
|
include_operate_point_info: 是否包含动作点信息
|
|||
|
include_extended_fields: 是否包含扩展字段
|
|||
|
page: 页码
|
|||
|
page_size: 每页数量
|
|||
|
db: 数据库会话
|
|||
|
|
|||
|
Returns:
|
|||
|
ApiResponse[StorageLocationListResponse]: 库位列表响应
|
|||
|
"""
|
|||
|
try:
|
|||
|
# 构建请求对象
|
|||
|
request = StorageLocationListRequest(
|
|||
|
scene_id=scene_id,
|
|||
|
storage_area_id=storage_area_id,
|
|||
|
station_name=station_name,
|
|||
|
storage_location_name=storage_location_name,
|
|||
|
layer_name=layer_name,
|
|||
|
is_disabled=is_disabled,
|
|||
|
is_occupied=is_occupied,
|
|||
|
is_locked=is_locked,
|
|||
|
is_empty_tray=is_empty_tray,
|
|||
|
include_operate_point_info=include_operate_point_info,
|
|||
|
include_extended_fields=include_extended_fields,
|
|||
|
page=page,
|
|||
|
page_size=page_size
|
|||
|
)
|
|||
|
|
|||
|
# 调用服务层方法获取库位列表
|
|||
|
result = OperatePointService.get_storage_location_list(db=db, request=request)
|
|||
|
|
|||
|
return api_response(message="查询成功", data=result)
|
|||
|
|
|||
|
except ValueError as e:
|
|||
|
# 数据验证错误
|
|||
|
return error_response(str(e), 400)
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"获取库位列表失败: {str(e)}")
|
|||
|
return error_response(f"获取库位列表失败: {str(e)}", 500)
|
|||
|
|
|||
|
|
|||
|
|
|||
|
@router.put("/status", response_model=ApiResponse[StorageLocationStatusUpdateResponse])
|
|||
|
async def update_storage_location_status(
|
|||
|
request: StorageLocationStatusUpdateRequest,
|
|||
|
db: Session = Depends(get_db)
|
|||
|
):
|
|||
|
"""
|
|||
|
更新库位状态
|
|||
|
|
|||
|
支持的操作类型:
|
|||
|
- occupy: 占用库位
|
|||
|
- release: 释放库位
|
|||
|
- lock: 锁定库位(需要提供锁定者)
|
|||
|
- unlock: 解锁库位
|
|||
|
- enable: 启用库位
|
|||
|
- disable: 禁用库位
|
|||
|
- set_empty_tray: 设置为空托盘
|
|||
|
- clear_empty_tray: 清除空托盘状态
|
|||
|
|
|||
|
Args:
|
|||
|
request: 库位状态更新请求
|
|||
|
db: 数据库会话
|
|||
|
|
|||
|
Returns:
|
|||
|
ApiResponse[StorageLocationStatusUpdateResponse]: 状态更新响应
|
|||
|
"""
|
|||
|
try:
|
|||
|
# 验证操作类型 - 使用枚举类型
|
|||
|
valid_actions = [action.value for action in StorageLocationActionEnum]
|
|||
|
if request.action not in valid_actions:
|
|||
|
return error_response(f"不支持的操作类型: {request.action},支持的操作:{', '.join(valid_actions)}", 400)
|
|||
|
|
|||
|
# 锁定操作必须提供锁定者
|
|||
|
if request.action == StorageLocationActionEnum.LOCK and not request.locked_by:
|
|||
|
return error_response("锁定操作必须提供锁定者", 400)
|
|||
|
|
|||
|
# 调用服务层方法
|
|||
|
result = OperatePointService.update_storage_location_status(db=db, request=request)
|
|||
|
|
|||
|
return api_response(message="状态更新完成", data=result)
|
|||
|
|
|||
|
except ValueError as e:
|
|||
|
return error_response(str(e), 400)
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"更新库位状态失败: {str(e)}")
|
|||
|
return error_response(f"更新库位状态失败: {str(e)}", 500)
|
|||
|
|
|||
|
|
|||
|
@router.put("/batch-status", response_model=ApiResponse[BatchStorageLocationStatusUpdateResponse])
|
|||
|
async def batch_update_storage_location_status(
|
|||
|
request: BatchStorageLocationStatusUpdateRequest,
|
|||
|
db: Session = Depends(get_db)
|
|||
|
):
|
|||
|
"""
|
|||
|
批量更新库位状态
|
|||
|
|
|||
|
支持的操作类型:
|
|||
|
- occupy: 占用库位
|
|||
|
- release: 释放库位
|
|||
|
- lock: 锁定库位(需要提供锁定者)
|
|||
|
- unlock: 解锁库位
|
|||
|
- enable: 启用库位
|
|||
|
- disable: 禁用库位
|
|||
|
- set_empty_tray: 设置为空托盘
|
|||
|
- clear_empty_tray: 清除空托盘状态
|
|||
|
|
|||
|
Args:
|
|||
|
request: 批量库位状态更新请求
|
|||
|
db: 数据库会话
|
|||
|
|
|||
|
Returns:
|
|||
|
ApiResponse[BatchStorageLocationStatusUpdateResponse]: 批量状态更新响应
|
|||
|
"""
|
|||
|
try:
|
|||
|
# 验证操作类型 - 使用枚举类型
|
|||
|
valid_actions = [action.value for action in StorageLocationActionEnum]
|
|||
|
if request.action not in valid_actions:
|
|||
|
return error_response(f"不支持的操作类型: {request.action},支持的操作:{', '.join(valid_actions)}", 400)
|
|||
|
|
|||
|
# 锁定操作必须提供锁定者
|
|||
|
if request.action == StorageLocationActionEnum.LOCK and not request.locked_by:
|
|||
|
return error_response("锁定操作必须提供锁定者", 400)
|
|||
|
|
|||
|
# 验证库位ID列表
|
|||
|
if not request.storage_location_ids:
|
|||
|
return error_response("库位ID列表不能为空", 400)
|
|||
|
|
|||
|
if len(request.storage_location_ids) > 100:
|
|||
|
return error_response("批量操作的库位数量不能超过100个", 400)
|
|||
|
|
|||
|
# 调用服务层方法
|
|||
|
result = OperatePointService.batch_update_storage_location_status(db=db, request=request)
|
|||
|
|
|||
|
# 构建更详细的响应消息
|
|||
|
success_details = []
|
|||
|
if result.success_count > 0:
|
|||
|
success_details.append(f"成功操作 {result.success_count} 个库位")
|
|||
|
if result.failed_count > 0:
|
|||
|
success_details.append(f"失败操作 {result.failed_count} 个库位")
|
|||
|
|
|||
|
# 统计无需更改的操作
|
|||
|
no_change_count = sum(1 for r in result.results
|
|||
|
if r.success and "无需重复操作" in r.message)
|
|||
|
actual_update_count = result.success_count - no_change_count
|
|||
|
|
|||
|
if no_change_count > 0:
|
|||
|
success_details.append(f"其中 {no_change_count} 个库位已是目标状态")
|
|||
|
if actual_update_count > 0:
|
|||
|
success_details.append(f"实际更新 {actual_update_count} 个库位")
|
|||
|
|
|||
|
detailed_message = f"批量状态更新完成:{', '.join(success_details)}"
|
|||
|
|
|||
|
return api_response(message=detailed_message, data=result)
|
|||
|
|
|||
|
except ValueError as e:
|
|||
|
return error_response(str(e), 400)
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"批量更新库位状态失败: {str(e)}")
|
|||
|
return error_response(f"批量更新库位状态失败: {str(e)}", 500)
|
|||
|
|
|||
|
|
|||
|
@router.get("/actions")
|
|||
|
async def get_supported_actions():
|
|||
|
"""
|
|||
|
获取支持的操作类型列表
|
|||
|
|
|||
|
Returns:
|
|||
|
ApiResponse: 支持的操作类型列表及其说明
|
|||
|
"""
|
|||
|
try:
|
|||
|
descriptions = get_action_descriptions()
|
|||
|
|
|||
|
actions = []
|
|||
|
for action_enum in StorageLocationActionEnum:
|
|||
|
actions.append({
|
|||
|
"value": action_enum.value,
|
|||
|
"description": descriptions.get(action_enum, "")
|
|||
|
})
|
|||
|
|
|||
|
return api_response(
|
|||
|
message="获取支持的操作类型成功",
|
|||
|
data={
|
|||
|
"actions": actions,
|
|||
|
"count": len(actions)
|
|||
|
}
|
|||
|
)
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"获取支持的操作类型失败: {str(e)}")
|
|||
|
return error_response(f"获取支持的操作类型失败: {str(e)}", 500)
|
|||
|
|
|||
|
|
|||
|
@router.get("/{storage_location_id}/status")
|
|||
|
async def get_storage_location_status(
|
|||
|
storage_location_id: str,
|
|||
|
db: Session = Depends(get_db)
|
|||
|
):
|
|||
|
"""
|
|||
|
获取单个库位状态信息
|
|||
|
|
|||
|
Args:
|
|||
|
storage_location_id: 库位ID
|
|||
|
db: 数据库会话
|
|||
|
|
|||
|
Returns:
|
|||
|
ApiResponse: 库位状态信息
|
|||
|
"""
|
|||
|
try:
|
|||
|
# 查询库位
|
|||
|
storage_location = db.query(OperatePointLayer).filter(
|
|||
|
OperatePointLayer.id == storage_location_id,
|
|||
|
OperatePointLayer.is_deleted == False
|
|||
|
).first()
|
|||
|
|
|||
|
if not storage_location:
|
|||
|
return error_response("库位不存在", 404)
|
|||
|
|
|||
|
# 获取状态信息
|
|||
|
status = OperatePointService._get_storage_location_status(storage_location)
|
|||
|
|
|||
|
return api_response(message="查询成功", data=status)
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"获取库位状态失败: {str(e)}")
|
|||
|
return error_response(f"获取库位状态失败: {str(e)}", 500)
|
|||
|
|
|||
|
|
|||
|
# 扩展属性管理接口
|
|||
|
@router.post("/extended-properties", response_model=ApiResponse[ExtendedPropertyCreateResponse])
|
|||
|
async def create_extended_property(
|
|||
|
request: ExtendedPropertyCreateRequest,
|
|||
|
db: Session = Depends(get_db)
|
|||
|
):
|
|||
|
"""
|
|||
|
创建扩展属性
|
|||
|
|
|||
|
用于创建新的扩展属性定义,这些属性可以在库位管理中使用。
|
|||
|
|
|||
|
重要提示:
|
|||
|
- 创建扩展属性后,会自动将该属性添加到所有现有的库位层中
|
|||
|
- 每个库位层的config_json会自动更新,包含新的扩展属性配置
|
|||
|
- 新属性会使用指定的默认值进行初始化
|
|||
|
|
|||
|
支持的属性类型:
|
|||
|
- string: 字符串
|
|||
|
- integer: 整数
|
|||
|
- float: 浮点数
|
|||
|
- boolean: 布尔值
|
|||
|
- date: 日期
|
|||
|
- datetime: 日期时间
|
|||
|
- text: 长文本
|
|||
|
- select: 下拉选择
|
|||
|
- multiselect: 多选
|
|||
|
|
|||
|
Args:
|
|||
|
request: 扩展属性创建请求
|
|||
|
db: 数据库会话
|
|||
|
|
|||
|
Returns:
|
|||
|
ApiResponse[ExtendedPropertyCreateResponse]: 创建响应
|
|||
|
"""
|
|||
|
try:
|
|||
|
# 调用服务层方法
|
|||
|
result = OperatePointService.create_extended_property(db=db, request=request)
|
|||
|
|
|||
|
return api_response(message="扩展属性创建成功", data=result)
|
|||
|
|
|||
|
except ValueError as e:
|
|||
|
return error_response(str(e), 400)
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"创建扩展属性失败: {str(e)}")
|
|||
|
return error_response(f"创建扩展属性失败: {str(e)}", 500)
|
|||
|
|
|||
|
|
|||
|
@router.get("/extended-properties", response_model=ApiResponse[ExtendedPropertyListResponse])
|
|||
|
async def get_extended_property_list(
|
|||
|
property_name: Optional[str] = Query(None, description="属性名称(支持模糊搜索)"),
|
|||
|
property_type: Optional[str] = Query(None, description="属性类型"),
|
|||
|
category: Optional[str] = Query(None, description="属性分类"),
|
|||
|
is_enabled: Optional[bool] = Query(None, description="是否启用"),
|
|||
|
page: int = Query(1, ge=1, description="页码"),
|
|||
|
page_size: int = Query(20, ge=1, le=100, description="每页数量"),
|
|||
|
db: Session = Depends(get_db)
|
|||
|
):
|
|||
|
"""
|
|||
|
获取扩展属性列表
|
|||
|
|
|||
|
支持多种筛选条件:
|
|||
|
- 属性名称:支持模糊搜索
|
|||
|
- 属性类型:精确匹配
|
|||
|
- 属性分类:精确匹配
|
|||
|
- 是否启用:筛选启用/禁用的属性
|
|||
|
|
|||
|
返回数据包含:
|
|||
|
- 属性基本信息(ID、名称、类型等)
|
|||
|
- 属性设置(是否必填、是否启用等)
|
|||
|
- 验证规则和选项配置
|
|||
|
- 显示设置(宽度、格式等)
|
|||
|
- 创建和更新时间
|
|||
|
|
|||
|
Args:
|
|||
|
property_name: 属性名称
|
|||
|
property_type: 属性类型
|
|||
|
category: 属性分类
|
|||
|
is_enabled: 是否启用
|
|||
|
page: 页码
|
|||
|
page_size: 每页数量
|
|||
|
db: 数据库会话
|
|||
|
|
|||
|
Returns:
|
|||
|
ApiResponse[ExtendedPropertyListResponse]: 属性列表响应
|
|||
|
"""
|
|||
|
try:
|
|||
|
# 构建请求对象
|
|||
|
request = ExtendedPropertyListRequest(
|
|||
|
property_name=property_name,
|
|||
|
property_type=property_type,
|
|||
|
category=category,
|
|||
|
is_enabled=is_enabled,
|
|||
|
page=page,
|
|||
|
page_size=page_size
|
|||
|
)
|
|||
|
|
|||
|
# 调用服务层方法
|
|||
|
result = OperatePointService.get_extended_property_list(db=db, request=request)
|
|||
|
|
|||
|
return api_response(message="查询成功", data=result)
|
|||
|
|
|||
|
except ValueError as e:
|
|||
|
return error_response(str(e), 400)
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"获取扩展属性列表失败: {str(e)}")
|
|||
|
return error_response(f"获取扩展属性列表失败: {str(e)}", 500)
|
|||
|
|
|||
|
|
|||
|
@router.delete("/extended-properties/{property_id}", response_model=ApiResponse[ExtendedPropertyDeleteResponse])
|
|||
|
async def delete_extended_property(
|
|||
|
property_id: str,
|
|||
|
db: Session = Depends(get_db)
|
|||
|
):
|
|||
|
"""
|
|||
|
删除扩展属性
|
|||
|
|
|||
|
删除指定的扩展属性(软删除)。
|
|||
|
|
|||
|
重要提示:
|
|||
|
- 删除扩展属性后,会自动从所有现有的库位层中移除该属性
|
|||
|
- 每个库位层的config_json会自动更新,清除已删除的扩展属性配置
|
|||
|
- 此操作会影响所有库位的扩展属性数据
|
|||
|
|
|||
|
注意:此操作不可逆,请谨慎使用。
|
|||
|
|
|||
|
Args:
|
|||
|
property_id: 属性ID
|
|||
|
db: 数据库会话
|
|||
|
|
|||
|
Returns:
|
|||
|
ApiResponse[ExtendedPropertyDeleteResponse]: 删除响应
|
|||
|
"""
|
|||
|
try:
|
|||
|
# 调用服务层方法
|
|||
|
result = OperatePointService.delete_extended_property(db=db, property_id=property_id)
|
|||
|
|
|||
|
return api_response(message="扩展属性删除成功", data=result)
|
|||
|
|
|||
|
except ValueError as e:
|
|||
|
return error_response(str(e), 400)
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"删除扩展属性失败: {str(e)}")
|
|||
|
return error_response(f"删除扩展属性失败: {str(e)}", 500)
|
|||
|
|
|||
|
|
|||
|
@router.get("/extended-properties/types")
|
|||
|
async def get_extended_property_types():
|
|||
|
"""
|
|||
|
获取支持的扩展属性类型列表
|
|||
|
|
|||
|
返回系统支持的所有扩展属性类型及其说明。
|
|||
|
|
|||
|
Returns:
|
|||
|
ApiResponse: 支持的属性类型列表
|
|||
|
"""
|
|||
|
try:
|
|||
|
from data.models.extended_property import ExtendedPropertyTypeEnum
|
|||
|
|
|||
|
# 属性类型说明
|
|||
|
type_descriptions = {
|
|||
|
ExtendedPropertyTypeEnum.STRING: "字符串 - 适用于短文本输入",
|
|||
|
ExtendedPropertyTypeEnum.INTEGER: "整数 - 适用于整数值",
|
|||
|
ExtendedPropertyTypeEnum.FLOAT: "浮点数 - 适用于小数值",
|
|||
|
ExtendedPropertyTypeEnum.BOOLEAN: "布尔值 - 适用于是/否选择",
|
|||
|
ExtendedPropertyTypeEnum.DATE: "日期 - 适用于日期选择",
|
|||
|
ExtendedPropertyTypeEnum.DATETIME: "日期时间 - 适用于日期和时间选择",
|
|||
|
ExtendedPropertyTypeEnum.TEXT: "长文本 - 适用于多行文本输入",
|
|||
|
ExtendedPropertyTypeEnum.SELECT: "下拉选择 - 适用于单选择",
|
|||
|
ExtendedPropertyTypeEnum.MULTISELECT: "多选 - 适用于多选择"
|
|||
|
}
|
|||
|
|
|||
|
types = []
|
|||
|
for type_enum in ExtendedPropertyTypeEnum:
|
|||
|
types.append({
|
|||
|
"value": type_enum.value,
|
|||
|
"description": type_descriptions.get(type_enum, "")
|
|||
|
})
|
|||
|
|
|||
|
return api_response(
|
|||
|
message="获取扩展属性类型成功",
|
|||
|
data={
|
|||
|
"types": types,
|
|||
|
"count": len(types)
|
|||
|
}
|
|||
|
)
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"获取扩展属性类型失败: {str(e)}")
|
|||
|
return error_response(f"获取扩展属性类型失败: {str(e)}", 500)
|
|||
|
|
|||
|
|
|||
|
@router.get("/operation-logs", response_model=ApiResponse[StorageLocationLogListResponse])
|
|||
|
async def get_storage_location_operation_logs(
|
|||
|
storage_location_id: Optional[str] = Query(None, description="库位ID"),
|
|||
|
operator: Optional[str] = Query(None, description="操作人(支持模糊搜索)"),
|
|||
|
operation_type: Optional[str] = Query(None, description="操作类型"),
|
|||
|
start_time: Optional[str] = Query(None, description="开始时间 (格式: YYYY-MM-DD HH:MM:SS)"),
|
|||
|
end_time: Optional[str] = Query(None, description="结束时间 (格式: YYYY-MM-DD HH:MM:SS)"),
|
|||
|
page: int = Query(1, ge=1, description="页码"),
|
|||
|
page_size: int = Query(20, ge=1, le=100, description="每页数量"),
|
|||
|
db: Session = Depends(get_db)
|
|||
|
):
|
|||
|
"""
|
|||
|
获取库位操作记录列表
|
|||
|
|
|||
|
获取库位相关的操作记录,包括:
|
|||
|
- 状态更新操作(占用、释放、锁定、解锁、启用、禁用等)
|
|||
|
- 库位信息编辑操作
|
|||
|
- 其他库位相关的操作
|
|||
|
|
|||
|
支持多种筛选条件:
|
|||
|
- 库位ID:查询特定库位的操作记录
|
|||
|
- 操作人:支持模糊搜索操作人姓名
|
|||
|
- 操作类型:筛选特定类型的操作
|
|||
|
- 时间范围:指定操作时间的开始和结束时间
|
|||
|
|
|||
|
操作记录按时间倒序排列(最新的操作在前)。
|
|||
|
|
|||
|
Args:
|
|||
|
storage_location_id: 库位ID
|
|||
|
operator: 操作人
|
|||
|
operation_type: 操作类型
|
|||
|
start_time: 开始时间
|
|||
|
end_time: 结束时间
|
|||
|
page: 页码
|
|||
|
page_size: 每页数量
|
|||
|
db: 数据库会话
|
|||
|
|
|||
|
Returns:
|
|||
|
ApiResponse[StorageLocationLogListResponse]: 操作记录列表响应
|
|||
|
"""
|
|||
|
try:
|
|||
|
# 时间格式转换
|
|||
|
start_time_dt = None
|
|||
|
end_time_dt = None
|
|||
|
|
|||
|
if start_time:
|
|||
|
try:
|
|||
|
from datetime import datetime
|
|||
|
start_time_dt = datetime.strptime(start_time, "%Y-%m-%d %H:%M:%S")
|
|||
|
except ValueError:
|
|||
|
return error_response("开始时间格式错误,请使用格式:YYYY-MM-DD HH:MM:SS", 400)
|
|||
|
|
|||
|
if end_time:
|
|||
|
try:
|
|||
|
from datetime import datetime
|
|||
|
end_time_dt = datetime.strptime(end_time, "%Y-%m-%d %H:%M:%S")
|
|||
|
except ValueError:
|
|||
|
return error_response("结束时间格式错误,请使用格式:YYYY-MM-DD HH:MM:SS", 400)
|
|||
|
|
|||
|
# 验证时间范围
|
|||
|
if start_time_dt and end_time_dt and start_time_dt > end_time_dt:
|
|||
|
return error_response("开始时间不能大于结束时间", 400)
|
|||
|
|
|||
|
# 构建请求对象
|
|||
|
request = StorageLocationLogListRequest(
|
|||
|
storage_location_id=storage_location_id,
|
|||
|
operator=operator,
|
|||
|
operation_type=operation_type,
|
|||
|
start_time=start_time_dt,
|
|||
|
end_time=end_time_dt,
|
|||
|
page=page,
|
|||
|
page_size=page_size
|
|||
|
)
|
|||
|
|
|||
|
# 调用服务层方法
|
|||
|
result = OperatePointService.get_storage_location_logs(db=db, request=request)
|
|||
|
|
|||
|
return api_response(message="查询操作记录成功", data=result)
|
|||
|
|
|||
|
except ValueError as e:
|
|||
|
return error_response(str(e), 400)
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"获取库位操作记录失败: {str(e)}")
|
|||
|
return error_response(f"获取库位操作记录失败: {str(e)}", 500)
|
|||
|
|
|||
|
|
|||
|
@router.get("/{storage_location_id}", response_model=ApiResponse[StorageLocationDetailResponse])
|
|||
|
async def get_storage_location_detail(
|
|||
|
storage_location_id: str,
|
|||
|
db: Session = Depends(get_db)
|
|||
|
):
|
|||
|
"""
|
|||
|
获取库位详情
|
|||
|
|
|||
|
获取指定库位的详细信息,包括:
|
|||
|
- 库位基本信息和当前状态
|
|||
|
- 动作点详细信息
|
|||
|
- 扩展字段定义和值
|
|||
|
- 状态变更历史记录
|
|||
|
|
|||
|
Args:
|
|||
|
storage_location_id: 库位ID
|
|||
|
db: 数据库会话
|
|||
|
|
|||
|
Returns:
|
|||
|
ApiResponse[StorageLocationDetailResponse]: 库位详情响应
|
|||
|
"""
|
|||
|
try:
|
|||
|
# 调用服务层方法
|
|||
|
result = OperatePointService.get_storage_location_detail(db=db, storage_location_id=storage_location_id)
|
|||
|
|
|||
|
return api_response(message="获取库位详情成功", data=result)
|
|||
|
|
|||
|
except ValueError as e:
|
|||
|
return error_response(str(e), 404)
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"获取库位详情失败: {str(e)}")
|
|||
|
return error_response(f"获取库位详情失败: {str(e)}", 500)
|
|||
|
|
|||
|
|
|||
|
@router.put("/{storage_location_id}", response_model=ApiResponse[StorageLocationEditResponse])
|
|||
|
async def edit_storage_location(
|
|||
|
storage_location_id: str,
|
|||
|
request: StorageLocationEditRequest,
|
|||
|
db: Session = Depends(get_db)
|
|||
|
):
|
|||
|
"""
|
|||
|
编辑库位信息
|
|||
|
|
|||
|
允许修改库位的各种属性,包括:
|
|||
|
- 货物信息:货物内容、重量、体积等
|
|||
|
- 库位规格:最大承重、最大体积、层高等
|
|||
|
- 状态字段:是否锁定、是否禁用、是否空托盘
|
|||
|
- 扩展字段:自定义的扩展属性值
|
|||
|
- 其他属性:标签、描述等
|
|||
|
|
|||
|
注意:
|
|||
|
- 只有传入的字段且值发生变化时才会被更新
|
|||
|
- 层名称(layer_name)不能通过此接口修改
|
|||
|
- 扩展字段必须在系统中已定义且已启用
|
|||
|
- 如果所有字段都没有发生变化,会返回相应提示信息
|
|||
|
|
|||
|
Args:
|
|||
|
storage_location_id: 库位ID
|
|||
|
request: 库位编辑请求
|
|||
|
db: 数据库会话
|
|||
|
|
|||
|
Returns:
|
|||
|
ApiResponse[StorageLocationEditResponse]: 编辑响应
|
|||
|
"""
|
|||
|
try:
|
|||
|
# 调用服务层方法
|
|||
|
result = OperatePointService.edit_storage_location(
|
|||
|
db=db,
|
|||
|
storage_location_id=storage_location_id,
|
|||
|
request=request
|
|||
|
)
|
|||
|
|
|||
|
return api_response(message="库位信息编辑成功", data=result)
|
|||
|
|
|||
|
except ValueError as e:
|
|||
|
return error_response(str(e), 400)
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"编辑库位信息失败: {str(e)}")
|
|||
|
return error_response(f"编辑库位信息失败: {str(e)}", 500)
|
|||
|
|
|||
|
|
|||
|
|