smartrack-sdk 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of smartrack-sdk might be problematic. Click here for more details.
- examples/__init__.py +7 -0
- examples/basic_usage.py +360 -0
- examples/callback_server.py +435 -0
- examples/test_client.py +303 -0
- smartrack_sdk/__init__.py +129 -0
- smartrack_sdk/callback/__init__.py +19 -0
- smartrack_sdk/callback/fastapi_handler.py +151 -0
- smartrack_sdk/callback/flask_handler.py +137 -0
- smartrack_sdk/callback/handler.py +220 -0
- smartrack_sdk/callback/service.py +14 -0
- smartrack_sdk/client/__init__.py +13 -0
- smartrack_sdk/client/smart_rack_client.py +523 -0
- smartrack_sdk/exceptions/__init__.py +21 -0
- smartrack_sdk/exceptions/api.py +29 -0
- smartrack_sdk/exceptions/base.py +25 -0
- smartrack_sdk/exceptions/configuration.py +28 -0
- smartrack_sdk/exceptions/network.py +50 -0
- smartrack_sdk/exceptions/validation.py +28 -0
- smartrack_sdk/models/__init__.py +85 -0
- smartrack_sdk/models/callback.py +184 -0
- smartrack_sdk/models/enums.py +203 -0
- smartrack_sdk/models/requests.py +291 -0
- smartrack_sdk/models/responses.py +209 -0
- smartrack_sdk/tests/__init__.py +7 -0
- smartrack_sdk/tests/test_callback.py +295 -0
- smartrack_sdk/tests/test_client.py +264 -0
- smartrack_sdk-1.0.0.dist-info/METADATA +557 -0
- smartrack_sdk-1.0.0.dist-info/RECORD +31 -0
- smartrack_sdk-1.0.0.dist-info/WHEEL +5 -0
- smartrack_sdk-1.0.0.dist-info/entry_points.txt +2 -0
- smartrack_sdk-1.0.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Flask WebAPI回调处理器
|
|
6
|
+
提供Flask框架的WebAPI回调处理功能
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
from typing import Dict, Any, TYPE_CHECKING
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from flask import Flask
|
|
14
|
+
|
|
15
|
+
try:
|
|
16
|
+
from flask import Flask, request, jsonify
|
|
17
|
+
FLASK_AVAILABLE = True
|
|
18
|
+
except ImportError:
|
|
19
|
+
FLASK_AVAILABLE = False
|
|
20
|
+
|
|
21
|
+
from .handler import CellEventHandler, CellEventCallbackService
|
|
22
|
+
from ..models.callback import CellEventHandleRequest, CellEventHandleResponse
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class FlaskCellEventHandler:
|
|
28
|
+
"""Flask储位事件回调处理器"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, app: 'Flask' = None, event_handler: CellEventHandler = None):
|
|
31
|
+
"""
|
|
32
|
+
初始化Flask回调处理器
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
app: Flask应用实例
|
|
36
|
+
event_handler: 事件处理器
|
|
37
|
+
"""
|
|
38
|
+
if not FLASK_AVAILABLE:
|
|
39
|
+
raise ImportError("Flask not available. Install with: pip install Flask")
|
|
40
|
+
|
|
41
|
+
self.event_handler = event_handler or CellEventCallbackService()
|
|
42
|
+
|
|
43
|
+
if app:
|
|
44
|
+
self.init_app(app)
|
|
45
|
+
|
|
46
|
+
def init_app(self, app: 'Flask') -> None:
|
|
47
|
+
"""
|
|
48
|
+
初始化Flask应用
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
app: Flask应用实例
|
|
52
|
+
"""
|
|
53
|
+
app.add_url_rule(
|
|
54
|
+
'/api/SmartRack/CellEventHandle',
|
|
55
|
+
endpoint='cell_event_handle',
|
|
56
|
+
view_func=self.handle_cell_event,
|
|
57
|
+
methods=['POST']
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
app.add_url_rule(
|
|
61
|
+
'/api/SmartRack/health',
|
|
62
|
+
endpoint='health',
|
|
63
|
+
view_func=self.health,
|
|
64
|
+
methods=['GET']
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
logger.info("Flask SmartRack回调路由注册完成")
|
|
68
|
+
|
|
69
|
+
def handle_cell_event(self):
|
|
70
|
+
"""处理储位事件回调请求"""
|
|
71
|
+
try:
|
|
72
|
+
# 获取请求数据
|
|
73
|
+
request_data = request.get_json()
|
|
74
|
+
if not request_data:
|
|
75
|
+
return jsonify({
|
|
76
|
+
'code': 100100,
|
|
77
|
+
'message': '请求数据为空',
|
|
78
|
+
'sessionId': '',
|
|
79
|
+
'timestamp': self._get_timestamp()
|
|
80
|
+
}), 400
|
|
81
|
+
|
|
82
|
+
# 创建回调请求对象
|
|
83
|
+
callback_request = CellEventHandleRequest.from_dict(request_data)
|
|
84
|
+
logger.info(f"收到储位事件回调: {callback_request}")
|
|
85
|
+
|
|
86
|
+
# 处理事件
|
|
87
|
+
response = self.event_handler.handle(callback_request)
|
|
88
|
+
|
|
89
|
+
# 返回响应
|
|
90
|
+
return jsonify(response.to_dict())
|
|
91
|
+
|
|
92
|
+
except Exception as e:
|
|
93
|
+
logger.error("处理储位事件回调时发生错误", exc_info=True)
|
|
94
|
+
return jsonify({
|
|
95
|
+
'code': 100101,
|
|
96
|
+
'message': f'处理回调时发生错误: {str(e)}',
|
|
97
|
+
'sessionId': '',
|
|
98
|
+
'timestamp': self._get_timestamp()
|
|
99
|
+
}), 500
|
|
100
|
+
|
|
101
|
+
def health(self):
|
|
102
|
+
"""健康检查接口"""
|
|
103
|
+
return jsonify({
|
|
104
|
+
'status': 'ok',
|
|
105
|
+
'message': 'SmartRack Callback Service is running',
|
|
106
|
+
'timestamp': self._get_timestamp()
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
def _get_timestamp(self) -> str:
|
|
110
|
+
"""获取当前时间戳"""
|
|
111
|
+
from datetime import datetime
|
|
112
|
+
return datetime.now().isoformat()
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def create_flask_app(event_handler: CellEventHandler = None) -> 'Flask':
|
|
116
|
+
"""
|
|
117
|
+
创建Flask应用
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
event_handler: 事件处理器
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
Flask应用实例
|
|
124
|
+
"""
|
|
125
|
+
if not FLASK_AVAILABLE:
|
|
126
|
+
raise ImportError("Flask not available. Install with: pip install Flask")
|
|
127
|
+
|
|
128
|
+
app = Flask(__name__)
|
|
129
|
+
|
|
130
|
+
# 配置
|
|
131
|
+
app.config['JSON_AS_ASCII'] = False
|
|
132
|
+
|
|
133
|
+
# 初始化回调处理器
|
|
134
|
+
flask_handler = FlaskCellEventHandler(app, event_handler)
|
|
135
|
+
|
|
136
|
+
logger.info("Flask应用创建完成")
|
|
137
|
+
return app
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
回调处理器接口和默认实现
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from abc import ABC, abstractmethod
|
|
10
|
+
from typing import List
|
|
11
|
+
|
|
12
|
+
from ..models.callback import CellEventHandleRequest, CellEventHandleResponse, CellEventData
|
|
13
|
+
from ..models.enums import OperationResultCode, TriggerState
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class CellEventHandler(ABC):
|
|
19
|
+
"""储位事件回调处理器接口"""
|
|
20
|
+
|
|
21
|
+
@abstractmethod
|
|
22
|
+
def handle(self, request: CellEventHandleRequest) -> CellEventHandleResponse:
|
|
23
|
+
"""
|
|
24
|
+
处理储位事件回调
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
request: 回调请求
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
回调响应
|
|
31
|
+
"""
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class CellEventCallbackService(CellEventHandler):
|
|
36
|
+
"""默认储位事件回调处理服务"""
|
|
37
|
+
|
|
38
|
+
def handle(self, request: CellEventHandleRequest) -> CellEventHandleResponse:
|
|
39
|
+
"""
|
|
40
|
+
处理储位事件回调
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
request: 回调请求
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
回调响应
|
|
47
|
+
"""
|
|
48
|
+
logger.info(f"处理储位事件回调 - 用户: {request.user_id}, 客户端: {request.client_id}")
|
|
49
|
+
|
|
50
|
+
event_data_list = request.data
|
|
51
|
+
if not event_data_list:
|
|
52
|
+
logger.warning("没有事件数据")
|
|
53
|
+
return CellEventHandleResponse.error(100102, "没有事件数据", request.session_id)
|
|
54
|
+
|
|
55
|
+
try:
|
|
56
|
+
# 处理每个事件数据
|
|
57
|
+
for event_data in event_data_list:
|
|
58
|
+
self._process_single_event(event_data)
|
|
59
|
+
|
|
60
|
+
# 所有事件处理成功
|
|
61
|
+
return CellEventHandleResponse.success(request.session_id)
|
|
62
|
+
|
|
63
|
+
except Exception as e:
|
|
64
|
+
logger.error("处理事件失败", exc_info=True)
|
|
65
|
+
return CellEventHandleResponse.error(100103, f"处理事件失败: {str(e)}", request.session_id)
|
|
66
|
+
|
|
67
|
+
def _process_single_event(self, event_data: CellEventData) -> None:
|
|
68
|
+
"""
|
|
69
|
+
处理单个事件数据
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
event_data: 事件数据
|
|
73
|
+
"""
|
|
74
|
+
logger.info(f"处理事件 - 储位: {event_data.cell_id}, GRN: {event_data.grn}, "
|
|
75
|
+
f"状态: {event_data.cell_state.name_en}, 结果: {event_data.code}")
|
|
76
|
+
|
|
77
|
+
# 检查操作结果
|
|
78
|
+
if event_data.code != OperationResultCode.SUCCESS.value:
|
|
79
|
+
self._handle_operation_error(event_data)
|
|
80
|
+
return
|
|
81
|
+
|
|
82
|
+
# 根据触发状态处理不同操作
|
|
83
|
+
if event_data.cell_state == TriggerState.PUT_IN:
|
|
84
|
+
self._handle_put_in_event(event_data)
|
|
85
|
+
elif event_data.cell_state == TriggerState.TAKE_OUT:
|
|
86
|
+
self._handle_take_out_event(event_data)
|
|
87
|
+
else:
|
|
88
|
+
logger.warning(f"未知的储位状态: {event_data.cell_state}")
|
|
89
|
+
|
|
90
|
+
def _handle_put_in_event(self, event_data: CellEventData) -> None:
|
|
91
|
+
"""
|
|
92
|
+
处理放入事件
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
event_data: 事件数据
|
|
96
|
+
"""
|
|
97
|
+
logger.info(f"处理入库事件 - 储位: {event_data.cell_id}, GRN: {event_data.grn}")
|
|
98
|
+
|
|
99
|
+
# 这里可以添加具体的入库业务逻辑
|
|
100
|
+
try:
|
|
101
|
+
# 验证数据
|
|
102
|
+
if not event_data.grn or not event_data.grn.strip():
|
|
103
|
+
raise ValueError("GRN不能为空")
|
|
104
|
+
|
|
105
|
+
# 更新库存
|
|
106
|
+
self._update_inventory_for_put_in(event_data)
|
|
107
|
+
|
|
108
|
+
# 发送通知
|
|
109
|
+
self._send_put_in_notification(event_data)
|
|
110
|
+
|
|
111
|
+
logger.info(f"入库事件处理完成 - 储位: {event_data.cell_id}")
|
|
112
|
+
|
|
113
|
+
except Exception as e:
|
|
114
|
+
logger.error(f"入库事件处理失败 - 储位: {event_data.cell_id}", exc_info=True)
|
|
115
|
+
raise RuntimeError(f"入库处理失败: {str(e)}")
|
|
116
|
+
|
|
117
|
+
def _handle_take_out_event(self, event_data: CellEventData) -> None:
|
|
118
|
+
"""
|
|
119
|
+
处理取出事件
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
event_data: 事件数据
|
|
123
|
+
"""
|
|
124
|
+
logger.info(f"处理出库事件 - 储位: {event_data.cell_id}, GRN: {event_data.grn}")
|
|
125
|
+
|
|
126
|
+
# 这里可以添加具体的出库业务逻辑
|
|
127
|
+
try:
|
|
128
|
+
# 验证数据
|
|
129
|
+
if not event_data.grn or not event_data.grn.strip():
|
|
130
|
+
raise ValueError("GRN不能为空")
|
|
131
|
+
|
|
132
|
+
# 更新库存
|
|
133
|
+
self._update_inventory_for_take_out(event_data)
|
|
134
|
+
|
|
135
|
+
# 发送通知
|
|
136
|
+
self._send_take_out_notification(event_data)
|
|
137
|
+
|
|
138
|
+
logger.info(f"出库事件处理完成 - 储位: {event_data.cell_id}")
|
|
139
|
+
|
|
140
|
+
except Exception as e:
|
|
141
|
+
logger.error(f"出库事件处理失败 - 储位: {event_data.cell_id}", exc_info=True)
|
|
142
|
+
raise RuntimeError(f"出库处理失败: {str(e)}")
|
|
143
|
+
|
|
144
|
+
def _handle_operation_error(self, event_data: CellEventData) -> None:
|
|
145
|
+
"""
|
|
146
|
+
处理操作错误
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
event_data: 事件数据
|
|
150
|
+
"""
|
|
151
|
+
logger.error(f"操作错误 - 储位: {event_data.cell_id}, GRN: {event_data.grn}, "
|
|
152
|
+
f"代码: {event_data.code}, 消息: {event_data.message}")
|
|
153
|
+
|
|
154
|
+
# 处理不同类型的错误
|
|
155
|
+
try:
|
|
156
|
+
if event_data.code == OperationResultCode.ILLEGAL_STOCK_IN.value:
|
|
157
|
+
self._handle_illegal_stock_in(event_data)
|
|
158
|
+
elif event_data.code == OperationResultCode.ILLEGAL_STOCK_OUT.value:
|
|
159
|
+
self._handle_illegal_stock_out(event_data)
|
|
160
|
+
elif event_data.code == OperationResultCode.ILLEGAL_STOCK_IN_CLEAR_ERROR.value:
|
|
161
|
+
self._handle_illegal_stock_in_clear_error(event_data)
|
|
162
|
+
elif event_data.code == OperationResultCode.ILLEGAL_STOCK_OUT_CLEAR_ERROR.value:
|
|
163
|
+
self._handle_illegal_stock_out_clear_error(event_data)
|
|
164
|
+
else:
|
|
165
|
+
self._handle_unknown_error(event_data)
|
|
166
|
+
except Exception as e:
|
|
167
|
+
logger.error(f"处理操作错误时发生异常", exc_info=True)
|
|
168
|
+
|
|
169
|
+
# 以下方法为示例实现,实际业务中需要根据具体需求实现
|
|
170
|
+
|
|
171
|
+
def _update_inventory_for_put_in(self, event_data: CellEventData) -> None:
|
|
172
|
+
"""更新库存信息 - 入库"""
|
|
173
|
+
logger.debug(f"更新库存 - 入库: 储位={event_data.cell_id}, GRN={event_data.grn}")
|
|
174
|
+
# 实际实现中,这里会更新数据库中的库存记录
|
|
175
|
+
|
|
176
|
+
def _update_inventory_for_take_out(self, event_data: CellEventData) -> None:
|
|
177
|
+
"""更新库存信息 - 出库"""
|
|
178
|
+
logger.debug(f"更新库存 - 出库: 储位={event_data.cell_id}, GRN={event_data.grn}")
|
|
179
|
+
# 实际实现中,这里会更新数据库中的库存记录
|
|
180
|
+
|
|
181
|
+
def _send_put_in_notification(self, event_data: CellEventData) -> None:
|
|
182
|
+
"""发送入库通知"""
|
|
183
|
+
logger.debug(f"发送入库通知 - 储位: {event_data.cell_id}, GRN: {event_data.grn}")
|
|
184
|
+
# 实际实现中,这里会发送邮件、短信或其他通知
|
|
185
|
+
|
|
186
|
+
def _send_take_out_notification(self, event_data: CellEventData) -> None:
|
|
187
|
+
"""发送出库通知"""
|
|
188
|
+
logger.debug(f"发送出库通知 - 储位: {event_data.cell_id}, GRN: {event_data.grn}")
|
|
189
|
+
# 实际实现中,这里会发送邮件、短信或其他通知
|
|
190
|
+
|
|
191
|
+
def _handle_illegal_stock_in(self, event_data: CellEventData) -> None:
|
|
192
|
+
"""处理非法上架"""
|
|
193
|
+
logger.warning(f"非法上架 - 储位: {event_data.cell_id}, GRN: {event_data.grn}")
|
|
194
|
+
self._send_alert("非法上架警告", event_data)
|
|
195
|
+
|
|
196
|
+
def _handle_illegal_stock_out(self, event_data: CellEventData) -> None:
|
|
197
|
+
"""处理非法下架"""
|
|
198
|
+
logger.warning(f"非法下架 - 储位: {event_data.cell_id}, GRN: {event_data.grn}")
|
|
199
|
+
self._send_alert("非法下架警告", event_data)
|
|
200
|
+
|
|
201
|
+
def _handle_illegal_stock_in_clear_error(self, event_data: CellEventData) -> None:
|
|
202
|
+
"""处理非法上架清错"""
|
|
203
|
+
logger.warning(f"非法上架清错 - 储位: {event_data.cell_id}, GRN: {event_data.grn}")
|
|
204
|
+
self._send_alert("非法上架清错警告", event_data)
|
|
205
|
+
|
|
206
|
+
def _handle_illegal_stock_out_clear_error(self, event_data: CellEventData) -> None:
|
|
207
|
+
"""处理非法下架清错"""
|
|
208
|
+
logger.warning(f"非法下架清错 - 储位: {event_data.cell_id}, GRN: {event_data.grn}")
|
|
209
|
+
self._send_alert("非法下架清错警告", event_data)
|
|
210
|
+
|
|
211
|
+
def _handle_unknown_error(self, event_data: CellEventData) -> None:
|
|
212
|
+
"""处理未知错误"""
|
|
213
|
+
logger.error(f"未知错误 - 储位: {event_data.cell_id}, GRN: {event_data.grn}, "
|
|
214
|
+
f"错误代码: {event_data.code}")
|
|
215
|
+
self._send_alert("未知错误警告", event_data)
|
|
216
|
+
|
|
217
|
+
def _send_alert(self, alert_type: str, event_data: CellEventData) -> None:
|
|
218
|
+
"""发送告警通知"""
|
|
219
|
+
logger.debug(f"发送告警 - 类型: {alert_type}, 储位: {event_data.cell_id}, GRN: {event_data.grn}")
|
|
220
|
+
# 实际实现中,这里会发送告警邮件、短信或其他通知
|