2025-04-30 16:57:46 +08:00
|
|
|
|
#!/usr/bin/env python
|
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
库位处理器模块
|
2025-07-14 10:29:37 +08:00
|
|
|
|
提供与库区操作相关的各种处理器
|
2025-04-30 16:57:46 +08:00
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import logging
|
|
|
|
|
import json
|
|
|
|
|
import asyncio
|
|
|
|
|
import aiohttp
|
|
|
|
|
import uuid
|
|
|
|
|
from typing import Dict, Any, List, Optional
|
2025-07-14 10:29:37 +08:00
|
|
|
|
from sqlalchemy import and_, or_, desc, asc, select, update
|
|
|
|
|
from sqlalchemy.orm import Session
|
2025-04-30 16:57:46 +08:00
|
|
|
|
from services.execution.task_context import TaskContext
|
|
|
|
|
from .base import BlockHandler, register_handler
|
|
|
|
|
from config.settings import settings
|
|
|
|
|
from utils.logger import get_logger
|
|
|
|
|
from .model.block_name import StorageBlockName
|
2025-07-14 10:29:37 +08:00
|
|
|
|
from data.session import get_async_session
|
|
|
|
|
from data.models.operate_point_layer import OperatePointLayer
|
|
|
|
|
from data.models.extended_property import ExtendedProperty, ExtendedPropertyTypeEnum
|
2025-04-30 16:57:46 +08:00
|
|
|
|
|
|
|
|
|
# 获取日志记录器
|
|
|
|
|
logger = get_logger("services.execution.handlers.storage_location")
|
|
|
|
|
|
|
|
|
|
# 创建一个基础库位处理器类,包含通用方法
|
|
|
|
|
class StorageBlockHandler(BlockHandler):
|
2025-07-14 10:29:37 +08:00
|
|
|
|
pass
|
2025-04-30 16:57:46 +08:00
|
|
|
|
|
|
|
|
|
# 批量设置库位处理器
|
|
|
|
|
@register_handler(StorageBlockName.BATCH_SETTING_SITE)
|
|
|
|
|
class BatchSettingSiteBlockHandler(StorageBlockHandler):
|
|
|
|
|
"""批量设置库位处理器"""
|
|
|
|
|
|
|
|
|
|
async def execute(
|
|
|
|
|
self,
|
|
|
|
|
block: Dict[str, Any],
|
|
|
|
|
input_params: Dict[str, Any],
|
|
|
|
|
context: TaskContext
|
|
|
|
|
) -> Dict[str, Any]:
|
|
|
|
|
"""执行批量设置库位操作"""
|
|
|
|
|
try:
|
|
|
|
|
# 获取关键参数用于验证
|
|
|
|
|
site_ids = input_params.get("siteIds", [])
|
|
|
|
|
group_names = input_params.get("groupNames", [])
|
|
|
|
|
filled = input_params.get("filled")
|
2025-07-14 10:29:37 +08:00
|
|
|
|
# 获取地图ID
|
|
|
|
|
map_id = context.map_id
|
2025-04-30 16:57:46 +08:00
|
|
|
|
# 参数检查
|
|
|
|
|
if not site_ids and not group_names:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": "库位ID和库区集不能同时为空"
|
|
|
|
|
}
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
2025-07-14 10:29:37 +08:00
|
|
|
|
|
|
|
|
|
if not map_id:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": "地图ID不能为空"
|
|
|
|
|
}
|
2025-04-30 16:57:46 +08:00
|
|
|
|
if filled is None:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": "占用参数不能为空"
|
|
|
|
|
}
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
# 直接调用外部API执行批量设置,传递所有input_params
|
|
|
|
|
result = await self._call_external_api("batch_setting_site", input_params)
|
|
|
|
|
if result.get("success", False):
|
|
|
|
|
result["message"] = f"批量设置库位成功,共设置 {len(site_ids) or len(group_names)} 个库位"
|
|
|
|
|
else:
|
|
|
|
|
result["message"] = f"批量设置库位失败: {result.get('message', '未知错误')}"
|
|
|
|
|
|
|
|
|
|
# 记录执行结果
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
except Exception as e:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"批量设置库位执行异常: {str(e)}"
|
|
|
|
|
}
|
|
|
|
|
# 记录异常
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
# 获取密集库位处理器
|
|
|
|
|
@register_handler(StorageBlockName.GET_IDLE_CROWDED_SITE)
|
|
|
|
|
class GetIdleCrowdedSiteBlockHandler(StorageBlockHandler):
|
|
|
|
|
"""获取密集库位处理器"""
|
|
|
|
|
|
|
|
|
|
async def execute(
|
|
|
|
|
self,
|
|
|
|
|
block: Dict[str, Any],
|
|
|
|
|
input_params: Dict[str, Any],
|
|
|
|
|
context: TaskContext
|
|
|
|
|
) -> Dict[str, Any]:
|
|
|
|
|
"""执行获取密集库位操作"""
|
|
|
|
|
try:
|
|
|
|
|
# 获取关键参数用于验证
|
|
|
|
|
group_name = input_params.get("groupName", [])
|
|
|
|
|
filled = input_params.get("filled")
|
|
|
|
|
retry = input_params.get("retry", False)
|
|
|
|
|
retry_num = input_params.get("retryNum", 1)
|
|
|
|
|
|
|
|
|
|
# 参数检查
|
|
|
|
|
if not group_name:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": "库区集不能为空"
|
|
|
|
|
}
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
if filled is None:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": "取/放参数不能为空"
|
|
|
|
|
}
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
# 确保retry_num是整数类型
|
|
|
|
|
try:
|
|
|
|
|
retry_num = int(retry_num)
|
|
|
|
|
except (ValueError, TypeError):
|
|
|
|
|
logger.warning(f"retry_num参数类型错误或无法转换为整数: {retry_num}, 将使用默认值1")
|
|
|
|
|
retry_num = 1
|
|
|
|
|
input_params["retryNum"] = retry_num
|
|
|
|
|
|
|
|
|
|
# 确保retry_period是数字
|
|
|
|
|
retry_period = input_params.get("retryPeriod")
|
|
|
|
|
if retry_period is not None:
|
|
|
|
|
try:
|
|
|
|
|
retry_sleep = float(retry_period) / 1000
|
|
|
|
|
except (ValueError, TypeError):
|
|
|
|
|
logger.warning(f"retry_period参数类型错误或无法转换为数字: {retry_period}, 使用默认值1秒")
|
|
|
|
|
retry_sleep = 1
|
|
|
|
|
else:
|
|
|
|
|
retry_sleep = 1
|
|
|
|
|
|
|
|
|
|
# 调用外部API执行获取密集库位
|
|
|
|
|
attempts = 1
|
|
|
|
|
# 确保max_attempts是整数
|
|
|
|
|
max_attempts = retry_num if retry and retry_num is not None else 1
|
|
|
|
|
|
|
|
|
|
result = None
|
|
|
|
|
while attempts <= max_attempts:
|
|
|
|
|
result = await self._call_external_api("get_idle_crowded_site", input_params)
|
|
|
|
|
|
|
|
|
|
if result.get("success", False) and result.get("data", {}).get("siteId"):
|
|
|
|
|
# 获取成功
|
|
|
|
|
site_id = result.get("data", {}).get("siteId")
|
|
|
|
|
# 设置上下文变量
|
|
|
|
|
context.set_variable("siteId", site_id)
|
|
|
|
|
context.set_block_output(block.get("name"), {"siteId": site_id})
|
|
|
|
|
|
|
|
|
|
result["message"] = f"获取密集库位成功,库位ID: {site_id}"
|
|
|
|
|
break
|
|
|
|
|
else:
|
|
|
|
|
# 获取失败,判断是否需要重试
|
|
|
|
|
if retry and attempts < max_attempts:
|
|
|
|
|
logger.info(f"获取密集库位失败,第 {attempts} 次重试,等待 {retry_sleep} 秒后重试")
|
|
|
|
|
await asyncio.sleep(retry_sleep)
|
|
|
|
|
attempts += 1
|
|
|
|
|
else:
|
|
|
|
|
result["message"] = f"获取密集库位失败: {result.get('message', '未找到合适的库位')}"
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
# 记录执行结果
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
except Exception as e:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"获取密集库位执行异常: {str(e)}"
|
|
|
|
|
}
|
|
|
|
|
# 记录异常
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
# 获取库位处理器
|
|
|
|
|
@register_handler(StorageBlockName.GET_IDLE_SITE)
|
|
|
|
|
class GetIdleSiteBlockHandler(StorageBlockHandler):
|
|
|
|
|
"""获取库位处理器"""
|
|
|
|
|
|
|
|
|
|
async def execute(
|
|
|
|
|
self,
|
|
|
|
|
block: Dict[str, Any],
|
|
|
|
|
input_params: Dict[str, Any],
|
|
|
|
|
context: TaskContext
|
|
|
|
|
) -> Dict[str, Any]:
|
|
|
|
|
"""执行获取库位操作"""
|
|
|
|
|
try:
|
|
|
|
|
# 获取关键参数用于验证
|
|
|
|
|
locked = input_params.get("locked")
|
|
|
|
|
retry_period = input_params.get("retryPeriod")
|
2025-07-14 10:29:37 +08:00
|
|
|
|
# 获取地图ID
|
|
|
|
|
map_id = context.map_id
|
|
|
|
|
logger.info(f"获取库位处理器参数: {input_params}")
|
2025-04-30 16:57:46 +08:00
|
|
|
|
|
|
|
|
|
# 必填参数检查
|
|
|
|
|
if locked is None:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": "是否已锁定参数不能为空"
|
|
|
|
|
}
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
# 确保retry_period是数字类型
|
|
|
|
|
if retry_period is not None:
|
|
|
|
|
try:
|
|
|
|
|
retry_period = float(retry_period)
|
|
|
|
|
retry_sleep = retry_period / 1000
|
|
|
|
|
except (ValueError, TypeError):
|
|
|
|
|
logger.warning(f"retry_period参数类型错误或无法转换为数字: {retry_period}, 将使用默认值1000")
|
|
|
|
|
retry_sleep = 1
|
|
|
|
|
else:
|
|
|
|
|
retry_sleep = 1
|
|
|
|
|
|
|
|
|
|
# 设置重试计数器(用于日志记录)
|
|
|
|
|
retry_count = 0
|
|
|
|
|
|
|
|
|
|
while True: # 无限循环,直到找到库位
|
|
|
|
|
# 尝试获取库位
|
2025-07-14 10:29:37 +08:00
|
|
|
|
result = await self._query_idle_site_from_db(input_params, context.task_record_id, map_id)
|
2025-04-30 16:57:46 +08:00
|
|
|
|
if result.get("success", False) and result.get("data", {}).get("siteId"):
|
|
|
|
|
# 获取成功
|
|
|
|
|
site_id = result.get("data", {}).get("siteId")
|
|
|
|
|
# 设置上下文变量
|
|
|
|
|
context.set_variable("siteId", site_id)
|
|
|
|
|
context.set_block_output(block.get("name"), {"siteId": site_id})
|
|
|
|
|
|
|
|
|
|
# 根据重试次数设置不同的成功消息
|
|
|
|
|
if retry_count == 0:
|
|
|
|
|
result["message"] = f"获取库位成功,库位ID: {site_id}"
|
|
|
|
|
else:
|
|
|
|
|
result["message"] = f"第{retry_count}次重试获取库位成功,库位ID: {site_id}"
|
|
|
|
|
|
|
|
|
|
# 记录执行结果并退出循环
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
else:
|
|
|
|
|
# 获取失败,记录日志并继续重试
|
|
|
|
|
retry_count += 1
|
|
|
|
|
logger.info(f"获取库位失败,第{retry_count}次重试,等待 {retry_sleep} 秒后继续")
|
|
|
|
|
|
|
|
|
|
# 更新临时结果消息(仅用于日志记录)
|
|
|
|
|
temp_result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"第{retry_count}次尝试获取库位失败: {result.get('message', '未找到合适的库位')},将继续重试"
|
|
|
|
|
}
|
|
|
|
|
await self._record_task_log(block, temp_result, context)
|
|
|
|
|
|
|
|
|
|
# 等待指定时间后继续重试
|
|
|
|
|
await asyncio.sleep(retry_sleep)
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"获取库位执行异常: {str(e)}"
|
|
|
|
|
}
|
|
|
|
|
# 记录异常
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
|
2025-07-14 10:29:37 +08:00
|
|
|
|
async def _query_idle_site_from_db(self, input_params: Dict[str, Any], task_record_id: str, map_id:str) -> Dict[str, Any]:
|
|
|
|
|
"""从数据库查询空闲库位"""
|
|
|
|
|
try:
|
|
|
|
|
# 解析输入参数
|
|
|
|
|
site_id = input_params.get("siteId") # 库位ID
|
|
|
|
|
content = input_params.get("content") # 货物
|
|
|
|
|
filled = input_params.get("filled") # 是否有货物
|
|
|
|
|
locked = input_params.get("locked") # 是否已锁定(必填)
|
|
|
|
|
group_name = input_params.get("groupName") # 库区名
|
|
|
|
|
order_desc = input_params.get("orderDesc", True) # 是否为降序
|
|
|
|
|
lock_after_get = input_params.get("lock") # 获取库位后是否锁定
|
|
|
|
|
|
|
|
|
|
# 转换字符串类型的布尔值
|
|
|
|
|
if isinstance(filled, str):
|
|
|
|
|
filled = filled.lower() in ('true', '1', 'yes')
|
|
|
|
|
if isinstance(locked, str):
|
|
|
|
|
locked = locked.lower() in ('true', '1', 'yes')
|
|
|
|
|
if isinstance(order_desc, str):
|
|
|
|
|
order_desc = order_desc.lower() in ('true', '1', 'yes')
|
|
|
|
|
if isinstance(lock_after_get, str):
|
|
|
|
|
lock_after_get = lock_after_get.lower() in ('true', '1', 'yes')
|
|
|
|
|
|
|
|
|
|
logger.info(f"查询库位参数: site_id={site_id}, content={content}, filled={filled}, locked={locked}, group_name={group_name}, order_desc={order_desc}, lock_after_get={lock_after_get}")
|
|
|
|
|
|
|
|
|
|
async with get_async_session() as session:
|
|
|
|
|
# 构建查询条件
|
|
|
|
|
query = select(OperatePointLayer)
|
|
|
|
|
conditions = []
|
|
|
|
|
conditions.append(OperatePointLayer.is_empty_tray == False)
|
|
|
|
|
# 必填条件:是否已锁定
|
|
|
|
|
conditions.append(OperatePointLayer.is_locked == locked)
|
|
|
|
|
conditions.append(OperatePointLayer.is_disabled == False)
|
|
|
|
|
|
|
|
|
|
# 可选条件:库位ID
|
|
|
|
|
if site_id:
|
|
|
|
|
conditions.append(OperatePointLayer.id == site_id)
|
|
|
|
|
|
|
|
|
|
# 可选条件:货物内容
|
|
|
|
|
if content:
|
|
|
|
|
conditions.append(OperatePointLayer.goods_content == content)
|
|
|
|
|
|
|
|
|
|
# 可选条件:是否有货物
|
|
|
|
|
if filled:
|
|
|
|
|
conditions.append(OperatePointLayer.is_occupied == filled)
|
|
|
|
|
|
|
|
|
|
# 可选条件:库区名
|
|
|
|
|
if group_name:
|
|
|
|
|
conditions.append(OperatePointLayer.area_name == group_name)
|
|
|
|
|
|
|
|
|
|
# 应用所有条件
|
|
|
|
|
if conditions:
|
|
|
|
|
query = query.where(and_(*conditions))
|
|
|
|
|
|
|
|
|
|
# 排序:按station_name排序
|
|
|
|
|
if order_desc:
|
|
|
|
|
query = query.order_by(desc(OperatePointLayer.station_name))
|
|
|
|
|
else:
|
|
|
|
|
query = query.order_by(asc(OperatePointLayer.station_name))
|
|
|
|
|
|
|
|
|
|
# 只获取第一个匹配的库位
|
|
|
|
|
query = query.limit(1)
|
|
|
|
|
|
|
|
|
|
# 执行查询
|
|
|
|
|
result = await session.execute(query)
|
|
|
|
|
layer = result.scalar_one_or_none()
|
|
|
|
|
|
|
|
|
|
if layer:
|
|
|
|
|
found_site_id = layer.id
|
|
|
|
|
|
|
|
|
|
# 如果需要在获取后锁定库位
|
|
|
|
|
if lock_after_get:
|
|
|
|
|
try:
|
|
|
|
|
# 更新库位锁定状态
|
|
|
|
|
from sqlalchemy import update
|
|
|
|
|
update_stmt = update(OperatePointLayer).where(
|
|
|
|
|
OperatePointLayer.id == found_site_id
|
|
|
|
|
).values(
|
|
|
|
|
is_locked=True,
|
|
|
|
|
locked_by=f"task_{task_record_id}" # 生成一个锁定者ID
|
|
|
|
|
)
|
|
|
|
|
await session.execute(update_stmt)
|
|
|
|
|
await session.commit()
|
|
|
|
|
|
|
|
|
|
logger.info(f"库位 {found_site_id} 已成功锁定")
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
"success": True,
|
|
|
|
|
"message": f"查询库位成功并已锁定,库位ID: {found_site_id}",
|
|
|
|
|
"data": {
|
|
|
|
|
"siteId": found_site_id
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
except Exception as lock_error:
|
|
|
|
|
logger.error(f"锁定库位失败: {str(lock_error)}")
|
|
|
|
|
# 即使锁定失败,也返回查询成功的结果
|
|
|
|
|
return {
|
|
|
|
|
"success": True,
|
|
|
|
|
"message": f"查询库位成功,但锁定失败: {str(lock_error)},库位ID: {found_site_id}",
|
|
|
|
|
"data": {
|
|
|
|
|
"siteId": found_site_id
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else:
|
|
|
|
|
# 不需要锁定,直接返回查询结果
|
|
|
|
|
return {
|
|
|
|
|
"success": True,
|
|
|
|
|
"message": f"查询库位成功,库位ID: {found_site_id}",
|
|
|
|
|
"data": {
|
|
|
|
|
"siteId": found_site_id
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else:
|
|
|
|
|
# 未找到匹配的库位
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": "未找到符合条件的库位"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"查询库位异常: {str(e)}")
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"查询库位异常: {str(e)}"
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-30 16:57:46 +08:00
|
|
|
|
# 获取任务实例加锁库位处理器
|
|
|
|
|
@register_handler(StorageBlockName.GET_LOCKED_SITES_BY_TASK_RECORD_ID)
|
|
|
|
|
class GetLockedSitesByTaskRecordIdBlockHandler(StorageBlockHandler):
|
|
|
|
|
"""根据任务实例ID获取所有加锁库位处理器"""
|
|
|
|
|
|
|
|
|
|
async def execute(
|
|
|
|
|
self,
|
|
|
|
|
block: Dict[str, Any],
|
|
|
|
|
input_params: Dict[str, Any],
|
|
|
|
|
context: TaskContext
|
|
|
|
|
) -> Dict[str, Any]:
|
|
|
|
|
"""获取任务实例关联的所有已锁库位"""
|
|
|
|
|
try:
|
|
|
|
|
# 获取参数进行验证
|
|
|
|
|
task_record_id = input_params.get("taskRecordId")
|
|
|
|
|
|
|
|
|
|
# 必填参数检查
|
|
|
|
|
if not task_record_id:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": "任务实例ID不能为空"
|
|
|
|
|
}
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
# 直接调用外部API获取库位列表
|
|
|
|
|
result = await self._call_external_api("get_locked_sites_by_task_record_id", input_params)
|
|
|
|
|
|
|
|
|
|
if result.get("success", False):
|
|
|
|
|
# 获取成功,设置上下文变量
|
|
|
|
|
locked_site_list = result.get("data", {}).get("lockedSiteIdList", [])
|
|
|
|
|
context.set_variable("lockedSiteIdList", locked_site_list)
|
|
|
|
|
context.set_block_output(block.get("name"), {"lockedSiteIdList": locked_site_list})
|
|
|
|
|
|
|
|
|
|
result["message"] = f"获取任务锁定库位成功,共 {len(locked_site_list)} 个库位"
|
|
|
|
|
else:
|
|
|
|
|
result["message"] = f"获取任务锁定库位失败: {result.get('message', '未知错误')}"
|
|
|
|
|
|
|
|
|
|
# 记录执行结果
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
except Exception as e:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"获取任务锁定库位执行异常: {str(e)}"
|
|
|
|
|
}
|
|
|
|
|
# 记录异常
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
# 获取库位扩展属性处理器
|
|
|
|
|
@register_handler(StorageBlockName.GET_SITE_ATTR)
|
|
|
|
|
class GetSiteAttrBlockHandler(StorageBlockHandler):
|
|
|
|
|
"""获取库位扩展属性处理器"""
|
|
|
|
|
|
|
|
|
|
async def execute(
|
|
|
|
|
self,
|
|
|
|
|
block: Dict[str, Any],
|
|
|
|
|
input_params: Dict[str, Any],
|
|
|
|
|
context: TaskContext
|
|
|
|
|
) -> Dict[str, Any]:
|
|
|
|
|
"""获取库位扩展属性值"""
|
|
|
|
|
try:
|
|
|
|
|
# 获取参数进行验证
|
|
|
|
|
site_id = input_params.get("siteId")
|
|
|
|
|
attr_name = input_params.get("attrName")
|
2025-07-14 10:29:37 +08:00
|
|
|
|
map_id = context.map_id
|
2025-04-30 16:57:46 +08:00
|
|
|
|
# 必填参数检查
|
|
|
|
|
if not site_id:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": "库位ID不能为空"
|
|
|
|
|
}
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
if not attr_name:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": "属性名称不能为空"
|
|
|
|
|
}
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
|
2025-07-14 10:29:37 +08:00
|
|
|
|
# 直接从数据库获取扩展属性值
|
|
|
|
|
result = await self._get_site_attr_from_db(site_id, attr_name, map_id)
|
2025-04-30 16:57:46 +08:00
|
|
|
|
|
|
|
|
|
if result.get("success", False):
|
|
|
|
|
# 获取成功,设置上下文变量
|
|
|
|
|
attr_value = result.get("data", {}).get("attrValue")
|
|
|
|
|
context.set_variable("attrValue", attr_value)
|
|
|
|
|
context.set_block_output(block.get("name"), {"attrValue": attr_value})
|
|
|
|
|
|
|
|
|
|
# 记录执行结果
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
except Exception as e:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"获取库位属性值执行异常: {str(e)}"
|
|
|
|
|
}
|
|
|
|
|
# 记录异常
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
|
2025-07-14 10:29:37 +08:00
|
|
|
|
async def _get_site_attr_from_db(self, site_id: str, attr_name: str, map_id: str) -> Dict[str, Any]:
|
|
|
|
|
"""从数据库获取库位扩展属性值"""
|
|
|
|
|
try:
|
|
|
|
|
async with get_async_session() as session:
|
|
|
|
|
# 先检查库位是否存在
|
|
|
|
|
site_check_query = select(OperatePointLayer).where(
|
|
|
|
|
OperatePointLayer.id == site_id,
|
|
|
|
|
OperatePointLayer.is_deleted == False,
|
|
|
|
|
OperatePointLayer.scene_id == map_id,
|
|
|
|
|
OperatePointLayer.is_disabled == False
|
|
|
|
|
)
|
|
|
|
|
site_check_result = await session.execute(site_check_query)
|
|
|
|
|
existing_layer = site_check_result.scalar_one_or_none()
|
|
|
|
|
|
|
|
|
|
if not existing_layer:
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"库位不存在: {site_id}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 检查扩展属性是否已定义
|
|
|
|
|
extended_property_query = select(ExtendedProperty).where(
|
|
|
|
|
ExtendedProperty.property_name == attr_name,
|
|
|
|
|
ExtendedProperty.is_deleted == False
|
|
|
|
|
)
|
|
|
|
|
extended_property_result = await session.execute(extended_property_query)
|
|
|
|
|
extended_property = extended_property_result.scalar_one_or_none()
|
|
|
|
|
|
|
|
|
|
if not extended_property:
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"扩展属性 '{attr_name}' 不存在"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 解析config_json获取扩展属性值
|
|
|
|
|
import json
|
|
|
|
|
|
|
|
|
|
attr_value = None
|
|
|
|
|
attr_type = extended_property.property_type.value
|
|
|
|
|
is_required = extended_property.is_required
|
|
|
|
|
|
|
|
|
|
if existing_layer.config_json:
|
|
|
|
|
try:
|
|
|
|
|
config = json.loads(existing_layer.config_json)
|
|
|
|
|
if 'extended_fields' in config and attr_name in config['extended_fields']:
|
|
|
|
|
attr_info = config['extended_fields'][attr_name]
|
|
|
|
|
attr_value = attr_info.get('value')
|
|
|
|
|
# 如果config中有类型信息,使用config中的类型
|
|
|
|
|
if 'type' in attr_info:
|
|
|
|
|
attr_type = attr_info['type']
|
|
|
|
|
# 如果config中有required信息,使用config中的required
|
|
|
|
|
if 'is_required' in attr_info:
|
|
|
|
|
is_required = attr_info['is_required']
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"解析库位层 {site_id} 的config_json失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
# 如果没有找到属性值,使用默认值
|
|
|
|
|
if attr_value is None:
|
|
|
|
|
attr_value = extended_property.default_value
|
|
|
|
|
|
|
|
|
|
logger.info(f"获取库位 {site_id} 扩展属性 {attr_name} 成功: {attr_value}")
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
"success": True,
|
|
|
|
|
"message": f"获取库位扩展属性成功,{attr_name}: {attr_value}",
|
|
|
|
|
"data": {
|
|
|
|
|
"attrValue": attr_value,
|
|
|
|
|
"attrType": attr_type,
|
|
|
|
|
"isRequired": is_required,
|
|
|
|
|
"propertyName": attr_name
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"获取库位扩展属性异常: {str(e)}")
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"获取库位扩展属性异常: {str(e)}"
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-30 16:57:46 +08:00
|
|
|
|
# 查询库位处理器
|
|
|
|
|
@register_handler(StorageBlockName.QUERY_IDLE_SITE)
|
|
|
|
|
class QueryIdleSiteBlockHandler(StorageBlockHandler):
|
|
|
|
|
"""查询库位处理器"""
|
|
|
|
|
|
|
|
|
|
async def execute(
|
|
|
|
|
self,
|
|
|
|
|
block: Dict[str, Any],
|
|
|
|
|
input_params: Dict[str, Any],
|
|
|
|
|
context: TaskContext
|
|
|
|
|
) -> Dict[str, Any]:
|
|
|
|
|
"""查询库位"""
|
|
|
|
|
try:
|
|
|
|
|
# 直接调用外部API查询库位
|
2025-07-14 10:29:37 +08:00
|
|
|
|
# result = await self._call_external_api("query_idle_site", input_params)
|
2025-04-30 16:57:46 +08:00
|
|
|
|
|
|
|
|
|
if result.get("success", False) and result.get("data", {}).get("site"):
|
|
|
|
|
# 查询成功,设置上下文变量
|
|
|
|
|
site = result.get("data", {}).get("site")
|
|
|
|
|
context.set_variable("site", site)
|
|
|
|
|
context.set_block_output(block.get("name"), {"site": site})
|
|
|
|
|
|
|
|
|
|
site_id = site.get("id", "未知")
|
|
|
|
|
result["message"] = f"查询库位成功,库位ID: {site_id}"
|
|
|
|
|
else:
|
|
|
|
|
result["message"] = f"查询库位失败: {result.get('message', '未找到符合条件的库位')}"
|
|
|
|
|
|
|
|
|
|
# 记录执行结果
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
except Exception as e:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"查询库位执行异常: {str(e)}"
|
|
|
|
|
}
|
|
|
|
|
# 记录异常
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
# 设置库位扩展属性处理器
|
|
|
|
|
@register_handler(StorageBlockName.SET_SITE_ATTR)
|
|
|
|
|
class SetSiteAttrBlockHandler(StorageBlockHandler):
|
|
|
|
|
"""设置库位扩展属性处理器"""
|
|
|
|
|
|
|
|
|
|
async def execute(
|
|
|
|
|
self,
|
|
|
|
|
block: Dict[str, Any],
|
|
|
|
|
input_params: Dict[str, Any],
|
|
|
|
|
context: TaskContext
|
|
|
|
|
) -> Dict[str, Any]:
|
|
|
|
|
"""设置库位扩展属性值"""
|
|
|
|
|
try:
|
|
|
|
|
# 获取参数进行验证
|
|
|
|
|
site_id = input_params.get("siteId")
|
|
|
|
|
attr_name = input_params.get("attrName")
|
|
|
|
|
attr_value = input_params.get("attrValue")
|
2025-07-14 10:29:37 +08:00
|
|
|
|
map_id = context.map_id
|
2025-04-30 16:57:46 +08:00
|
|
|
|
|
|
|
|
|
# 必填参数检查
|
|
|
|
|
if not site_id:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": "库位ID不能为空"
|
|
|
|
|
}
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
if not attr_name:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": "属性名称不能为空"
|
|
|
|
|
}
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
|
2025-07-14 10:29:37 +08:00
|
|
|
|
# 直接操作数据库设置扩展属性值
|
|
|
|
|
result = await self._set_site_attr_in_db(site_id, attr_name, attr_value, map_id)
|
2025-04-30 16:57:46 +08:00
|
|
|
|
|
|
|
|
|
# 记录执行结果
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
except Exception as e:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"设置库位属性值执行异常: {str(e)}"
|
|
|
|
|
}
|
|
|
|
|
# 记录异常
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
|
2025-07-14 10:29:37 +08:00
|
|
|
|
async def _set_site_attr_in_db(self, site_id: str, attr_name: str, attr_value: Any, map_id:str) -> Dict[str, Any]:
|
|
|
|
|
"""在数据库中设置库位扩展属性值"""
|
|
|
|
|
try:
|
|
|
|
|
async with get_async_session() as session:
|
|
|
|
|
# 先检查库位是否存在
|
|
|
|
|
site_check_query = select(OperatePointLayer).where(
|
|
|
|
|
OperatePointLayer.id == site_id,
|
|
|
|
|
OperatePointLayer.is_deleted == False,
|
|
|
|
|
OperatePointLayer.scene_id == map_id
|
|
|
|
|
)
|
|
|
|
|
site_check_result = await session.execute(site_check_query)
|
|
|
|
|
existing_layer = site_check_result.scalar_one_or_none()
|
|
|
|
|
|
|
|
|
|
if not existing_layer:
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"库位不存在: {site_id}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 检查扩展属性是否已定义
|
|
|
|
|
extended_property_query = select(ExtendedProperty).where(
|
|
|
|
|
ExtendedProperty.property_name == attr_name,
|
|
|
|
|
ExtendedProperty.is_deleted == False
|
|
|
|
|
)
|
|
|
|
|
extended_property_result = await session.execute(extended_property_query)
|
|
|
|
|
extended_property = extended_property_result.scalar_one_or_none()
|
|
|
|
|
|
|
|
|
|
# 如果扩展属性不存在,则创建新的扩展属性
|
|
|
|
|
if not extended_property:
|
|
|
|
|
try:
|
|
|
|
|
# 推断属性类型
|
|
|
|
|
# property_type = self._infer_property_type(attr_value)
|
|
|
|
|
|
|
|
|
|
# 创建新的扩展属性
|
|
|
|
|
extended_property = ExtendedProperty(
|
|
|
|
|
property_key=attr_name,
|
|
|
|
|
property_name=attr_name,
|
|
|
|
|
property_type=ExtendedPropertyTypeEnum.STRING,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
session.add(extended_property)
|
|
|
|
|
await session.commit()
|
|
|
|
|
await session.refresh(extended_property)
|
|
|
|
|
|
|
|
|
|
logger.info(f"自动创建扩展属性: {attr_name}")
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"创建扩展属性失败: {str(e)}")
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"创建扩展属性失败: {str(e)}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 如果扩展属性存在但被禁用,启用它
|
|
|
|
|
elif not extended_property.is_enabled:
|
|
|
|
|
try:
|
|
|
|
|
# 启用扩展属性
|
|
|
|
|
enable_stmt = update(ExtendedProperty).where(
|
|
|
|
|
ExtendedProperty.id == extended_property.id
|
|
|
|
|
).values(
|
|
|
|
|
is_enabled=True,
|
|
|
|
|
updated_at=datetime.datetime.now()
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
await session.execute(enable_stmt)
|
|
|
|
|
await session.commit()
|
|
|
|
|
|
|
|
|
|
# 刷新对象状态
|
|
|
|
|
await session.refresh(extended_property)
|
|
|
|
|
|
|
|
|
|
logger.info(f"启用扩展属性: {attr_name}")
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"启用扩展属性失败: {str(e)}")
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"启用扩展属性失败: {str(e)}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 解析现有的config_json
|
|
|
|
|
import json
|
|
|
|
|
import datetime
|
|
|
|
|
|
|
|
|
|
config = {}
|
|
|
|
|
if existing_layer.config_json:
|
|
|
|
|
try:
|
|
|
|
|
config = json.loads(existing_layer.config_json)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"解析库位层 {site_id} 的config_json失败: {str(e)}")
|
|
|
|
|
config = {}
|
|
|
|
|
|
|
|
|
|
# 确保extended_fields字段存在
|
|
|
|
|
if 'extended_fields' not in config:
|
|
|
|
|
config['extended_fields'] = {}
|
|
|
|
|
|
|
|
|
|
# 更新扩展属性值
|
|
|
|
|
config['extended_fields'][attr_name] = {
|
|
|
|
|
'value': attr_value,
|
|
|
|
|
'type': extended_property.property_type.value,
|
|
|
|
|
'is_required': extended_property.is_required,
|
|
|
|
|
'updated_at': datetime.datetime.now().isoformat()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 更新config_json
|
|
|
|
|
try:
|
|
|
|
|
updated_config_json = json.dumps(config, ensure_ascii=False, indent=2)
|
|
|
|
|
|
|
|
|
|
# 更新数据库
|
|
|
|
|
update_stmt = update(OperatePointLayer).where(
|
|
|
|
|
OperatePointLayer.id == site_id
|
|
|
|
|
).values(
|
|
|
|
|
config_json=updated_config_json,
|
|
|
|
|
updated_at=datetime.datetime.now()
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
await session.execute(update_stmt)
|
|
|
|
|
await session.commit()
|
|
|
|
|
|
|
|
|
|
value_display = attr_value if attr_value is not None else "null"
|
|
|
|
|
logger.info(f"库位 {site_id} 扩展属性 {attr_name} 已成功设置为: {value_display}")
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
"success": True,
|
|
|
|
|
"message": f"设置库位扩展属性成功,{site_id}.{attr_name} = {value_display}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"序列化库位层 {site_id} 的config_json失败: {str(e)}")
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"更新扩展属性失败: {str(e)}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"设置库位扩展属性异常: {str(e)}")
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"设置库位扩展属性异常: {str(e)}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# def _infer_property_type(self, value: Any):
|
|
|
|
|
# """推断属性类型"""
|
|
|
|
|
# if isinstance(value, bool):
|
|
|
|
|
# return ExtendedPropertyTypeEnum.BOOLEAN
|
|
|
|
|
# elif isinstance(value, int):
|
|
|
|
|
# return ExtendedPropertyTypeEnum.INTEGER
|
|
|
|
|
# elif isinstance(value, float):
|
|
|
|
|
# return ExtendedPropertyTypeEnum.NUMBER
|
|
|
|
|
# elif isinstance(value, str):
|
|
|
|
|
# # 尝试判断是否为日期
|
|
|
|
|
# try:
|
|
|
|
|
# from datetime import datetime
|
|
|
|
|
# datetime.fromisoformat(value.replace('Z', '+00:00'))
|
|
|
|
|
# return ExtendedPropertyTypeEnum.DATE
|
|
|
|
|
# except:
|
|
|
|
|
# return ExtendedPropertyTypeEnum.STRING
|
|
|
|
|
# elif isinstance(value, list):
|
|
|
|
|
# return ExtendedPropertyTypeEnum.ARRAY
|
|
|
|
|
# elif isinstance(value, dict):
|
|
|
|
|
# return ExtendedPropertyTypeEnum.OBJECT
|
|
|
|
|
# else:
|
|
|
|
|
# return ExtendedPropertyTypeEnum.STRING
|
|
|
|
|
|
2025-04-30 16:57:46 +08:00
|
|
|
|
# 设置库位货物处理器
|
|
|
|
|
@register_handler(StorageBlockName.SET_SITE_CONTENT)
|
|
|
|
|
class SetSiteContentBlockHandler(StorageBlockHandler):
|
|
|
|
|
"""设置库位货物处理器"""
|
|
|
|
|
|
|
|
|
|
async def execute(
|
|
|
|
|
self,
|
|
|
|
|
block: Dict[str, Any],
|
|
|
|
|
input_params: Dict[str, Any],
|
|
|
|
|
context: TaskContext
|
|
|
|
|
) -> Dict[str, Any]:
|
|
|
|
|
"""设置库位货物"""
|
|
|
|
|
try:
|
|
|
|
|
# 获取参数进行验证
|
|
|
|
|
site_id = input_params.get("siteId")
|
|
|
|
|
content = input_params.get("content")
|
2025-07-14 10:29:37 +08:00
|
|
|
|
map_id = context.map_id
|
2025-04-30 16:57:46 +08:00
|
|
|
|
# 必填参数检查
|
|
|
|
|
if not site_id:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": "库位ID不能为空"
|
|
|
|
|
}
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
if content is None:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
2025-07-14 10:29:37 +08:00
|
|
|
|
"message": "货物内容不能为空"
|
2025-04-30 16:57:46 +08:00
|
|
|
|
}
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
|
2025-07-14 10:29:37 +08:00
|
|
|
|
# 直接操作数据库设置库位货物
|
|
|
|
|
result = await self._set_site_content_in_db(site_id, content, map_id)
|
2025-04-30 16:57:46 +08:00
|
|
|
|
|
|
|
|
|
# 记录执行结果
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
except Exception as e:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"设置库位货物执行异常: {str(e)}"
|
|
|
|
|
}
|
|
|
|
|
# 记录异常
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
|
2025-07-14 10:29:37 +08:00
|
|
|
|
async def _set_site_content_in_db(self, site_id: str, content: str, map_id:str) -> Dict[str, Any]:
|
|
|
|
|
"""在数据库中设置库位货物内容"""
|
|
|
|
|
try:
|
|
|
|
|
async with get_async_session() as session:
|
|
|
|
|
# 先检查库位是否存在
|
|
|
|
|
site_check_query = select(OperatePointLayer).where(
|
|
|
|
|
OperatePointLayer.id == site_id,
|
|
|
|
|
OperatePointLayer.scene_id == map_id
|
|
|
|
|
)
|
|
|
|
|
site_check_result = await session.execute(site_check_query)
|
|
|
|
|
existing_layer = site_check_result.scalar_one_or_none()
|
|
|
|
|
|
|
|
|
|
if not existing_layer:
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"库位不存在: {site_id}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 库位存在,设置货物内容
|
|
|
|
|
from sqlalchemy import update
|
|
|
|
|
import datetime
|
|
|
|
|
|
|
|
|
|
update_stmt = update(OperatePointLayer).where(
|
|
|
|
|
OperatePointLayer.id == site_id,
|
|
|
|
|
OperatePointLayer.scene_id == map_id
|
|
|
|
|
).values(
|
|
|
|
|
goods_content=content,
|
|
|
|
|
# goods_stored_at=datetime.datetime.now(), # 记录货物存放时间
|
|
|
|
|
last_access_at=datetime.datetime.now() # 更新最后访问时间
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
await session.execute(update_stmt)
|
|
|
|
|
await session.commit()
|
|
|
|
|
|
|
|
|
|
logger.info(f"库位 {site_id} 货物内容已成功设置为: {content}")
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
"success": True,
|
|
|
|
|
"message": f"设置库位货物成功,库位ID: {site_id},货物内容: {content}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"设置库位货物异常: {str(e)}")
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"设置库位货物异常: {str(e)}"
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-30 16:57:46 +08:00
|
|
|
|
# 设置库位为空处理器
|
|
|
|
|
@register_handler(StorageBlockName.SET_SITE_EMPTY)
|
|
|
|
|
class SetSiteEmptyBlockHandler(StorageBlockHandler):
|
|
|
|
|
"""设置库位为空处理器"""
|
|
|
|
|
|
|
|
|
|
async def execute(
|
|
|
|
|
self,
|
|
|
|
|
block: Dict[str, Any],
|
|
|
|
|
input_params: Dict[str, Any],
|
|
|
|
|
context: TaskContext
|
|
|
|
|
) -> Dict[str, Any]:
|
|
|
|
|
"""设置库位为空"""
|
|
|
|
|
try:
|
|
|
|
|
# 获取参数进行验证
|
|
|
|
|
site_id = input_params.get("siteId")
|
2025-07-14 10:29:37 +08:00
|
|
|
|
map_id = context.map_id
|
2025-04-30 16:57:46 +08:00
|
|
|
|
# 必填参数检查
|
|
|
|
|
if not site_id:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": "库位ID不能为空"
|
|
|
|
|
}
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
|
2025-07-14 10:29:37 +08:00
|
|
|
|
result = await self._set_site_empty_in_db(site_id, map_id)
|
2025-04-30 16:57:46 +08:00
|
|
|
|
|
|
|
|
|
if result.get("success", False):
|
|
|
|
|
result["message"] = f"设置库位为空成功,库位ID: {site_id}"
|
|
|
|
|
else:
|
|
|
|
|
result["message"] = f"设置库位为空失败: {result.get('message', '未知错误')}"
|
|
|
|
|
|
|
|
|
|
# 记录执行结果
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
except Exception as e:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"设置库位为空执行异常: {str(e)}"
|
|
|
|
|
}
|
|
|
|
|
# 记录异常
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
2025-07-14 10:29:37 +08:00
|
|
|
|
|
|
|
|
|
async def _set_site_empty_in_db(self, site_id: str, map_id:str) -> Dict[str, Any]:
|
|
|
|
|
"""在数据库中设置库位为非占用状态"""
|
|
|
|
|
try:
|
|
|
|
|
async with get_async_session() as session:
|
|
|
|
|
# 先检查库位是否存在
|
|
|
|
|
site_check_query = select(OperatePointLayer).where(
|
|
|
|
|
OperatePointLayer.id == site_id,
|
|
|
|
|
OperatePointLayer.scene_id == map_id
|
|
|
|
|
)
|
|
|
|
|
site_check_result = await session.execute(site_check_query)
|
|
|
|
|
existing_layer = site_check_result.scalar_one_or_none()
|
|
|
|
|
|
|
|
|
|
if not existing_layer:
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"库位不存在: {site_id}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 检查库位是否已经被占用
|
|
|
|
|
if not existing_layer.is_occupied:
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"库位 {site_id} 已处于非占用状态,无法重复设置"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 库位存在且未被占用,设置为占用状态
|
|
|
|
|
from sqlalchemy import update
|
|
|
|
|
import datetime
|
|
|
|
|
|
|
|
|
|
update_stmt = update(OperatePointLayer).where(
|
|
|
|
|
OperatePointLayer.id == site_id,
|
|
|
|
|
OperatePointLayer.scene_id == map_id
|
|
|
|
|
).values(
|
|
|
|
|
is_occupied=False,
|
|
|
|
|
goods_content='', # 清空货物内容
|
|
|
|
|
goods_retrieved_at=datetime.datetime.now(), # 记录货物取出时间
|
|
|
|
|
last_access_at=datetime.datetime.now()
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
await session.execute(update_stmt)
|
|
|
|
|
await session.commit()
|
|
|
|
|
|
|
|
|
|
logger.info(f"库位 {site_id} 已成功设置为空闲状态")
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
"success": True,
|
|
|
|
|
"message": f"设置库位为空成功,库位ID: {site_id}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"设置库位为非占用异常: {str(e)}")
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"设置库位为非占用异常: {str(e)}"
|
|
|
|
|
}
|
2025-04-30 16:57:46 +08:00
|
|
|
|
|
|
|
|
|
# 设置库位为占用处理器
|
|
|
|
|
@register_handler(StorageBlockName.SET_SITE_FILLED)
|
|
|
|
|
class SetSiteFilledBlockHandler(StorageBlockHandler):
|
|
|
|
|
"""设置库位为占用处理器"""
|
|
|
|
|
|
|
|
|
|
async def execute(
|
|
|
|
|
self,
|
|
|
|
|
block: Dict[str, Any],
|
|
|
|
|
input_params: Dict[str, Any],
|
|
|
|
|
context: TaskContext
|
|
|
|
|
) -> Dict[str, Any]:
|
|
|
|
|
"""设置库位为占用"""
|
|
|
|
|
try:
|
|
|
|
|
# 获取参数进行验证
|
|
|
|
|
site_id = input_params.get("siteId")
|
2025-07-14 10:29:37 +08:00
|
|
|
|
map_id = context.map_id
|
2025-04-30 16:57:46 +08:00
|
|
|
|
# 必填参数检查
|
|
|
|
|
if not site_id:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": "库位ID不能为空"
|
|
|
|
|
}
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
|
2025-07-14 10:29:37 +08:00
|
|
|
|
# 直接操作数据库设置库位为占用
|
|
|
|
|
result = await self._set_site_filled_in_db(site_id, map_id)
|
2025-04-30 16:57:46 +08:00
|
|
|
|
# 记录执行结果
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
except Exception as e:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"设置库位为占用执行异常: {str(e)}"
|
|
|
|
|
}
|
|
|
|
|
# 记录异常
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
|
2025-07-14 10:29:37 +08:00
|
|
|
|
async def _set_site_filled_in_db(self, site_id: str, map_id:str) -> Dict[str, Any]:
|
|
|
|
|
"""在数据库中设置库位为占用状态"""
|
|
|
|
|
try:
|
|
|
|
|
async with get_async_session() as session:
|
|
|
|
|
# 先检查库位是否存在
|
|
|
|
|
site_check_query = select(OperatePointLayer).where(
|
|
|
|
|
OperatePointLayer.id == site_id,
|
|
|
|
|
OperatePointLayer.scene_id == map_id
|
|
|
|
|
)
|
|
|
|
|
site_check_result = await session.execute(site_check_query)
|
|
|
|
|
existing_layer = site_check_result.scalar_one_or_none()
|
|
|
|
|
|
|
|
|
|
if not existing_layer:
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"库位不存在: {site_id}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 检查库位是否已经被占用
|
|
|
|
|
if existing_layer.is_occupied:
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"库位 {site_id} 已处于占用状态,无法重复设置"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 库位存在且未被占用,设置为占用状态
|
|
|
|
|
from sqlalchemy import update
|
|
|
|
|
import datetime
|
|
|
|
|
|
|
|
|
|
update_stmt = update(OperatePointLayer).where(
|
|
|
|
|
OperatePointLayer.id == site_id,
|
|
|
|
|
OperatePointLayer.scene_id == map_id
|
|
|
|
|
).values(
|
|
|
|
|
is_occupied=True,
|
|
|
|
|
last_access_at=datetime.datetime.now()
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
await session.execute(update_stmt)
|
|
|
|
|
await session.commit()
|
|
|
|
|
|
|
|
|
|
logger.info(f"库位 {site_id} 已成功设置为占用状态")
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
"success": True,
|
|
|
|
|
"message": f"设置库位为占用成功,库位ID: {site_id}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"设置库位为占用异常: {str(e)}")
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"设置库位为占用异常: {str(e)}"
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-30 16:57:46 +08:00
|
|
|
|
# 锁定库位处理器
|
|
|
|
|
@register_handler(StorageBlockName.SET_SITE_LOCKED)
|
|
|
|
|
class SetSiteLockedBlockHandler(StorageBlockHandler):
|
|
|
|
|
"""锁定库位处理器"""
|
|
|
|
|
|
|
|
|
|
async def execute(
|
|
|
|
|
self,
|
|
|
|
|
block: Dict[str, Any],
|
|
|
|
|
input_params: Dict[str, Any],
|
|
|
|
|
context: TaskContext
|
|
|
|
|
) -> Dict[str, Any]:
|
|
|
|
|
"""锁定库位"""
|
|
|
|
|
try:
|
|
|
|
|
# 获取参数进行验证
|
|
|
|
|
site_id = input_params.get("siteId")
|
|
|
|
|
locked_id = input_params.get("lockedId")
|
|
|
|
|
if_fair = input_params.get("ifFair", False) # 是否为公平锁
|
2025-07-14 10:29:37 +08:00
|
|
|
|
retry_times = input_params.get("retryTimes", 0) # 重试次数,默认为0表示不重试
|
|
|
|
|
map_id = context.map_id
|
2025-04-30 16:57:46 +08:00
|
|
|
|
# 必填参数检查
|
|
|
|
|
if not site_id:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": "库位ID不能为空"
|
|
|
|
|
}
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
# 如果未指定锁定者ID,则使用当前任务ID
|
|
|
|
|
if not locked_id:
|
2025-07-14 10:29:37 +08:00
|
|
|
|
locked_id = context.block_record_id
|
2025-04-30 16:57:46 +08:00
|
|
|
|
input_params["lockedId"] = locked_id
|
|
|
|
|
|
2025-07-14 10:29:37 +08:00
|
|
|
|
# 确保retry_times是整数类型
|
2025-04-30 16:57:46 +08:00
|
|
|
|
try:
|
2025-07-14 10:29:37 +08:00
|
|
|
|
retry_times = int(retry_times)
|
2025-04-30 16:57:46 +08:00
|
|
|
|
except (ValueError, TypeError):
|
2025-07-14 10:29:37 +08:00
|
|
|
|
logger.warning(f"retryTimes参数类型错误或无法转换为整数: {retry_times}, 将使用默认值0")
|
|
|
|
|
retry_times = 0
|
2025-04-30 16:57:46 +08:00
|
|
|
|
|
2025-07-14 10:29:37 +08:00
|
|
|
|
logger.info(f"锁定库位参数: site_id={site_id}, locked_id={locked_id}, if_fair={if_fair}, retry_times={retry_times}")
|
2025-04-30 16:57:46 +08:00
|
|
|
|
|
2025-07-14 10:29:37 +08:00
|
|
|
|
# 直接从数据库锁定库位
|
|
|
|
|
result = await self._set_site_locked_in_db(site_id, locked_id, if_fair, retry_times, map_id)
|
|
|
|
|
|
|
|
|
|
if result.get("success", False):
|
|
|
|
|
# 设置上下文变量
|
|
|
|
|
context.set_variable("success", True)
|
|
|
|
|
context.set_block_output(block.get("name"), {"success": True})
|
|
|
|
|
else:
|
|
|
|
|
# 设置上下文变量
|
|
|
|
|
context.set_variable("success", False)
|
|
|
|
|
context.set_block_output(block.get("name"), {"success": False})
|
2025-04-30 16:57:46 +08:00
|
|
|
|
|
|
|
|
|
# 记录最终执行结果
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
except Exception as e:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"锁定库位执行异常: {str(e)}"
|
|
|
|
|
}
|
|
|
|
|
# 记录异常
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
|
2025-07-14 10:29:37 +08:00
|
|
|
|
async def _set_site_locked_in_db(self, site_id: str, locked_id: str, if_fair: bool, retry_times: int, map_id:str) -> Dict[str, Any]:
|
|
|
|
|
"""在数据库中锁定库位"""
|
|
|
|
|
try:
|
|
|
|
|
async with get_async_session() as session:
|
|
|
|
|
# 先检查库位是否存在
|
|
|
|
|
site_check_query = select(OperatePointLayer).where(
|
|
|
|
|
OperatePointLayer.id == site_id,
|
|
|
|
|
OperatePointLayer.scene_id == map_id
|
|
|
|
|
)
|
|
|
|
|
site_check_result = await session.execute(site_check_query)
|
|
|
|
|
existing_layer = site_check_result.scalar_one_or_none()
|
|
|
|
|
|
|
|
|
|
if not existing_layer:
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"库位不存在: {site_id}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 实现重试逻辑
|
|
|
|
|
attempts = 0
|
|
|
|
|
max_attempts = retry_times + 1 # 包括首次尝试
|
|
|
|
|
retry_sleep = 1 # 默认重试间隔为1秒
|
|
|
|
|
|
|
|
|
|
while attempts < max_attempts:
|
|
|
|
|
attempts += 1
|
|
|
|
|
|
|
|
|
|
# 重新查询库位当前状态
|
|
|
|
|
current_site_query = select(OperatePointLayer).where(
|
|
|
|
|
OperatePointLayer.id == site_id,
|
|
|
|
|
OperatePointLayer.scene_id == map_id,
|
|
|
|
|
OperatePointLayer.is_disabled == False,
|
|
|
|
|
# OperatePointLayer.is_empty_tray == False
|
|
|
|
|
)
|
|
|
|
|
current_site_result = await session.execute(current_site_query)
|
|
|
|
|
current_layer = current_site_result.scalar_one_or_none()
|
|
|
|
|
|
|
|
|
|
if not current_layer:
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"库位不存在: {site_id}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 检查库位是否已被锁定
|
|
|
|
|
if current_layer.is_locked:
|
|
|
|
|
# 如果是同一个锁定者,视为已锁定成功
|
|
|
|
|
if current_layer.locked_by == locked_id:
|
|
|
|
|
logger.info(f"库位 {site_id} 已被同一锁定者 {locked_id} 锁定")
|
|
|
|
|
return {
|
|
|
|
|
"success": True,
|
|
|
|
|
"message": f"库位已被同一锁定者锁定,库位ID: {site_id}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 被其他锁定者锁定,需要重试
|
|
|
|
|
if attempts < max_attempts:
|
|
|
|
|
logger.info(f"库位 {site_id} 已被其他锁定者 {current_layer.locked_by} 锁定,第 {attempts} 次尝试,等待 {retry_sleep} 秒后重试")
|
|
|
|
|
await asyncio.sleep(retry_sleep)
|
|
|
|
|
continue
|
|
|
|
|
else:
|
|
|
|
|
# 达到最大重试次数,返回失败
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"库位 {site_id} 已被其他锁定者 {current_layer.locked_by} 锁定,重试 {retry_times} 次后仍然失败"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 库位未被锁定,尝试锁定
|
|
|
|
|
try:
|
|
|
|
|
import datetime
|
|
|
|
|
update_stmt = update(OperatePointLayer).where(
|
|
|
|
|
OperatePointLayer.id == site_id,
|
|
|
|
|
OperatePointLayer.scene_id == map_id,
|
|
|
|
|
OperatePointLayer.is_locked == False # 乐观锁:只有未锁定时才能锁定
|
|
|
|
|
).values(
|
|
|
|
|
is_locked=True,
|
|
|
|
|
locked_by=locked_id,
|
|
|
|
|
last_access_at=datetime.datetime.now()
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
result = await session.execute(update_stmt)
|
|
|
|
|
|
|
|
|
|
# 检查是否成功更新
|
|
|
|
|
if result.rowcount > 0:
|
|
|
|
|
await session.commit()
|
|
|
|
|
|
|
|
|
|
if attempts == 1:
|
|
|
|
|
success_msg = f"锁定库位成功,库位ID: {site_id}"
|
|
|
|
|
else:
|
|
|
|
|
success_msg = f"第{attempts}次尝试锁定库位成功,库位ID: {site_id}"
|
|
|
|
|
|
|
|
|
|
logger.info(success_msg)
|
|
|
|
|
return {
|
|
|
|
|
"success": True,
|
|
|
|
|
"message": success_msg
|
|
|
|
|
}
|
|
|
|
|
else:
|
|
|
|
|
# 更新失败,可能在更新期间被其他进程锁定
|
|
|
|
|
if attempts < max_attempts:
|
|
|
|
|
logger.info(f"库位 {site_id} 锁定失败(可能被其他进程抢占),第 {attempts} 次尝试,等待 {retry_sleep} 秒后重试")
|
|
|
|
|
await asyncio.sleep(retry_sleep)
|
|
|
|
|
continue
|
|
|
|
|
else:
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"库位 {site_id} 锁定失败,可能被其他进程抢占,重试 {retry_times} 次后仍然失败"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"锁定库位时发生异常: {str(e)}")
|
|
|
|
|
if attempts < max_attempts:
|
|
|
|
|
logger.info(f"第 {attempts} 次尝试发生异常,等待 {retry_sleep} 秒后重试")
|
|
|
|
|
await asyncio.sleep(retry_sleep)
|
|
|
|
|
continue
|
|
|
|
|
else:
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"锁定库位时发生异常: {str(e)}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 不应该到达这里
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"锁定库位失败,未知错误"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"锁定库位异常: {str(e)}")
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"锁定库位异常: {str(e)}"
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-30 16:57:46 +08:00
|
|
|
|
# 设置库位标签处理器
|
|
|
|
|
@register_handler(StorageBlockName.SET_SITE_TAGS)
|
|
|
|
|
class SetSiteTagsBlockHandler(StorageBlockHandler):
|
|
|
|
|
"""设置库位标签处理器"""
|
|
|
|
|
|
|
|
|
|
async def execute(
|
|
|
|
|
self,
|
|
|
|
|
block: Dict[str, Any],
|
|
|
|
|
input_params: Dict[str, Any],
|
|
|
|
|
context: TaskContext
|
|
|
|
|
) -> Dict[str, Any]:
|
|
|
|
|
"""设置库位标签"""
|
|
|
|
|
try:
|
|
|
|
|
# 获取参数进行验证
|
|
|
|
|
site_id = input_params.get("siteId")
|
|
|
|
|
tags = input_params.get("tags")
|
2025-07-14 10:29:37 +08:00
|
|
|
|
replace_mode = input_params.get("replaceMode", False) # 是否替换模式,默认为追加模式
|
|
|
|
|
map_id = context.map_id
|
2025-04-30 16:57:46 +08:00
|
|
|
|
# 必填参数检查
|
|
|
|
|
if not site_id:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": "库位ID不能为空"
|
|
|
|
|
}
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
|
2025-07-14 10:29:37 +08:00
|
|
|
|
# 转换字符串类型的布尔值
|
|
|
|
|
if isinstance(replace_mode, str):
|
|
|
|
|
replace_mode = replace_mode.lower() in ('true', '1', 'yes')
|
2025-04-30 16:57:46 +08:00
|
|
|
|
|
2025-07-14 10:29:37 +08:00
|
|
|
|
logger.info(f"设置库位标签参数: site_id={site_id}, tags={tags}, replace_mode={replace_mode}")
|
|
|
|
|
|
|
|
|
|
# 直接从数据库设置库位标签
|
|
|
|
|
result = await self._set_site_tags_in_db(site_id, tags, replace_mode, map_id)
|
2025-04-30 16:57:46 +08:00
|
|
|
|
|
|
|
|
|
# 记录执行结果
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
except Exception as e:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"设置库位标签执行异常: {str(e)}"
|
|
|
|
|
}
|
|
|
|
|
# 记录异常
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
|
2025-07-14 10:29:37 +08:00
|
|
|
|
async def _set_site_tags_in_db(self, site_id: str, tags: str, replace_mode: bool = False, map_id:str=None) -> Dict[str, Any]:
|
|
|
|
|
"""在数据库中设置库位标签"""
|
|
|
|
|
try:
|
|
|
|
|
async with get_async_session() as session:
|
|
|
|
|
# 先检查库位是否存在
|
|
|
|
|
site_check_query = select(OperatePointLayer).where(
|
|
|
|
|
OperatePointLayer.id == site_id,
|
|
|
|
|
OperatePointLayer.scene_id == map_id,
|
|
|
|
|
OperatePointLayer.is_disabled == False
|
|
|
|
|
)
|
|
|
|
|
site_check_result = await session.execute(site_check_query)
|
|
|
|
|
existing_layer = site_check_result.scalar_one_or_none()
|
|
|
|
|
|
|
|
|
|
if not existing_layer:
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"库位不存在: {site_id}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 处理标签数据
|
|
|
|
|
new_tags_set = set()
|
|
|
|
|
|
|
|
|
|
# 解析新输入的标签
|
|
|
|
|
if tags:
|
|
|
|
|
# 支持多种分隔符:逗号、分号、空格等
|
|
|
|
|
import re
|
|
|
|
|
tag_list = re.split(r'[,,;;\s]+', str(tags).strip())
|
|
|
|
|
new_tags_set.update(tag.strip() for tag in tag_list if tag.strip())
|
|
|
|
|
|
|
|
|
|
# 处理现有标签
|
|
|
|
|
final_tags_set = set()
|
|
|
|
|
if not replace_mode and existing_layer.tags:
|
|
|
|
|
# 追加模式:合并现有标签
|
|
|
|
|
existing_tag_list = re.split(r'[,,;;\s]+', existing_layer.tags.strip())
|
|
|
|
|
final_tags_set.update(tag.strip() for tag in existing_tag_list if tag.strip())
|
|
|
|
|
|
|
|
|
|
# 合并新标签
|
|
|
|
|
final_tags_set.update(new_tags_set)
|
|
|
|
|
|
|
|
|
|
# 去重并用逗号连接
|
|
|
|
|
final_tags = ','.join(sorted(final_tags_set)) if final_tags_set else ''
|
|
|
|
|
|
|
|
|
|
# 执行更新操作
|
|
|
|
|
try:
|
|
|
|
|
import datetime
|
|
|
|
|
update_stmt = update(OperatePointLayer).where(
|
|
|
|
|
OperatePointLayer.id == site_id,
|
|
|
|
|
OperatePointLayer.scene_id == map_id
|
|
|
|
|
).values(
|
|
|
|
|
tags=final_tags,
|
|
|
|
|
last_access_at=datetime.datetime.now()
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
result = await session.execute(update_stmt)
|
|
|
|
|
|
|
|
|
|
# 检查是否成功更新
|
|
|
|
|
if result.rowcount > 0:
|
|
|
|
|
await session.commit()
|
|
|
|
|
|
|
|
|
|
tags_display = final_tags if final_tags else "空"
|
|
|
|
|
operation_mode = "替换" if replace_mode else "追加"
|
|
|
|
|
success_msg = f"设置库位标签成功({operation_mode}模式),库位ID: {site_id},标签: {tags_display}"
|
|
|
|
|
logger.info(success_msg)
|
|
|
|
|
return {
|
|
|
|
|
"success": True,
|
|
|
|
|
"message": success_msg
|
|
|
|
|
}
|
|
|
|
|
else:
|
|
|
|
|
# 更新失败
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"设置库位标签失败,库位 {site_id} 可能不存在或已被删除"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"设置库位标签时发生异常: {str(e)}")
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"设置库位标签时发生异常: {str(e)}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"设置库位标签异常: {str(e)}")
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"设置库位标签异常: {str(e)}"
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-30 16:57:46 +08:00
|
|
|
|
# 解锁库位处理器
|
|
|
|
|
@register_handler(StorageBlockName.SET_SITE_UNLOCKED)
|
|
|
|
|
class SetSiteUnlockedBlockHandler(StorageBlockHandler):
|
|
|
|
|
"""解锁库位处理器"""
|
|
|
|
|
|
|
|
|
|
async def execute(
|
|
|
|
|
self,
|
|
|
|
|
block: Dict[str, Any],
|
|
|
|
|
input_params: Dict[str, Any],
|
|
|
|
|
context: TaskContext
|
|
|
|
|
) -> Dict[str, Any]:
|
|
|
|
|
"""解锁库位"""
|
|
|
|
|
try:
|
|
|
|
|
# 获取参数进行验证
|
|
|
|
|
site_id = input_params.get("siteId")
|
|
|
|
|
un_locked_id = input_params.get("unLockedId")
|
2025-07-14 10:29:37 +08:00
|
|
|
|
map_id = context.map_id
|
2025-04-30 16:57:46 +08:00
|
|
|
|
# 必填参数检查
|
|
|
|
|
if not site_id:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": "库位ID不能为空"
|
|
|
|
|
}
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
# 如果未指定解锁者ID,则使用当前任务ID
|
|
|
|
|
if not un_locked_id:
|
|
|
|
|
un_locked_id = context.get_task_record_id()
|
|
|
|
|
input_params["unLockedId"] = un_locked_id
|
|
|
|
|
logger.info(f"未指定解锁者ID,使用当前任务ID: {un_locked_id}")
|
|
|
|
|
|
2025-07-14 10:29:37 +08:00
|
|
|
|
logger.info(f"解锁库位参数: site_id={site_id}, un_locked_id={un_locked_id}")
|
|
|
|
|
|
|
|
|
|
# 直接从数据库解锁库位
|
|
|
|
|
result = await self._set_site_unlocked_in_db(site_id, un_locked_id, map_id)
|
2025-04-30 16:57:46 +08:00
|
|
|
|
|
|
|
|
|
if result.get("success", False):
|
|
|
|
|
# 设置上下文变量
|
|
|
|
|
context.set_variable("unlockSuccess", True)
|
|
|
|
|
context.set_block_output(block.get("name"), {"unlockSuccess": True})
|
|
|
|
|
else:
|
|
|
|
|
# 设置上下文变量
|
|
|
|
|
context.set_variable("unlockSuccess", False)
|
|
|
|
|
context.set_block_output(block.get("name"), {"unlockSuccess": False})
|
|
|
|
|
|
|
|
|
|
# 记录执行结果
|
|
|
|
|
await self._record_task_log(block, result, context)
|
|
|
|
|
return result
|
|
|
|
|
except Exception as e:
|
|
|
|
|
result = {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"解锁库位执行异常: {str(e)}"
|
|
|
|
|
}
|
|
|
|
|
# 记录异常
|
|
|
|
|
await self._record_task_log(block, result, context)
|
2025-07-14 10:29:37 +08:00
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
async def _set_site_unlocked_in_db(self, site_id: str, un_locked_id: str, map_id:str) -> Dict[str, Any]:
|
|
|
|
|
"""在数据库中解锁库位"""
|
|
|
|
|
try:
|
|
|
|
|
async with get_async_session() as session:
|
|
|
|
|
# 先检查库位是否存在
|
|
|
|
|
site_check_query = select(OperatePointLayer).where(
|
|
|
|
|
OperatePointLayer.id == site_id,
|
|
|
|
|
OperatePointLayer.scene_id == map_id,
|
|
|
|
|
OperatePointLayer.is_disabled == False
|
|
|
|
|
)
|
|
|
|
|
site_check_result = await session.execute(site_check_query)
|
|
|
|
|
existing_layer = site_check_result.scalar_one_or_none()
|
|
|
|
|
|
|
|
|
|
if not existing_layer:
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"库位不存在: {site_id}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 检查库位是否已被锁定
|
|
|
|
|
if not existing_layer.is_locked:
|
|
|
|
|
# 库位未被锁定,直接返回成功
|
|
|
|
|
logger.info(f"库位 {site_id} 未被锁定,解锁操作视为成功")
|
|
|
|
|
return {
|
|
|
|
|
"success": True,
|
|
|
|
|
"message": f"库位未被锁定,解锁操作成功,库位ID: {site_id}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 检查解锁者权限
|
|
|
|
|
if existing_layer.locked_by != un_locked_id:
|
|
|
|
|
# 解锁者不是当前锁定者,执行失败
|
|
|
|
|
logger.error(f"解锁者 {un_locked_id} 不是库位 {site_id} 的锁定者 {existing_layer.locked_by},解锁失败")
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"解锁失败,解锁者 {un_locked_id} 不是库位 {site_id} 的锁定者 {existing_layer.locked_by}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 执行解锁操作
|
|
|
|
|
try:
|
|
|
|
|
import datetime
|
|
|
|
|
update_stmt = update(OperatePointLayer).where(
|
|
|
|
|
OperatePointLayer.id == site_id,
|
|
|
|
|
OperatePointLayer.scene_id == map_id,
|
|
|
|
|
OperatePointLayer.is_locked == True # 只有锁定的库位才能解锁
|
|
|
|
|
).values(
|
|
|
|
|
is_locked=False,
|
|
|
|
|
locked_by=None, # 清空锁定者
|
|
|
|
|
last_access_at=datetime.datetime.now()
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
result = await session.execute(update_stmt)
|
|
|
|
|
|
|
|
|
|
# 检查是否成功更新
|
|
|
|
|
if result.rowcount > 0:
|
|
|
|
|
await session.commit()
|
|
|
|
|
|
|
|
|
|
success_msg = f"解锁库位成功,库位ID: {site_id}"
|
|
|
|
|
logger.info(success_msg)
|
|
|
|
|
return {
|
|
|
|
|
"success": True,
|
|
|
|
|
"message": success_msg
|
|
|
|
|
}
|
|
|
|
|
else:
|
|
|
|
|
# 更新失败,可能在更新期间库位状态发生变化
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"解锁库位失败,库位 {site_id} 可能已被其他进程解锁"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"解锁库位时发生异常: {str(e)}")
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"解锁库位时发生异常: {str(e)}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"解锁库位异常: {str(e)}")
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"解锁库位异常: {str(e)}"
|
|
|
|
|
}
|