pixelarraythirdparty 1.1.4__tar.gz → 1.1.6__tar.gz

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.
Files changed (26) hide show
  1. {pixelarraythirdparty-1.1.4 → pixelarraythirdparty-1.1.6}/MANIFEST.in +1 -1
  2. {pixelarraythirdparty-1.1.4 → pixelarraythirdparty-1.1.6}/PKG-INFO +1 -1
  3. {pixelarraythirdparty-1.1.4 → pixelarraythirdparty-1.1.6}/pixelarraythirdparty/__init__.py +5 -1
  4. pixelarraythirdparty-1.1.6/pixelarraythirdparty/feedback/feedback.py +251 -0
  5. {pixelarraythirdparty-1.1.4 → pixelarraythirdparty-1.1.6}/pixelarraythirdparty/order/order.py +73 -0
  6. pixelarraythirdparty-1.1.6/pixelarraythirdparty/project/__init__.py +0 -0
  7. pixelarraythirdparty-1.1.6/pixelarraythirdparty/project/project.py +78 -0
  8. pixelarraythirdparty-1.1.6/pixelarraythirdparty/user/__init__.py +0 -0
  9. {pixelarraythirdparty-1.1.4 → pixelarraythirdparty-1.1.6}/pixelarraythirdparty.egg-info/PKG-INFO +1 -1
  10. {pixelarraythirdparty-1.1.4 → pixelarraythirdparty-1.1.6}/pixelarraythirdparty.egg-info/SOURCES.txt +4 -0
  11. {pixelarraythirdparty-1.1.4 → pixelarraythirdparty-1.1.6}/pyproject.toml +1 -1
  12. {pixelarraythirdparty-1.1.4 → pixelarraythirdparty-1.1.6}/LICENSE +0 -0
  13. {pixelarraythirdparty-1.1.4 → pixelarraythirdparty-1.1.6}/pixelarraythirdparty/client.py +0 -0
  14. {pixelarraythirdparty-1.1.4 → pixelarraythirdparty-1.1.6}/pixelarraythirdparty/cron/__init__.py +0 -0
  15. {pixelarraythirdparty-1.1.4 → pixelarraythirdparty-1.1.6}/pixelarraythirdparty/cron/cron.py +0 -0
  16. {pixelarraythirdparty-1.1.4/pixelarraythirdparty/order → pixelarraythirdparty-1.1.6/pixelarraythirdparty/feedback}/__init__.py +0 -0
  17. {pixelarraythirdparty-1.1.4/pixelarraythirdparty/product → pixelarraythirdparty-1.1.6/pixelarraythirdparty/order}/__init__.py +0 -0
  18. {pixelarraythirdparty-1.1.4/pixelarraythirdparty/user → pixelarraythirdparty-1.1.6/pixelarraythirdparty/product}/__init__.py +0 -0
  19. {pixelarraythirdparty-1.1.4 → pixelarraythirdparty-1.1.6}/pixelarraythirdparty/product/product.py +0 -0
  20. {pixelarraythirdparty-1.1.4 → pixelarraythirdparty-1.1.6}/pixelarraythirdparty/unified_login/__init__.py +0 -0
  21. {pixelarraythirdparty-1.1.4 → pixelarraythirdparty-1.1.6}/pixelarraythirdparty/unified_login/unified_login.py +0 -0
  22. {pixelarraythirdparty-1.1.4 → pixelarraythirdparty-1.1.6}/pixelarraythirdparty/user/user.py +0 -0
  23. {pixelarraythirdparty-1.1.4 → pixelarraythirdparty-1.1.6}/pixelarraythirdparty.egg-info/dependency_links.txt +0 -0
  24. {pixelarraythirdparty-1.1.4 → pixelarraythirdparty-1.1.6}/pixelarraythirdparty.egg-info/requires.txt +0 -0
  25. {pixelarraythirdparty-1.1.4 → pixelarraythirdparty-1.1.6}/pixelarraythirdparty.egg-info/top_level.txt +0 -0
  26. {pixelarraythirdparty-1.1.4 → pixelarraythirdparty-1.1.6}/setup.cfg +0 -0
@@ -1,7 +1,7 @@
1
1
  include README.md
2
2
  include requirements.txt
3
3
  include LICENSE
4
- recursive-include pixelarraylib *.py
4
+ recursive-include pixelarraythirdparty *.py
5
5
  recursive-exclude * __pycache__
6
6
  recursive-exclude * *.py[co]
7
7
  recursive-exclude test_case *
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pixelarraythirdparty
3
- Version: 1.1.4
3
+ Version: 1.1.6
4
4
  Summary: PixelArray 第三方微服务客户端
5
5
  Author-email: Lu qi <qi.lu@pixelarrayai.com>
6
6
  License-Expression: MIT
@@ -9,9 +9,11 @@ PixelArray 第三方微服务客户端
9
9
  - cron: 定时任务管理模块
10
10
  - user: 用户管理模块
11
11
  - unified_login: 统一登录模块
12
+ - feedback: 客户反馈模块
13
+ - project: 项目管理模块
12
14
  """
13
15
 
14
- __version__ = "1.1.4"
16
+ __version__ = "1.1.6"
15
17
  __author__ = "Lu qi"
16
18
  __email__ = "qi.lu@pixelarrayai.com"
17
19
 
@@ -21,4 +23,6 @@ __all__ = [
21
23
  "cron",
22
24
  "user",
23
25
  "order",
26
+ "feedback",
27
+ "project",
24
28
  ]
@@ -0,0 +1,251 @@
1
+ from pixelarraythirdparty.client import AsyncClient
2
+ from typing import List, Optional, Union, BinaryIO
3
+ import aiohttp
4
+ import os
5
+ import mimetypes
6
+
7
+
8
+ class FeedbackManagerAsync(AsyncClient):
9
+ async def create_feedback(
10
+ self,
11
+ project_id: int,
12
+ content: str,
13
+ contact_info: str,
14
+ images: Optional[List[Union[str, BinaryIO, bytes]]] = None,
15
+ video: Optional[Union[str, BinaryIO, bytes]] = None,
16
+ ):
17
+ """
18
+ description:
19
+ 创建新的客户反馈,反馈必须关联一个项目。支持上传图片和视频文件。
20
+ parameters:
21
+ project_id(int): 项目ID,必须关联一个已存在的项目
22
+ content(str): 反馈内容
23
+ contact_info(str): 反馈人联系方式
24
+ images(List[Union[str, BinaryIO, bytes]], optional): 图片文件列表,可以是文件路径、文件对象或字节数据,最多9张
25
+ video(Union[str, BinaryIO, bytes], optional): 视频文件,可以是文件路径、文件对象或字节数据,最多1个
26
+ return:
27
+ data(dict): 反馈信息
28
+ - id(int): 反馈ID
29
+ - project_id(int): 项目ID
30
+ - content(str): 反馈内容
31
+ - contact_info(str): 反馈人联系方式
32
+ - created_at(str): 反馈创建时间
33
+ success(bool): 操作是否成功
34
+ """
35
+ # 先创建反馈
36
+ data = {
37
+ "project_id": project_id,
38
+ "content": content,
39
+ "contact_info": contact_info,
40
+ }
41
+ feedback_data, success = await self._request("POST", "/api/feedback/create", json=data)
42
+ if not success:
43
+ return {}, False
44
+
45
+ feedback_id = feedback_data.get("id")
46
+ if not feedback_id:
47
+ return feedback_data, True
48
+
49
+ # 如果有文件需要上传,则上传文件
50
+ if images or video:
51
+ # 上传图片
52
+ if images:
53
+ if len(images) > 9:
54
+ return {}, False # 图片数量超过限制
55
+ for image in images:
56
+ await self._upload_file(feedback_id, image, "image")
57
+
58
+ # 上传视频
59
+ if video:
60
+ await self._upload_file(feedback_id, video, "video")
61
+
62
+ return feedback_data, True
63
+
64
+ async def _upload_file(
65
+ self,
66
+ feedback_id: int,
67
+ file_data: Union[str, BinaryIO, bytes],
68
+ file_type: str,
69
+ ):
70
+ """
71
+ 内部方法:上传文件到OSS
72
+ """
73
+ # 读取文件数据
74
+ file_content = None
75
+ file_name = None
76
+ file_size = None
77
+ mime_type = None
78
+
79
+ if isinstance(file_data, str):
80
+ # 文件路径
81
+ file_name = os.path.basename(file_data)
82
+ with open(file_data, "rb") as f:
83
+ file_content = f.read()
84
+ file_size = len(file_content)
85
+ mime_type, _ = mimetypes.guess_type(file_data)
86
+ elif isinstance(file_data, bytes):
87
+ # 字节数据
88
+ file_name = f"file.{file_type}"
89
+ file_content = file_data
90
+ file_size = len(file_content)
91
+ else:
92
+ # 文件对象
93
+ file_name = getattr(file_data, "name", None) or getattr(file_data, "filename", f"file.{file_type}")
94
+ try:
95
+ # 尝试异步读取
96
+ if hasattr(file_data, "read"):
97
+ read_method = file_data.read
98
+ # 检查是否是协程函数
99
+ import inspect
100
+ if inspect.iscoroutinefunction(read_method):
101
+ file_content = await read_method()
102
+ else:
103
+ file_content = read_method()
104
+ else:
105
+ file_content = file_data
106
+ except Exception as e:
107
+ print(f"读取文件对象失败: {str(e)}")
108
+ return False
109
+ file_size = len(file_content) if file_content else 0
110
+ mime_type, _ = mimetypes.guess_type(file_name)
111
+
112
+ if not file_content:
113
+ return False
114
+
115
+ # 获取上传签名链接
116
+ upload_data = {
117
+ "feedback_id": feedback_id,
118
+ "file_name": file_name,
119
+ "file_type": file_type,
120
+ }
121
+ if file_size:
122
+ upload_data["file_size"] = file_size
123
+ if mime_type:
124
+ upload_data["mime_type"] = mime_type
125
+
126
+ upload_info, success = await self._request(
127
+ "POST", "/api/feedback/files/upload/presigned-url", json=upload_data
128
+ )
129
+ if not success:
130
+ return False
131
+
132
+ presigned_url = upload_info.get("presigned_url")
133
+ oss_path = upload_info.get("oss_path")
134
+
135
+ if not presigned_url:
136
+ return False
137
+
138
+ # 上传文件到OSS
139
+ try:
140
+ headers = {}
141
+ if mime_type:
142
+ headers["Content-Type"] = mime_type
143
+
144
+ async with aiohttp.ClientSession() as session:
145
+ async with session.put(presigned_url, data=file_content, headers=headers) as resp:
146
+ if resp.status not in [200, 204]:
147
+ return False
148
+ except Exception as e:
149
+ print(f"上传文件到OSS失败: {str(e)}")
150
+ return False
151
+
152
+ # 调用上传完成接口
153
+ complete_data = {
154
+ "feedback_id": feedback_id,
155
+ "oss_path": oss_path,
156
+ "file_name": file_name,
157
+ "file_type": file_type,
158
+ }
159
+ if file_size:
160
+ complete_data["file_size"] = file_size
161
+ if mime_type:
162
+ complete_data["mime_type"] = mime_type
163
+
164
+ _, success = await self._request(
165
+ "POST", "/api/feedback/files/upload/complete", json=complete_data
166
+ )
167
+ return success
168
+
169
+ async def list_feedback(
170
+ self,
171
+ page: int = 1,
172
+ page_size: int = 10,
173
+ project_id: int = None,
174
+ ):
175
+ """
176
+ description:
177
+ 分页查询反馈列表,支持按项目ID进行筛选。
178
+ parameters:
179
+ page(int): 页码
180
+ page_size(int): 每页数量
181
+ project_id(int): 项目ID筛选,可选
182
+ return:
183
+ data(dict): 反馈列表信息
184
+ - feedbacks(list): 反馈列表
185
+ - id(int): 反馈ID
186
+ - project_id(int): 项目ID
187
+ - project_name(str): 项目名称
188
+ - content(str): 反馈内容
189
+ - contact_info(str): 反馈人联系方式
190
+ - created_at(str): 反馈创建时间
191
+ - total(int): 总反馈数量
192
+ - page(int): 当前页码
193
+ - page_size(int): 每页数量
194
+ success(bool): 操作是否成功
195
+ """
196
+ params = {
197
+ "page": page,
198
+ "page_size": page_size,
199
+ }
200
+ if project_id is not None:
201
+ params["project_id"] = project_id
202
+ data, success = await self._request("GET", "/api/feedback/list", params=params)
203
+ if not success:
204
+ return {}, False
205
+ return data, True
206
+
207
+ async def get_feedback_detail(self, feedback_id: int):
208
+ """
209
+ description:
210
+ 根据反馈ID获取反馈的详细信息。如果反馈中包含图片和视频,返回的文件信息会包含预览签名链接。
211
+ parameters:
212
+ feedback_id(int): 反馈ID
213
+ return:
214
+ data(dict): 反馈详细信息
215
+ - id(int): 反馈ID
216
+ - project_id(int): 项目ID
217
+ - project_name(str): 项目名称
218
+ - content(str): 反馈内容
219
+ - contact_info(str): 反馈人联系方式
220
+ - created_at(str): 反馈创建时间
221
+ - files(list, optional): 文件列表,如果反馈包含文件
222
+ - id(int): 文件ID
223
+ - feedback_id(int): 反馈ID
224
+ - oss_path(str): OSS存储路径
225
+ - file_name(str): 文件名
226
+ - file_type(str): 文件类型("image"或"video")
227
+ - file_size(int, optional): 文件大小(字节)
228
+ - mime_type(str, optional): MIME类型
229
+ - presigned_url(str): 预览签名链接,有效期1小时
230
+ success(bool): 操作是否成功
231
+ """
232
+ data, success = await self._request("GET", f"/api/feedback/{feedback_id}")
233
+ if not success:
234
+ return {}, False
235
+ return data, True
236
+
237
+ async def delete_feedback(self, feedback_id: int):
238
+ """
239
+ description:
240
+ 根据反馈ID删除指定的反馈记录。仅管理员可删除反馈。
241
+ parameters:
242
+ feedback_id(int): 反馈ID
243
+ return:
244
+ data(None): 删除成功时返回None
245
+ success(bool): 操作是否成功
246
+ """
247
+ data, success = await self._request("DELETE", f"/api/feedback/{feedback_id}")
248
+ if not success:
249
+ return {}, False
250
+ return data, True
251
+
@@ -259,3 +259,76 @@ class OrderManagerAsync(AsyncClient):
259
259
  if not success:
260
260
  return {}, False
261
261
  return data, True
262
+
263
+ async def get_revenue_trend(
264
+ self,
265
+ start_date: str = None,
266
+ end_date: str = None,
267
+ dimension: str = "day",
268
+ ):
269
+ """
270
+ description:
271
+ 获取收入趋势统计,只统计已支付(PAID)的订单。支持按日、按周、按月三种维度统计。
272
+ parameters:
273
+ start_date(str): 开始日期,格式:YYYY-MM-DD,可选
274
+ end_date(str): 结束日期,格式:YYYY-MM-DD,可选
275
+ dimension(str): 统计维度,可选值:"day"(按日)、"week"(按周)、"month"(按月),默认为"day"
276
+ return:
277
+ data(dict): 收入趋势统计信息
278
+ - trend(list): 趋势数据列表
279
+ - date(str): 日期或时间段
280
+ - revenue(float): 收入金额(元)
281
+ - order_count(int): 订单数量
282
+ - dimension(str): 统计维度
283
+ - start_date(str): 开始日期
284
+ - end_date(str): 结束日期
285
+ success(bool): 操作是否成功
286
+ """
287
+ params = {
288
+ "dimension": dimension,
289
+ }
290
+ if start_date is not None:
291
+ params["start_date"] = start_date
292
+ if end_date is not None:
293
+ params["end_date"] = end_date
294
+ data, success = await self._request("GET", "/api/orders/stats/revenue-trend", params=params)
295
+ if not success:
296
+ return {}, False
297
+ return data, True
298
+
299
+ async def get_product_revenue_ranking(
300
+ self,
301
+ start_date: str = None,
302
+ end_date: str = None,
303
+ limit: int = 10,
304
+ ):
305
+ """
306
+ description:
307
+ 获取产品收入排名,只统计已支付(PAID)的订单。按收入金额降序排列。
308
+ parameters:
309
+ start_date(str): 开始日期,格式:YYYY-MM-DD,可选
310
+ end_date(str): 结束日期,格式:YYYY-MM-DD,可选
311
+ limit(int): 返回排名数量,默认为10,最大100
312
+ return:
313
+ data(dict): 产品收入排名信息
314
+ - ranking(list): 排名列表
315
+ - product_id(int): 产品ID
316
+ - product_name(str): 产品名称
317
+ - product_category(str): 产品分类
318
+ - revenue(float): 收入金额(元)
319
+ - order_count(int): 订单数量
320
+ - start_date(str): 开始日期
321
+ - end_date(str): 结束日期
322
+ success(bool): 操作是否成功
323
+ """
324
+ params = {
325
+ "limit": limit,
326
+ }
327
+ if start_date is not None:
328
+ params["start_date"] = start_date
329
+ if end_date is not None:
330
+ params["end_date"] = end_date
331
+ data, success = await self._request("GET", "/api/orders/stats/product-ranking", params=params)
332
+ if not success:
333
+ return {}, False
334
+ return data, True
@@ -0,0 +1,78 @@
1
+ from pixelarraythirdparty.client import AsyncClient
2
+
3
+
4
+ class ProjectManagerAsync(AsyncClient):
5
+ async def create_project(
6
+ self,
7
+ name: str,
8
+ ):
9
+ """
10
+ description:
11
+ 创建新的项目。
12
+ parameters:
13
+ name(str): 项目名称
14
+ return:
15
+ data(dict): 项目信息
16
+ - id(int): 项目ID
17
+ - name(str): 项目名称
18
+ - created_at(str): 项目创建时间
19
+ success(bool): 操作是否成功
20
+ """
21
+ data = {
22
+ "name": name,
23
+ }
24
+ data, success = await self._request("POST", "/api/projects/create", json=data)
25
+ if not success:
26
+ return {}, False
27
+ return data, True
28
+
29
+ async def list_project(
30
+ self,
31
+ page: int = 1,
32
+ page_size: int = 10,
33
+ name: str = None,
34
+ ):
35
+ """
36
+ description:
37
+ 分页查询项目列表,支持按项目名称进行搜索。
38
+ parameters:
39
+ page(int): 页码
40
+ page_size(int): 每页数量
41
+ name(str): 项目名称搜索,支持模糊匹配
42
+ return:
43
+ data(dict): 项目列表信息
44
+ - projects(list): 项目列表
45
+ - id(int): 项目ID
46
+ - name(str): 项目名称
47
+ - created_at(str): 项目创建时间
48
+ - total(int): 总项目数量
49
+ - page(int): 当前页码
50
+ - page_size(int): 每页数量
51
+ success(bool): 操作是否成功
52
+ """
53
+ params = {
54
+ "page": page,
55
+ "page_size": page_size,
56
+ }
57
+ if name is not None:
58
+ params["name"] = name
59
+ data, success = await self._request("GET", "/api/projects/list", params=params)
60
+ if not success:
61
+ return {}, False
62
+ return data, True
63
+
64
+ async def delete_project(self, project_id: int):
65
+ """
66
+ description:
67
+ 根据项目ID删除指定的项目记录。如果项目下还有产品或反馈,则不允许删除。
68
+ parameters:
69
+ project_id(int): 项目ID
70
+ return:
71
+ data(None): 删除成功时返回None
72
+ success(bool): 操作是否成功
73
+ """
74
+ data, success = await self._request("DELETE", f"/api/projects/{project_id}")
75
+ if not success:
76
+ return {}, False
77
+ return data, True
78
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pixelarraythirdparty
3
- Version: 1.1.4
3
+ Version: 1.1.6
4
4
  Summary: PixelArray 第三方微服务客户端
5
5
  Author-email: Lu qi <qi.lu@pixelarrayai.com>
6
6
  License-Expression: MIT
@@ -10,10 +10,14 @@ pixelarraythirdparty.egg-info/requires.txt
10
10
  pixelarraythirdparty.egg-info/top_level.txt
11
11
  pixelarraythirdparty/cron/__init__.py
12
12
  pixelarraythirdparty/cron/cron.py
13
+ pixelarraythirdparty/feedback/__init__.py
14
+ pixelarraythirdparty/feedback/feedback.py
13
15
  pixelarraythirdparty/order/__init__.py
14
16
  pixelarraythirdparty/order/order.py
15
17
  pixelarraythirdparty/product/__init__.py
16
18
  pixelarraythirdparty/product/product.py
19
+ pixelarraythirdparty/project/__init__.py
20
+ pixelarraythirdparty/project/project.py
17
21
  pixelarraythirdparty/unified_login/__init__.py
18
22
  pixelarraythirdparty/unified_login/unified_login.py
19
23
  pixelarraythirdparty/user/__init__.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pixelarraythirdparty"
7
- version = "1.1.4"
7
+ version = "1.1.6"
8
8
  authors = [
9
9
  {name = "Lu qi", email = "qi.lu@pixelarrayai.com"},
10
10
  ]