tamar-file-hub-client 0.0.1__py3-none-any.whl → 0.0.2__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.
- file_hub_client/__init__.py +39 -0
- file_hub_client/client.py +43 -6
- file_hub_client/rpc/async_client.py +91 -11
- file_hub_client/rpc/gen/taple_service_pb2.py +225 -0
- file_hub_client/rpc/gen/taple_service_pb2_grpc.py +1626 -0
- file_hub_client/rpc/generate_grpc.py +2 -2
- file_hub_client/rpc/interceptors.py +550 -0
- file_hub_client/rpc/protos/taple_service.proto +874 -0
- file_hub_client/rpc/sync_client.py +91 -9
- file_hub_client/schemas/__init__.py +60 -0
- file_hub_client/schemas/taple.py +413 -0
- file_hub_client/services/__init__.py +5 -0
- file_hub_client/services/file/async_blob_service.py +558 -482
- file_hub_client/services/file/async_file_service.py +18 -9
- file_hub_client/services/file/base_file_service.py +19 -6
- file_hub_client/services/file/sync_blob_service.py +554 -478
- file_hub_client/services/file/sync_file_service.py +18 -9
- file_hub_client/services/folder/async_folder_service.py +20 -11
- file_hub_client/services/folder/sync_folder_service.py +20 -11
- file_hub_client/services/taple/__init__.py +10 -0
- file_hub_client/services/taple/async_taple_service.py +2281 -0
- file_hub_client/services/taple/base_taple_service.py +353 -0
- file_hub_client/services/taple/idempotent_taple_mixin.py +142 -0
- file_hub_client/services/taple/sync_taple_service.py +2256 -0
- file_hub_client/utils/__init__.py +43 -1
- file_hub_client/utils/file_utils.py +59 -11
- file_hub_client/utils/idempotency.py +196 -0
- file_hub_client/utils/logging.py +315 -0
- file_hub_client/utils/retry.py +241 -2
- file_hub_client/utils/smart_retry.py +403 -0
- tamar_file_hub_client-0.0.2.dist-info/METADATA +2050 -0
- tamar_file_hub_client-0.0.2.dist-info/RECORD +57 -0
- tamar_file_hub_client-0.0.1.dist-info/METADATA +0 -874
- tamar_file_hub_client-0.0.1.dist-info/RECORD +0 -44
- {tamar_file_hub_client-0.0.1.dist-info → tamar_file_hub_client-0.0.2.dist-info}/WHEEL +0 -0
- {tamar_file_hub_client-0.0.1.dist-info → tamar_file_hub_client-0.0.2.dist-info}/top_level.txt +0 -0
@@ -14,6 +14,9 @@ from typing import Optional, Dict, List, Tuple
|
|
14
14
|
from ..enums import Role
|
15
15
|
from ..errors import ConnectionError
|
16
16
|
from ..schemas.context import UserContext, RequestContext, FullContext
|
17
|
+
from ..utils.logging import get_logger, setup_logging
|
18
|
+
from .interceptors import create_sync_interceptors
|
19
|
+
import logging
|
17
20
|
|
18
21
|
|
19
22
|
class SyncGrpcClient:
|
@@ -22,7 +25,7 @@ class SyncGrpcClient:
|
|
22
25
|
def __init__(
|
23
26
|
self,
|
24
27
|
host: str = "localhost",
|
25
|
-
port: int =
|
28
|
+
port: Optional[int] = None,
|
26
29
|
secure: bool = False,
|
27
30
|
credentials: Optional[dict] = None,
|
28
31
|
options: Optional[list] = None,
|
@@ -31,13 +34,15 @@ class SyncGrpcClient:
|
|
31
34
|
default_metadata: Optional[Dict[str, str]] = None,
|
32
35
|
user_context: Optional[UserContext] = None,
|
33
36
|
request_context: Optional[RequestContext] = None,
|
37
|
+
enable_logging: bool = True,
|
38
|
+
log_level: str = "INFO",
|
34
39
|
):
|
35
40
|
"""
|
36
41
|
初始化同步gRPC客户端
|
37
42
|
|
38
43
|
Args:
|
39
|
-
host:
|
40
|
-
port:
|
44
|
+
host: 服务器地址(可以是域名或IP)
|
45
|
+
port: 服务器端口(可选,如果不指定则根据secure自动选择)
|
41
46
|
secure: 是否使用安全连接(TLS)
|
42
47
|
credentials: 认证凭据字典(如 {'api_key': 'xxx'})
|
43
48
|
options: gRPC通道选项
|
@@ -46,10 +51,19 @@ class SyncGrpcClient:
|
|
46
51
|
default_metadata: 默认的元数据(如 org_id, user_id 等)
|
47
52
|
user_context: 用户上下文
|
48
53
|
request_context: 请求上下文
|
54
|
+
enable_logging: 是否启用日志记录
|
55
|
+
log_level: 日志级别
|
49
56
|
"""
|
50
57
|
self.host = host
|
51
58
|
self.port = port
|
52
|
-
|
59
|
+
|
60
|
+
# 构建地址:如果没有指定端口,则使用域名作为地址
|
61
|
+
if port is not None:
|
62
|
+
self.address = f"{host}:{port}"
|
63
|
+
else:
|
64
|
+
# 如果没有指定端口,直接使用host
|
65
|
+
# gRPC会自动根据secure选择默认端口(80/443)
|
66
|
+
self.address = host
|
53
67
|
self.secure = secure
|
54
68
|
self.credentials = credentials
|
55
69
|
self.options = options or []
|
@@ -59,6 +73,20 @@ class SyncGrpcClient:
|
|
59
73
|
self._channel: Optional[grpc.Channel] = None
|
60
74
|
self._stubs = {}
|
61
75
|
self._stub_lock = threading.Lock()
|
76
|
+
|
77
|
+
# 日志配置
|
78
|
+
self.enable_logging = enable_logging
|
79
|
+
self.log_level = log_level
|
80
|
+
|
81
|
+
# 只有在明确启用日志时才设置SDK日志
|
82
|
+
# 默认不设置,让用户自己控制
|
83
|
+
if enable_logging:
|
84
|
+
# 检查是否已经有处理器,避免重复设置
|
85
|
+
sdk_logger = get_logger()
|
86
|
+
if not sdk_logger.handlers:
|
87
|
+
setup_logging(level=log_level, enable_grpc_logging=True, use_json_format=True)
|
88
|
+
|
89
|
+
self.logger = get_logger()
|
62
90
|
|
63
91
|
# 上下文管理
|
64
92
|
self._user_context = user_context
|
@@ -86,7 +114,7 @@ class SyncGrpcClient:
|
|
86
114
|
return RequestContext(
|
87
115
|
client_ip=client_ip,
|
88
116
|
client_type="python-sdk",
|
89
|
-
client_version="1.0.0",
|
117
|
+
client_version="1.0.0",
|
90
118
|
user_agent=f"FileHubClient/1.0.0 Python/{platform.python_version()} {platform.system()}/{platform.release()}"
|
91
119
|
)
|
92
120
|
|
@@ -126,6 +154,9 @@ class SyncGrpcClient:
|
|
126
154
|
|
127
155
|
channel_credentials = self._create_channel_credentials()
|
128
156
|
|
157
|
+
# 创建拦截器
|
158
|
+
interceptors = create_sync_interceptors() if self.enable_logging else []
|
159
|
+
|
129
160
|
if channel_credentials:
|
130
161
|
self._channel = grpc.secure_channel(
|
131
162
|
self.address,
|
@@ -137,6 +168,10 @@ class SyncGrpcClient:
|
|
137
168
|
self.address,
|
138
169
|
options=self.options
|
139
170
|
)
|
171
|
+
|
172
|
+
# 如果有拦截器,需要包装channel
|
173
|
+
if interceptors:
|
174
|
+
self._channel = grpc.intercept_channel(self._channel, *interceptors)
|
140
175
|
|
141
176
|
# 连接
|
142
177
|
try:
|
@@ -145,12 +180,41 @@ class SyncGrpcClient:
|
|
145
180
|
raise ConnectionError(f"连接超时:{self.address}")
|
146
181
|
|
147
182
|
# 连接成功
|
183
|
+
if self.enable_logging:
|
184
|
+
log_record = logging.LogRecord(
|
185
|
+
name=self.logger.name,
|
186
|
+
level=logging.INFO,
|
187
|
+
pathname="",
|
188
|
+
lineno=0,
|
189
|
+
msg=f"🔗 已连接到 gRPC 服务器",
|
190
|
+
args=(),
|
191
|
+
exc_info=None
|
192
|
+
)
|
193
|
+
log_record.log_type = "info"
|
194
|
+
log_record.data = {"server": self.address}
|
195
|
+
self.logger.handle(log_record)
|
148
196
|
return
|
149
197
|
|
150
198
|
except Exception as e:
|
151
199
|
last_error = e
|
152
200
|
if attempt < self.retry_count - 1:
|
153
|
-
|
201
|
+
if self.enable_logging:
|
202
|
+
log_record = logging.LogRecord(
|
203
|
+
name=self.logger.name,
|
204
|
+
level=logging.WARNING,
|
205
|
+
pathname="",
|
206
|
+
lineno=0,
|
207
|
+
msg=f"⚠️ 连接失败,正在重试",
|
208
|
+
args=(),
|
209
|
+
exc_info=None
|
210
|
+
)
|
211
|
+
log_record.log_type = "info"
|
212
|
+
log_record.data = {
|
213
|
+
"attempt": attempt + 1,
|
214
|
+
"max_attempts": self.retry_count,
|
215
|
+
"error": str(e)
|
216
|
+
}
|
217
|
+
self.logger.handle(log_record)
|
154
218
|
if self._channel:
|
155
219
|
self._channel.close()
|
156
220
|
self._channel = None
|
@@ -168,6 +232,19 @@ class SyncGrpcClient:
|
|
168
232
|
def close(self):
|
169
233
|
"""关闭连接"""
|
170
234
|
if self._channel:
|
235
|
+
if self.enable_logging:
|
236
|
+
log_record = logging.LogRecord(
|
237
|
+
name=self.logger.name,
|
238
|
+
level=logging.INFO,
|
239
|
+
pathname="",
|
240
|
+
lineno=0,
|
241
|
+
msg=f"👋 正在关闭 gRPC 连接",
|
242
|
+
args=(),
|
243
|
+
exc_info=None
|
244
|
+
)
|
245
|
+
log_record.log_type = "info"
|
246
|
+
log_record.data = {"server": self.address}
|
247
|
+
self.logger.handle(log_record)
|
171
248
|
self._channel.close()
|
172
249
|
self._channel = None
|
173
250
|
self._stubs.clear()
|
@@ -191,11 +268,12 @@ class SyncGrpcClient:
|
|
191
268
|
self._stubs[stub_name] = stub_class(self._channel)
|
192
269
|
return self._stubs[stub_name]
|
193
270
|
|
194
|
-
def build_metadata(self, **kwargs) -> List[Tuple[str, str]]:
|
271
|
+
def build_metadata(self, *, request_id: Optional[str] = None, **kwargs) -> List[Tuple[str, str]]:
|
195
272
|
"""
|
196
273
|
构建请求元数据
|
197
274
|
|
198
275
|
Args:
|
276
|
+
request_id: 显式指定的请求ID,如果提供则优先使用
|
199
277
|
**kwargs: 要覆盖或添加的元数据
|
200
278
|
|
201
279
|
Returns:
|
@@ -209,8 +287,12 @@ class SyncGrpcClient:
|
|
209
287
|
# 添加/覆盖传入的元数据
|
210
288
|
metadata.update(kwargs)
|
211
289
|
|
212
|
-
#
|
213
|
-
if
|
290
|
+
# 处理 request_id(优先级:显式传入 > metadata中的x-request-id > RequestContext > 自动生成)
|
291
|
+
if request_id is not None:
|
292
|
+
# 优先使用显式传入的 request_id
|
293
|
+
metadata['x-request-id'] = request_id
|
294
|
+
elif 'x-request-id' not in metadata:
|
295
|
+
# 如果没有显式传入且metadata中也没有,则尝试从RequestContext获取或自动生成
|
214
296
|
metadata['x-request-id'] = (
|
215
297
|
self._request_context.extra.get("request_id") or str(uuid.uuid4())
|
216
298
|
)
|
@@ -20,6 +20,36 @@ from .context import (
|
|
20
20
|
RequestContext,
|
21
21
|
FullContext,
|
22
22
|
)
|
23
|
+
from .taple import (
|
24
|
+
Table,
|
25
|
+
Sheet,
|
26
|
+
Column,
|
27
|
+
Row,
|
28
|
+
Cell,
|
29
|
+
MergedCell,
|
30
|
+
TableView,
|
31
|
+
CellUpdate,
|
32
|
+
TableResponse,
|
33
|
+
SheetResponse,
|
34
|
+
ColumnResponse,
|
35
|
+
RowResponse,
|
36
|
+
CellResponse,
|
37
|
+
MergedCellResponse,
|
38
|
+
ViewResponse,
|
39
|
+
ListSheetsResponse,
|
40
|
+
ListColumnsResponse,
|
41
|
+
ListRowsResponse,
|
42
|
+
BatchCreateRowsResponse,
|
43
|
+
BatchUpdateCellsResponse,
|
44
|
+
GetCellsByRangeResponse,
|
45
|
+
ListMergedCellsResponse,
|
46
|
+
ListViewsResponse,
|
47
|
+
ImportTableDataResponse,
|
48
|
+
TableViewResponse,
|
49
|
+
BatchCreateTableViewResult,
|
50
|
+
BatchCreateTableViewsResponse,
|
51
|
+
ListTableViewsResponse,
|
52
|
+
)
|
23
53
|
|
24
54
|
__all__ = [
|
25
55
|
# 文件相关
|
@@ -40,4 +70,34 @@ __all__ = [
|
|
40
70
|
"UserContext",
|
41
71
|
"RequestContext",
|
42
72
|
"FullContext",
|
73
|
+
|
74
|
+
# Taple 相关
|
75
|
+
"Table",
|
76
|
+
"Sheet",
|
77
|
+
"Column",
|
78
|
+
"Row",
|
79
|
+
"Cell",
|
80
|
+
"MergedCell",
|
81
|
+
"TableView",
|
82
|
+
"CellUpdate",
|
83
|
+
"TableResponse",
|
84
|
+
"SheetResponse",
|
85
|
+
"ColumnResponse",
|
86
|
+
"RowResponse",
|
87
|
+
"CellResponse",
|
88
|
+
"MergedCellResponse",
|
89
|
+
"ViewResponse",
|
90
|
+
"ListSheetsResponse",
|
91
|
+
"ListColumnsResponse",
|
92
|
+
"ListRowsResponse",
|
93
|
+
"BatchCreateRowsResponse",
|
94
|
+
"BatchUpdateCellsResponse",
|
95
|
+
"GetCellsByRangeResponse",
|
96
|
+
"ListMergedCellsResponse",
|
97
|
+
"ListViewsResponse",
|
98
|
+
"ImportTableDataResponse",
|
99
|
+
"TableViewResponse",
|
100
|
+
"BatchCreateTableViewResult",
|
101
|
+
"BatchCreateTableViewsResponse",
|
102
|
+
"ListTableViewsResponse",
|
43
103
|
]
|
@@ -0,0 +1,413 @@
|
|
1
|
+
"""
|
2
|
+
Taple 相关数据模型
|
3
|
+
"""
|
4
|
+
from datetime import datetime
|
5
|
+
from typing import Optional, List, Dict, Any
|
6
|
+
from pydantic import BaseModel, Field
|
7
|
+
|
8
|
+
|
9
|
+
class Table(BaseModel):
|
10
|
+
"""表格信息模型"""
|
11
|
+
id: str = Field(..., description="表格ID")
|
12
|
+
file_id: str = Field(..., description="关联文件ID")
|
13
|
+
org_id: str = Field(..., description="组织ID")
|
14
|
+
user_id: str = Field(..., description="用户ID")
|
15
|
+
name: Optional[str] = Field(None, description="表格名称")
|
16
|
+
description: Optional[str] = Field(None, description="表格描述")
|
17
|
+
created_by_role: str = Field(..., description="创建者角色")
|
18
|
+
created_by: str = Field(..., description="创建者ID")
|
19
|
+
created_at: datetime = Field(..., description="创建时间")
|
20
|
+
updated_at: datetime = Field(..., description="更新时间")
|
21
|
+
deleted_at: Optional[datetime] = Field(None, description="删除时间")
|
22
|
+
|
23
|
+
class Config:
|
24
|
+
json_encoders = {
|
25
|
+
datetime: lambda v: v.isoformat()
|
26
|
+
}
|
27
|
+
|
28
|
+
|
29
|
+
class Sheet(BaseModel):
|
30
|
+
"""工作表信息模型"""
|
31
|
+
id: str = Field(..., description="工作表ID")
|
32
|
+
table_id: str = Field(..., description="所属表格ID")
|
33
|
+
org_id: str = Field(..., description="组织ID")
|
34
|
+
user_id: str = Field(..., description="用户ID")
|
35
|
+
name: str = Field(..., description="工作表名称")
|
36
|
+
description: Optional[str] = Field(None, description="工作表描述")
|
37
|
+
position: int = Field(..., description="工作表位置")
|
38
|
+
version: int = Field(..., description="版本号")
|
39
|
+
created_by_role: str = Field(..., description="创建者角色")
|
40
|
+
created_by: str = Field(..., description="创建者ID")
|
41
|
+
created_at: datetime = Field(..., description="创建时间")
|
42
|
+
updated_at: datetime = Field(..., description="更新时间")
|
43
|
+
deleted_at: Optional[datetime] = Field(None, description="删除时间")
|
44
|
+
|
45
|
+
class Config:
|
46
|
+
json_encoders = {
|
47
|
+
datetime: lambda v: v.isoformat()
|
48
|
+
}
|
49
|
+
|
50
|
+
|
51
|
+
class Column(BaseModel):
|
52
|
+
"""列信息模型"""
|
53
|
+
id: str = Field(..., description="列ID")
|
54
|
+
sheet_id: str = Field(..., description="所属工作表ID")
|
55
|
+
org_id: str = Field(..., description="组织ID")
|
56
|
+
user_id: str = Field(..., description="用户ID")
|
57
|
+
column_key: str = Field(..., description="列索引key")
|
58
|
+
name: str = Field(..., description="列名称")
|
59
|
+
column_type: str = Field(..., description="列数据类型")
|
60
|
+
description: Optional[str] = Field(None, description="列描述信息")
|
61
|
+
position: int = Field(..., description="列位置")
|
62
|
+
width: Optional[int] = Field(None, description="列宽度")
|
63
|
+
hidden: Optional[bool] = Field(None, description="是否隐藏")
|
64
|
+
properties: Optional[Dict[str, Any]] = Field(None, description="列属性")
|
65
|
+
version: int = Field(..., description="版本号")
|
66
|
+
created_by_role: str = Field(..., description="创建者角色")
|
67
|
+
created_by: str = Field(..., description="创建者ID")
|
68
|
+
created_at: datetime = Field(..., description="创建时间")
|
69
|
+
updated_at: datetime = Field(..., description="更新时间")
|
70
|
+
deleted_at: Optional[datetime] = Field(None, description="删除时间")
|
71
|
+
|
72
|
+
class Config:
|
73
|
+
json_encoders = {
|
74
|
+
datetime: lambda v: v.isoformat()
|
75
|
+
}
|
76
|
+
|
77
|
+
|
78
|
+
class Row(BaseModel):
|
79
|
+
"""行信息模型"""
|
80
|
+
id: str = Field(..., description="行ID")
|
81
|
+
sheet_id: str = Field(..., description="所属工作表ID")
|
82
|
+
org_id: str = Field(..., description="组织ID")
|
83
|
+
user_id: str = Field(..., description="用户ID")
|
84
|
+
row_key: str = Field(..., description="行索引key")
|
85
|
+
position: int = Field(..., description="行位置")
|
86
|
+
height: Optional[int] = Field(None, description="行高度")
|
87
|
+
hidden: Optional[bool] = Field(None, description="是否隐藏")
|
88
|
+
version: int = Field(..., description="版本号")
|
89
|
+
created_by_role: str = Field(..., description="创建者角色")
|
90
|
+
created_by: str = Field(..., description="创建者ID")
|
91
|
+
created_at: datetime = Field(..., description="创建时间")
|
92
|
+
updated_at: datetime = Field(..., description="更新时间")
|
93
|
+
deleted_at: Optional[datetime] = Field(None, description="删除时间")
|
94
|
+
|
95
|
+
class Config:
|
96
|
+
json_encoders = {
|
97
|
+
datetime: lambda v: v.isoformat()
|
98
|
+
}
|
99
|
+
|
100
|
+
|
101
|
+
class Cell(BaseModel):
|
102
|
+
"""单元格信息模型"""
|
103
|
+
id: str = Field(..., description="单元格ID")
|
104
|
+
sheet_id: str = Field(..., description="所属工作表ID")
|
105
|
+
column_id: str = Field(..., description="所属列ID")
|
106
|
+
row_id: str = Field(..., description="所属行ID")
|
107
|
+
org_id: str = Field(..., description="组织ID")
|
108
|
+
user_id: str = Field(..., description="用户ID")
|
109
|
+
column_key: str = Field(..., description="列索引key")
|
110
|
+
row_key: str = Field(..., description="行索引key")
|
111
|
+
raw_value: Optional[str] = Field(None, description="原始值")
|
112
|
+
formatted_value: Optional[str] = Field(None, description="格式化值")
|
113
|
+
formula: Optional[str] = Field(None, description="公式")
|
114
|
+
styles: Optional[Dict[str, Any]] = Field(None, description="样式")
|
115
|
+
data_type: Optional[str] = Field(None, description="数据类型")
|
116
|
+
version: int = Field(..., description="版本号")
|
117
|
+
created_by_role: str = Field(..., description="创建者角色")
|
118
|
+
created_by: str = Field(..., description="创建者ID")
|
119
|
+
created_at: datetime = Field(..., description="创建时间")
|
120
|
+
updated_at: datetime = Field(..., description="更新时间")
|
121
|
+
deleted_at: Optional[datetime] = Field(None, description="删除时间")
|
122
|
+
|
123
|
+
class Config:
|
124
|
+
json_encoders = {
|
125
|
+
datetime: lambda v: v.isoformat()
|
126
|
+
}
|
127
|
+
|
128
|
+
|
129
|
+
class MergedCell(BaseModel):
|
130
|
+
"""合并单元格信息模型"""
|
131
|
+
id: str = Field(..., description="合并单元格ID")
|
132
|
+
sheet_id: str = Field(..., description="所属工作表ID")
|
133
|
+
org_id: str = Field(..., description="组织ID")
|
134
|
+
user_id: str = Field(..., description="用户ID")
|
135
|
+
start_column_id: str = Field(..., description="起始列ID")
|
136
|
+
end_column_id: str = Field(..., description="结束列ID")
|
137
|
+
start_row_id: str = Field(..., description="起始行ID")
|
138
|
+
end_row_id: str = Field(..., description="结束行ID")
|
139
|
+
created_at: datetime = Field(..., description="创建时间")
|
140
|
+
updated_at: datetime = Field(..., description="更新时间")
|
141
|
+
deleted_at: Optional[datetime] = Field(None, description="删除时间")
|
142
|
+
|
143
|
+
class Config:
|
144
|
+
json_encoders = {
|
145
|
+
datetime: lambda v: v.isoformat()
|
146
|
+
}
|
147
|
+
|
148
|
+
|
149
|
+
class TableView(BaseModel):
|
150
|
+
"""表格视图信息模型"""
|
151
|
+
id: str = Field(..., description="视图ID")
|
152
|
+
table_id: str = Field(..., description="所属表格ID")
|
153
|
+
sheet_id: str = Field(..., description="所属工作表ID")
|
154
|
+
org_id: str = Field(..., description="组织ID")
|
155
|
+
user_id: str = Field(..., description="用户ID")
|
156
|
+
file_id: str = Field(..., description="关联文件ID")
|
157
|
+
|
158
|
+
# 视图配置字段
|
159
|
+
filter_criteria: Optional[Dict[str, Any]] = Field(None, description="过滤条件(JSON)")
|
160
|
+
sort_criteria: Optional[Dict[str, Any]] = Field(None, description="排序条件(JSON)")
|
161
|
+
visible_columns: Optional[List[str]] = Field(None, description="可见列(列key数组)")
|
162
|
+
group_criteria: Optional[Dict[str, Any]] = Field(None, description="分组条件(JSON)")
|
163
|
+
|
164
|
+
# 创建者信息
|
165
|
+
created_by_role: str = Field(..., description="创建者角色:user-用户;agent-智能体")
|
166
|
+
created_by: str = Field(..., description="创建者ID")
|
167
|
+
|
168
|
+
# 视图基本信息
|
169
|
+
view_name: str = Field(..., description="视图名称")
|
170
|
+
view_type: str = Field(..., description="视图类型:table-表格视图; gantt-甘特图; calendar-日历视图等")
|
171
|
+
|
172
|
+
# 视图状态
|
173
|
+
is_hidden: bool = Field(False, description="是否隐藏")
|
174
|
+
is_default: bool = Field(False, description="是否默认视图")
|
175
|
+
|
176
|
+
# 扩展配置
|
177
|
+
config: Optional[Dict[str, Any]] = Field(None, description="视图扩展配置(JSON)")
|
178
|
+
|
179
|
+
# 时间戳
|
180
|
+
created_at: datetime = Field(..., description="创建时间")
|
181
|
+
updated_at: datetime = Field(..., description="更新时间")
|
182
|
+
deleted_at: Optional[datetime] = Field(None, description="删除时间")
|
183
|
+
|
184
|
+
class Config:
|
185
|
+
json_encoders = {
|
186
|
+
datetime: lambda v: v.isoformat()
|
187
|
+
}
|
188
|
+
|
189
|
+
|
190
|
+
# 请求和响应模型
|
191
|
+
class CellUpdate(BaseModel):
|
192
|
+
"""单元格更新模型"""
|
193
|
+
column_key: str = Field(..., description="列索引key")
|
194
|
+
row_key: str = Field(..., description="行索引key")
|
195
|
+
raw_value: Optional[str] = Field(None, description="原始值")
|
196
|
+
formula: Optional[str] = Field(None, description="公式")
|
197
|
+
styles: Optional[Dict[str, Any]] = Field(None, description="样式")
|
198
|
+
|
199
|
+
|
200
|
+
# 响应模型
|
201
|
+
class TableResponse(BaseModel):
|
202
|
+
"""表格响应模型"""
|
203
|
+
table: Table = Field(..., description="表格信息")
|
204
|
+
|
205
|
+
|
206
|
+
class SheetResponse(BaseModel):
|
207
|
+
"""工作表响应模型"""
|
208
|
+
sheet: Sheet = Field(..., description="工作表信息")
|
209
|
+
|
210
|
+
|
211
|
+
class ColumnResponse(BaseModel):
|
212
|
+
"""列响应模型"""
|
213
|
+
column: Optional[Column] = Field(None, description="列信息")
|
214
|
+
current_version: Optional[int] = Field(None, description="当前版本号")
|
215
|
+
applied_immediately: Optional[bool] = Field(None, description="是否立即应用")
|
216
|
+
|
217
|
+
|
218
|
+
class RowResponse(BaseModel):
|
219
|
+
"""行响应模型"""
|
220
|
+
row: Optional[Row] = Field(None, description="行信息")
|
221
|
+
current_version: Optional[int] = Field(None, description="当前版本号")
|
222
|
+
applied_immediately: Optional[bool] = Field(None, description="是否立即应用")
|
223
|
+
success: Optional[bool] = Field(None, description="操作是否成功")
|
224
|
+
error_message: Optional[str] = Field(None, description="错误信息")
|
225
|
+
conflict_info: Optional['ConflictInfo'] = Field(None, description="冲突信息")
|
226
|
+
|
227
|
+
|
228
|
+
class CellResponse(BaseModel):
|
229
|
+
"""单元格响应模型"""
|
230
|
+
cell: Optional[Cell] = Field(None, description="单元格信息")
|
231
|
+
current_version: Optional[int] = Field(None, description="当前版本号")
|
232
|
+
applied_immediately: Optional[bool] = Field(None, description="是否立即应用")
|
233
|
+
success: Optional[bool] = Field(None, description="操作是否成功")
|
234
|
+
error_message: Optional[str] = Field(None, description="错误信息")
|
235
|
+
conflict_info: Optional['ConflictInfo'] = Field(None, description="冲突信息")
|
236
|
+
|
237
|
+
|
238
|
+
class MergedCellResponse(BaseModel):
|
239
|
+
"""合并单元格响应模型"""
|
240
|
+
merged_cell: MergedCell = Field(..., description="合并单元格信息")
|
241
|
+
|
242
|
+
|
243
|
+
class ViewResponse(BaseModel):
|
244
|
+
"""视图响应模型"""
|
245
|
+
view: TableView = Field(..., description="视图信息")
|
246
|
+
|
247
|
+
|
248
|
+
# 列表响应模型
|
249
|
+
class ListSheetsResponse(BaseModel):
|
250
|
+
"""工作表列表响应模型"""
|
251
|
+
sheets: List[Sheet] = Field(..., description="工作表列表")
|
252
|
+
|
253
|
+
|
254
|
+
class ListColumnsResponse(BaseModel):
|
255
|
+
"""列列表响应模型"""
|
256
|
+
columns: List[Column] = Field(..., description="列列表")
|
257
|
+
|
258
|
+
|
259
|
+
class ListRowsResponse(BaseModel):
|
260
|
+
"""行列表响应模型"""
|
261
|
+
rows: List[Row] = Field(..., description="行列表")
|
262
|
+
total: int = Field(..., description="总数")
|
263
|
+
|
264
|
+
|
265
|
+
class BatchCreateRowsResponse(BaseModel):
|
266
|
+
"""批量创建行响应模型"""
|
267
|
+
rows: List[Row] = Field(..., description="创建的行列表")
|
268
|
+
|
269
|
+
|
270
|
+
class BatchUpdateCellsResponse(BaseModel):
|
271
|
+
"""批量更新单元格响应模型"""
|
272
|
+
cells: List[Cell] = Field(..., description="更新的单元格列表")
|
273
|
+
|
274
|
+
|
275
|
+
class GetCellsByRangeResponse(BaseModel):
|
276
|
+
"""按范围获取单元格响应模型"""
|
277
|
+
cells: List[Cell] = Field(..., description="单元格列表")
|
278
|
+
|
279
|
+
|
280
|
+
class ListMergedCellsResponse(BaseModel):
|
281
|
+
"""合并单元格列表响应模型"""
|
282
|
+
merged_cells: List[MergedCell] = Field(..., description="合并单元格列表")
|
283
|
+
|
284
|
+
|
285
|
+
class ListViewsResponse(BaseModel):
|
286
|
+
"""视图列表响应模型"""
|
287
|
+
views: List[TableView] = Field(..., description="视图列表")
|
288
|
+
|
289
|
+
|
290
|
+
class GetSheetVersionResponse(BaseModel):
|
291
|
+
"""获取工作表版本响应模型"""
|
292
|
+
sheet_id: str = Field(..., description="工作表ID")
|
293
|
+
version: int = Field(..., description="当前版本号")
|
294
|
+
metadata: Optional[Sheet] = Field(None, description="工作表元数据")
|
295
|
+
|
296
|
+
|
297
|
+
class GetSheetDataResponse(BaseModel):
|
298
|
+
"""获取工作表数据响应模型"""
|
299
|
+
sheet_id: str = Field(..., description="工作表ID")
|
300
|
+
version: int = Field(..., description="当前版本号")
|
301
|
+
metadata: Optional[Sheet] = Field(None, description="工作表元数据")
|
302
|
+
columns: List[Column] = Field(default_factory=list, description="列数据")
|
303
|
+
rows: List[Row] = Field(default_factory=list, description="行数据")
|
304
|
+
cells: List[Cell] = Field(default_factory=list, description="单元格数据")
|
305
|
+
last_updated: Optional[datetime] = Field(None, description="最后更新时间")
|
306
|
+
|
307
|
+
class Config:
|
308
|
+
json_encoders = {
|
309
|
+
datetime: lambda v: v.isoformat()
|
310
|
+
}
|
311
|
+
|
312
|
+
|
313
|
+
class ConflictInfo(BaseModel):
|
314
|
+
"""冲突信息模型"""
|
315
|
+
has_conflict: bool = Field(..., description="是否有冲突")
|
316
|
+
server_version: int = Field(..., description="服务器版本号")
|
317
|
+
conflict_type: str = Field(..., description="冲突类型")
|
318
|
+
conflicted_columns: List[str] = Field(default_factory=list, description="冲突的列")
|
319
|
+
resolution_suggestion: Optional[str] = Field(None, description="解决建议")
|
320
|
+
|
321
|
+
|
322
|
+
class BatchEditSheetResponse(BaseModel):
|
323
|
+
"""批量编辑工作表响应模型"""
|
324
|
+
success: bool = Field(..., description="是否成功")
|
325
|
+
batch_id: str = Field(..., description="批次ID")
|
326
|
+
current_version: int = Field(..., description="当前版本号")
|
327
|
+
results: List[Any] = Field(default_factory=list, description="操作结果")
|
328
|
+
error_message: Optional[str] = Field(None, description="错误信息")
|
329
|
+
conflict_info: Optional[ConflictInfo] = Field(None, description="冲突信息")
|
330
|
+
|
331
|
+
|
332
|
+
class CloneTableDataResponse(BaseModel):
|
333
|
+
"""克隆表格数据响应模型"""
|
334
|
+
success: bool = Field(..., description="是否成功")
|
335
|
+
new_table_id: str = Field(..., description="新创建的表格ID")
|
336
|
+
new_file_id: str = Field(..., description="新创建的文件ID")
|
337
|
+
sheets_cloned: int = Field(..., description="克隆的工作表数量")
|
338
|
+
cells_cloned: int = Field(..., description="克隆的单元格数量")
|
339
|
+
error_message: Optional[str] = Field(None, description="错误信息")
|
340
|
+
created_at: datetime = Field(..., description="创建时间")
|
341
|
+
|
342
|
+
class Config:
|
343
|
+
json_encoders = {
|
344
|
+
datetime: lambda v: v.isoformat()
|
345
|
+
}
|
346
|
+
|
347
|
+
|
348
|
+
class ExportTableDataResponse(BaseModel):
|
349
|
+
"""导出表格数据响应模型"""
|
350
|
+
success: bool = Field(..., description="是否成功")
|
351
|
+
export_id: str = Field(..., description="导出记录ID")
|
352
|
+
file_url: str = Field(..., description="GCS文件URL(内部使用)")
|
353
|
+
download_url: str = Field(..., description="下载链接(带签名的临时链接)")
|
354
|
+
file_size: int = Field(..., description="文件大小(字节)")
|
355
|
+
file_name: str = Field(..., description="导出的文件名")
|
356
|
+
format: str = Field(..., description="导出格式") # Changed from 'ExportFormat' to str
|
357
|
+
sheets_exported: int = Field(..., description="导出的工作表数量")
|
358
|
+
error_message: Optional[str] = Field(None, description="错误信息")
|
359
|
+
created_at: datetime = Field(..., description="导出时间")
|
360
|
+
expires_at: datetime = Field(..., description="下载链接过期时间")
|
361
|
+
|
362
|
+
class Config:
|
363
|
+
json_encoders = {
|
364
|
+
datetime: lambda v: v.isoformat()
|
365
|
+
}
|
366
|
+
|
367
|
+
|
368
|
+
|
369
|
+
|
370
|
+
class TableViewResponse(BaseModel):
|
371
|
+
"""表格视图响应模型"""
|
372
|
+
view: TableView = Field(..., description="视图信息")
|
373
|
+
|
374
|
+
|
375
|
+
class BatchCreateTableViewResult(BaseModel):
|
376
|
+
"""批量创建表格视图结果模型"""
|
377
|
+
success: bool = Field(..., description="是否成功")
|
378
|
+
view: Optional[TableView] = Field(None, description="成功时返回创建的视图")
|
379
|
+
error_message: Optional[str] = Field(None, description="失败时的错误信息")
|
380
|
+
view_name: Optional[str] = Field(None, description="视图名称(用于标识是哪个视图)")
|
381
|
+
|
382
|
+
|
383
|
+
class BatchCreateTableViewsResponse(BaseModel):
|
384
|
+
"""批量创建表格视图响应模型"""
|
385
|
+
results: List[BatchCreateTableViewResult] = Field(default_factory=list, description="批量创建结果")
|
386
|
+
success_count: int = Field(..., description="成功创建的数量")
|
387
|
+
failed_count: int = Field(..., description="失败的数量")
|
388
|
+
|
389
|
+
|
390
|
+
class ListTableViewsResponse(BaseModel):
|
391
|
+
"""列出表格视图响应模型"""
|
392
|
+
views: List[TableView] = Field(default_factory=list, description="视图列表")
|
393
|
+
total_count: int = Field(..., description="总数量")
|
394
|
+
|
395
|
+
|
396
|
+
class ImportTableDataResponse(BaseModel):
|
397
|
+
"""导入表格数据响应模型"""
|
398
|
+
success: bool = Field(..., description="是否成功")
|
399
|
+
table_id: str = Field(..., description="导入的表格ID(新建或现有)")
|
400
|
+
file_id: Optional[str] = Field(None, description="创建的文件ID(如果创建了新表格)")
|
401
|
+
sheets_imported: int = Field(..., description="导入的工作表数量")
|
402
|
+
rows_imported: int = Field(..., description="导入的行数")
|
403
|
+
cells_imported: int = Field(..., description="导入的单元格数量")
|
404
|
+
sheet_results: List[Dict[str, Any]] = Field(default_factory=list, description="每个工作表的导入结果")
|
405
|
+
error_message: Optional[str] = Field(None, description="错误信息(如果失败)")
|
406
|
+
warnings: List[Dict[str, Any]] = Field(default_factory=list, description="警告信息")
|
407
|
+
created_at: datetime = Field(..., description="导入时间")
|
408
|
+
processing_time_ms: int = Field(..., description="处理时间(毫秒)")
|
409
|
+
|
410
|
+
class Config:
|
411
|
+
json_encoders = {
|
412
|
+
datetime: lambda v: v.isoformat()
|
413
|
+
}
|