pixelarraythirdparty 1.2.5__tar.gz → 1.2.7__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.
- {pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/PKG-INFO +1 -1
- {pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty/__init__.py +3 -1
- {pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty/client.py +30 -6
- {pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty/cron/cron.py +3 -3
- {pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty/feedback/feedback.py +53 -5
- {pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty/order/order.py +13 -13
- {pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty/product/product.py +6 -6
- {pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty/project/project.py +3 -3
- pixelarraythirdparty-1.2.7/pixelarraythirdparty/temporary_phone/__init__.py +10 -0
- pixelarraythirdparty-1.2.7/pixelarraythirdparty/temporary_phone/temporary_phone.py +47 -0
- {pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty/unified_login/__init__.py +2 -0
- {pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty/unified_login/unified_login.py +132 -23
- {pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty/user/user.py +6 -6
- {pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty.egg-info/PKG-INFO +1 -1
- {pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty.egg-info/SOURCES.txt +2 -0
- {pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pyproject.toml +1 -1
- {pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/LICENSE +0 -0
- {pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/MANIFEST.in +0 -0
- {pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty/cron/__init__.py +0 -0
- {pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty/feedback/__init__.py +0 -0
- {pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty/order/__init__.py +0 -0
- {pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty/product/__init__.py +0 -0
- {pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty/project/__init__.py +0 -0
- {pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty/user/__init__.py +0 -0
- {pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty.egg-info/dependency_links.txt +0 -0
- {pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty.egg-info/requires.txt +0 -0
- {pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty.egg-info/top_level.txt +0 -0
- {pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/setup.cfg +0 -0
|
@@ -11,9 +11,10 @@ PixelArray 第三方微服务客户端
|
|
|
11
11
|
- unified_login: 统一登录模块
|
|
12
12
|
- feedback: 客户反馈模块
|
|
13
13
|
- project: 项目管理模块
|
|
14
|
+
- temporary_phone: 临时手机号接码模块
|
|
14
15
|
"""
|
|
15
16
|
|
|
16
|
-
__version__ = "1.2.
|
|
17
|
+
__version__ = "1.2.7"
|
|
17
18
|
__author__ = "Lu qi"
|
|
18
19
|
__email__ = "qi.lu@pixelarrayai.com"
|
|
19
20
|
|
|
@@ -25,4 +26,5 @@ __all__ = [
|
|
|
25
26
|
"order",
|
|
26
27
|
"feedback",
|
|
27
28
|
"project",
|
|
29
|
+
"temporary_phone",
|
|
28
30
|
]
|
|
@@ -28,29 +28,53 @@ class AsyncClient:
|
|
|
28
28
|
url(str): 请求URL路径(相对于base_url)
|
|
29
29
|
**kwargs: 其他请求参数,如json、params、headers等
|
|
30
30
|
return:
|
|
31
|
-
data(Dict[str, Any]):
|
|
31
|
+
data(Dict[str, Any]): 成功时为服务端 data 字段;失败时为包含 message/status_code 等的字典,调用方可通过 message 查看失败原因
|
|
32
32
|
success(bool): 请求是否成功
|
|
33
33
|
"""
|
|
34
|
-
# 如果kwargs中有headers,则合并headers
|
|
35
34
|
headers = self.headers.copy()
|
|
36
35
|
if "headers" in kwargs:
|
|
37
36
|
headers.update(kwargs["headers"])
|
|
38
37
|
kwargs = {k: v for k, v in kwargs.items() if k != "headers"}
|
|
39
38
|
|
|
39
|
+
result = None
|
|
40
|
+
raw_text = ""
|
|
41
|
+
resp_status = 0
|
|
42
|
+
|
|
40
43
|
async with aiohttp.ClientSession() as session:
|
|
41
44
|
req_method = getattr(session, method.lower())
|
|
42
45
|
async with req_method(
|
|
43
46
|
f"{self.base_url}{url}", headers=headers, **kwargs
|
|
44
47
|
) as resp:
|
|
48
|
+
raw_text = await resp.text()
|
|
49
|
+
resp_status = resp.status
|
|
45
50
|
if resp.status == 200:
|
|
46
51
|
try:
|
|
47
|
-
|
|
52
|
+
import json
|
|
53
|
+
result = json.loads(raw_text)
|
|
48
54
|
if result.get("success") is True:
|
|
49
55
|
return result.get("data", {}), True
|
|
50
|
-
|
|
51
|
-
|
|
56
|
+
data_val = result.get("data")
|
|
57
|
+
if data_val is not None and (
|
|
58
|
+
(isinstance(data_val, dict) and data_val)
|
|
59
|
+
or (isinstance(data_val, list) and data_val)
|
|
60
|
+
):
|
|
61
|
+
return result.get("data", {}), True
|
|
62
|
+
except Exception:
|
|
63
|
+
pass
|
|
64
|
+
elif raw_text:
|
|
65
|
+
try:
|
|
66
|
+
import json
|
|
67
|
+
result = json.loads(raw_text)
|
|
68
|
+
except Exception:
|
|
52
69
|
pass
|
|
53
|
-
|
|
70
|
+
# 失败时返回包含 message 的字典,便于调用方展示具体失败原因
|
|
71
|
+
err = {
|
|
72
|
+
"message": (result.get("message") if result and isinstance(result, dict) else None) or (raw_text[:500] if raw_text else None) or "请求失败",
|
|
73
|
+
"status_code": (result.get("status_code") if result and isinstance(result, dict) else None) or resp_status,
|
|
74
|
+
}
|
|
75
|
+
if result and isinstance(result, dict) and "data" in result:
|
|
76
|
+
err["data"] = result.get("data")
|
|
77
|
+
return err, False
|
|
54
78
|
|
|
55
79
|
async def _request_raw(
|
|
56
80
|
self, method: str, url: str, **kwargs
|
|
@@ -27,7 +27,7 @@ class CronManagerAsync(AsyncClient):
|
|
|
27
27
|
"""
|
|
28
28
|
data, success = await self._request("GET", "/api/cron/tasks/scheduled")
|
|
29
29
|
if not success:
|
|
30
|
-
return
|
|
30
|
+
return data, False
|
|
31
31
|
return data, True
|
|
32
32
|
|
|
33
33
|
async def get_cron_tasks_detail(self, task_name: str):
|
|
@@ -51,7 +51,7 @@ class CronManagerAsync(AsyncClient):
|
|
|
51
51
|
"""
|
|
52
52
|
data, success = await self._request("GET", f"/api/cron/tasks/{task_name}")
|
|
53
53
|
if not success:
|
|
54
|
-
return
|
|
54
|
+
return data, False
|
|
55
55
|
return data, True
|
|
56
56
|
|
|
57
57
|
async def trigger_cron_task(self, task_name: str, args: list, kwargs: dict):
|
|
@@ -76,5 +76,5 @@ class CronManagerAsync(AsyncClient):
|
|
|
76
76
|
json={"args": args, "kwargs": kwargs},
|
|
77
77
|
)
|
|
78
78
|
if not success:
|
|
79
|
-
return
|
|
79
|
+
return data, False
|
|
80
80
|
return data, True
|
{pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty/feedback/feedback.py
RENAMED
|
@@ -40,7 +40,7 @@ class FeedbackManagerAsync(AsyncClient):
|
|
|
40
40
|
}
|
|
41
41
|
feedback_data, success = await self._request("POST", "/api/feedback/create", json=data)
|
|
42
42
|
if not success:
|
|
43
|
-
return
|
|
43
|
+
return feedback_data, False
|
|
44
44
|
|
|
45
45
|
feedback_id = feedback_data.get("id")
|
|
46
46
|
if not feedback_id:
|
|
@@ -51,7 +51,7 @@ class FeedbackManagerAsync(AsyncClient):
|
|
|
51
51
|
# 上传图片
|
|
52
52
|
if images:
|
|
53
53
|
if len(images) > 9:
|
|
54
|
-
return {}, False
|
|
54
|
+
return {"message": "图片数量超过限制(最多9张)"}, False
|
|
55
55
|
for image in images:
|
|
56
56
|
await self._upload_file(feedback_id, image, "image")
|
|
57
57
|
|
|
@@ -208,7 +208,7 @@ class FeedbackManagerAsync(AsyncClient):
|
|
|
208
208
|
params["project_id"] = project_id
|
|
209
209
|
data, success = await self._request("GET", "/api/feedback/list", params=params)
|
|
210
210
|
if not success:
|
|
211
|
-
return
|
|
211
|
+
return data, False
|
|
212
212
|
return data, True
|
|
213
213
|
|
|
214
214
|
async def get_feedback_detail(self, feedback_id: int):
|
|
@@ -238,7 +238,7 @@ class FeedbackManagerAsync(AsyncClient):
|
|
|
238
238
|
"""
|
|
239
239
|
data, success = await self._request("GET", f"/api/feedback/{feedback_id}")
|
|
240
240
|
if not success:
|
|
241
|
-
return
|
|
241
|
+
return data, False
|
|
242
242
|
return data, True
|
|
243
243
|
|
|
244
244
|
async def delete_feedback(self, feedback_id: int):
|
|
@@ -253,6 +253,54 @@ class FeedbackManagerAsync(AsyncClient):
|
|
|
253
253
|
"""
|
|
254
254
|
data, success = await self._request("DELETE", f"/api/feedback/{feedback_id}")
|
|
255
255
|
if not success:
|
|
256
|
-
return
|
|
256
|
+
return data, False
|
|
257
|
+
return data, True
|
|
258
|
+
|
|
259
|
+
async def delete_feedback_batch(self, feedback_ids: List[int]):
|
|
260
|
+
"""
|
|
261
|
+
description:
|
|
262
|
+
批量删除反馈记录。仅管理员可操作。不存在的 ID 会跳过,返回实际删除数量。
|
|
263
|
+
parameters:
|
|
264
|
+
feedback_ids(List[int]): 反馈 ID 列表,最多 100 条
|
|
265
|
+
return:
|
|
266
|
+
data(dict): 成功时包含 deleted_count(int) 实际删除数量
|
|
267
|
+
success(bool): 操作是否成功
|
|
268
|
+
"""
|
|
269
|
+
if not feedback_ids:
|
|
270
|
+
return {"message": "请提供要删除的反馈 ID 列表"}, False
|
|
271
|
+
payload = {"feedback_ids": feedback_ids}
|
|
272
|
+
data, success = await self._request("POST", "/api/feedback/batch-delete", json=payload)
|
|
273
|
+
if not success:
|
|
274
|
+
return data, False
|
|
275
|
+
return data, True
|
|
276
|
+
|
|
277
|
+
async def create_ticket_link(
|
|
278
|
+
self,
|
|
279
|
+
project_id: int,
|
|
280
|
+
expires_in_hours: int = 24,
|
|
281
|
+
):
|
|
282
|
+
"""
|
|
283
|
+
description:
|
|
284
|
+
创建工单填写页链接。其它服务调用此接口后,将返回的 ticket_url 下发给用户,
|
|
285
|
+
用户打开链接即可在页面上填写并提交工单(无需登录)。链接有时效且单次有效。
|
|
286
|
+
parameters:
|
|
287
|
+
project_id(int): 项目ID
|
|
288
|
+
expires_in_hours(int, optional): 链接有效小时数,默认 24,范围 1~720
|
|
289
|
+
return:
|
|
290
|
+
data(dict): 成功时包含
|
|
291
|
+
- ticket_url(str): 工单填写页完整 URL
|
|
292
|
+
- token(str): 工单 token
|
|
293
|
+
- expires_at(str): 过期时间
|
|
294
|
+
success(bool): 操作是否成功
|
|
295
|
+
"""
|
|
296
|
+
payload = {
|
|
297
|
+
"project_id": project_id,
|
|
298
|
+
"expires_in_hours": expires_in_hours,
|
|
299
|
+
}
|
|
300
|
+
data, success = await self._request(
|
|
301
|
+
"POST", "/api/feedback/create-ticket-link", json=payload
|
|
302
|
+
)
|
|
303
|
+
if not success:
|
|
304
|
+
return data, False
|
|
257
305
|
return data, True
|
|
258
306
|
|
{pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty/order/order.py
RENAMED
|
@@ -40,7 +40,7 @@ class OrderManagerAsync(AsyncClient):
|
|
|
40
40
|
}
|
|
41
41
|
data, success = await self._request("POST", "/api/orders/create", json=data)
|
|
42
42
|
if not success:
|
|
43
|
-
return
|
|
43
|
+
return data, False
|
|
44
44
|
return data, True
|
|
45
45
|
|
|
46
46
|
async def create_order_v2(
|
|
@@ -73,7 +73,7 @@ class OrderManagerAsync(AsyncClient):
|
|
|
73
73
|
}
|
|
74
74
|
data, success = await self._request("POST", "/api/orders/create_v2", json=data)
|
|
75
75
|
if not success:
|
|
76
|
-
return
|
|
76
|
+
return data, False
|
|
77
77
|
return data, True
|
|
78
78
|
|
|
79
79
|
async def list_order(
|
|
@@ -118,7 +118,7 @@ class OrderManagerAsync(AsyncClient):
|
|
|
118
118
|
params["out_trade_no"] = out_trade_no
|
|
119
119
|
data, success = await self._request("GET", "/api/orders/list", params=params)
|
|
120
120
|
if not success:
|
|
121
|
-
return
|
|
121
|
+
return data, False
|
|
122
122
|
return data, True
|
|
123
123
|
|
|
124
124
|
async def get_order_detail(self, out_trade_no: str):
|
|
@@ -151,7 +151,7 @@ class OrderManagerAsync(AsyncClient):
|
|
|
151
151
|
"""
|
|
152
152
|
data, success = await self._request("GET", f"/api/orders/{out_trade_no}")
|
|
153
153
|
if not success:
|
|
154
|
-
return
|
|
154
|
+
return data, False
|
|
155
155
|
return data, True
|
|
156
156
|
|
|
157
157
|
async def update_order_status(self, out_trade_no: str, payment_status: str):
|
|
@@ -183,7 +183,7 @@ class OrderManagerAsync(AsyncClient):
|
|
|
183
183
|
"PUT", f"/api/orders/{out_trade_no}/status", json=data
|
|
184
184
|
)
|
|
185
185
|
if not success:
|
|
186
|
-
return
|
|
186
|
+
return data, False
|
|
187
187
|
return data, True
|
|
188
188
|
|
|
189
189
|
async def delete_order(self, out_trade_no: str):
|
|
@@ -198,7 +198,7 @@ class OrderManagerAsync(AsyncClient):
|
|
|
198
198
|
"""
|
|
199
199
|
data, success = await self._request("DELETE", f"/api/orders/{out_trade_no}")
|
|
200
200
|
if not success:
|
|
201
|
-
return
|
|
201
|
+
return data, False
|
|
202
202
|
return data, True
|
|
203
203
|
|
|
204
204
|
async def get_order_stats(self):
|
|
@@ -217,7 +217,7 @@ class OrderManagerAsync(AsyncClient):
|
|
|
217
217
|
"""
|
|
218
218
|
data, success = await self._request("GET", "/api/orders/stats/summary")
|
|
219
219
|
if not success:
|
|
220
|
-
return
|
|
220
|
+
return data, False
|
|
221
221
|
return data, True
|
|
222
222
|
|
|
223
223
|
async def generate_qr_code(self, out_trade_no: str):
|
|
@@ -236,7 +236,7 @@ class OrderManagerAsync(AsyncClient):
|
|
|
236
236
|
order_detail, success = await self.get_order_detail(out_trade_no)
|
|
237
237
|
print(order_detail)
|
|
238
238
|
if not success:
|
|
239
|
-
return
|
|
239
|
+
return order_detail, False
|
|
240
240
|
|
|
241
241
|
if order_detail.get("payment_channel") == "WECHAT":
|
|
242
242
|
url = "/api/orders/wx_pay/generate_qr_code"
|
|
@@ -255,7 +255,7 @@ class OrderManagerAsync(AsyncClient):
|
|
|
255
255
|
raise ValueError("Invalid payment channel")
|
|
256
256
|
data, success = await self._request("POST", url, json=request_data)
|
|
257
257
|
if not success:
|
|
258
|
-
return
|
|
258
|
+
return data, False
|
|
259
259
|
return data, True
|
|
260
260
|
|
|
261
261
|
async def refund_order(self, out_trade_no: str):
|
|
@@ -274,7 +274,7 @@ class OrderManagerAsync(AsyncClient):
|
|
|
274
274
|
"""
|
|
275
275
|
order_detail, success = await self.get_order_detail(out_trade_no)
|
|
276
276
|
if not success:
|
|
277
|
-
return
|
|
277
|
+
return order_detail, False
|
|
278
278
|
|
|
279
279
|
if order_detail.get("payment_channel") == "WECHAT":
|
|
280
280
|
url = "/api/orders/wx_pay/refund"
|
|
@@ -290,7 +290,7 @@ class OrderManagerAsync(AsyncClient):
|
|
|
290
290
|
raise ValueError("Invalid payment channel")
|
|
291
291
|
data, success = await self._request("POST", url, json=request_data)
|
|
292
292
|
if not success:
|
|
293
|
-
return
|
|
293
|
+
return data, False
|
|
294
294
|
return data, True
|
|
295
295
|
|
|
296
296
|
async def get_revenue_trend(
|
|
@@ -326,7 +326,7 @@ class OrderManagerAsync(AsyncClient):
|
|
|
326
326
|
params["end_date"] = end_date
|
|
327
327
|
data, success = await self._request("GET", "/api/orders/stats/revenue-trend", params=params)
|
|
328
328
|
if not success:
|
|
329
|
-
return
|
|
329
|
+
return data, False
|
|
330
330
|
return data, True
|
|
331
331
|
|
|
332
332
|
async def get_product_revenue_ranking(
|
|
@@ -363,5 +363,5 @@ class OrderManagerAsync(AsyncClient):
|
|
|
363
363
|
params["end_date"] = end_date
|
|
364
364
|
data, success = await self._request("GET", "/api/orders/stats/product-ranking", params=params)
|
|
365
365
|
if not success:
|
|
366
|
-
return
|
|
366
|
+
return data, False
|
|
367
367
|
return data, True
|
{pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty/product/product.py
RENAMED
|
@@ -56,7 +56,7 @@ class ProductManagerAsync(AsyncClient):
|
|
|
56
56
|
}
|
|
57
57
|
data, success = await self._request("POST", "/api/products/create", json=data)
|
|
58
58
|
if not success:
|
|
59
|
-
return
|
|
59
|
+
return data, False
|
|
60
60
|
return data, True
|
|
61
61
|
|
|
62
62
|
async def list_product(
|
|
@@ -96,7 +96,7 @@ class ProductManagerAsync(AsyncClient):
|
|
|
96
96
|
params["name"] = name
|
|
97
97
|
data, success = await self._request("GET", "/api/products/list", params=params)
|
|
98
98
|
if not success:
|
|
99
|
-
return
|
|
99
|
+
return data, False
|
|
100
100
|
return data, True
|
|
101
101
|
|
|
102
102
|
async def get_product_detail(self, product_id: str):
|
|
@@ -111,7 +111,7 @@ class ProductManagerAsync(AsyncClient):
|
|
|
111
111
|
"""
|
|
112
112
|
data, success = await self._request("GET", f"/api/products/{product_id}")
|
|
113
113
|
if not success:
|
|
114
|
-
return
|
|
114
|
+
return data, False
|
|
115
115
|
return data, True
|
|
116
116
|
|
|
117
117
|
async def update_product(
|
|
@@ -172,7 +172,7 @@ class ProductManagerAsync(AsyncClient):
|
|
|
172
172
|
"PUT", f"/api/products/{product_id}", json=data
|
|
173
173
|
)
|
|
174
174
|
if not success:
|
|
175
|
-
return
|
|
175
|
+
return data, False
|
|
176
176
|
return data, True
|
|
177
177
|
|
|
178
178
|
async def delete_product(self, product_id: str):
|
|
@@ -187,7 +187,7 @@ class ProductManagerAsync(AsyncClient):
|
|
|
187
187
|
"""
|
|
188
188
|
data, success = await self._request("DELETE", f"/api/products/{product_id}")
|
|
189
189
|
if not success:
|
|
190
|
-
return
|
|
190
|
+
return data, False
|
|
191
191
|
return data, True
|
|
192
192
|
|
|
193
193
|
async def get_product_categories(self):
|
|
@@ -201,5 +201,5 @@ class ProductManagerAsync(AsyncClient):
|
|
|
201
201
|
"""
|
|
202
202
|
data, success = await self._request("GET", "/api/products/categories/list")
|
|
203
203
|
if not success:
|
|
204
|
-
return
|
|
204
|
+
return data, False
|
|
205
205
|
return data, True
|
{pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty/project/project.py
RENAMED
|
@@ -23,7 +23,7 @@ class ProjectManagerAsync(AsyncClient):
|
|
|
23
23
|
}
|
|
24
24
|
data, success = await self._request("POST", "/api/projects/create", json=data)
|
|
25
25
|
if not success:
|
|
26
|
-
return
|
|
26
|
+
return data, False
|
|
27
27
|
return data, True
|
|
28
28
|
|
|
29
29
|
async def list_project(
|
|
@@ -58,7 +58,7 @@ class ProjectManagerAsync(AsyncClient):
|
|
|
58
58
|
params["name"] = name
|
|
59
59
|
data, success = await self._request("GET", "/api/projects/list", params=params)
|
|
60
60
|
if not success:
|
|
61
|
-
return
|
|
61
|
+
return data, False
|
|
62
62
|
return data, True
|
|
63
63
|
|
|
64
64
|
async def delete_project(self, project_id: int):
|
|
@@ -73,6 +73,6 @@ class ProjectManagerAsync(AsyncClient):
|
|
|
73
73
|
"""
|
|
74
74
|
data, success = await self._request("DELETE", f"/api/projects/{project_id}")
|
|
75
75
|
if not success:
|
|
76
|
-
return
|
|
76
|
+
return data, False
|
|
77
77
|
return data, True
|
|
78
78
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
临时手机号接码客户端
|
|
6
|
+
基于RapidAPI的temp-phone-number-sms实现
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from typing import Dict, Tuple
|
|
10
|
+
from ..client import AsyncClient
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TemporaryPhoneAsync(AsyncClient):
|
|
14
|
+
"""
|
|
15
|
+
临时手机号接码异步客户端类
|
|
16
|
+
提供临时手机号接码相关的API调用方法
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
async def get_number_sms(self, phone_number: str) -> Tuple[Dict, bool]:
|
|
20
|
+
"""
|
|
21
|
+
description:
|
|
22
|
+
获取手机号短信
|
|
23
|
+
parameters:
|
|
24
|
+
phone_number(str): 手机号码
|
|
25
|
+
return:
|
|
26
|
+
data(dict): 响应数据
|
|
27
|
+
success(bool): 是否成功
|
|
28
|
+
"""
|
|
29
|
+
result = await self._request(
|
|
30
|
+
"GET", "/api/temporary-phone/number-sms", params={"phone_number": phone_number}
|
|
31
|
+
)
|
|
32
|
+
return result
|
|
33
|
+
|
|
34
|
+
async def get_temp_number(self, country_code: str) -> Tuple[Dict, bool]:
|
|
35
|
+
"""
|
|
36
|
+
description:
|
|
37
|
+
获取临时手机号
|
|
38
|
+
parameters:
|
|
39
|
+
country_code(str): 国家代码(如:in, us, br等)
|
|
40
|
+
return:
|
|
41
|
+
data(dict): 响应数据
|
|
42
|
+
success(bool): 是否成功
|
|
43
|
+
"""
|
|
44
|
+
result = await self._request(
|
|
45
|
+
"GET", "/api/temporary-phone/temp-number", params={"country_code": country_code}
|
|
46
|
+
)
|
|
47
|
+
return result
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import urllib.parse
|
|
3
3
|
import webbrowser
|
|
4
|
-
from typing import Dict, Optional, Tuple
|
|
4
|
+
from typing import Any, Dict, Optional, Tuple
|
|
5
5
|
|
|
6
6
|
from pixelarraythirdparty.client import AsyncClient
|
|
7
7
|
|
|
@@ -120,7 +120,7 @@ class OAuth2Login(AsyncClient):
|
|
|
120
120
|
endpoint = self._get_endpoint("auth_url")
|
|
121
121
|
data, success = await self._request("POST", endpoint)
|
|
122
122
|
if not success:
|
|
123
|
-
return
|
|
123
|
+
return data, False
|
|
124
124
|
auth_url = data.get("auth_url")
|
|
125
125
|
if not auth_url:
|
|
126
126
|
return None, False
|
|
@@ -159,7 +159,7 @@ class OAuth2Login(AsyncClient):
|
|
|
159
159
|
|
|
160
160
|
await asyncio.sleep(interval)
|
|
161
161
|
|
|
162
|
-
return {}, False
|
|
162
|
+
return {"message": "登录超时或未完成授权"}, False
|
|
163
163
|
|
|
164
164
|
async def refresh_access_token(self, refresh_token: str) -> Tuple[Dict, bool]:
|
|
165
165
|
"""
|
|
@@ -178,7 +178,7 @@ class OAuth2Login(AsyncClient):
|
|
|
178
178
|
try:
|
|
179
179
|
endpoint = self._get_endpoint("refresh_token")
|
|
180
180
|
except ValueError:
|
|
181
|
-
return {}, False
|
|
181
|
+
return {"message": "该登录方式不支持 refresh_token"}, False
|
|
182
182
|
|
|
183
183
|
data, success = await self._request(
|
|
184
184
|
"POST",
|
|
@@ -186,7 +186,7 @@ class OAuth2Login(AsyncClient):
|
|
|
186
186
|
json={"refresh_token": refresh_token},
|
|
187
187
|
)
|
|
188
188
|
if not success:
|
|
189
|
-
return
|
|
189
|
+
return data, False
|
|
190
190
|
return data, True
|
|
191
191
|
|
|
192
192
|
|
|
@@ -230,7 +230,7 @@ class GoogleLogin(AsyncClient):
|
|
|
230
230
|
"POST", "/api/unified-login/google/auth-url"
|
|
231
231
|
)
|
|
232
232
|
if not success:
|
|
233
|
-
return
|
|
233
|
+
return data, False
|
|
234
234
|
auth_url = data.get("auth_url")
|
|
235
235
|
if not auth_url:
|
|
236
236
|
return None, False
|
|
@@ -268,7 +268,7 @@ class GoogleLogin(AsyncClient):
|
|
|
268
268
|
|
|
269
269
|
await asyncio.sleep(interval)
|
|
270
270
|
|
|
271
|
-
return {}, False
|
|
271
|
+
return {"message": "登录超时或未完成授权"}, False
|
|
272
272
|
|
|
273
273
|
async def refresh_access_token(self, refresh_token: str) -> Tuple[Dict, bool]:
|
|
274
274
|
"""
|
|
@@ -286,7 +286,7 @@ class GoogleLogin(AsyncClient):
|
|
|
286
286
|
json={"refresh_token": refresh_token},
|
|
287
287
|
)
|
|
288
288
|
if not success:
|
|
289
|
-
return
|
|
289
|
+
return data, False
|
|
290
290
|
return data, True
|
|
291
291
|
|
|
292
292
|
|
|
@@ -351,7 +351,7 @@ class WechatLogin(AsyncClient):
|
|
|
351
351
|
|
|
352
352
|
data, success = await self._request("POST", endpoint)
|
|
353
353
|
if not success:
|
|
354
|
-
return
|
|
354
|
+
return data, False
|
|
355
355
|
auth_url = data.get("auth_url")
|
|
356
356
|
if not auth_url:
|
|
357
357
|
return None, False
|
|
@@ -398,7 +398,7 @@ class WechatLogin(AsyncClient):
|
|
|
398
398
|
|
|
399
399
|
await asyncio.sleep(interval)
|
|
400
400
|
|
|
401
|
-
return {}, False
|
|
401
|
+
return {"message": "登录超时或未完成授权"}, False
|
|
402
402
|
|
|
403
403
|
|
|
404
404
|
class GitHubLogin(AsyncClient):
|
|
@@ -439,7 +439,7 @@ class GitHubLogin(AsyncClient):
|
|
|
439
439
|
"POST", "/api/unified-login/github/auth-url"
|
|
440
440
|
)
|
|
441
441
|
if not success:
|
|
442
|
-
return
|
|
442
|
+
return data, False
|
|
443
443
|
auth_url = data.get("auth_url")
|
|
444
444
|
if not auth_url:
|
|
445
445
|
return None, False
|
|
@@ -477,7 +477,7 @@ class GitHubLogin(AsyncClient):
|
|
|
477
477
|
|
|
478
478
|
await asyncio.sleep(interval)
|
|
479
479
|
|
|
480
|
-
return {}, False
|
|
480
|
+
return {"message": "登录超时或未完成授权"}, False
|
|
481
481
|
|
|
482
482
|
|
|
483
483
|
class DouyinLogin(AsyncClient):
|
|
@@ -518,7 +518,7 @@ class DouyinLogin(AsyncClient):
|
|
|
518
518
|
"POST", "/api/unified-login/douyin/auth-url"
|
|
519
519
|
)
|
|
520
520
|
if not success:
|
|
521
|
-
return
|
|
521
|
+
return data, False
|
|
522
522
|
auth_url = data.get("auth_url")
|
|
523
523
|
if not auth_url:
|
|
524
524
|
return None, False
|
|
@@ -556,7 +556,7 @@ class DouyinLogin(AsyncClient):
|
|
|
556
556
|
|
|
557
557
|
await asyncio.sleep(interval)
|
|
558
558
|
|
|
559
|
-
return {}, False
|
|
559
|
+
return {"message": "登录超时或未完成授权"}, False
|
|
560
560
|
|
|
561
561
|
|
|
562
562
|
class TiktokLogin(AsyncClient):
|
|
@@ -597,7 +597,7 @@ class TiktokLogin(AsyncClient):
|
|
|
597
597
|
"POST", "/api/unified-login/tiktok/auth-url"
|
|
598
598
|
)
|
|
599
599
|
if not success:
|
|
600
|
-
return
|
|
600
|
+
return data, False
|
|
601
601
|
auth_url = data.get("auth_url")
|
|
602
602
|
if not auth_url:
|
|
603
603
|
return None, False
|
|
@@ -635,7 +635,7 @@ class TiktokLogin(AsyncClient):
|
|
|
635
635
|
|
|
636
636
|
await asyncio.sleep(interval)
|
|
637
637
|
|
|
638
|
-
return {}, False
|
|
638
|
+
return {"message": "登录超时或未完成授权"}, False
|
|
639
639
|
|
|
640
640
|
|
|
641
641
|
class GitLabLogin(AsyncClient):
|
|
@@ -676,7 +676,7 @@ class GitLabLogin(AsyncClient):
|
|
|
676
676
|
"POST", "/api/unified-login/gitlab/auth-url"
|
|
677
677
|
)
|
|
678
678
|
if not success:
|
|
679
|
-
return
|
|
679
|
+
return data, False
|
|
680
680
|
auth_url = data.get("auth_url")
|
|
681
681
|
if not auth_url:
|
|
682
682
|
return None, False
|
|
@@ -714,7 +714,7 @@ class GitLabLogin(AsyncClient):
|
|
|
714
714
|
|
|
715
715
|
await asyncio.sleep(interval)
|
|
716
716
|
|
|
717
|
-
return {}, False
|
|
717
|
+
return {"message": "登录超时或未完成授权"}, False
|
|
718
718
|
|
|
719
719
|
async def refresh_access_token(self, refresh_token: str) -> Tuple[Dict, bool]:
|
|
720
720
|
"""
|
|
@@ -735,7 +735,7 @@ class GitLabLogin(AsyncClient):
|
|
|
735
735
|
json={"refresh_token": refresh_token},
|
|
736
736
|
)
|
|
737
737
|
if not success:
|
|
738
|
-
return
|
|
738
|
+
return data, False
|
|
739
739
|
return data, True
|
|
740
740
|
|
|
741
741
|
|
|
@@ -809,7 +809,7 @@ class SMSLogin(AsyncClient):
|
|
|
809
809
|
|
|
810
810
|
await asyncio.sleep(interval)
|
|
811
811
|
|
|
812
|
-
return {}, False
|
|
812
|
+
return {"message": "登录超时或未完成授权"}, False
|
|
813
813
|
|
|
814
814
|
async def login(
|
|
815
815
|
self, phone: str, code: str, timeout: int = 180
|
|
@@ -824,7 +824,7 @@ class SMSLogin(AsyncClient):
|
|
|
824
824
|
"""
|
|
825
825
|
success = await self.verify_code(phone, code)
|
|
826
826
|
if not success:
|
|
827
|
-
return {}, False
|
|
827
|
+
return {"message": "验证码校验失败"}, False
|
|
828
828
|
|
|
829
829
|
return await self._wait_for_login(phone, timeout)
|
|
830
830
|
|
|
@@ -899,7 +899,7 @@ class EmailLogin(AsyncClient):
|
|
|
899
899
|
|
|
900
900
|
await asyncio.sleep(interval)
|
|
901
901
|
|
|
902
|
-
return {}, False
|
|
902
|
+
return {"message": "登录超时或未完成授权"}, False
|
|
903
903
|
|
|
904
904
|
async def login(
|
|
905
905
|
self, email: str, code: str, timeout: int = 180
|
|
@@ -914,6 +914,115 @@ class EmailLogin(AsyncClient):
|
|
|
914
914
|
"""
|
|
915
915
|
success = await self.verify_code(email, code)
|
|
916
916
|
if not success:
|
|
917
|
-
return {}, False
|
|
917
|
+
return {"message": "验证码校验失败"}, False
|
|
918
918
|
|
|
919
919
|
return await self._wait_for_login(email, timeout)
|
|
920
|
+
|
|
921
|
+
|
|
922
|
+
class PasswordLogin(AsyncClient):
|
|
923
|
+
"""
|
|
924
|
+
密码登录客户端(邮箱 + 密码,统一登录外部用户表)
|
|
925
|
+
|
|
926
|
+
使用示例:
|
|
927
|
+
```
|
|
928
|
+
password_client = PasswordLogin(api_key="your_api_key")
|
|
929
|
+
# 注册/设置密码
|
|
930
|
+
success = await password_client.register(
|
|
931
|
+
user_identifier="user@example.com",
|
|
932
|
+
password="your_password",
|
|
933
|
+
display_name="可选展示名",
|
|
934
|
+
)
|
|
935
|
+
if success:
|
|
936
|
+
# 登录
|
|
937
|
+
user_info, success = await password_client.login(
|
|
938
|
+
user_identifier="user@example.com",
|
|
939
|
+
password="your_password",
|
|
940
|
+
)
|
|
941
|
+
# 修改密码
|
|
942
|
+
success = await password_client.change_password(
|
|
943
|
+
user_identifier="user@example.com",
|
|
944
|
+
old_password="old_password",
|
|
945
|
+
new_password="new_password",
|
|
946
|
+
)
|
|
947
|
+
```
|
|
948
|
+
"""
|
|
949
|
+
|
|
950
|
+
def __init__(self, api_key: str):
|
|
951
|
+
super().__init__(api_key)
|
|
952
|
+
|
|
953
|
+
async def register(
|
|
954
|
+
self,
|
|
955
|
+
user_identifier: str,
|
|
956
|
+
password: str,
|
|
957
|
+
display_name: Optional[str] = None,
|
|
958
|
+
) -> bool:
|
|
959
|
+
"""
|
|
960
|
+
注册/设置密码:将密码哈希后写入外部用户表(login_method=password)。
|
|
961
|
+
若该邮箱已存在则更新密码与展示名称。
|
|
962
|
+
|
|
963
|
+
:param user_identifier: 用户标识(邮箱)
|
|
964
|
+
:param password: 密码(6~100 位)
|
|
965
|
+
:param display_name: 展示名称,可选
|
|
966
|
+
:return: 是否成功
|
|
967
|
+
"""
|
|
968
|
+
payload: Dict[str, Any] = {
|
|
969
|
+
"user_identifier": user_identifier,
|
|
970
|
+
"password": password,
|
|
971
|
+
}
|
|
972
|
+
if display_name is not None:
|
|
973
|
+
payload["display_name"] = display_name
|
|
974
|
+
_, success = await self._request(
|
|
975
|
+
"POST",
|
|
976
|
+
"/api/unified-login/password/register",
|
|
977
|
+
json=payload,
|
|
978
|
+
)
|
|
979
|
+
return bool(success)
|
|
980
|
+
|
|
981
|
+
async def login(
|
|
982
|
+
self,
|
|
983
|
+
user_identifier: str,
|
|
984
|
+
password: str,
|
|
985
|
+
) -> Tuple[Dict, bool]:
|
|
986
|
+
"""
|
|
987
|
+
密码登录:校验邮箱与密码,成功则返回用户信息(与其它统一登录方式格式一致)。
|
|
988
|
+
|
|
989
|
+
:param user_identifier: 用户标识(邮箱)
|
|
990
|
+
:param password: 密码
|
|
991
|
+
:return: (用户信息字典, 是否成功)
|
|
992
|
+
"""
|
|
993
|
+
data, success = await self._request(
|
|
994
|
+
"POST",
|
|
995
|
+
"/api/unified-login/password/login",
|
|
996
|
+
json={
|
|
997
|
+
"user_identifier": user_identifier,
|
|
998
|
+
"password": password,
|
|
999
|
+
},
|
|
1000
|
+
)
|
|
1001
|
+
if not success:
|
|
1002
|
+
return data if isinstance(data, dict) else {"message": data}, False
|
|
1003
|
+
return data, True
|
|
1004
|
+
|
|
1005
|
+
async def change_password(
|
|
1006
|
+
self,
|
|
1007
|
+
user_identifier: str,
|
|
1008
|
+
old_password: str,
|
|
1009
|
+
new_password: str,
|
|
1010
|
+
) -> bool:
|
|
1011
|
+
"""
|
|
1012
|
+
修改密码:校验原密码后更新为新密码。
|
|
1013
|
+
|
|
1014
|
+
:param user_identifier: 用户标识(邮箱)
|
|
1015
|
+
:param old_password: 当前密码
|
|
1016
|
+
:param new_password: 新密码(6~100 位)
|
|
1017
|
+
:return: 是否成功
|
|
1018
|
+
"""
|
|
1019
|
+
_, success = await self._request(
|
|
1020
|
+
"POST",
|
|
1021
|
+
"/api/unified-login/password/change",
|
|
1022
|
+
json={
|
|
1023
|
+
"user_identifier": user_identifier,
|
|
1024
|
+
"old_password": old_password,
|
|
1025
|
+
"new_password": new_password,
|
|
1026
|
+
},
|
|
1027
|
+
)
|
|
1028
|
+
return bool(success)
|
|
@@ -34,7 +34,7 @@ class UserManagerAsync(AsyncClient):
|
|
|
34
34
|
params["is_active"] = is_active
|
|
35
35
|
data, success = await self._request("GET", "/api/users/list", params=params)
|
|
36
36
|
if not success:
|
|
37
|
-
return
|
|
37
|
+
return data, False
|
|
38
38
|
return data, True
|
|
39
39
|
|
|
40
40
|
async def create_user(self, username: str, password: str, email: str, role: str):
|
|
@@ -64,7 +64,7 @@ class UserManagerAsync(AsyncClient):
|
|
|
64
64
|
}
|
|
65
65
|
data, success = await self._request("POST", "/api/users/create", json=data)
|
|
66
66
|
if not success:
|
|
67
|
-
return
|
|
67
|
+
return data, False
|
|
68
68
|
return data, True
|
|
69
69
|
|
|
70
70
|
async def update_user(
|
|
@@ -98,7 +98,7 @@ class UserManagerAsync(AsyncClient):
|
|
|
98
98
|
}
|
|
99
99
|
data, success = await self._request("PUT", f"/api/users/{user_id}", json=data)
|
|
100
100
|
if not success:
|
|
101
|
-
return
|
|
101
|
+
return data, False
|
|
102
102
|
return data, True
|
|
103
103
|
|
|
104
104
|
async def delete_user(self, user_id: int):
|
|
@@ -113,7 +113,7 @@ class UserManagerAsync(AsyncClient):
|
|
|
113
113
|
"""
|
|
114
114
|
data, success = await self._request("DELETE", f"/api/users/{user_id}")
|
|
115
115
|
if not success:
|
|
116
|
-
return
|
|
116
|
+
return data, False
|
|
117
117
|
return data, True
|
|
118
118
|
|
|
119
119
|
async def get_user_detail(self, user_id: int):
|
|
@@ -135,7 +135,7 @@ class UserManagerAsync(AsyncClient):
|
|
|
135
135
|
"""
|
|
136
136
|
data, success = await self._request("GET", f"/api/users/{user_id}")
|
|
137
137
|
if not success:
|
|
138
|
-
return
|
|
138
|
+
return data, False
|
|
139
139
|
return data, True
|
|
140
140
|
|
|
141
141
|
async def reset_user_password(self, user_id: int, new_password: str):
|
|
@@ -154,5 +154,5 @@ class UserManagerAsync(AsyncClient):
|
|
|
154
154
|
"POST", f"/api/users/{user_id}/reset-password", json=data
|
|
155
155
|
)
|
|
156
156
|
if not success:
|
|
157
|
-
return
|
|
157
|
+
return data, False
|
|
158
158
|
return data, True
|
{pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty.egg-info/SOURCES.txt
RENAMED
|
@@ -18,6 +18,8 @@ pixelarraythirdparty/product/__init__.py
|
|
|
18
18
|
pixelarraythirdparty/product/product.py
|
|
19
19
|
pixelarraythirdparty/project/__init__.py
|
|
20
20
|
pixelarraythirdparty/project/project.py
|
|
21
|
+
pixelarraythirdparty/temporary_phone/__init__.py
|
|
22
|
+
pixelarraythirdparty/temporary_phone/temporary_phone.py
|
|
21
23
|
pixelarraythirdparty/unified_login/__init__.py
|
|
22
24
|
pixelarraythirdparty/unified_login/unified_login.py
|
|
23
25
|
pixelarraythirdparty/user/__init__.py
|
|
File without changes
|
|
File without changes
|
{pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty/cron/__init__.py
RENAMED
|
File without changes
|
{pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty/feedback/__init__.py
RENAMED
|
File without changes
|
{pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty/order/__init__.py
RENAMED
|
File without changes
|
{pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty/product/__init__.py
RENAMED
|
File without changes
|
{pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty/project/__init__.py
RENAMED
|
File without changes
|
{pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty/user/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{pixelarraythirdparty-1.2.5 → pixelarraythirdparty-1.2.7}/pixelarraythirdparty.egg-info/requires.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|