commit 6994b185a3b476255a7cd36b6baa24e4d8f27ebf Author: qiaoxinjiu Date: Thu Jan 22 19:10:37 2026 +0800 addproject diff --git a/Log/.__run.lock b/Log/.__run.lock new file mode 100644 index 0000000..e69de29 diff --git a/Log/run.log b/Log/run.log new file mode 100644 index 0000000..f72fc0e --- /dev/null +++ b/Log/run.log @@ -0,0 +1,1121 @@ +2025-12-16 17:09:32,191 [tid:17728 pid:18080] UBRD_interface.py[line:36] INFO your input:{'tenantName': '芋道源码', 'username': 'admin', 'password': ',9^e4YTCg&e4', 'rememberMe': True} +2025-12-16 17:13:05,259 [tid:19620 pid:19884] UBRD_interface.py[line:36] INFO your input:{'tenantName': '芋道源码', 'username': 'admin', 'password': ',9^e4YTCg&e4', 'rememberMe': True} +2025-12-16 17:13:16,149 [tid:20696 pid:19716] UBRD_interface.py[line:36] INFO your input:{'tenantName': '芋道源码', 'username': 'admin', 'password': ',9^e4YTCg&e4', 'rememberMe': True} +2025-12-16 17:39:05,309 [tid:19928 pid:11512] UBRD_interface.py[line:36] INFO your input:{'tenantName': '芋道源码', 'username': 'admin', 'password': ',9^e4YTCg&e4', 'rememberMe': True} +2025-12-16 17:39:30,940 [tid:3244 pid:18764] UBRD_interface.py[line:36] INFO your input:{'tenantName': '芋道源码', 'username': 'admin', 'password': ',9^e4YTCg&e4', 'rememberMe': True} +2025-12-16 17:39:36,485 [tid:3244 pid:18764] runner.py[line:271] INFO 登录系统为39.170.26.156,用户名为默认值:lrq +2025-12-16 17:39:36,536 [tid:3244 pid:18764] runner.py[line:282] INFO 请求地址:http://39.170.26.156:8380/admin-api/system/auth/login +2025-12-16 17:39:36,537 [tid:3244 pid:18764] runner.py[line:283] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'admin', 'password': ',9^e4YTCg&e4', 'rememberMe': True}} +2025-12-16 17:39:36,538 [tid:3244 pid:18764] runner.py[line:333] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'huohua-podenv': 'HHC-115008'} +2025-12-16 17:39:36,636 [tid:3244 pid:18764] runner.py[line:346] INFO ------状态码:200, 返回信息:{'code': 400, 'msg': '请求的租户标识未传递,请进行排查', 'data': None} +2025-12-16 17:39:36,637 [tid:3244 pid:18764] runner.py[line:377] INFO 返回数据:{'code': 400, 'msg': '请求的租户标识未传递,请进行排查', 'data': None} +2025-12-16 17:47:05,333 [tid:4576 pid:12124] UBRD_interface.py[line:36] INFO your input:{'tenantName': '芋道源码', 'username': 'admin', 'password': ',9^e4YTCg&e4', 'rememberMe': True} +2025-12-16 17:47:10,504 [tid:4576 pid:12124] runner.py[line:111] INFO 登录系统为39.170.26.156,用户名为手动输入:lrq +2025-12-16 17:47:10,505 [tid:4576 pid:12124] runner.py[line:112] INFO 请求地址:http://39.170.26.156:8380/admin-api/system/auth/login +2025-12-16 17:47:10,506 [tid:4576 pid:12124] runner.py[line:113] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'admin', 'password': ',9^e4YTCg&e4', 'rememberMe': True}} +2025-12-16 17:47:10,507 [tid:4576 pid:12124] runner.py[line:141] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'Basic c3BhcmtsZS13ZWI6c3BhcmtsZS13ZWI=', 'huohua-podenv': 'HHC-115008'} +2025-12-16 17:47:10,626 [tid:4576 pid:12124] runner.py[line:154] INFO ------状态码:200, 返回信息:{'code': 400, 'msg': '请求的租户标识未传递,请进行排查', 'data': None} +2025-12-16 17:47:10,630 [tid:4576 pid:12124] runner.py[line:185] INFO 返回数据:{'code': 400, 'msg': '请求的租户标识未传递,请进行排查', 'data': None} +2025-12-16 17:47:35,562 [tid:9160 pid:14508] UBRD_interface.py[line:36] INFO your input:{'tenantName': '芋道源码', 'username': 'admin', 'password': ',9^e4YTCg&e4', 'rememberMe': True} +2025-12-17 18:09:41,406 [tid:5040 pid:21772] UBRD_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'admin', 'password': ',9^e4YTCg&e4', 'rememberMe': True} +2025-12-17 18:10:02,503 [tid:5040 pid:21772] runner.py[line:111] INFO 登录系统为39.170.26.156,用户名为手动输入:admin +2025-12-17 18:10:03,308 [tid:5040 pid:21772] runner.py[line:112] INFO 请求地址:http://39.170.26.156:8380/admin-api/system/auth/login +2025-12-17 18:10:03,690 [tid:5040 pid:21772] runner.py[line:113] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'admin', 'password': ',9^e4YTCg&e4', 'rememberMe': True}} +2025-12-17 18:11:14,137 [tid:14320 pid:21952] UBRD_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'admin', 'password': ',9^e4YTCg&e4', 'rememberMe': True} +2025-12-17 18:11:17,187 [tid:14320 pid:21952] runner.py[line:111] INFO 登录系统为39.170.26.156,用户名为手动输入:admin +2025-12-17 18:11:17,189 [tid:14320 pid:21952] runner.py[line:112] INFO 请求地址:http://39.170.26.156:8380/admin-api/system/auth/login +2025-12-17 18:11:17,190 [tid:14320 pid:21952] runner.py[line:113] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'admin', 'password': ',9^e4YTCg&e4', 'rememberMe': True}} +2025-12-17 18:11:32,640 [tid:2484 pid:20220] UBRD_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'admin', 'password': ',9^e4YTCg&e4', 'rememberMe': True} +2025-12-17 18:11:34,815 [tid:2484 pid:20220] runner.py[line:111] INFO 登录系统为39.170.26.156,用户名为手动输入:admin +2025-12-17 18:11:34,816 [tid:2484 pid:20220] runner.py[line:112] INFO 请求地址:http://39.170.26.156:8380/admin-api/system/auth/login +2025-12-17 18:11:34,817 [tid:2484 pid:20220] runner.py[line:113] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'admin', 'password': ',9^e4YTCg&e4', 'rememberMe': True}} +2025-12-17 18:14:33,403 [tid:12816 pid:5112] UBRD_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'admin', 'password': ',9^e4YTCg&e4', 'rememberMe': True} +2025-12-17 18:14:37,594 [tid:12816 pid:5112] runner.py[line:111] INFO 登录系统为39.170.26.156,用户名为手动输入:admin +2025-12-17 18:14:37,595 [tid:12816 pid:5112] runner.py[line:112] INFO 请求地址:http://39.170.26.156:8380/admin-api/system/auth/login +2025-12-17 18:14:37,595 [tid:12816 pid:5112] runner.py[line:113] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'admin', 'password': ',9^e4YTCg&e4', 'rememberMe': True}} +2025-12-17 18:14:48,017 [tid:1740 pid:17960] UBRD_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'admin', 'password': ',9^e4YTCg&e4', 'rememberMe': True} +2025-12-17 18:14:55,415 [tid:1740 pid:17960] runner.py[line:111] INFO 登录系统为39.170.26.156,用户名为手动输入:admin +2025-12-17 18:14:55,416 [tid:1740 pid:17960] runner.py[line:112] INFO 请求地址:http://39.170.26.156:8380/admin-api/system/auth/login +2025-12-17 18:14:55,416 [tid:1740 pid:17960] runner.py[line:113] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'admin', 'password': ',9^e4YTCg&e4', 'rememberMe': True}} +2025-12-17 19:26:37,988 [tid:14152 pid:20468] UBRD_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'admin', 'password': ',9^e4YTCg&e4', 'rememberMe': True} +2025-12-17 19:26:37,990 [tid:14152 pid:20468] runner.py[line:111] INFO 登录系统为39.170.26.156,用户名为手动输入:admin +2025-12-17 19:26:37,991 [tid:14152 pid:20468] runner.py[line:112] INFO 请求地址:http://39.170.26.156:8380/admin-api/system/auth/login +2025-12-17 19:26:37,991 [tid:14152 pid:20468] runner.py[line:113] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'admin', 'password': ',9^e4YTCg&e4', 'rememberMe': True}} +2025-12-17 19:27:02,142 [tid:14412 pid:580] UBRD_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'admin', 'password': ',9^e4YTCg&e4', 'rememberMe': True} +2025-12-17 19:27:02,143 [tid:14412 pid:580] runner.py[line:111] INFO 登录系统为39.170.26.156,用户名为手动输入:admin +2025-12-17 19:27:02,144 [tid:14412 pid:580] runner.py[line:112] INFO 请求地址:http://39.170.26.156:8380/admin-api/system/auth/login +2025-12-17 19:27:02,145 [tid:14412 pid:580] runner.py[line:113] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'admin', 'password': ',9^e4YTCg&e4', 'rememberMe': True}} +2025-12-17 19:27:02,146 [tid:14412 pid:580] runner.py[line:137] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'Basic c3BhcmtsZS13ZWI6c3BhcmtsZS13ZWI='} +2025-12-17 19:27:02,331 [tid:14412 pid:580] runner.py[line:150] INFO ------状态码:200, 返回信息:{'code': 400, 'msg': '请求的租户标识未传递,请进行排查', 'data': None} +2025-12-17 19:27:02,333 [tid:14412 pid:580] runner.py[line:181] INFO 返回数据:{'code': 400, 'msg': '请求的租户标识未传递,请进行排查', 'data': None} +2025-12-17 19:36:29,610 [tid:6360 pid:19332] UBRD_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': True} +2025-12-17 19:36:29,611 [tid:6360 pid:19332] runner.py[line:111] INFO 登录系统为39.170.26.156,用户名为手动输入:admin +2025-12-17 19:36:29,612 [tid:6360 pid:19332] runner.py[line:112] INFO 请求地址:http://39.170.26.156:8382/admin-api/admin-api/system/auth/login +2025-12-17 19:36:29,612 [tid:6360 pid:19332] runner.py[line:113] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': True}} +2025-12-17 19:36:29,613 [tid:6360 pid:19332] runner.py[line:137] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'Basic c3BhcmtsZS13ZWI6c3BhcmtsZS13ZWI='} +2025-12-17 19:36:29,738 [tid:6360 pid:19332] runner.py[line:150] INFO ------状态码:200, 返回信息:{'code': 401, 'msg': '账号未登录', 'data': None} +2025-12-17 19:36:29,740 [tid:6360 pid:19332] runner.py[line:166] WARNING 缓存session过期,清理缓存! +2025-12-17 19:36:29,741 [tid:6360 pid:19332] runner.py[line:137] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'Basic c3BhcmtsZS13ZWI6c3BhcmtsZS13ZWI='} +2025-12-17 19:36:29,822 [tid:6360 pid:19332] runner.py[line:150] INFO ------状态码:200, 返回信息:{'code': 401, 'msg': '账号未登录', 'data': None} +2025-12-17 19:36:29,823 [tid:6360 pid:19332] runner.py[line:166] WARNING 缓存session过期,清理缓存! +2025-12-17 19:36:29,824 [tid:6360 pid:19332] runner.py[line:181] INFO 返回数据:{'code': 401, 'msg': '账号未登录', 'data': None} +2025-12-17 19:53:36,459 [tid:1156 pid:9028] UBRD_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': True} +2025-12-17 19:53:36,460 [tid:1156 pid:9028] runner.py[line:111] INFO 登录系统为39.170.26.156,用户名为手动输入:admin +2025-12-17 19:53:36,460 [tid:1156 pid:9028] runner.py[line:112] INFO 请求地址:http://39.170.26.156:8382/admin-api/admin-api/system/auth/login +2025-12-17 19:53:36,461 [tid:1156 pid:9028] runner.py[line:113] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': True}} +2025-12-17 19:53:36,462 [tid:1156 pid:9028] runner.py[line:137] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'Basic c3BhcmtsZS13ZWI6c3BhcmtsZS13ZWI=', 'tenant-id': 1} +2025-12-17 19:53:36,464 [tid:1156 pid:9028] runner.py[line:62] INFO 返回数据:Value for header {tenant-id: 1} must be of type str or bytes, not +2025-12-17 19:53:51,280 [tid:13692 pid:8320] UBRD_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': True} +2025-12-17 19:53:51,281 [tid:13692 pid:8320] runner.py[line:111] INFO 登录系统为39.170.26.156,用户名为手动输入:admin +2025-12-17 19:53:51,281 [tid:13692 pid:8320] runner.py[line:112] INFO 请求地址:http://39.170.26.156:8382/admin-api/admin-api/system/auth/login +2025-12-17 19:53:51,282 [tid:13692 pid:8320] runner.py[line:113] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': True}} +2025-12-17 19:53:51,283 [tid:13692 pid:8320] runner.py[line:137] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'Basic c3BhcmtsZS13ZWI6c3BhcmtsZS13ZWI=', 'tenant-id': '1'} +2025-12-17 19:53:51,402 [tid:13692 pid:8320] runner.py[line:150] INFO ------状态码:200, 返回信息:{'code': 401, 'msg': '账号未登录', 'data': None} +2025-12-17 19:53:51,403 [tid:13692 pid:8320] runner.py[line:166] WARNING 缓存session过期,清理缓存! +2025-12-17 19:53:51,403 [tid:13692 pid:8320] runner.py[line:137] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'Basic c3BhcmtsZS13ZWI6c3BhcmtsZS13ZWI=', 'tenant-id': '1'} +2025-12-17 19:53:51,487 [tid:13692 pid:8320] runner.py[line:150] INFO ------状态码:200, 返回信息:{'code': 401, 'msg': '账号未登录', 'data': None} +2025-12-17 19:53:51,488 [tid:13692 pid:8320] runner.py[line:166] WARNING 缓存session过期,清理缓存! +2025-12-17 19:53:51,488 [tid:13692 pid:8320] runner.py[line:181] INFO 返回数据:{'code': 401, 'msg': '账号未登录', 'data': None} +2025-12-17 19:54:08,497 [tid:20008 pid:19840] UBRD_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': True} +2025-12-17 19:54:25,888 [tid:20008 pid:19840] runner.py[line:111] INFO 登录系统为39.170.26.156,用户名为手动输入:admin +2025-12-17 19:54:26,628 [tid:20008 pid:19840] runner.py[line:112] INFO 请求地址:http://39.170.26.156:8382/admin-api/admin-api/system/auth/login +2025-12-17 19:54:27,119 [tid:20008 pid:19840] runner.py[line:113] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': True}} +2025-12-17 19:55:08,319 [tid:20008 pid:19840] runner.py[line:137] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'Basic c3BhcmtsZS13ZWI6c3BhcmtsZS13ZWI=', 'tenant-id': '1'} +2025-12-17 19:56:23,057 [tid:22220 pid:16480] UBRD_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-17 19:56:23,058 [tid:22220 pid:16480] runner.py[line:111] INFO 登录系统为39.170.26.156,用户名为手动输入:admin +2025-12-17 19:56:23,059 [tid:22220 pid:16480] runner.py[line:112] INFO 请求地址:http://39.170.26.156:8382/admin-api/admin-api/system/auth/login +2025-12-17 19:56:23,059 [tid:22220 pid:16480] runner.py[line:113] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-17 19:56:23,060 [tid:22220 pid:16480] runner.py[line:137] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'Basic c3BhcmtsZS13ZWI6c3BhcmtsZS13ZWI=', 'tenant-id': '1'} +2025-12-17 19:56:23,183 [tid:22220 pid:16480] runner.py[line:150] INFO ------状态码:200, 返回信息:{'code': 401, 'msg': '账号未登录', 'data': None} +2025-12-17 19:56:23,184 [tid:22220 pid:16480] runner.py[line:166] WARNING 缓存session过期,清理缓存! +2025-12-17 19:56:23,184 [tid:22220 pid:16480] runner.py[line:137] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'Basic c3BhcmtsZS13ZWI6c3BhcmtsZS13ZWI=', 'tenant-id': '1'} +2025-12-17 19:56:23,262 [tid:22220 pid:16480] runner.py[line:150] INFO ------状态码:200, 返回信息:{'code': 401, 'msg': '账号未登录', 'data': None} +2025-12-17 19:56:23,263 [tid:22220 pid:16480] runner.py[line:166] WARNING 缓存session过期,清理缓存! +2025-12-17 19:56:23,264 [tid:22220 pid:16480] runner.py[line:181] INFO 返回数据:{'code': 401, 'msg': '账号未登录', 'data': None} +2025-12-17 19:56:58,940 [tid:16252 pid:11964] UBRD_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-17 19:57:02,953 [tid:16252 pid:11964] runner.py[line:111] INFO 登录系统为39.170.26.156,用户名为手动输入:admin +2025-12-17 19:57:02,954 [tid:16252 pid:11964] runner.py[line:112] INFO 请求地址:http://39.170.26.156:8382/admin-api/admin-api/system/auth/login +2025-12-17 19:57:02,955 [tid:16252 pid:11964] runner.py[line:113] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-17 19:57:02,956 [tid:16252 pid:11964] runner.py[line:137] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'Basic c3BhcmtsZS13ZWI6c3BhcmtsZS13ZWI=', 'tenant-id': '1'} +2025-12-17 19:57:40,932 [tid:16348 pid:8412] UBRD_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-17 19:57:44,002 [tid:16348 pid:8412] runner.py[line:111] INFO 登录系统为39.170.26.156,用户名为手动输入:admin +2025-12-17 19:57:44,003 [tid:16348 pid:8412] runner.py[line:112] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-17 19:57:44,003 [tid:16348 pid:8412] runner.py[line:113] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-17 19:57:44,004 [tid:16348 pid:8412] runner.py[line:137] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'Basic c3BhcmtsZS13ZWI6c3BhcmtsZS13ZWI=', 'tenant-id': '1'} +2025-12-17 19:58:21,925 [tid:16348 pid:8412] runner.py[line:150] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'userId': 143, 'accessToken': '9cc11470fa104a54941ac183fca4c499', 'refreshToken': 'b7368f810f3f44a6af5dbb97bc29f1ce', 'expiresTime': 1765974467194}} +2025-12-17 19:58:29,353 [tid:16348 pid:8412] runner.py[line:181] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'userId': 143, 'accessToken': '9cc11470fa104a54941ac183fca4c499', 'refreshToken': 'b7368f810f3f44a6af5dbb97bc29f1ce', 'expiresTime': 1765974467194}} +2025-12-22 14:40:22,344 [tid:13600 pid:13664] UBRD_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 14:40:22,345 [tid:13600 pid:13664] runner.py[line:111] INFO 登录系统为39.170.26.156,用户名为手动输入:admin +2025-12-22 14:40:22,346 [tid:13600 pid:13664] runner.py[line:112] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 14:40:22,347 [tid:13600 pid:13664] runner.py[line:113] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 14:40:22,348 [tid:13600 pid:13664] runner.py[line:137] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'Basic c3BhcmtsZS13ZWI6c3BhcmtsZS13ZWI=', 'tenant-id': '1'} +2025-12-22 14:40:22,577 [tid:13600 pid:13664] runner.py[line:150] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'userId': 143, 'accessToken': 'b5155ef7745b4d688cc3f58f20e2793b', 'refreshToken': '194c8b4ff60245ffa3b8e6e72ba58276', 'expiresTime': 1766387423783}} +2025-12-22 14:40:22,578 [tid:13600 pid:13664] runner.py[line:181] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'userId': 143, 'accessToken': 'b5155ef7745b4d688cc3f58f20e2793b', 'refreshToken': '194c8b4ff60245ffa3b8e6e72ba58276', 'expiresTime': 1766387423783}} +2025-12-22 14:42:56,609 [tid:7576 pid:8076] UBRD_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 14:43:07,687 [tid:7576 pid:8076] runner.py[line:111] INFO 登录系统为39.170.26.156,用户名为手动输入:admin +2025-12-22 14:43:08,504 [tid:7576 pid:8076] runner.py[line:112] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 14:43:09,281 [tid:7576 pid:8076] runner.py[line:113] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 14:43:19,011 [tid:7576 pid:8076] runner.py[line:137] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'Basic c3BhcmtsZS13ZWI6c3BhcmtsZS13ZWI=', 'tenant-id': '1'} +2025-12-22 16:05:03,969 [tid:21436 pid:760] ZZYY_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 16:05:21,344 [tid:21436 pid:760] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:jwadmin +2025-12-22 16:05:22,065 [tid:21436 pid:760] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 16:05:22,546 [tid:21436 pid:760] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 16:06:22,429 [tid:9040 pid:11564] ZZYY_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 16:06:27,178 [tid:9040 pid:11564] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:jwadmin +2025-12-22 16:06:27,655 [tid:9040 pid:11564] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 16:06:27,917 [tid:9040 pid:11564] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 16:10:55,051 [tid:9296 pid:21388] ZZYY_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 16:11:01,328 [tid:9296 pid:21388] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 16:11:02,022 [tid:9296 pid:21388] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 16:11:03,035 [tid:9296 pid:21388] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 16:12:52,017 [tid:10960 pid:18160] ZZYY_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 16:12:56,440 [tid:10960 pid:18160] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 16:12:56,761 [tid:10960 pid:18160] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 16:12:56,945 [tid:10960 pid:18160] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 16:14:04,557 [tid:17920 pid:3276] ZZYY_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 16:14:09,762 [tid:17920 pid:3276] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 16:14:09,764 [tid:17920 pid:3276] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 16:14:09,765 [tid:17920 pid:3276] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 16:17:36,766 [tid:11296 pid:11568] ZZYY_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 16:17:38,357 [tid:11296 pid:11568] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 16:17:38,358 [tid:11296 pid:11568] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 16:17:38,359 [tid:11296 pid:11568] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 16:18:37,162 [tid:420 pid:8384] ZZYY_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 16:18:38,596 [tid:420 pid:8384] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 16:18:38,597 [tid:420 pid:8384] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 16:18:38,598 [tid:420 pid:8384] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 16:26:35,361 [tid:524 pid:14888] ZZYY_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 16:26:38,595 [tid:524 pid:14888] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 16:26:38,596 [tid:524 pid:14888] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 16:26:38,597 [tid:524 pid:14888] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 16:35:54,033 [tid:23972 pid:15712] ZZYY_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 16:35:56,123 [tid:23972 pid:15712] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 16:35:56,124 [tid:23972 pid:15712] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 16:35:56,125 [tid:23972 pid:15712] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 16:35:56,126 [tid:23972 pid:15712] runner.py[line:59] INFO req_type,输入错误!不支持请求方法post +2025-12-22 16:35:56,127 [tid:23972 pid:15712] runner.py[line:62] INFO 返回数据:req_type,输入错误!不支持请求方法post +2025-12-22 16:36:20,632 [tid:24216 pid:20920] ZZYY_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 16:36:22,704 [tid:24216 pid:20920] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 16:36:22,705 [tid:24216 pid:20920] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 16:36:22,706 [tid:24216 pid:20920] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 16:36:22,707 [tid:24216 pid:20920] runner.py[line:62] INFO 返回数据:request() got an unexpected keyword argument 'tenantName' +2025-12-22 16:37:52,111 [tid:3516 pid:23216] ZZYY_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 16:37:53,787 [tid:3516 pid:23216] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 16:37:53,789 [tid:3516 pid:23216] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 16:37:53,790 [tid:3516 pid:23216] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 16:37:54,010 [tid:3516 pid:23216] runner.py[line:62] INFO 返回数据:request() got an unexpected keyword argument 'tenantName' +2025-12-22 16:38:27,353 [tid:13316 pid:23616] ZZYY_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 16:38:29,270 [tid:13316 pid:23616] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 16:38:29,272 [tid:13316 pid:23616] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 16:38:29,273 [tid:13316 pid:23616] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 16:38:29,443 [tid:13316 pid:23616] runner.py[line:62] INFO 返回数据:request() got an unexpected keyword argument 'tenantName' +2025-12-22 16:44:23,772 [tid:22548 pid:17768] ZZYY_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 16:44:26,312 [tid:22548 pid:17768] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 16:44:26,314 [tid:22548 pid:17768] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 16:44:26,315 [tid:22548 pid:17768] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 16:44:26,482 [tid:22548 pid:17768] runner.py[line:62] INFO 返回数据:request() got an unexpected keyword argument 'tenantName' +2025-12-22 16:46:21,889 [tid:24124 pid:22988] ZZYY_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 16:46:23,687 [tid:24124 pid:22988] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 16:46:23,688 [tid:24124 pid:22988] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 16:46:23,689 [tid:24124 pid:22988] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 16:46:46,118 [tid:6420 pid:23384] ZZYY_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 16:46:47,679 [tid:6420 pid:23384] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 16:46:47,680 [tid:6420 pid:23384] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 16:46:47,681 [tid:6420 pid:23384] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 16:51:00,920 [tid:9776 pid:13568] ZZYY_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 16:51:00,921 [tid:9776 pid:13568] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 16:51:00,922 [tid:9776 pid:13568] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 16:51:00,922 [tid:9776 pid:13568] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 16:52:04,956 [tid:24152 pid:22580] ZZYY_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 16:52:05,794 [tid:24152 pid:22580] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 16:52:05,795 [tid:24152 pid:22580] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 16:52:05,796 [tid:24152 pid:22580] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 16:53:53,473 [tid:22108 pid:17612] ZZYY_interface.py[line:33] INFO your input:{'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 16:53:56,669 [tid:22108 pid:17612] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 16:53:56,671 [tid:22108 pid:17612] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 16:53:56,672 [tid:22108 pid:17612] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 16:59:26,929 [tid:19404 pid:24304] ZZYY_interface.py[line:42] INFO your input:{'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 16:59:26,930 [tid:19404 pid:24304] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 16:59:26,931 [tid:19404 pid:24304] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 16:59:26,932 [tid:19404 pid:24304] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 17:01:21,868 [tid:8288 pid:4868] ZZYY_interface.py[line:42] INFO your input:{'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 17:01:21,869 [tid:8288 pid:4868] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 17:01:21,870 [tid:8288 pid:4868] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 17:01:21,871 [tid:8288 pid:4868] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 17:01:47,345 [tid:1508 pid:6784] ZZYY_interface.py[line:42] INFO your input:{'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 17:01:47,346 [tid:1508 pid:6784] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 17:01:47,347 [tid:1508 pid:6784] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 17:01:47,348 [tid:1508 pid:6784] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '芋道源码', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 17:02:09,937 [tid:20928 pid:9452] ZZYY_interface.py[line:42] INFO your input:{'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 17:02:09,938 [tid:20928 pid:9452] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 17:02:09,939 [tid:20928 pid:9452] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 17:02:09,940 [tid:20928 pid:9452] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 17:02:41,834 [tid:16592 pid:4024] ZZYY_interface.py[line:42] INFO your input:{'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 17:02:47,740 [tid:16592 pid:4024] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 17:02:47,742 [tid:16592 pid:4024] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 17:02:47,743 [tid:16592 pid:4024] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 17:04:29,519 [tid:22568 pid:23232] ZZYY_interface.py[line:42] INFO your input:{'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 17:04:31,072 [tid:22568 pid:23232] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 17:04:31,074 [tid:22568 pid:23232] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 17:04:31,075 [tid:22568 pid:23232] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 17:06:26,452 [tid:24520 pid:14300] ZZYY_interface.py[line:42] INFO your input:{'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 17:06:28,446 [tid:24520 pid:14300] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 17:06:28,447 [tid:24520 pid:14300] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 17:06:28,448 [tid:24520 pid:14300] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 17:07:17,278 [tid:23460 pid:17568] ZZYY_interface.py[line:42] INFO your input:{'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 17:07:22,491 [tid:23460 pid:17568] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 17:07:22,492 [tid:23460 pid:17568] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 17:07:22,493 [tid:23460 pid:17568] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 17:08:35,809 [tid:4804 pid:2148] ZZYY_interface.py[line:42] INFO your input:{'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 17:08:37,318 [tid:4804 pid:2148] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 17:08:37,319 [tid:4804 pid:2148] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 17:08:37,320 [tid:4804 pid:2148] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 17:10:37,395 [tid:13148 pid:14148] ZZYY_interface.py[line:42] INFO your input:{'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 17:10:40,381 [tid:13148 pid:14148] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 17:10:40,382 [tid:13148 pid:14148] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 17:10:40,383 [tid:13148 pid:14148] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 17:11:27,191 [tid:23736 pid:22888] ZZYY_interface.py[line:42] INFO your input:{'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 17:11:27,192 [tid:23736 pid:22888] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 17:11:27,194 [tid:23736 pid:22888] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 17:11:27,194 [tid:23736 pid:22888] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 17:16:32,403 [tid:14596 pid:5984] ZZYY_interface.py[line:42] INFO your input:{'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 17:16:34,422 [tid:14596 pid:5984] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 17:16:34,423 [tid:14596 pid:5984] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 17:16:34,424 [tid:14596 pid:5984] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 17:19:23,777 [tid:23780 pid:13400] ZZYY_interface.py[line:42] INFO your input:{'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 17:19:27,533 [tid:23780 pid:13400] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 17:19:27,534 [tid:23780 pid:13400] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 17:19:27,535 [tid:23780 pid:13400] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 17:19:27,536 [tid:23780 pid:13400] runner.py[line:134] INFO 使用传入的登录接口和参数进行登录 +2025-12-22 17:19:50,201 [tid:5184 pid:22640] ZZYY_interface.py[line:42] INFO your input:{'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 17:19:51,940 [tid:5184 pid:22640] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 17:19:51,941 [tid:5184 pid:22640] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 17:19:51,942 [tid:5184 pid:22640] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 17:20:06,789 [tid:15280 pid:24316] ZZYY_interface.py[line:42] INFO your input:{'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 17:20:07,500 [tid:15280 pid:24316] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 17:20:07,501 [tid:15280 pid:24316] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 17:20:07,502 [tid:15280 pid:24316] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 17:55:50,297 [tid:23592 pid:21804] ZZYY_interface.py[line:42] INFO your input:{'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 17:55:51,244 [tid:23592 pid:21804] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 17:55:51,246 [tid:23592 pid:21804] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 17:55:51,247 [tid:23592 pid:21804] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 18:03:58,576 [tid:23416 pid:3088] ZZYY_interface.py[line:42] INFO your input:{'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 18:03:58,577 [tid:23416 pid:3088] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 18:03:58,578 [tid:23416 pid:3088] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 18:03:58,579 [tid:23416 pid:3088] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-22 18:06:04,137 [tid:18764 pid:23572] ZZYY_interface.py[line:42] INFO your input:{'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-22 18:06:04,138 [tid:18764 pid:23572] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-22 18:06:04,139 [tid:18764 pid:23572] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-22 18:06:04,140 [tid:18764 pid:23572] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-24 19:32:20,904 [tid:7176 pid:8428] ZZYY_interface.py[line:42] INFO your input:{'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-24 19:32:20,906 [tid:7176 pid:8428] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-24 19:32:20,907 [tid:7176 pid:8428] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-24 19:32:20,908 [tid:7176 pid:8428] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-24 19:32:25,553 [tid:1828 pid:20828] ZZYY_interface.py[line:42] INFO your input:{'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-24 19:32:28,824 [tid:1828 pid:20828] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-24 19:32:28,825 [tid:1828 pid:20828] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-24 19:32:28,826 [tid:1828 pid:20828] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-24 19:34:44,166 [tid:21652 pid:21356] ZZYY_interface.py[line:42] INFO your input:{'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-24 19:34:44,166 [tid:21652 pid:21356] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-24 19:34:44,167 [tid:21652 pid:21356] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-24 19:34:44,168 [tid:21652 pid:21356] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-24 19:36:51,639 [tid:22212 pid:8216] ZZYY_interface.py[line:42] INFO your input:{'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-24 19:36:51,640 [tid:22212 pid:8216] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-24 19:36:51,641 [tid:22212 pid:8216] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-24 19:36:51,642 [tid:22212 pid:8216] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-24 19:36:51,643 [tid:22212 pid:8216] runner.py[line:134] INFO 检测到 kwargs 中包含登录信息,使用传入的参数进行登录 +2025-12-24 19:38:51,924 [tid:904 pid:20728] ZZYY_interface.py[line:42] INFO your input:{'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-24 19:38:53,967 [tid:904 pid:20728] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-24 19:38:53,969 [tid:904 pid:20728] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-24 19:38:53,970 [tid:904 pid:20728] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-24 19:38:53,971 [tid:904 pid:20728] runner.py[line:134] INFO 检测到 kwargs 中包含登录信息,使用传入的参数进行登录 +2025-12-24 19:38:54,172 [tid:904 pid:20728] runner.py[line:160] INFO api_url 是登录接口,直接返回登录响应 +2025-12-24 19:39:23,087 [tid:13152 pid:22304] ZZYY_interface.py[line:42] INFO your input:{'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-24 19:39:24,377 [tid:13152 pid:22304] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-24 19:39:24,378 [tid:13152 pid:22304] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-24 19:39:24,379 [tid:13152 pid:22304] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-24 19:39:24,381 [tid:13152 pid:22304] runner.py[line:134] INFO 检测到 kwargs 中包含登录信息,使用传入的参数进行登录 +2025-12-24 19:39:39,294 [tid:13152 pid:22304] runner.py[line:160] INFO api_url 是登录接口,直接返回登录响应 +2025-12-24 19:40:01,145 [tid:9648 pid:12044] ZZYY_interface.py[line:42] INFO your input:{'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-24 19:40:04,041 [tid:9648 pid:12044] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-24 19:40:04,042 [tid:9648 pid:12044] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/system/auth/login +2025-12-24 19:40:04,043 [tid:9648 pid:12044] runner.py[line:116] INFO 请求数据:{'json': {'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'}} +2025-12-24 19:40:04,045 [tid:9648 pid:12044] runner.py[line:134] INFO 检测到 kwargs 中包含登录信息,使用传入的参数进行登录 +2025-12-24 19:40:41,949 [tid:9648 pid:12044] runner.py[line:160] INFO api_url 是登录接口,直接返回登录响应 +2025-12-24 19:48:11,938 [tid:13268 pid:17420] ZZYY_interface.py[line:44] INFO your input:{'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2025-12-24 19:48:11,939 [tid:13268 pid:17420] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2025-12-24 19:48:11,940 [tid:13268 pid:17420] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/erp/purchase-workbench/get-todo +2025-12-24 19:48:11,941 [tid:13268 pid:17420] runner.py[line:116] INFO 请求数据:{} +2025-12-24 19:48:11,942 [tid:13268 pid:17420] runner.py[line:138] INFO 使用配置文件中的登录信息进行登录 +2025-12-24 19:48:12,149 [tid:13268 pid:17420] runner.py[line:175] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'a56bcc80c39e448687fbaaa50d538785', 'tenant-id': '1', 'ssotoken': 'a56bcc80c39e448687fbaaa50d538785', 'sso-token': 'a56bcc80c39e448687fbaaa50d538785', 'Accesstoken': 'a56bcc80c39e448687fbaaa50d538785', 'access-token': 'a56bcc80c39e448687fbaaa50d538785', 'token': 'a56bcc80c39e448687fbaaa50d538785'} +2025-12-24 19:48:12,251 [tid:13268 pid:17420] runner.py[line:193] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 652, 'businessSn': 'PO251223116', 'todoTime': 1766576313502, 'content': '包含SKU:0个,总数:960', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 650, 'businessSn': 'PO251223120', 'todoTime': 1766576313502, 'content': '包含SKU:1个,总数:167', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 649, 'businessSn': 'PO251223121', 'todoTime': 1766576313502, 'content': '包含SKU:1个,总数:540', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 651, 'businessSn': 'PO251223118', 'todoTime': 1766576313502, 'content': '包含SKU:1个,总数:468', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 653, 'businessSn': 'PO251223113', 'todoTime': 1766576313502, 'content': '包含SKU:2个,总数:6200', 'redundancy': None}], 'inProcessTask': []}} +2025-12-24 19:48:12,252 [tid:13268 pid:17420] runner.py[line:228] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 652, 'businessSn': 'PO251223116', 'todoTime': 1766576313502, 'content': '包含SKU:0个,总数:960', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 650, 'businessSn': 'PO251223120', 'todoTime': 1766576313502, 'content': '包含SKU:1个,总数:167', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 649, 'businessSn': 'PO251223121', 'todoTime': 1766576313502, 'content': '包含SKU:1个,总数:540', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 651, 'businessSn': 'PO251223118', 'todoTime': 1766576313502, 'content': '包含SKU:1个,总数:468', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 653, 'businessSn': 'PO251223113', 'todoTime': 1766576313502, 'content': '包含SKU:2个,总数:6200', 'redundancy': None}], 'inProcessTask': []}} +2026-01-09 11:52:47,141 [tid:13472 pid:20208] ZZYY_interface.py[line:44] INFO your input:{'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2026-01-09 11:52:47,143 [tid:13472 pid:20208] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2026-01-09 11:52:47,144 [tid:13472 pid:20208] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/erp/purchase-workbench/get-todo +2026-01-09 11:52:47,144 [tid:13472 pid:20208] runner.py[line:116] INFO 请求数据:{} +2026-01-09 11:52:47,145 [tid:13472 pid:20208] runner.py[line:138] INFO 使用配置文件中的登录信息进行登录 +2026-01-09 11:52:47,368 [tid:13472 pid:20208] runner.py[line:173] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '2cc8968c872441e4b51bd74f68e9892d', 'tenant-id': '1', 'ssotoken': '2cc8968c872441e4b51bd74f68e9892d', 'sso-token': '2cc8968c872441e4b51bd74f68e9892d', 'Accesstoken': '2cc8968c872441e4b51bd74f68e9892d', 'access-token': '2cc8968c872441e4b51bd74f68e9892d', 'token': '2cc8968c872441e4b51bd74f68e9892d'} +2026-01-09 11:52:47,498 [tid:13472 pid:20208] runner.py[line:189] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [], 'inProcessTask': []}} +2026-01-09 11:52:47,499 [tid:13472 pid:20208] runner.py[line:222] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [], 'inProcessTask': []}} +2026-01-09 11:59:12,330 [tid:8992 pid:8460] ZZYY_interface.py[line:44] INFO your input:{'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2026-01-09 11:59:12,331 [tid:8992 pid:8460] runner.py[line:114] INFO 登录系统为39.170.26.156,用户名为手动输入:purchase +2026-01-09 11:59:12,332 [tid:8992 pid:8460] runner.py[line:115] INFO 请求地址:http://39.170.26.156:8382/admin-api/erp/purchase-workbench/get-todo +2026-01-09 11:59:12,333 [tid:8992 pid:8460] runner.py[line:116] INFO 请求数据:{} +2026-01-09 11:59:12,334 [tid:8992 pid:8460] runner.py[line:138] INFO 使用配置文件中的登录信息进行登录 +2026-01-09 11:59:12,703 [tid:8992 pid:8460] runner.py[line:173] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '44f25b0909864ae297d59067eef543b1', 'tenant-id': '1', 'ssotoken': '44f25b0909864ae297d59067eef543b1', 'sso-token': '44f25b0909864ae297d59067eef543b1', 'Accesstoken': '44f25b0909864ae297d59067eef543b1', 'access-token': '44f25b0909864ae297d59067eef543b1', 'token': '44f25b0909864ae297d59067eef543b1'} +2026-01-09 11:59:12,879 [tid:8992 pid:8460] runner.py[line:189] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 9576, 'businessSn': 'PO251224010', 'todoTime': 1766562213000, 'content': '包含SKU:1个,总数:400', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 9577, 'businessSn': 'PO251224009', 'todoTime': 1766562213000, 'content': '包含SKU:2个,总数:2800', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 10209, 'businessSn': 'PO251223072', 'todoTime': 1766469714000, 'content': '包含SKU:1个,总数:383', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 10207, 'businessSn': 'PO251223073', 'todoTime': 1766469714000, 'content': '包含SKU:1个,总数:6500', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 10212, 'businessSn': 'PO251223071', 'todoTime': 1766469714000, 'content': '包含SKU:1个,总数:600', 'redundancy': None}], 'inProcessTask': []}} +2026-01-09 11:59:12,880 [tid:8992 pid:8460] runner.py[line:222] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 9576, 'businessSn': 'PO251224010', 'todoTime': 1766562213000, 'content': '包含SKU:1个,总数:400', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 9577, 'businessSn': 'PO251224009', 'todoTime': 1766562213000, 'content': '包含SKU:2个,总数:2800', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 10209, 'businessSn': 'PO251223072', 'todoTime': 1766469714000, 'content': '包含SKU:1个,总数:383', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 10207, 'businessSn': 'PO251223073', 'todoTime': 1766469714000, 'content': '包含SKU:1个,总数:6500', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 10212, 'businessSn': 'PO251223071', 'todoTime': 1766469714000, 'content': '包含SKU:1个,总数:600', 'redundancy': None}], 'inProcessTask': []}} +2026-01-15 18:23:40,089 [tid:14312 pid:22856] ZZYY_interface.py[line:46] INFO your input:{'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2026-01-15 18:23:40,094 [tid:14312 pid:22856] runner.py[line:114] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-15 18:23:40,095 [tid:14312 pid:22856] runner.py[line:115] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-15 18:23:40,095 [tid:14312 pid:22856] runner.py[line:116] INFO 请求数据:{} +2026-01-15 18:23:40,096 [tid:14312 pid:22856] runner.py[line:138] INFO 使用配置文件中的登录信息进行登录 +2026-01-15 19:48:50,186 [tid:21316 pid:7960] ZZYY_interface.py[line:46] INFO your input:{'tenantName': '境内组织', 'username': 'sunliping01', 'password': '1qaz@WSX', 'rememberMe': 'true'} +2026-01-15 19:48:50,188 [tid:21316 pid:7960] runner.py[line:114] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-15 19:48:50,189 [tid:21316 pid:7960] runner.py[line:115] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-15 19:48:50,190 [tid:21316 pid:7960] runner.py[line:116] INFO 请求数据:{} +2026-01-15 19:48:50,191 [tid:21316 pid:7960] runner.py[line:138] INFO 使用配置文件中的登录信息进行登录 +2026-01-15 19:48:50,622 [tid:21316 pid:7960] runner.py[line:173] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'a89d9379040b455191a0d17e7c3c22f9', 'tenant-id': '1', 'ssotoken': 'a89d9379040b455191a0d17e7c3c22f9', 'sso-token': 'a89d9379040b455191a0d17e7c3c22f9', 'Accesstoken': 'a89d9379040b455191a0d17e7c3c22f9', 'access-token': 'a89d9379040b455191a0d17e7c3c22f9', 'token': 'a89d9379040b455191a0d17e7c3c22f9'} +2026-01-15 19:48:50,810 [tid:21316 pid:7960] runner.py[line:189] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 11247, 'businessSn': 'PO260114003', 'todoTime': 1768358443000, 'content': '包含SKU:1个,总数:168', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 11244, 'businessSn': 'PO251230022', 'todoTime': 1768355952000, 'content': '包含SKU:6个,总数:1698', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 10354, 'businessSn': 'PO251224009', 'todoTime': 1766562213000, 'content': '包含SKU:2个,总数:2800', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 10353, 'businessSn': 'PO251224010', 'todoTime': 1766562213000, 'content': '包含SKU:1个,总数:400', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 11152, 'businessSn': 'PO251223072', 'todoTime': 1766469714000, 'content': '包含SKU:1个,总数:383', 'redundancy': None}], 'inProcessTask': []}} +2026-01-15 19:48:50,811 [tid:21316 pid:7960] runner.py[line:222] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 11247, 'businessSn': 'PO260114003', 'todoTime': 1768358443000, 'content': '包含SKU:1个,总数:168', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 11244, 'businessSn': 'PO251230022', 'todoTime': 1768355952000, 'content': '包含SKU:6个,总数:1698', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 10354, 'businessSn': 'PO251224009', 'todoTime': 1766562213000, 'content': '包含SKU:2个,总数:2800', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 10353, 'businessSn': 'PO251224010', 'todoTime': 1766562213000, 'content': '包含SKU:1个,总数:400', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 11152, 'businessSn': 'PO251223072', 'todoTime': 1766469714000, 'content': '包含SKU:1个,总数:383', 'redundancy': None}], 'inProcessTask': []}} +2026-01-15 19:54:42,448 [tid:17128 pid:21592] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-15 19:54:42,449 [tid:17128 pid:21592] runner.py[line:114] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-15 19:54:42,450 [tid:17128 pid:21592] runner.py[line:115] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-15 19:54:42,450 [tid:17128 pid:21592] runner.py[line:116] INFO 请求数据:{} +2026-01-15 19:54:42,451 [tid:17128 pid:21592] runner.py[line:138] INFO 使用配置文件中的登录信息进行登录 +2026-01-15 19:54:42,771 [tid:17128 pid:21592] runner.py[line:173] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '6729224e76d64c54a9e70bc3049109eb', 'tenant-id': '1', 'ssotoken': '6729224e76d64c54a9e70bc3049109eb', 'sso-token': '6729224e76d64c54a9e70bc3049109eb', 'Accesstoken': '6729224e76d64c54a9e70bc3049109eb', 'access-token': '6729224e76d64c54a9e70bc3049109eb', 'token': '6729224e76d64c54a9e70bc3049109eb'} +2026-01-15 19:54:42,944 [tid:17128 pid:21592] runner.py[line:189] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 11247, 'businessSn': 'PO260114003', 'todoTime': 1768358443000, 'content': '包含SKU:1个,总数:168', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 11244, 'businessSn': 'PO251230022', 'todoTime': 1768355952000, 'content': '包含SKU:6个,总数:1698', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 10354, 'businessSn': 'PO251224009', 'todoTime': 1766562213000, 'content': '包含SKU:2个,总数:2800', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 10353, 'businessSn': 'PO251224010', 'todoTime': 1766562213000, 'content': '包含SKU:1个,总数:400', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 11152, 'businessSn': 'PO251223072', 'todoTime': 1766469714000, 'content': '包含SKU:1个,总数:383', 'redundancy': None}], 'inProcessTask': []}} +2026-01-15 19:54:42,945 [tid:17128 pid:21592] runner.py[line:222] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 11247, 'businessSn': 'PO260114003', 'todoTime': 1768358443000, 'content': '包含SKU:1个,总数:168', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 11244, 'businessSn': 'PO251230022', 'todoTime': 1768355952000, 'content': '包含SKU:6个,总数:1698', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 10354, 'businessSn': 'PO251224009', 'todoTime': 1766562213000, 'content': '包含SKU:2个,总数:2800', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 10353, 'businessSn': 'PO251224010', 'todoTime': 1766562213000, 'content': '包含SKU:1个,总数:400', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 11152, 'businessSn': 'PO251223072', 'todoTime': 1766469714000, 'content': '包含SKU:1个,总数:383', 'redundancy': None}], 'inProcessTask': []}} +2026-01-17 08:54:53,166 [tid:15708 pid:1344] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-17 08:54:53,167 [tid:15708 pid:1344] runner.py[line:114] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:SZPurchase +2026-01-17 08:54:53,168 [tid:15708 pid:1344] runner.py[line:115] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/SZPurchase-workbench/get-todo +2026-01-17 08:54:53,169 [tid:15708 pid:1344] runner.py[line:116] INFO 请求数据:{} +2026-01-17 09:46:39,836 [tid:20032 pid:5052] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-17 09:46:39,837 [tid:20032 pid:5052] runner.py[line:114] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-17 09:46:39,838 [tid:20032 pid:5052] runner.py[line:115] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/SZPurchase-workbench/get-todo +2026-01-17 09:46:39,838 [tid:20032 pid:5052] runner.py[line:116] INFO 请求数据:{} +2026-01-17 09:46:39,839 [tid:20032 pid:5052] runner.py[line:138] INFO 使用配置文件中的登录信息进行登录 +2026-01-17 09:46:40,347 [tid:20032 pid:5052] runner.py[line:173] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'ee235fdef6be498e8eabfd65f687d6cd', 'tenant-id': '1', 'ssotoken': 'ee235fdef6be498e8eabfd65f687d6cd', 'sso-token': 'ee235fdef6be498e8eabfd65f687d6cd', 'Accesstoken': 'ee235fdef6be498e8eabfd65f687d6cd', 'access-token': 'ee235fdef6be498e8eabfd65f687d6cd', 'token': 'ee235fdef6be498e8eabfd65f687d6cd'} +2026-01-17 09:46:40,478 [tid:20032 pid:5052] runner.py[line:189] INFO ------状态码:200, 返回信息:{'code': 501, 'msg': '[ERP 模块 yudao-module-erp - 已禁用][参考 https://doc.iocoder.cn/erp/build/ 开启]', 'data': None} +2026-01-17 09:46:40,481 [tid:20032 pid:5052] runner.py[line:222] INFO 返回数据:{'code': 501, 'msg': '[ERP 模块 yudao-module-erp - 已禁用][参考 https://doc.iocoder.cn/erp/build/ 开启]', 'data': None} +2026-01-17 09:47:35,530 [tid:16640 pid:8092] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-17 09:47:35,531 [tid:16640 pid:8092] runner.py[line:114] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-17 09:47:35,531 [tid:16640 pid:8092] runner.py[line:115] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/SZPurchase-workbench/get-todo +2026-01-17 09:47:35,532 [tid:16640 pid:8092] runner.py[line:116] INFO 请求数据:{} +2026-01-17 09:47:35,533 [tid:16640 pid:8092] runner.py[line:138] INFO 使用配置文件中的登录信息进行登录 +2026-01-17 09:47:35,831 [tid:16640 pid:8092] runner.py[line:173] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'db53ac12cf5a4f4da390d67dea4d7983', 'tenant-id': '1', 'ssotoken': 'db53ac12cf5a4f4da390d67dea4d7983', 'sso-token': 'db53ac12cf5a4f4da390d67dea4d7983', 'Accesstoken': 'db53ac12cf5a4f4da390d67dea4d7983', 'access-token': 'db53ac12cf5a4f4da390d67dea4d7983', 'token': 'db53ac12cf5a4f4da390d67dea4d7983'} +2026-01-17 09:47:35,947 [tid:16640 pid:8092] runner.py[line:189] INFO ------状态码:200, 返回信息:{'code': 501, 'msg': '[ERP 模块 yudao-module-erp - 已禁用][参考 https://doc.iocoder.cn/erp/build/ 开启]', 'data': None} +2026-01-17 09:47:35,950 [tid:16640 pid:8092] runner.py[line:222] INFO 返回数据:{'code': 501, 'msg': '[ERP 模块 yudao-module-erp - 已禁用][参考 https://doc.iocoder.cn/erp/build/ 开启]', 'data': None} +2026-01-17 09:47:54,881 [tid:16776 pid:11692] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-17 09:48:00,203 [tid:16776 pid:11692] runner.py[line:114] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-17 09:48:00,900 [tid:16776 pid:11692] runner.py[line:115] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/SZPurchase-workbench/get-todo +2026-01-17 09:48:01,337 [tid:16776 pid:11692] runner.py[line:116] INFO 请求数据:{} +2026-01-17 09:48:17,532 [tid:16776 pid:11692] runner.py[line:138] INFO 使用配置文件中的登录信息进行登录 +2026-01-17 09:48:49,729 [tid:16776 pid:11692] runner.py[line:173] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '5fec57c5bf6f4920997399e6b4f34bdb', 'tenant-id': '1', 'ssotoken': '5fec57c5bf6f4920997399e6b4f34bdb', 'sso-token': '5fec57c5bf6f4920997399e6b4f34bdb', 'Accesstoken': '5fec57c5bf6f4920997399e6b4f34bdb', 'access-token': '5fec57c5bf6f4920997399e6b4f34bdb', 'token': '5fec57c5bf6f4920997399e6b4f34bdb'} +2026-01-17 09:48:53,924 [tid:16776 pid:11692] runner.py[line:189] INFO ------状态码:200, 返回信息:{'code': 501, 'msg': '[ERP 模块 yudao-module-erp - 已禁用][参考 https://doc.iocoder.cn/erp/build/ 开启]', 'data': None} +2026-01-17 09:48:59,545 [tid:16776 pid:11692] runner.py[line:222] INFO 返回数据:{'code': 501, 'msg': '[ERP 模块 yudao-module-erp - 已禁用][参考 https://doc.iocoder.cn/erp/build/ 开启]', 'data': None} +2026-01-17 09:49:49,819 [tid:3848 pid:15264] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-17 09:49:49,820 [tid:3848 pid:15264] runner.py[line:114] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-17 09:49:49,824 [tid:3848 pid:15264] runner.py[line:115] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-17 09:49:49,825 [tid:3848 pid:15264] runner.py[line:116] INFO 请求数据:{} +2026-01-17 09:49:49,826 [tid:3848 pid:15264] runner.py[line:138] INFO 使用配置文件中的登录信息进行登录 +2026-01-17 09:49:50,128 [tid:3848 pid:15264] runner.py[line:173] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '031434fe23054215a3227e2aca61fcff', 'tenant-id': '1', 'ssotoken': '031434fe23054215a3227e2aca61fcff', 'sso-token': '031434fe23054215a3227e2aca61fcff', 'Accesstoken': '031434fe23054215a3227e2aca61fcff', 'access-token': '031434fe23054215a3227e2aca61fcff', 'token': '031434fe23054215a3227e2aca61fcff'} +2026-01-17 09:49:50,312 [tid:3848 pid:15264] runner.py[line:189] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 11259, 'businessSn': 'PO260116003', 'todoTime': 1768535404000, 'content': '包含SKU:1个,总数:3000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 11251, 'businessSn': 'PO260115010', 'todoTime': 1768471468000, 'content': '包含SKU:2个,总数:304', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 11249, 'businessSn': 'PO260115011', 'todoTime': 1768471467000, 'content': '包含SKU:1个,总数:1000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 11247, 'businessSn': 'PO260114003', 'todoTime': 1768358443000, 'content': '包含SKU:1个,总数:168', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 11244, 'businessSn': 'PO251230022', 'todoTime': 1768355952000, 'content': '包含SKU:6个,总数:1698', 'redundancy': None}], 'inProcessTask': []}} +2026-01-17 09:49:50,313 [tid:3848 pid:15264] runner.py[line:222] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 11259, 'businessSn': 'PO260116003', 'todoTime': 1768535404000, 'content': '包含SKU:1个,总数:3000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 11251, 'businessSn': 'PO260115010', 'todoTime': 1768471468000, 'content': '包含SKU:2个,总数:304', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 11249, 'businessSn': 'PO260115011', 'todoTime': 1768471467000, 'content': '包含SKU:1个,总数:1000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 11247, 'businessSn': 'PO260114003', 'todoTime': 1768358443000, 'content': '包含SKU:1个,总数:168', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 11244, 'businessSn': 'PO251230022', 'todoTime': 1768355952000, 'content': '包含SKU:6个,总数:1698', 'redundancy': None}], 'inProcessTask': []}} +2026-01-17 10:03:08,133 [tid:10964 pid:14440] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-17 10:03:08,134 [tid:10964 pid:14440] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-17 10:03:08,134 [tid:10964 pid:14440] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-17 10:03:08,135 [tid:10964 pid:14440] runner.py[line:117] INFO 请求数据:{} +2026-01-17 10:03:08,136 [tid:10964 pid:14440] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-17 10:03:08,678 [tid:10964 pid:14440] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'fda7a9ddf93244609bd1711f3f54a9c6', 'tenant-id': '1', 'ssotoken': 'fda7a9ddf93244609bd1711f3f54a9c6', 'sso-token': 'fda7a9ddf93244609bd1711f3f54a9c6', 'Accesstoken': 'fda7a9ddf93244609bd1711f3f54a9c6', 'access-token': 'fda7a9ddf93244609bd1711f3f54a9c6', 'token': 'fda7a9ddf93244609bd1711f3f54a9c6'} +2026-01-17 10:03:09,116 [tid:10964 pid:14440] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 11259, 'businessSn': 'PO260116003', 'todoTime': 1768535404000, 'content': '包含SKU:1个,总数:3000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 11251, 'businessSn': 'PO260115010', 'todoTime': 1768471468000, 'content': '包含SKU:2个,总数:304', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 11249, 'businessSn': 'PO260115011', 'todoTime': 1768471467000, 'content': '包含SKU:1个,总数:1000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 11247, 'businessSn': 'PO260114003', 'todoTime': 1768358443000, 'content': '包含SKU:1个,总数:168', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 11244, 'businessSn': 'PO251230022', 'todoTime': 1768355952000, 'content': '包含SKU:6个,总数:1698', 'redundancy': None}], 'inProcessTask': []}} +2026-01-17 10:03:09,116 [tid:10964 pid:14440] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 11259, 'businessSn': 'PO260116003', 'todoTime': 1768535404000, 'content': '包含SKU:1个,总数:3000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 11251, 'businessSn': 'PO260115010', 'todoTime': 1768471468000, 'content': '包含SKU:2个,总数:304', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 11249, 'businessSn': 'PO260115011', 'todoTime': 1768471467000, 'content': '包含SKU:1个,总数:1000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 11247, 'businessSn': 'PO260114003', 'todoTime': 1768358443000, 'content': '包含SKU:1个,总数:168', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 11244, 'businessSn': 'PO251230022', 'todoTime': 1768355952000, 'content': '包含SKU:6个,总数:1698', 'redundancy': None}], 'inProcessTask': []}} +2026-01-17 17:30:43,024 [tid:23116 pid:2140] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-17 17:30:43,026 [tid:23116 pid:2140] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-17 17:30:43,026 [tid:23116 pid:2140] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-17 17:30:43,027 [tid:23116 pid:2140] runner.py[line:117] INFO 请求数据:{} +2026-01-17 17:30:43,028 [tid:23116 pid:2140] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-17 17:30:43,131 [tid:23116 pid:2140] runner.py[line:63] INFO 返回数据:HTTPSConnectionPool(host='smart-management-api-dev.best-envision.com', port=443): Max retries exceeded with url: /admin-api/system/auth/login (Caused by ProxyError('Cannot connect to proxy.', FileNotFoundError(2, 'No such file or directory'))) +2026-01-17 17:31:16,351 [tid:26252 pid:18756] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-17 17:31:16,352 [tid:26252 pid:18756] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-17 17:31:16,353 [tid:26252 pid:18756] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-17 17:31:16,354 [tid:26252 pid:18756] runner.py[line:117] INFO 请求数据:{} +2026-01-17 17:31:16,354 [tid:26252 pid:18756] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-17 17:31:16,435 [tid:26252 pid:18756] runner.py[line:63] INFO 返回数据:HTTPSConnectionPool(host='smart-management-api-dev.best-envision.com', port=443): Max retries exceeded with url: /admin-api/system/auth/login (Caused by ProxyError('Cannot connect to proxy.', FileNotFoundError(2, 'No such file or directory'))) +2026-01-17 17:32:25,552 [tid:15296 pid:23608] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-17 17:32:25,558 [tid:15296 pid:23608] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-17 17:32:25,559 [tid:15296 pid:23608] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-17 17:32:25,560 [tid:15296 pid:23608] runner.py[line:117] INFO 请求数据:{} +2026-01-17 17:32:34,212 [tid:15296 pid:23608] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-17 17:32:36,144 [tid:15296 pid:23608] runner.py[line:63] INFO 返回数据:HTTPSConnectionPool(host='smart-management-api-dev.best-envision.com', port=443): Max retries exceeded with url: /admin-api/system/auth/login (Caused by ProxyError('Cannot connect to proxy.', FileNotFoundError(2, 'No such file or directory'))) +2026-01-17 17:33:45,714 [tid:20736 pid:18988] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-17 17:33:45,718 [tid:20736 pid:18988] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-17 17:33:45,718 [tid:20736 pid:18988] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-17 17:33:45,719 [tid:20736 pid:18988] runner.py[line:117] INFO 请求数据:{} +2026-01-17 17:33:51,118 [tid:20736 pid:18988] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-17 17:33:58,218 [tid:20736 pid:18988] runner.py[line:63] INFO 返回数据:HTTPSConnectionPool(host='smart-management-api-dev.best-envision.com', port=443): Max retries exceeded with url: /admin-api/system/auth/login (Caused by ProxyError('Cannot connect to proxy.', FileNotFoundError(2, 'No such file or directory'))) +2026-01-17 17:43:17,374 [tid:23888 pid:8036] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-17 17:43:17,377 [tid:23888 pid:8036] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-17 17:43:17,378 [tid:23888 pid:8036] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-17 17:43:17,379 [tid:23888 pid:8036] runner.py[line:117] INFO 请求数据:{} +2026-01-17 17:43:17,381 [tid:23888 pid:8036] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-17 17:43:19,906 [tid:23888 pid:8036] runner.py[line:63] INFO 返回数据:request() got an unexpected keyword argument 'tenantName' +2026-01-17 17:44:23,837 [tid:10060 pid:25512] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-17 17:44:23,840 [tid:10060 pid:25512] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-17 17:44:23,841 [tid:10060 pid:25512] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-17 17:44:23,842 [tid:10060 pid:25512] runner.py[line:117] INFO 请求数据:{} +2026-01-17 17:44:23,843 [tid:10060 pid:25512] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-17 17:44:26,277 [tid:10060 pid:25512] runner.py[line:63] INFO 返回数据:HTTPSConnectionPool(host='smart-management-api-dev.best-envision.com', port=443): Max retries exceeded with url: /admin-api/system/auth/login (Caused by ProxyError('Cannot connect to proxy.', FileNotFoundError(2, 'No such file or directory'))) +2026-01-17 17:50:58,093 [tid:24488 pid:23888] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-17 17:50:58,094 [tid:24488 pid:23888] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-17 17:50:58,094 [tid:24488 pid:23888] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-17 17:50:58,095 [tid:24488 pid:23888] runner.py[line:117] INFO 请求数据:{} +2026-01-17 17:50:58,096 [tid:24488 pid:23888] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-17 17:50:58,182 [tid:24488 pid:23888] runner.py[line:63] INFO 返回数据:HTTPSConnectionPool(host='smart-management-api-dev.best-envision.com', port=443): Max retries exceeded with url: /admin-api/system/auth/login (Caused by ProxyError('Cannot connect to proxy.', FileNotFoundError(2, 'No such file or directory'))) +2026-01-17 17:51:30,946 [tid:20180 pid:7320] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-17 17:51:30,947 [tid:20180 pid:7320] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-17 17:51:30,948 [tid:20180 pid:7320] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-17 17:51:30,949 [tid:20180 pid:7320] runner.py[line:117] INFO 请求数据:{} +2026-01-17 17:51:30,950 [tid:20180 pid:7320] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-17 17:51:31,351 [tid:20180 pid:7320] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'd3dc77eb40794931b08f2bdc1c3a6602', 'tenant-id': '1', 'ssotoken': 'd3dc77eb40794931b08f2bdc1c3a6602', 'sso-token': 'd3dc77eb40794931b08f2bdc1c3a6602', 'Accesstoken': 'd3dc77eb40794931b08f2bdc1c3a6602', 'access-token': 'd3dc77eb40794931b08f2bdc1c3a6602', 'token': 'd3dc77eb40794931b08f2bdc1c3a6602'} +2026-01-17 17:51:31,527 [tid:20180 pid:7320] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 27598, 'businessSn': 'PO260115010', 'todoTime': 1768564784000, 'content': '包含SKU:2个,总数:304', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27777, 'businessSn': 'PO260116003', 'todoTime': 1768535404000, 'content': '包含SKU:1个,总数:3000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27602, 'businessSn': 'PO260113069', 'todoTime': 1768476015000, 'content': '包含SKU:1个,总数:300', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27597, 'businessSn': 'PO260115011', 'todoTime': 1768471467000, 'content': '包含SKU:1个,总数:1000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27556, 'businessSn': 'PO260114003', 'todoTime': 1768358443000, 'content': '包含SKU:1个,总数:168', 'redundancy': None}], 'inProcessTask': []}} +2026-01-17 17:51:31,528 [tid:20180 pid:7320] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 27598, 'businessSn': 'PO260115010', 'todoTime': 1768564784000, 'content': '包含SKU:2个,总数:304', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27777, 'businessSn': 'PO260116003', 'todoTime': 1768535404000, 'content': '包含SKU:1个,总数:3000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27602, 'businessSn': 'PO260113069', 'todoTime': 1768476015000, 'content': '包含SKU:1个,总数:300', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27597, 'businessSn': 'PO260115011', 'todoTime': 1768471467000, 'content': '包含SKU:1个,总数:1000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27556, 'businessSn': 'PO260114003', 'todoTime': 1768358443000, 'content': '包含SKU:1个,总数:168', 'redundancy': None}], 'inProcessTask': []}} +2026-01-17 17:53:24,634 [tid:25660 pid:26620] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-17 17:53:24,635 [tid:25660 pid:26620] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-17 17:53:24,636 [tid:25660 pid:26620] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-17 17:53:24,636 [tid:25660 pid:26620] runner.py[line:117] INFO 请求数据:{} +2026-01-17 17:53:24,637 [tid:25660 pid:26620] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-17 17:53:24,953 [tid:25660 pid:26620] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '95142b344d874f3b9326cb7bd6d5315f', 'tenant-id': '1', 'ssotoken': '95142b344d874f3b9326cb7bd6d5315f', 'sso-token': '95142b344d874f3b9326cb7bd6d5315f', 'Accesstoken': '95142b344d874f3b9326cb7bd6d5315f', 'access-token': '95142b344d874f3b9326cb7bd6d5315f', 'token': '95142b344d874f3b9326cb7bd6d5315f'} +2026-01-17 17:53:25,138 [tid:25660 pid:26620] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 27598, 'businessSn': 'PO260115010', 'todoTime': 1768564784000, 'content': '包含SKU:2个,总数:304', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27777, 'businessSn': 'PO260116003', 'todoTime': 1768535404000, 'content': '包含SKU:1个,总数:3000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27602, 'businessSn': 'PO260113069', 'todoTime': 1768476015000, 'content': '包含SKU:1个,总数:300', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27597, 'businessSn': 'PO260115011', 'todoTime': 1768471467000, 'content': '包含SKU:1个,总数:1000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27556, 'businessSn': 'PO260114003', 'todoTime': 1768358443000, 'content': '包含SKU:1个,总数:168', 'redundancy': None}], 'inProcessTask': []}} +2026-01-17 17:53:25,139 [tid:25660 pid:26620] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 27598, 'businessSn': 'PO260115010', 'todoTime': 1768564784000, 'content': '包含SKU:2个,总数:304', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27777, 'businessSn': 'PO260116003', 'todoTime': 1768535404000, 'content': '包含SKU:1个,总数:3000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27602, 'businessSn': 'PO260113069', 'todoTime': 1768476015000, 'content': '包含SKU:1个,总数:300', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27597, 'businessSn': 'PO260115011', 'todoTime': 1768471467000, 'content': '包含SKU:1个,总数:1000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27556, 'businessSn': 'PO260114003', 'todoTime': 1768358443000, 'content': '包含SKU:1个,总数:168', 'redundancy': None}], 'inProcessTask': []}} +2026-01-17 17:53:37,620 [tid:20244 pid:9564] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-17 17:53:37,621 [tid:20244 pid:9564] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-17 17:53:37,622 [tid:20244 pid:9564] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-17 17:53:37,622 [tid:20244 pid:9564] runner.py[line:117] INFO 请求数据:{} +2026-01-17 17:53:37,623 [tid:20244 pid:9564] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-17 17:53:37,921 [tid:20244 pid:9564] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'a11372d59ab74a76ab0f6d9558ba997a', 'tenant-id': '1', 'ssotoken': 'a11372d59ab74a76ab0f6d9558ba997a', 'sso-token': 'a11372d59ab74a76ab0f6d9558ba997a', 'Accesstoken': 'a11372d59ab74a76ab0f6d9558ba997a', 'access-token': 'a11372d59ab74a76ab0f6d9558ba997a', 'token': 'a11372d59ab74a76ab0f6d9558ba997a'} +2026-01-17 17:53:38,093 [tid:20244 pid:9564] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 27598, 'businessSn': 'PO260115010', 'todoTime': 1768564784000, 'content': '包含SKU:2个,总数:304', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27777, 'businessSn': 'PO260116003', 'todoTime': 1768535404000, 'content': '包含SKU:1个,总数:3000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27602, 'businessSn': 'PO260113069', 'todoTime': 1768476015000, 'content': '包含SKU:1个,总数:300', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27597, 'businessSn': 'PO260115011', 'todoTime': 1768471467000, 'content': '包含SKU:1个,总数:1000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27556, 'businessSn': 'PO260114003', 'todoTime': 1768358443000, 'content': '包含SKU:1个,总数:168', 'redundancy': None}], 'inProcessTask': []}} +2026-01-17 17:53:38,094 [tid:20244 pid:9564] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 27598, 'businessSn': 'PO260115010', 'todoTime': 1768564784000, 'content': '包含SKU:2个,总数:304', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27777, 'businessSn': 'PO260116003', 'todoTime': 1768535404000, 'content': '包含SKU:1个,总数:3000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27602, 'businessSn': 'PO260113069', 'todoTime': 1768476015000, 'content': '包含SKU:1个,总数:300', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27597, 'businessSn': 'PO260115011', 'todoTime': 1768471467000, 'content': '包含SKU:1个,总数:1000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27556, 'businessSn': 'PO260114003', 'todoTime': 1768358443000, 'content': '包含SKU:1个,总数:168', 'redundancy': None}], 'inProcessTask': []}} +2026-01-17 18:43:42,952 [tid:25260 pid:23800] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-17 18:43:42,953 [tid:25260 pid:23800] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-17 18:43:42,954 [tid:25260 pid:23800] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-17 18:43:42,954 [tid:25260 pid:23800] runner.py[line:117] INFO 请求数据:{} +2026-01-17 18:43:42,955 [tid:25260 pid:23800] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-17 18:43:43,307 [tid:25260 pid:23800] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'f42c06ec2c274f8a99626da3cde15af4', 'tenant-id': '1', 'ssotoken': 'f42c06ec2c274f8a99626da3cde15af4', 'sso-token': 'f42c06ec2c274f8a99626da3cde15af4', 'Accesstoken': 'f42c06ec2c274f8a99626da3cde15af4', 'access-token': 'f42c06ec2c274f8a99626da3cde15af4', 'token': 'f42c06ec2c274f8a99626da3cde15af4'} +2026-01-17 18:43:43,522 [tid:25260 pid:23800] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 27598, 'businessSn': 'PO260115010', 'todoTime': 1768564784000, 'content': '包含SKU:2个,总数:304', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27777, 'businessSn': 'PO260116003', 'todoTime': 1768535404000, 'content': '包含SKU:1个,总数:3000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27602, 'businessSn': 'PO260113069', 'todoTime': 1768476015000, 'content': '包含SKU:1个,总数:300', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27597, 'businessSn': 'PO260115011', 'todoTime': 1768471467000, 'content': '包含SKU:1个,总数:1000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27556, 'businessSn': 'PO260114003', 'todoTime': 1768358443000, 'content': '包含SKU:1个,总数:168', 'redundancy': None}], 'inProcessTask': []}} +2026-01-17 18:43:43,523 [tid:25260 pid:23800] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 27598, 'businessSn': 'PO260115010', 'todoTime': 1768564784000, 'content': '包含SKU:2个,总数:304', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27777, 'businessSn': 'PO260116003', 'todoTime': 1768535404000, 'content': '包含SKU:1个,总数:3000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27602, 'businessSn': 'PO260113069', 'todoTime': 1768476015000, 'content': '包含SKU:1个,总数:300', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27597, 'businessSn': 'PO260115011', 'todoTime': 1768471467000, 'content': '包含SKU:1个,总数:1000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27556, 'businessSn': 'PO260114003', 'todoTime': 1768358443000, 'content': '包含SKU:1个,总数:168', 'redundancy': None}], 'inProcessTask': []}} +2026-01-17 18:43:43,525 [tid:25260 pid:23800] sqlhelper.py[line:123] INFO SQL语句:SELECT * FROM `smart_management_st`.`erp_purchase_order` WHERE `order_sn` = 'PO260115010' +2026-01-17 18:43:43,526 [tid:25260 pid:23800] sqlhelper.py[line:128] INFO SQL语句:SELECT * FROM `smart_management_st`.`erp_purchase_order` WHERE `order_sn` = 'PO260115010' +2026-01-17 18:43:45,663 [tid:25260 pid:23800] sqlhelper.py[line:88] ERROR syntax error at or near "`" +LINE 1: SELECT * FROM `smart_management_st`.`erp_purchase_order` WHE... + ^ + +2026-01-17 18:44:58,757 [tid:24228 pid:22420] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-17 18:44:58,758 [tid:24228 pid:22420] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-17 18:44:58,759 [tid:24228 pid:22420] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-17 18:44:58,760 [tid:24228 pid:22420] runner.py[line:117] INFO 请求数据:{} +2026-01-17 18:44:58,761 [tid:24228 pid:22420] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-17 18:44:59,079 [tid:24228 pid:22420] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'ba69479919dd40398b6bc282315fc4a9', 'tenant-id': '1', 'ssotoken': 'ba69479919dd40398b6bc282315fc4a9', 'sso-token': 'ba69479919dd40398b6bc282315fc4a9', 'Accesstoken': 'ba69479919dd40398b6bc282315fc4a9', 'access-token': 'ba69479919dd40398b6bc282315fc4a9', 'token': 'ba69479919dd40398b6bc282315fc4a9'} +2026-01-17 18:44:59,259 [tid:24228 pid:22420] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 27598, 'businessSn': 'PO260115010', 'todoTime': 1768564784000, 'content': '包含SKU:2个,总数:304', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27777, 'businessSn': 'PO260116003', 'todoTime': 1768535404000, 'content': '包含SKU:1个,总数:3000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27602, 'businessSn': 'PO260113069', 'todoTime': 1768476015000, 'content': '包含SKU:1个,总数:300', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27597, 'businessSn': 'PO260115011', 'todoTime': 1768471467000, 'content': '包含SKU:1个,总数:1000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27556, 'businessSn': 'PO260114003', 'todoTime': 1768358443000, 'content': '包含SKU:1个,总数:168', 'redundancy': None}], 'inProcessTask': []}} +2026-01-17 18:44:59,262 [tid:24228 pid:22420] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 27598, 'businessSn': 'PO260115010', 'todoTime': 1768564784000, 'content': '包含SKU:2个,总数:304', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27777, 'businessSn': 'PO260116003', 'todoTime': 1768535404000, 'content': '包含SKU:1个,总数:3000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27602, 'businessSn': 'PO260113069', 'todoTime': 1768476015000, 'content': '包含SKU:1个,总数:300', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27597, 'businessSn': 'PO260115011', 'todoTime': 1768471467000, 'content': '包含SKU:1个,总数:1000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27556, 'businessSn': 'PO260114003', 'todoTime': 1768358443000, 'content': '包含SKU:1个,总数:168', 'redundancy': None}], 'inProcessTask': []}} +2026-01-17 18:44:59,268 [tid:24228 pid:22420] sqlhelper.py[line:123] INFO SQL语句:SELECT * FROM `smart_management_st`.`erp_purchase_order` WHERE `order_sn` = 'PO260115010' +2026-01-17 18:44:59,271 [tid:24228 pid:22420] sqlhelper.py[line:128] INFO SQL语句:SELECT * FROM `smart_management_st`.`erp_purchase_order` WHERE `order_sn` = 'PO260115010' +2026-01-17 18:45:01,283 [tid:24228 pid:22420] sqlhelper.py[line:88] ERROR syntax error at or near "`" +LINE 1: SELECT * FROM `smart_management_st`.`erp_purchase_order` WHE... + ^ + +2026-01-19 11:19:44,113 [tid:4844 pid:17996] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-19 11:19:44,114 [tid:4844 pid:17996] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-19 11:19:44,115 [tid:4844 pid:17996] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-19 11:19:44,116 [tid:4844 pid:17996] runner.py[line:117] INFO 请求数据:{} +2026-01-19 11:19:44,116 [tid:4844 pid:17996] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-19 11:19:44,731 [tid:4844 pid:17996] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '3427ebaf8c6a4ec3a80c71f6f6a81a5a', 'tenant-id': '1', 'ssotoken': '3427ebaf8c6a4ec3a80c71f6f6a81a5a', 'sso-token': '3427ebaf8c6a4ec3a80c71f6f6a81a5a', 'Accesstoken': '3427ebaf8c6a4ec3a80c71f6f6a81a5a', 'access-token': '3427ebaf8c6a4ec3a80c71f6f6a81a5a', 'token': '3427ebaf8c6a4ec3a80c71f6f6a81a5a'} +2026-01-19 11:19:45,238 [tid:4844 pid:17996] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 28122, 'businessSn': 'PO260115010', 'todoTime': 1768564784000, 'content': '包含SKU:2个,总数:304', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 26147, 'businessSn': 'PO260116003', 'todoTime': 1768535404000, 'content': '包含SKU:1个,总数:3000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 28225, 'businessSn': 'PO260115009', 'todoTime': 1768471468000, 'content': '包含SKU:1个,总数:1035', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 28120, 'businessSn': 'PO260115011', 'todoTime': 1768471467000, 'content': '包含SKU:1个,总数:1000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27984, 'businessSn': 'PO260114003', 'todoTime': 1768358443000, 'content': '包含SKU:1个,总数:168', 'redundancy': None}], 'inProcessTask': []}} +2026-01-19 11:19:45,239 [tid:4844 pid:17996] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 28122, 'businessSn': 'PO260115010', 'todoTime': 1768564784000, 'content': '包含SKU:2个,总数:304', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 26147, 'businessSn': 'PO260116003', 'todoTime': 1768535404000, 'content': '包含SKU:1个,总数:3000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 28225, 'businessSn': 'PO260115009', 'todoTime': 1768471468000, 'content': '包含SKU:1个,总数:1035', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 28120, 'businessSn': 'PO260115011', 'todoTime': 1768471467000, 'content': '包含SKU:1个,总数:1000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27984, 'businessSn': 'PO260114003', 'todoTime': 1768358443000, 'content': '包含SKU:1个,总数:168', 'redundancy': None}], 'inProcessTask': []}} +2026-01-19 11:19:45,242 [tid:4844 pid:17996] sqlhelper.py[line:123] INFO SQL语句:SELECT * FROM `smart_management_st`.`erp_purchase_order` WHERE `order_sn` = 'PO260115010' +2026-01-19 11:19:45,243 [tid:4844 pid:17996] sqlhelper.py[line:128] INFO SQL语句:SELECT * FROM `smart_management_st`.`erp_purchase_order` WHERE `order_sn` = 'PO260115010' +2026-01-19 11:19:47,130 [tid:4844 pid:17996] sqlhelper.py[line:88] ERROR syntax error at or near "`" +LINE 1: SELECT * FROM `smart_management_st`.`erp_purchase_order` WHE... + ^ + +2026-01-19 19:54:17,979 [tid:19816 pid:20528] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-19 19:54:17,981 [tid:19816 pid:20528] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-19 19:54:17,981 [tid:19816 pid:20528] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-19 19:54:17,982 [tid:19816 pid:20528] runner.py[line:117] INFO 请求数据:{} +2026-01-19 19:54:17,983 [tid:19816 pid:20528] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-19 19:54:18,497 [tid:19816 pid:20528] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '60bcb4e5a6694282be2c9bfdf28d41a0', 'tenant-id': '1', 'ssotoken': '60bcb4e5a6694282be2c9bfdf28d41a0', 'sso-token': '60bcb4e5a6694282be2c9bfdf28d41a0', 'Accesstoken': '60bcb4e5a6694282be2c9bfdf28d41a0', 'access-token': '60bcb4e5a6694282be2c9bfdf28d41a0', 'token': '60bcb4e5a6694282be2c9bfdf28d41a0'} +2026-01-19 19:54:19,063 [tid:19816 pid:20528] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 28122, 'businessSn': 'PO260115010', 'todoTime': 1768564784000, 'content': '包含SKU:2个,总数:304', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 26147, 'businessSn': 'PO260116003', 'todoTime': 1768535404000, 'content': '包含SKU:1个,总数:3000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 28225, 'businessSn': 'PO260115009', 'todoTime': 1768471468000, 'content': '包含SKU:1个,总数:1035', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 28120, 'businessSn': 'PO260115011', 'todoTime': 1768471467000, 'content': '包含SKU:1个,总数:1000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27984, 'businessSn': 'PO260114003', 'todoTime': 1768358443000, 'content': '包含SKU:1个,总数:168', 'redundancy': None}], 'inProcessTask': []}} +2026-01-19 19:54:19,064 [tid:19816 pid:20528] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 28122, 'businessSn': 'PO260115010', 'todoTime': 1768564784000, 'content': '包含SKU:2个,总数:304', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 26147, 'businessSn': 'PO260116003', 'todoTime': 1768535404000, 'content': '包含SKU:1个,总数:3000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 28225, 'businessSn': 'PO260115009', 'todoTime': 1768471468000, 'content': '包含SKU:1个,总数:1035', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 28120, 'businessSn': 'PO260115011', 'todoTime': 1768471467000, 'content': '包含SKU:1个,总数:1000', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 27984, 'businessSn': 'PO260114003', 'todoTime': 1768358443000, 'content': '包含SKU:1个,总数:168', 'redundancy': None}], 'inProcessTask': []}} +2026-01-19 19:54:19,066 [tid:19816 pid:20528] pgsqlhelper.py[line:75] INFO SQL语句:SELECT * FROM `smart_management_st`.`erp_purchase_order` WHERE `order_sn` = 'PO260115010' +2026-01-19 19:54:21,301 [tid:19816 pid:20528] pgsqlhelper.py[line:53] ERROR syntax error at or near "`" +LINE 1: SELECT * FROM `smart_management_st`.`erp_purchase_order` WHE... + ^ + +2026-01-19 20:13:25,316 [tid:20344 pid:21648] pgsqlhelper.py[line:53] ERROR relation "account.account" does not exist +LINE 1: SELECT * FROM account.account LIMIT 5; + ^ + +2026-01-22 10:55:05,411 [tid:8732 pid:18372] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-22 10:55:05,415 [tid:8732 pid:18372] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 10:55:05,416 [tid:8732 pid:18372] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 10:55:05,416 [tid:8732 pid:18372] runner.py[line:117] INFO 请求数据:{} +2026-01-22 10:55:05,417 [tid:8732 pid:18372] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 10:55:06,046 [tid:8732 pid:18372] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '67d14ab06a52494b981a063bbe47da99', 'tenant-id': '1', 'ssotoken': '67d14ab06a52494b981a063bbe47da99', 'sso-token': '67d14ab06a52494b981a063bbe47da99', 'Accesstoken': '67d14ab06a52494b981a063bbe47da99', 'access-token': '67d14ab06a52494b981a063bbe47da99', 'token': '67d14ab06a52494b981a063bbe47da99'} +2026-01-22 10:55:06,397 [tid:8732 pid:18372] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1768890450000, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1768890450000, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1768890450000, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1768890450000, 'content': '包含SKU:1个,总数:359', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1768890449000, 'content': '包含SKU:1个,总数:288', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 10:55:06,400 [tid:8732 pid:18372] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1768890450000, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1768890450000, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1768890450000, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1768890450000, 'content': '包含SKU:1个,总数:359', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1768890449000, 'content': '包含SKU:1个,总数:288', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 10:55:06,410 [tid:8732 pid:18372] pgsqlhelper.py[line:75] INFO SQL语句:SELECT * FROM `smart_management_st`.`erp_purchase_order` WHERE `order_sn` = 'PO260120035' +2026-01-22 10:55:08,364 [tid:8732 pid:18372] pgsqlhelper.py[line:53] ERROR syntax error at or near "`" +LINE 1: SELECT * FROM `smart_management_st`.`erp_purchase_order` WHE... + ^ + +2026-01-22 10:59:48,567 [tid:2188 pid:7832] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-22 10:59:48,568 [tid:2188 pid:7832] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 10:59:48,569 [tid:2188 pid:7832] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 10:59:48,570 [tid:2188 pid:7832] runner.py[line:117] INFO 请求数据:{} +2026-01-22 10:59:48,571 [tid:2188 pid:7832] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 10:59:48,986 [tid:2188 pid:7832] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'e365b9aa26af419ab84041a35aa53a54', 'tenant-id': '1', 'ssotoken': 'e365b9aa26af419ab84041a35aa53a54', 'sso-token': 'e365b9aa26af419ab84041a35aa53a54', 'Accesstoken': 'e365b9aa26af419ab84041a35aa53a54', 'access-token': 'e365b9aa26af419ab84041a35aa53a54', 'token': 'e365b9aa26af419ab84041a35aa53a54'} +2026-01-22 10:59:49,231 [tid:2188 pid:7832] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1768890450000, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1768890450000, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1768890450000, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1768890450000, 'content': '包含SKU:1个,总数:359', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1768890449000, 'content': '包含SKU:1个,总数:288', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 10:59:49,232 [tid:2188 pid:7832] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1768890450000, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1768890450000, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1768890450000, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1768890450000, 'content': '包含SKU:1个,总数:359', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1768890449000, 'content': '包含SKU:1个,总数:288', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:00:35,331 [tid:22520 pid:23496] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-22 11:00:35,332 [tid:22520 pid:23496] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 11:00:35,332 [tid:22520 pid:23496] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 11:00:35,334 [tid:22520 pid:23496] runner.py[line:117] INFO 请求数据:{} +2026-01-22 11:00:35,335 [tid:22520 pid:23496] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 11:00:35,590 [tid:22520 pid:23496] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '746370401d89432490e063ce2e1c9a5b', 'tenant-id': '1', 'ssotoken': '746370401d89432490e063ce2e1c9a5b', 'sso-token': '746370401d89432490e063ce2e1c9a5b', 'Accesstoken': '746370401d89432490e063ce2e1c9a5b', 'access-token': '746370401d89432490e063ce2e1c9a5b', 'token': '746370401d89432490e063ce2e1c9a5b'} +2026-01-22 11:00:35,788 [tid:22520 pid:23496] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:00:35,791 [tid:22520 pid:23496] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:00:37,695 [tid:22520 pid:23496] pgsqlhelper.py[line:84] ERROR 数据库错误: relation "smart_management_st.erp_purchase_order" does not exist +LINE 1: SELECT * FROM "smart_management_st"."erp_purchase_order" WHE... + ^ + +2026-01-22 11:00:37,696 [tid:22520 pid:23496] pgsqlhelper.py[line:85] ERROR 执行的SQL: SELECT * FROM "smart_management_st"."erp_purchase_order" WHERE "order_sn" = 'PO260120036' +2026-01-22 11:00:37,697 [tid:22520 pid:23496] pgsqlhelper.py[line:86] ERROR 参数: None +2026-01-22 11:01:42,045 [tid:10028 pid:19336] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-22 11:01:42,046 [tid:10028 pid:19336] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 11:01:42,047 [tid:10028 pid:19336] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 11:01:42,047 [tid:10028 pid:19336] runner.py[line:117] INFO 请求数据:{} +2026-01-22 11:01:42,048 [tid:10028 pid:19336] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 11:01:42,376 [tid:10028 pid:19336] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '2148c7fc25ad48cea85f07f081f89d0e', 'tenant-id': '1', 'ssotoken': '2148c7fc25ad48cea85f07f081f89d0e', 'sso-token': '2148c7fc25ad48cea85f07f081f89d0e', 'Accesstoken': '2148c7fc25ad48cea85f07f081f89d0e', 'access-token': '2148c7fc25ad48cea85f07f081f89d0e', 'token': '2148c7fc25ad48cea85f07f081f89d0e'} +2026-01-22 11:01:42,604 [tid:10028 pid:19336] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:01:42,606 [tid:10028 pid:19336] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:01:44,456 [tid:10028 pid:19336] pgsqlhelper.py[line:84] ERROR 数据库错误: relation "smart_management_st.erp_purchase_order" does not exist +LINE 1: SELECT * FROM "smart_management_st"."erp_purchase_order" WHE... + ^ + +2026-01-22 11:01:44,458 [tid:10028 pid:19336] pgsqlhelper.py[line:85] ERROR 执行的SQL: SELECT * FROM "smart_management_st"."erp_purchase_order" WHERE "order_sn" = "PO260120036" +2026-01-22 11:01:44,460 [tid:10028 pid:19336] pgsqlhelper.py[line:86] ERROR 参数: None +2026-01-22 11:02:05,746 [tid:17036 pid:22328] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-22 11:02:05,747 [tid:17036 pid:22328] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 11:02:05,748 [tid:17036 pid:22328] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 11:02:05,749 [tid:17036 pid:22328] runner.py[line:117] INFO 请求数据:{} +2026-01-22 11:02:05,750 [tid:17036 pid:22328] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 11:02:06,053 [tid:17036 pid:22328] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'ae9871ddb8d441fc9328545c666fee47', 'tenant-id': '1', 'ssotoken': 'ae9871ddb8d441fc9328545c666fee47', 'sso-token': 'ae9871ddb8d441fc9328545c666fee47', 'Accesstoken': 'ae9871ddb8d441fc9328545c666fee47', 'access-token': 'ae9871ddb8d441fc9328545c666fee47', 'token': 'ae9871ddb8d441fc9328545c666fee47'} +2026-01-22 11:02:06,249 [tid:17036 pid:22328] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:02:06,250 [tid:17036 pid:22328] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:02:08,065 [tid:17036 pid:22328] pgsqlhelper.py[line:84] ERROR 数据库错误: relation "smart_management_st.erp_purchase_order" does not exist +LINE 1: SELECT * FROM smart_management_st.erp_purchase_order WHERE o... + ^ + +2026-01-22 11:02:08,066 [tid:17036 pid:22328] pgsqlhelper.py[line:85] ERROR 执行的SQL: SELECT * FROM smart_management_st.erp_purchase_order WHERE order_sn = PO260120036 +2026-01-22 11:02:08,067 [tid:17036 pid:22328] pgsqlhelper.py[line:86] ERROR 参数: None +2026-01-22 11:04:30,907 [tid:7408 pid:23500] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-22 11:04:30,908 [tid:7408 pid:23500] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 11:04:30,908 [tid:7408 pid:23500] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 11:04:30,909 [tid:7408 pid:23500] runner.py[line:117] INFO 请求数据:{} +2026-01-22 11:04:30,910 [tid:7408 pid:23500] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 11:04:31,195 [tid:7408 pid:23500] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '1bc9152eef5a4bd6aae6c2c26a2430ff', 'tenant-id': '1', 'ssotoken': '1bc9152eef5a4bd6aae6c2c26a2430ff', 'sso-token': '1bc9152eef5a4bd6aae6c2c26a2430ff', 'Accesstoken': '1bc9152eef5a4bd6aae6c2c26a2430ff', 'access-token': '1bc9152eef5a4bd6aae6c2c26a2430ff', 'token': '1bc9152eef5a4bd6aae6c2c26a2430ff'} +2026-01-22 11:04:31,375 [tid:7408 pid:23500] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:04:31,376 [tid:7408 pid:23500] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:04:33,256 [tid:7408 pid:23500] pgsqlhelper.py[line:53] ERROR relation "smart_management_st.erp_purchase_order" does not exist +LINE 1: SELECT * FROM smart_management_st.erp_purchase_order WHERE o... + ^ + +2026-01-22 11:09:01,646 [tid:7876 pid:21252] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-22 11:09:01,647 [tid:7876 pid:21252] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 11:09:01,647 [tid:7876 pid:21252] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 11:09:01,648 [tid:7876 pid:21252] runner.py[line:117] INFO 请求数据:{} +2026-01-22 11:09:01,649 [tid:7876 pid:21252] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 11:09:02,061 [tid:7876 pid:21252] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '545793318f854f1ba7d790bbd6be089b', 'tenant-id': '1', 'ssotoken': '545793318f854f1ba7d790bbd6be089b', 'sso-token': '545793318f854f1ba7d790bbd6be089b', 'Accesstoken': '545793318f854f1ba7d790bbd6be089b', 'access-token': '545793318f854f1ba7d790bbd6be089b', 'token': '545793318f854f1ba7d790bbd6be089b'} +2026-01-22 11:09:02,279 [tid:7876 pid:21252] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:09:02,281 [tid:7876 pid:21252] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:09:38,635 [tid:21108 pid:7132] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-22 11:09:38,636 [tid:21108 pid:7132] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 11:09:38,636 [tid:21108 pid:7132] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 11:09:38,637 [tid:21108 pid:7132] runner.py[line:117] INFO 请求数据:{} +2026-01-22 11:09:38,638 [tid:21108 pid:7132] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 11:09:38,990 [tid:21108 pid:7132] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '99c4cdbb3b5a401bb2057f603e15e42f', 'tenant-id': '1', 'ssotoken': '99c4cdbb3b5a401bb2057f603e15e42f', 'sso-token': '99c4cdbb3b5a401bb2057f603e15e42f', 'Accesstoken': '99c4cdbb3b5a401bb2057f603e15e42f', 'access-token': '99c4cdbb3b5a401bb2057f603e15e42f', 'token': '99c4cdbb3b5a401bb2057f603e15e42f'} +2026-01-22 11:09:39,190 [tid:21108 pid:7132] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:09:39,191 [tid:21108 pid:7132] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:14:04,378 [tid:19980 pid:22452] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-22 11:14:04,379 [tid:19980 pid:22452] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 11:14:04,379 [tid:19980 pid:22452] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 11:14:04,380 [tid:19980 pid:22452] runner.py[line:117] INFO 请求数据:{} +2026-01-22 11:14:04,381 [tid:19980 pid:22452] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 11:14:04,692 [tid:19980 pid:22452] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'df195a3fc47d4f1b98bcaea47598a7b1', 'tenant-id': '1', 'ssotoken': 'df195a3fc47d4f1b98bcaea47598a7b1', 'sso-token': 'df195a3fc47d4f1b98bcaea47598a7b1', 'Accesstoken': 'df195a3fc47d4f1b98bcaea47598a7b1', 'access-token': 'df195a3fc47d4f1b98bcaea47598a7b1', 'token': 'df195a3fc47d4f1b98bcaea47598a7b1'} +2026-01-22 11:14:04,892 [tid:19980 pid:22452] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:14:04,896 [tid:19980 pid:22452] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:14:35,536 [tid:22044 pid:20880] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-22 11:14:35,538 [tid:22044 pid:20880] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 11:14:35,539 [tid:22044 pid:20880] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 11:14:35,539 [tid:22044 pid:20880] runner.py[line:117] INFO 请求数据:{} +2026-01-22 11:14:35,541 [tid:22044 pid:20880] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 11:14:35,836 [tid:22044 pid:20880] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '7c8e5babea1c41ac95aa4a32e22b91c9', 'tenant-id': '1', 'ssotoken': '7c8e5babea1c41ac95aa4a32e22b91c9', 'sso-token': '7c8e5babea1c41ac95aa4a32e22b91c9', 'Accesstoken': '7c8e5babea1c41ac95aa4a32e22b91c9', 'access-token': '7c8e5babea1c41ac95aa4a32e22b91c9', 'token': '7c8e5babea1c41ac95aa4a32e22b91c9'} +2026-01-22 11:14:36,036 [tid:22044 pid:20880] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:14:36,039 [tid:22044 pid:20880] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:41:44,524 [tid:21508 pid:22392] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-22 11:41:44,525 [tid:21508 pid:22392] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 11:41:44,526 [tid:21508 pid:22392] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 11:41:44,528 [tid:21508 pid:22392] runner.py[line:117] INFO 请求数据:{} +2026-01-22 11:41:44,529 [tid:21508 pid:22392] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 11:41:45,033 [tid:21508 pid:22392] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'd82858c055d84eae8f7ac8b877abb4fc', 'tenant-id': '1', 'ssotoken': 'd82858c055d84eae8f7ac8b877abb4fc', 'sso-token': 'd82858c055d84eae8f7ac8b877abb4fc', 'Accesstoken': 'd82858c055d84eae8f7ac8b877abb4fc', 'access-token': 'd82858c055d84eae8f7ac8b877abb4fc', 'token': 'd82858c055d84eae8f7ac8b877abb4fc'} +2026-01-22 11:41:45,303 [tid:21508 pid:22392] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:41:45,304 [tid:21508 pid:22392] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:41:52,473 [tid:21508 pid:22392] pgsqlhelper.py[line:53] ERROR relation "smart_management_st.erp_purchase_order" does not exist +LINE 1: SELECT * FROM smart_management_st.erp_purchase_order WHERE o... + ^ + +2026-01-22 11:42:05,902 [tid:13072 pid:23208] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-22 11:42:05,905 [tid:13072 pid:23208] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 11:42:05,906 [tid:13072 pid:23208] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 11:42:05,908 [tid:13072 pid:23208] runner.py[line:117] INFO 请求数据:{} +2026-01-22 11:42:05,911 [tid:13072 pid:23208] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 11:42:06,222 [tid:13072 pid:23208] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'dd3218456f3d4e108f8e3c5cf09eda97', 'tenant-id': '1', 'ssotoken': 'dd3218456f3d4e108f8e3c5cf09eda97', 'sso-token': 'dd3218456f3d4e108f8e3c5cf09eda97', 'Accesstoken': 'dd3218456f3d4e108f8e3c5cf09eda97', 'access-token': 'dd3218456f3d4e108f8e3c5cf09eda97', 'token': 'dd3218456f3d4e108f8e3c5cf09eda97'} +2026-01-22 11:42:06,408 [tid:13072 pid:23208] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:42:06,411 [tid:13072 pid:23208] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:42:38,344 [tid:11292 pid:7280] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-22 11:42:38,345 [tid:11292 pid:7280] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 11:42:38,346 [tid:11292 pid:7280] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 11:42:38,347 [tid:11292 pid:7280] runner.py[line:117] INFO 请求数据:{} +2026-01-22 11:42:38,348 [tid:11292 pid:7280] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 11:42:38,672 [tid:11292 pid:7280] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '5a1ef7697c1148f986acbc291a27f295', 'tenant-id': '1', 'ssotoken': '5a1ef7697c1148f986acbc291a27f295', 'sso-token': '5a1ef7697c1148f986acbc291a27f295', 'Accesstoken': '5a1ef7697c1148f986acbc291a27f295', 'access-token': '5a1ef7697c1148f986acbc291a27f295', 'token': '5a1ef7697c1148f986acbc291a27f295'} +2026-01-22 11:42:38,913 [tid:11292 pid:7280] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:42:38,914 [tid:11292 pid:7280] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:45:13,274 [tid:23224 pid:11668] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-22 11:45:13,275 [tid:23224 pid:11668] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 11:45:13,276 [tid:23224 pid:11668] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 11:45:13,277 [tid:23224 pid:11668] runner.py[line:117] INFO 请求数据:{} +2026-01-22 11:45:13,277 [tid:23224 pid:11668] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 11:45:13,586 [tid:23224 pid:11668] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'dd03de9e8a3241bfa2f75387705d003b', 'tenant-id': '1', 'ssotoken': 'dd03de9e8a3241bfa2f75387705d003b', 'sso-token': 'dd03de9e8a3241bfa2f75387705d003b', 'Accesstoken': 'dd03de9e8a3241bfa2f75387705d003b', 'access-token': 'dd03de9e8a3241bfa2f75387705d003b', 'token': 'dd03de9e8a3241bfa2f75387705d003b'} +2026-01-22 11:45:13,741 [tid:23224 pid:11668] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:45:13,742 [tid:23224 pid:11668] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:45:15,567 [tid:23224 pid:11668] pgsqlhelper.py[line:53] ERROR relation "smart_management_st.erp_purchase_order" does not exist +LINE 1: SELECT * FROM smart_management_st.erp_purchase_order WHERE o... + ^ + +2026-01-22 11:47:46,177 [tid:5836 pid:5656] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-22 11:47:46,178 [tid:5836 pid:5656] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 11:47:46,179 [tid:5836 pid:5656] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 11:47:46,180 [tid:5836 pid:5656] runner.py[line:117] INFO 请求数据:{} +2026-01-22 11:47:46,181 [tid:5836 pid:5656] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 11:47:46,470 [tid:5836 pid:5656] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'f7da2089c8f140a4a1f7de52b0c0da27', 'tenant-id': '1', 'ssotoken': 'f7da2089c8f140a4a1f7de52b0c0da27', 'sso-token': 'f7da2089c8f140a4a1f7de52b0c0da27', 'Accesstoken': 'f7da2089c8f140a4a1f7de52b0c0da27', 'access-token': 'f7da2089c8f140a4a1f7de52b0c0da27', 'token': 'f7da2089c8f140a4a1f7de52b0c0da27'} +2026-01-22 11:47:46,644 [tid:5836 pid:5656] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:47:46,646 [tid:5836 pid:5656] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:47:48,444 [tid:5836 pid:5656] pgsqlhelper.py[line:58] ERROR PostgreSQL执行SQL失败: relation "smart_management_st.erp_purchase_order" does not exist +LINE 1: SELECT * FROM "smart_management_st"."erp_purchase_order" WHE... + ^ +, SQL: SELECT * FROM "smart_management_st"."erp_purchase_order" WHERE "order_sn" = 'PO260120036' +2026-01-22 11:47:48,446 [tid:5836 pid:5656] index.py[line:78] WARNING 查询采购订单表失败,订单号:PO260120036,错误:('数据库操作失败,SQL语句:SELECT * FROM "smart_management_st"."erp_purchase_order" WHERE "order_sn" = \'PO260120036\', 错误: relation "smart_management_st.erp_purchase_order" does not exist\nLINE 1: SELECT * FROM "smart_management_st"."erp_purchase_order" WHE...\n ^\n',) +2026-01-22 11:49:53,799 [tid:22868 pid:23068] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-22 11:49:53,800 [tid:22868 pid:23068] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 11:49:53,801 [tid:22868 pid:23068] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 11:49:53,802 [tid:22868 pid:23068] runner.py[line:117] INFO 请求数据:{} +2026-01-22 11:49:53,803 [tid:22868 pid:23068] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 11:49:54,143 [tid:22868 pid:23068] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '53248e01b7b44f1a913095febed4ee1c', 'tenant-id': '1', 'ssotoken': '53248e01b7b44f1a913095febed4ee1c', 'sso-token': '53248e01b7b44f1a913095febed4ee1c', 'Accesstoken': '53248e01b7b44f1a913095febed4ee1c', 'access-token': '53248e01b7b44f1a913095febed4ee1c', 'token': '53248e01b7b44f1a913095febed4ee1c'} +2026-01-22 11:49:54,356 [tid:22868 pid:23068] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:49:54,359 [tid:22868 pid:23068] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:49:56,214 [tid:22868 pid:23068] pgsqlhelper.py[line:90] ERROR PostgreSQL执行SQL失败: relation "smart_management_st.erp_purchase_order" does not exist +LINE 1: SELECT * FROM "smart_management_st"."erp_purchase_order" WHE... + ^ +, SQL: SELECT * FROM "smart_management_st"."erp_purchase_order" WHERE "order_sn" = 'PO260120036' +2026-01-22 11:49:56,215 [tid:22868 pid:23068] index.py[line:78] WARNING 查询采购订单表失败,订单号:PO260120036,错误:('数据库操作失败,SQL语句:SELECT * FROM "smart_management_st"."erp_purchase_order" WHERE "order_sn" = \'PO260120036\', 错误: relation "smart_management_st.erp_purchase_order" does not exist\nLINE 1: SELECT * FROM "smart_management_st"."erp_purchase_order" WHE...\n ^\n',) +2026-01-22 11:52:34,292 [tid:23036 pid:23328] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-22 11:52:34,294 [tid:23036 pid:23328] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 11:52:34,294 [tid:23036 pid:23328] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 11:52:34,295 [tid:23036 pid:23328] runner.py[line:117] INFO 请求数据:{} +2026-01-22 11:52:34,296 [tid:23036 pid:23328] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 11:52:34,595 [tid:23036 pid:23328] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'ee5498336a064cdbb110adf6548e2d5e', 'tenant-id': '1', 'ssotoken': 'ee5498336a064cdbb110adf6548e2d5e', 'sso-token': 'ee5498336a064cdbb110adf6548e2d5e', 'Accesstoken': 'ee5498336a064cdbb110adf6548e2d5e', 'access-token': 'ee5498336a064cdbb110adf6548e2d5e', 'token': 'ee5498336a064cdbb110adf6548e2d5e'} +2026-01-22 11:52:34,773 [tid:23036 pid:23328] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:52:34,774 [tid:23036 pid:23328] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:52:34,776 [tid:23036 pid:23328] index.py[line:74] INFO ================================================================================ +2026-01-22 11:52:34,777 [tid:23036 pid:23328] index.py[line:75] INFO 开始诊断PostgreSQL数据库结构... +2026-01-22 11:52:34,778 [tid:23036 pid:23328] pgsqlhelper.py[line:112] INFO SQL语句:SELECT schema_name FROM information_schema.schemata WHERE schema_name NOT IN ('pg_catalog', 'information_schema', 'pg_toast') ORDER BY schema_name +2026-01-22 11:52:36,719 [tid:23036 pid:23328] pgsqlhelper.py[line:119] INFO 数据库查询结果:[RealDictRow([('schema_name', 'public')])] +2026-01-22 11:52:36,720 [tid:23036 pid:23328] index.py[line:80] INFO 所有可用的schema: ['public'] +2026-01-22 11:52:36,850 [tid:23036 pid:23328] index.py[line:85] INFO smart_management_st schema检查结果: None +2026-01-22 11:52:36,851 [tid:23036 pid:23328] pgsqlhelper.py[line:112] INFO SQL语句:SELECT table_name FROM information_schema.tables WHERE table_schema = 'smart_management_st' ORDER BY table_name +2026-01-22 11:52:36,965 [tid:23036 pid:23328] pgsqlhelper.py[line:119] INFO 数据库查询结果:[] +2026-01-22 11:52:36,966 [tid:23036 pid:23328] index.py[line:90] INFO smart_management_st schema下的所有表: [] +2026-01-22 11:52:36,967 [tid:23036 pid:23328] pgsqlhelper.py[line:112] INFO SQL语句:SELECT table_schema, table_name FROM information_schema.tables WHERE table_name = 'erp_purchase_order' +2026-01-22 11:52:37,104 [tid:23036 pid:23328] pgsqlhelper.py[line:119] INFO 数据库查询结果:[RealDictRow([('table_schema', 'public'), ('table_name', 'erp_purchase_order')])] +2026-01-22 11:52:37,105 [tid:23036 pid:23328] index.py[line:95] INFO erp_purchase_order表检查结果: [RealDictRow([('table_schema', 'public'), ('table_name', 'erp_purchase_order')])] +2026-01-22 11:52:37,241 [tid:23036 pid:23328] index.py[line:100] INFO public schema下的erp_purchase_order表: RealDictRow([('table_name', 'erp_purchase_order')]) +2026-01-22 11:52:37,242 [tid:23036 pid:23328] index.py[line:102] INFO ================================================================================ +2026-01-22 11:52:37,244 [tid:23036 pid:23328] index.py[line:123] INFO 尝试SQL格式 1 (双引号格式): SELECT * FROM "smart_management_st"."erp_purchase_order" WHERE "order_sn" = 'PO260120036' +2026-01-22 11:52:37,334 [tid:23036 pid:23328] pgsqlhelper.py[line:90] ERROR PostgreSQL执行SQL失败: relation "smart_management_st.erp_purchase_order" does not exist +LINE 1: SELECT * FROM "smart_management_st"."erp_purchase_order" WHE... + ^ +, SQL: SELECT * FROM "smart_management_st"."erp_purchase_order" WHERE "order_sn" = 'PO260120036' +2026-01-22 11:52:37,336 [tid:23036 pid:23328] index.py[line:129] WARNING ✗ SQL格式 1 (双引号格式) 失败: ('数据库操作失败,SQL语句:SELECT * FROM "smart_management_st"."erp_purchase_order" WHERE "order_sn" = \'PO260120036\', 错误: relation "smart_management_st.erp_purchase_order" does not exist\nLINE 1: SELECT * FROM "smart_management_st"."erp_purchase_order" WHE...\n ^\n',) +2026-01-22 11:52:37,381 [tid:23036 pid:23328] index.py[line:123] INFO 尝试SQL格式 2 (无引号格式): SELECT * FROM smart_management_st.erp_purchase_order WHERE order_sn = 'PO260120036' +2026-01-22 11:52:37,450 [tid:23036 pid:23328] pgsqlhelper.py[line:90] ERROR PostgreSQL执行SQL失败: relation "smart_management_st.erp_purchase_order" does not exist +LINE 1: SELECT * FROM smart_management_st.erp_purchase_order WHERE o... + ^ +, SQL: SELECT * FROM smart_management_st.erp_purchase_order WHERE order_sn = 'PO260120036' +2026-01-22 11:52:37,451 [tid:23036 pid:23328] index.py[line:129] WARNING ✗ SQL格式 2 (无引号格式) 失败: ('数据库操作失败,SQL语句:SELECT * FROM smart_management_st.erp_purchase_order WHERE order_sn = \'PO260120036\', 错误: relation "smart_management_st.erp_purchase_order" does not exist\nLINE 1: SELECT * FROM smart_management_st.erp_purchase_order WHERE o...\n ^\n',) +2026-01-22 11:52:37,485 [tid:23036 pid:23328] index.py[line:123] INFO 尝试SQL格式 3 (仅schema引号): SELECT * FROM "smart_management_st".erp_purchase_order WHERE order_sn = 'PO260120036' +2026-01-22 11:52:37,568 [tid:23036 pid:23328] pgsqlhelper.py[line:90] ERROR PostgreSQL执行SQL失败: relation "smart_management_st.erp_purchase_order" does not exist +LINE 1: SELECT * FROM "smart_management_st".erp_purchase_order WHERE... + ^ +, SQL: SELECT * FROM "smart_management_st".erp_purchase_order WHERE order_sn = 'PO260120036' +2026-01-22 11:52:37,569 [tid:23036 pid:23328] index.py[line:129] WARNING ✗ SQL格式 3 (仅schema引号) 失败: ('数据库操作失败,SQL语句:SELECT * FROM "smart_management_st".erp_purchase_order WHERE order_sn = \'PO260120036\', 错误: relation "smart_management_st.erp_purchase_order" does not exist\nLINE 1: SELECT * FROM "smart_management_st".erp_purchase_order WHERE...\n ^\n',) +2026-01-22 11:52:37,611 [tid:23036 pid:23328] index.py[line:123] INFO 尝试SQL格式 4 (public schema): SELECT * FROM public.erp_purchase_order WHERE order_sn = 'PO260120036' +2026-01-22 11:52:37,752 [tid:23036 pid:23328] index.py[line:125] INFO ✓ 查询成功!使用格式 4 (public schema), 结果: RealDictRow([('id', 54893), ('order_sn', 'PO260120036'), ('purchaser_id', 143), ('principal_ids', [{'id': 10384696, 'name': '胡利娟'}, {'id': 10378449, 'name': '孙丽萍'}]), ('purchase_company_id', 101), ('purchase_company_name', '深圳易威行贸易有限公司'), ('purchase_contact_mobile', ''), ('purchase_contact_name', ''), ('purchaser_contact_address', ''), ('supplier_id', 303), ('supplier_name', '广东万蚁健康产业发展有限公司'), ('supplier_contact_mobile', ''), ('supplier_contact_name', ''), ('supplier_contact_address', ''), ('warehouse_id', 10152), ('warehouse_name', '深圳中转仓-贸易FBA-JP'), ('order_time', datetime.datetime(2026, 1, 20, 14, 27, 30)), ('total_quantity', 200), ('total_amount', Decimal('3706.00')), ('total_price', Decimal('3706.00')), ('is_tax_included', 1), ('settlement_method', 'MONTHLY_SETTLEMENT'), ('currency_code', 'CNY'), ('payment_method', 'ONLINE_BANKING'), ('advance_ratio', Decimal('0.00')), ('contract_id', 0), ('related_plans', []), ('attachment_id', 0), ('freight_responsibility', ''), ('inbound_status', 0), ('inbound_status_text', '未入库'), ('inbound_quantity', 0), ('pending_inbound_quantity', 200), ('status', 0), ('status_text', '待创建合同'), ('payment_status', 0), ('payment_status_text', '未支付'), ('contract_status', 0), ('contract_status_text', ''), ('prepare_contract_sn', ''), ('transaction_location', ''), ('supplementary_terms', ''), ('cancel_reason', None), ('remark', None), ('creator', '143'), ('creator_name', '胡利娟'), ('create_time', datetime.datetime(2026, 1, 20, 14, 18, 59)), ('updater', '10378449'), ('updater_name', ''), ('update_time', datetime.datetime(2026, 1, 22, 11, 0, 0, 82418)), ('deleted', 0), ('tenant_id', 1), ('user_id', 143), ('dept_id', 106), ('contract_time', datetime.datetime(1, 1, 1, 0, 0))]) +2026-01-22 11:54:37,894 [tid:22964 pid:8820] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-22 11:54:37,895 [tid:22964 pid:8820] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 11:54:37,896 [tid:22964 pid:8820] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 11:54:37,896 [tid:22964 pid:8820] runner.py[line:117] INFO 请求数据:{} +2026-01-22 11:54:37,897 [tid:22964 pid:8820] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 11:54:38,161 [tid:22964 pid:8820] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'efc428a08c3d4bdebac881c0ee64cd03', 'tenant-id': '1', 'ssotoken': 'efc428a08c3d4bdebac881c0ee64cd03', 'sso-token': 'efc428a08c3d4bdebac881c0ee64cd03', 'Accesstoken': 'efc428a08c3d4bdebac881c0ee64cd03', 'access-token': 'efc428a08c3d4bdebac881c0ee64cd03', 'token': 'efc428a08c3d4bdebac881c0ee64cd03'} +2026-01-22 11:54:38,329 [tid:22964 pid:8820] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:54:38,331 [tid:22964 pid:8820] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:55:15,396 [tid:9852 pid:23152] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-22 11:55:15,397 [tid:9852 pid:23152] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 11:55:15,398 [tid:9852 pid:23152] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 11:55:15,399 [tid:9852 pid:23152] runner.py[line:117] INFO 请求数据:{} +2026-01-22 11:55:15,401 [tid:9852 pid:23152] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 11:55:15,706 [tid:9852 pid:23152] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '164a5509a54749d8b389b09a9d7892c9', 'tenant-id': '1', 'ssotoken': '164a5509a54749d8b389b09a9d7892c9', 'sso-token': '164a5509a54749d8b389b09a9d7892c9', 'Accesstoken': '164a5509a54749d8b389b09a9d7892c9', 'access-token': '164a5509a54749d8b389b09a9d7892c9', 'token': '164a5509a54749d8b389b09a9d7892c9'} +2026-01-22 11:55:15,888 [tid:9852 pid:23152] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:55:15,890 [tid:9852 pid:23152] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:57:38,784 [tid:16916 pid:7916] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-22 11:57:38,785 [tid:16916 pid:7916] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 11:57:38,786 [tid:16916 pid:7916] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 11:57:38,787 [tid:16916 pid:7916] runner.py[line:117] INFO 请求数据:{} +2026-01-22 11:57:38,787 [tid:16916 pid:7916] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 11:57:39,158 [tid:16916 pid:7916] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '760f93ffc357404ba86afd49db13f8b5', 'tenant-id': '1', 'ssotoken': '760f93ffc357404ba86afd49db13f8b5', 'sso-token': '760f93ffc357404ba86afd49db13f8b5', 'Accesstoken': '760f93ffc357404ba86afd49db13f8b5', 'access-token': '760f93ffc357404ba86afd49db13f8b5', 'token': '760f93ffc357404ba86afd49db13f8b5'} +2026-01-22 11:57:39,351 [tid:16916 pid:7916] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 11:57:39,352 [tid:16916 pid:7916] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 14:32:53,865 [tid:6268 pid:20912] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-22 14:32:53,866 [tid:6268 pid:20912] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 14:32:53,867 [tid:6268 pid:20912] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 14:32:53,868 [tid:6268 pid:20912] runner.py[line:117] INFO 请求数据:{} +2026-01-22 14:32:53,869 [tid:6268 pid:20912] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 14:32:54,247 [tid:6268 pid:20912] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '71641604691541199281a5cb2fe2f4ca', 'tenant-id': '1', 'ssotoken': '71641604691541199281a5cb2fe2f4ca', 'sso-token': '71641604691541199281a5cb2fe2f4ca', 'Accesstoken': '71641604691541199281a5cb2fe2f4ca', 'access-token': '71641604691541199281a5cb2fe2f4ca', 'token': '71641604691541199281a5cb2fe2f4ca'} +2026-01-22 14:32:54,447 [tid:6268 pid:20912] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 14:32:54,449 [tid:6268 pid:20912] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 14:50:06,490 [tid:3412 pid:21600] ZZYY_interface.py[line:64] INFO your input:{'page_no': 1, 'page_size': 10} +2026-01-22 14:50:06,496 [tid:3412 pid:21600] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 14:50:06,497 [tid:3412 pid:21600] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-order/page +2026-01-22 14:50:06,498 [tid:3412 pid:21600] runner.py[line:117] INFO 请求数据:{'json': {'page_no': 1, 'page_size': 10}} +2026-01-22 14:50:06,499 [tid:3412 pid:21600] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'Basic c3BhcmtsZS13ZWI6c3BhcmtsZS13ZWI=', 'tenant-id': '1'} +2026-01-22 14:50:06,882 [tid:3412 pid:21600] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 401, 'msg': '账号未登录', 'data': None} +2026-01-22 14:50:06,883 [tid:3412 pid:21600] runner.py[line:211] WARNING 缓存session过期,清理缓存! +2026-01-22 14:50:06,884 [tid:3412 pid:21600] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'Basic c3BhcmtsZS13ZWI6c3BhcmtsZS13ZWI=', 'tenant-id': '1'} +2026-01-22 14:50:06,991 [tid:3412 pid:21600] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 401, 'msg': '账号未登录', 'data': None} +2026-01-22 14:50:06,994 [tid:3412 pid:21600] runner.py[line:211] WARNING 缓存session过期,清理缓存! +2026-01-22 14:50:06,995 [tid:3412 pid:21600] runner.py[line:225] INFO 返回数据:{'code': 401, 'msg': '账号未登录', 'data': None} +2026-01-22 14:50:16,267 [tid:23456 pid:16820] ZZYY_interface.py[line:64] INFO your input:{'page_no': 1, 'page_size': 10} +2026-01-22 14:50:16,268 [tid:23456 pid:16820] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 14:50:16,269 [tid:23456 pid:16820] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-order/page +2026-01-22 14:50:16,270 [tid:23456 pid:16820] runner.py[line:117] INFO 请求数据:{'json': {'page_no': 1, 'page_size': 10}} +2026-01-22 14:50:16,271 [tid:23456 pid:16820] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'Basic c3BhcmtsZS13ZWI6c3BhcmtsZS13ZWI=', 'tenant-id': '1'} +2026-01-22 14:50:16,474 [tid:23456 pid:16820] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 401, 'msg': '账号未登录', 'data': None} +2026-01-22 14:50:16,475 [tid:23456 pid:16820] runner.py[line:211] WARNING 缓存session过期,清理缓存! +2026-01-22 14:50:16,476 [tid:23456 pid:16820] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'Basic c3BhcmtsZS13ZWI6c3BhcmtsZS13ZWI=', 'tenant-id': '1'} +2026-01-22 14:50:16,575 [tid:23456 pid:16820] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 401, 'msg': '账号未登录', 'data': None} +2026-01-22 14:50:16,576 [tid:23456 pid:16820] runner.py[line:211] WARNING 缓存session过期,清理缓存! +2026-01-22 14:50:16,577 [tid:23456 pid:16820] runner.py[line:225] INFO 返回数据:{'code': 401, 'msg': '账号未登录', 'data': None} +2026-01-22 14:50:29,106 [tid:19256 pid:12016] ZZYY_interface.py[line:45] INFO your input:{} +2026-01-22 14:50:29,107 [tid:19256 pid:12016] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 14:50:29,108 [tid:19256 pid:12016] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 14:50:29,109 [tid:19256 pid:12016] runner.py[line:117] INFO 请求数据:{} +2026-01-22 14:50:29,110 [tid:19256 pid:12016] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 14:50:29,388 [tid:19256 pid:12016] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '098e0b75b8cd44628dc50b684cf8c8bc', 'tenant-id': '1', 'ssotoken': '098e0b75b8cd44628dc50b684cf8c8bc', 'sso-token': '098e0b75b8cd44628dc50b684cf8c8bc', 'Accesstoken': '098e0b75b8cd44628dc50b684cf8c8bc', 'access-token': '098e0b75b8cd44628dc50b684cf8c8bc', 'token': '098e0b75b8cd44628dc50b684cf8c8bc'} +2026-01-22 14:50:29,566 [tid:19256 pid:12016] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 14:50:29,568 [tid:19256 pid:12016] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 14:54:17,329 [tid:1228 pid:23464] ZZYY_interface.py[line:58] INFO your input:{'page_no': 1, 'page_size': 10} +2026-01-22 14:54:17,330 [tid:1228 pid:23464] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 14:54:17,331 [tid:1228 pid:23464] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/admin-api/erp/purchase-order/page +2026-01-22 14:54:17,332 [tid:1228 pid:23464] runner.py[line:117] INFO 请求数据:{'json': {'page_no': 1, 'page_size': 10}} +2026-01-22 14:54:17,333 [tid:1228 pid:23464] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 14:54:17,617 [tid:1228 pid:23464] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '1b9bf39e8ec247ae8bd95d80f22cba7a', 'tenant-id': '1', 'ssotoken': '1b9bf39e8ec247ae8bd95d80f22cba7a', 'sso-token': '1b9bf39e8ec247ae8bd95d80f22cba7a', 'Accesstoken': '1b9bf39e8ec247ae8bd95d80f22cba7a', 'access-token': '1b9bf39e8ec247ae8bd95d80f22cba7a', 'token': '1b9bf39e8ec247ae8bd95d80f22cba7a'} +2026-01-22 14:54:17,951 [tid:1228 pid:23464] runner.py[line:192] INFO ------状态码:404, 返回信息:{'code': 500, 'msg': '系统异常', 'data': None} +2026-01-22 14:54:17,952 [tid:1228 pid:23464] runner.py[line:225] INFO 返回数据:{'code': 500, 'msg': '系统异常', 'data': None} +2026-01-22 14:55:57,114 [tid:22736 pid:22200] ZZYY_interface.py[line:58] INFO your input:{'pageNo': 1, 'pageSize': 10} +2026-01-22 14:55:57,116 [tid:22736 pid:22200] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 14:55:57,117 [tid:22736 pid:22200] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-order/page +2026-01-22 14:55:57,119 [tid:22736 pid:22200] runner.py[line:117] INFO 请求数据:{'json': {'pageNo': 1, 'pageSize': 10}} +2026-01-22 14:55:57,120 [tid:22736 pid:22200] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 14:55:57,425 [tid:22736 pid:22200] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '0a031d009dd940e09a63aebebef2cb47', 'tenant-id': '1', 'ssotoken': '0a031d009dd940e09a63aebebef2cb47', 'sso-token': '0a031d009dd940e09a63aebebef2cb47', 'Accesstoken': '0a031d009dd940e09a63aebebef2cb47', 'access-token': '0a031d009dd940e09a63aebebef2cb47', 'token': '0a031d009dd940e09a63aebebef2cb47'} +2026-01-22 14:55:57,885 [tid:22736 pid:22200] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'total': 695, 'list': [{'id': 47879, 'orderSn': 'PO251209036', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 138, 'supplierName': '东莞市魔趣科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1765260609000, 'totalQuantity': 242, 'totalAmount': 10115.6, 'totalPrice': 10115.6, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 242, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 3, 'statusText': '已完成', 'cancelReason': None, 'remark': None, 'createTime': 1765260228000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47873, 'orderSn': 'PO251209110', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 8142, 'warehouseName': '深圳中转仓-贸易B2B', 'orderTime': 1765273081000, 'totalQuantity': 150, 'totalAmount': 6900.0, 'totalPrice': 6900.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 150, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 3, 'statusText': '已完成', 'cancelReason': None, 'remark': None, 'createTime': 1765272598000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47872, 'orderSn': 'PO251209111', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 151, 'supplierName': '东莞市蒂贝电子科技有限公司', 'warehouseId': 8142, 'warehouseName': '深圳中转仓-贸易B2B', 'orderTime': 1765273081000, 'totalQuantity': 106, 'totalAmount': 9243.2, 'totalPrice': 9243.2, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 106, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 3, 'statusText': '已完成', 'cancelReason': None, 'remark': None, 'createTime': 1765272598000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47487, 'orderSn': 'PO251209045', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 494, 'supplierName': '东莞市女娲智能科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1765260608000, 'totalQuantity': 493, 'totalAmount': 12653.81, 'totalPrice': 12653.81, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 493, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 3, 'statusText': '已完成', 'cancelReason': None, 'remark': None, 'createTime': 1765260283000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47467, 'orderSn': 'PO251223017', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 187, 'supplierName': '上海恰然实业有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 223, 'totalAmount': 15556.48, 'totalPrice': 15556.48, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 223, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 3, 'statusText': '已完成', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}], 'redundancy1': '34166742.82', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 14:55:57,889 [tid:22736 pid:22200] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'total': 695, 'list': [{'id': 47879, 'orderSn': 'PO251209036', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 138, 'supplierName': '东莞市魔趣科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1765260609000, 'totalQuantity': 242, 'totalAmount': 10115.6, 'totalPrice': 10115.6, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 242, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 3, 'statusText': '已完成', 'cancelReason': None, 'remark': None, 'createTime': 1765260228000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47873, 'orderSn': 'PO251209110', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 8142, 'warehouseName': '深圳中转仓-贸易B2B', 'orderTime': 1765273081000, 'totalQuantity': 150, 'totalAmount': 6900.0, 'totalPrice': 6900.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 150, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 3, 'statusText': '已完成', 'cancelReason': None, 'remark': None, 'createTime': 1765272598000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47872, 'orderSn': 'PO251209111', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 151, 'supplierName': '东莞市蒂贝电子科技有限公司', 'warehouseId': 8142, 'warehouseName': '深圳中转仓-贸易B2B', 'orderTime': 1765273081000, 'totalQuantity': 106, 'totalAmount': 9243.2, 'totalPrice': 9243.2, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 106, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 3, 'statusText': '已完成', 'cancelReason': None, 'remark': None, 'createTime': 1765272598000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47487, 'orderSn': 'PO251209045', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 494, 'supplierName': '东莞市女娲智能科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1765260608000, 'totalQuantity': 493, 'totalAmount': 12653.81, 'totalPrice': 12653.81, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 493, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 3, 'statusText': '已完成', 'cancelReason': None, 'remark': None, 'createTime': 1765260283000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47467, 'orderSn': 'PO251223017', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 187, 'supplierName': '上海恰然实业有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 223, 'totalAmount': 15556.48, 'totalPrice': 15556.48, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 223, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 3, 'statusText': '已完成', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}], 'redundancy1': '34166742.82', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 15:05:44,747 [tid:7468 pid:14852] ZZYY_interface.py[line:58] INFO your input:{'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': '1', 'payment_status': '1', 'status': '1', 'order_sn': 'PO251209048'} +2026-01-22 15:05:44,748 [tid:7468 pid:14852] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 15:05:44,749 [tid:7468 pid:14852] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-order/page +2026-01-22 15:05:44,750 [tid:7468 pid:14852] runner.py[line:117] INFO 请求数据:{'json': {'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': '1', 'payment_status': '1', 'status': '1', 'order_sn': 'PO251209048'}} +2026-01-22 15:05:44,754 [tid:7468 pid:14852] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 15:05:45,235 [tid:7468 pid:14852] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '2e5d3cd637704a32be50902c898aaae7', 'tenant-id': '1', 'ssotoken': '2e5d3cd637704a32be50902c898aaae7', 'sso-token': '2e5d3cd637704a32be50902c898aaae7', 'Accesstoken': '2e5d3cd637704a32be50902c898aaae7', 'access-token': '2e5d3cd637704a32be50902c898aaae7', 'token': '2e5d3cd637704a32be50902c898aaae7'} +2026-01-22 15:05:45,467 [tid:7468 pid:14852] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'total': 0, 'list': [], 'redundancy1': '0', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 15:05:45,467 [tid:7468 pid:14852] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'total': 0, 'list': [], 'redundancy1': '0', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 15:06:20,802 [tid:12200 pid:11292] ZZYY_interface.py[line:58] INFO your input:{'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': '1', 'payment_status': '1', 'status': '1', 'order_sn': 'PO251209048'} +2026-01-22 15:06:20,804 [tid:12200 pid:11292] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 15:06:20,805 [tid:12200 pid:11292] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-order/page +2026-01-22 15:06:20,805 [tid:12200 pid:11292] runner.py[line:117] INFO 请求数据:{'json': {'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': '1', 'payment_status': '1', 'status': '1', 'order_sn': 'PO251209048'}} +2026-01-22 15:06:20,819 [tid:12200 pid:11292] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 15:06:21,115 [tid:12200 pid:11292] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '36db0fbdac3c4004b567085366a84ed6', 'tenant-id': '1', 'ssotoken': '36db0fbdac3c4004b567085366a84ed6', 'sso-token': '36db0fbdac3c4004b567085366a84ed6', 'Accesstoken': '36db0fbdac3c4004b567085366a84ed6', 'access-token': '36db0fbdac3c4004b567085366a84ed6', 'token': '36db0fbdac3c4004b567085366a84ed6'} +2026-01-22 15:06:21,324 [tid:12200 pid:11292] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'total': 0, 'list': [], 'redundancy1': '0', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 15:06:21,325 [tid:12200 pid:11292] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'total': 0, 'list': [], 'redundancy1': '0', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 15:13:18,762 [tid:23320 pid:9232] ZZYY_interface.py[line:58] INFO your input:{'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'} +2026-01-22 15:13:18,763 [tid:23320 pid:9232] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 15:13:18,764 [tid:23320 pid:9232] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-order/page +2026-01-22 15:13:18,765 [tid:23320 pid:9232] runner.py[line:117] INFO 请求数据:{'json': {'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'}} +2026-01-22 15:13:18,769 [tid:23320 pid:9232] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 15:13:19,136 [tid:23320 pid:9232] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '8d077c59c25748efb2b5956e2d6bf4e1', 'tenant-id': '1', 'ssotoken': '8d077c59c25748efb2b5956e2d6bf4e1', 'sso-token': '8d077c59c25748efb2b5956e2d6bf4e1', 'Accesstoken': '8d077c59c25748efb2b5956e2d6bf4e1', 'access-token': '8d077c59c25748efb2b5956e2d6bf4e1', 'token': '8d077c59c25748efb2b5956e2d6bf4e1'} +2026-01-22 15:13:19,398 [tid:23320 pid:9232] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'total': 317, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '22028026.92', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 15:13:19,399 [tid:23320 pid:9232] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'total': 317, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '22028026.92', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 16:39:22,967 [tid:28632 pid:28316] ZZYY_interface.py[line:58] INFO your input:{'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'} +2026-01-22 16:39:22,968 [tid:28632 pid:28316] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 16:39:22,969 [tid:28632 pid:28316] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-order/page +2026-01-22 16:39:22,970 [tid:28632 pid:28316] runner.py[line:117] INFO 请求数据:{'json': {'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'}} +2026-01-22 16:39:22,971 [tid:28632 pid:28316] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 16:39:23,097 [tid:28632 pid:28316] runner.py[line:63] INFO 返回数据:HTTPSConnectionPool(host='smart-management-api-dev.best-envision.com', port=443): Max retries exceeded with url: /admin-api/system/auth/login (Caused by ProxyError('Cannot connect to proxy.', FileNotFoundError(2, 'No such file or directory'))) +2026-01-22 16:46:36,146 [tid:29104 pid:30108] ZZYY_interface.py[line:58] INFO your input:{'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'} +2026-01-22 16:46:36,147 [tid:29104 pid:30108] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 16:46:36,148 [tid:29104 pid:30108] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-order/page +2026-01-22 16:46:36,149 [tid:29104 pid:30108] runner.py[line:117] INFO 请求数据:{'json': {'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'}} +2026-01-22 16:46:36,170 [tid:29104 pid:30108] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 16:46:36,255 [tid:29104 pid:30108] runner.py[line:63] INFO 返回数据:HTTPSConnectionPool(host='smart-management-api-dev.best-envision.com', port=443): Max retries exceeded with url: /admin-api/system/auth/login (Caused by ProxyError('Cannot connect to proxy.', FileNotFoundError(2, 'No such file or directory'))) +2026-01-22 16:47:07,176 [tid:13616 pid:25364] ZZYY_interface.py[line:41] INFO your input:{} +2026-01-22 16:47:07,177 [tid:13616 pid:25364] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 16:47:07,178 [tid:13616 pid:25364] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 16:47:07,179 [tid:13616 pid:25364] runner.py[line:117] INFO 请求数据:{} +2026-01-22 16:47:07,179 [tid:13616 pid:25364] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 16:47:07,261 [tid:13616 pid:25364] runner.py[line:63] INFO 返回数据:HTTPSConnectionPool(host='smart-management-api-dev.best-envision.com', port=443): Max retries exceeded with url: /admin-api/system/auth/login (Caused by ProxyError('Cannot connect to proxy.', FileNotFoundError(2, 'No such file or directory'))) +2026-01-22 16:47:07,517 [tid:13616 pid:25364] ZZYY_interface.py[line:58] INFO your input:{'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'} +2026-01-22 16:47:07,518 [tid:13616 pid:25364] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 16:47:07,519 [tid:13616 pid:25364] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-order/page +2026-01-22 16:47:07,519 [tid:13616 pid:25364] runner.py[line:117] INFO 请求数据:{'json': {'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'}} +2026-01-22 16:47:07,520 [tid:13616 pid:25364] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 16:47:07,597 [tid:13616 pid:25364] runner.py[line:63] INFO 返回数据:HTTPSConnectionPool(host='smart-management-api-dev.best-envision.com', port=443): Max retries exceeded with url: /admin-api/system/auth/login (Caused by ProxyError('Cannot connect to proxy.', FileNotFoundError(2, 'No such file or directory'))) +2026-01-22 16:50:21,339 [tid:29116 pid:21976] ZZYY_interface.py[line:41] INFO your input:{} +2026-01-22 16:50:21,340 [tid:29116 pid:21976] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 16:50:21,341 [tid:29116 pid:21976] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 16:50:21,341 [tid:29116 pid:21976] runner.py[line:117] INFO 请求数据:{} +2026-01-22 16:50:21,342 [tid:29116 pid:21976] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 16:50:21,765 [tid:29116 pid:21976] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '345c77ddee204170b1812a675b956d33', 'tenant-id': '1', 'ssotoken': '345c77ddee204170b1812a675b956d33', 'sso-token': '345c77ddee204170b1812a675b956d33', 'Accesstoken': '345c77ddee204170b1812a675b956d33', 'access-token': '345c77ddee204170b1812a675b956d33', 'token': '345c77ddee204170b1812a675b956d33'} +2026-01-22 16:50:21,958 [tid:29116 pid:21976] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 16:50:21,959 [tid:29116 pid:21976] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 16:50:23,779 [tid:29116 pid:21976] ZZYY_interface.py[line:58] INFO your input:{'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'} +2026-01-22 16:50:23,780 [tid:29116 pid:21976] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 16:50:23,781 [tid:29116 pid:21976] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-order/page +2026-01-22 16:50:23,782 [tid:29116 pid:21976] runner.py[line:117] INFO 请求数据:{'json': {'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'}} +2026-01-22 16:50:23,782 [tid:29116 pid:21976] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 16:50:24,039 [tid:29116 pid:21976] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '22efddb9db80497f8638e2e85c4d924b', 'tenant-id': '1', 'ssotoken': '22efddb9db80497f8638e2e85c4d924b', 'sso-token': '22efddb9db80497f8638e2e85c4d924b', 'Accesstoken': '22efddb9db80497f8638e2e85c4d924b', 'access-token': '22efddb9db80497f8638e2e85c4d924b', 'token': '22efddb9db80497f8638e2e85c4d924b'} +2026-01-22 16:50:24,319 [tid:29116 pid:21976] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'total': 314, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '21974228.52', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 16:50:24,321 [tid:29116 pid:21976] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'total': 314, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '21974228.52', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 16:51:08,561 [tid:27232 pid:19340] ZZYY_interface.py[line:41] INFO your input:{} +2026-01-22 16:51:08,562 [tid:27232 pid:19340] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 16:51:08,563 [tid:27232 pid:19340] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 16:51:08,564 [tid:27232 pid:19340] runner.py[line:117] INFO 请求数据:{} +2026-01-22 16:51:08,564 [tid:27232 pid:19340] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 16:51:08,864 [tid:27232 pid:19340] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'ed1fac5b19494106b0476e0c5d2fc11c', 'tenant-id': '1', 'ssotoken': 'ed1fac5b19494106b0476e0c5d2fc11c', 'sso-token': 'ed1fac5b19494106b0476e0c5d2fc11c', 'Accesstoken': 'ed1fac5b19494106b0476e0c5d2fc11c', 'access-token': 'ed1fac5b19494106b0476e0c5d2fc11c', 'token': 'ed1fac5b19494106b0476e0c5d2fc11c'} +2026-01-22 16:51:09,050 [tid:27232 pid:19340] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 16:51:09,051 [tid:27232 pid:19340] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 16:51:10,886 [tid:27232 pid:19340] ZZYY_interface.py[line:58] INFO your input:{'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'} +2026-01-22 16:51:10,888 [tid:27232 pid:19340] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 16:51:10,889 [tid:27232 pid:19340] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-order/page +2026-01-22 16:51:10,889 [tid:27232 pid:19340] runner.py[line:117] INFO 请求数据:{'json': {'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'}} +2026-01-22 16:51:10,890 [tid:27232 pid:19340] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 16:51:11,164 [tid:27232 pid:19340] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '0e8cdeb935554dc09229bac9668f824a', 'tenant-id': '1', 'ssotoken': '0e8cdeb935554dc09229bac9668f824a', 'sso-token': '0e8cdeb935554dc09229bac9668f824a', 'Accesstoken': '0e8cdeb935554dc09229bac9668f824a', 'access-token': '0e8cdeb935554dc09229bac9668f824a', 'token': '0e8cdeb935554dc09229bac9668f824a'} +2026-01-22 16:51:11,390 [tid:27232 pid:19340] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'total': 314, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '21974228.52', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 16:51:11,390 [tid:27232 pid:19340] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'total': 314, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '21974228.52', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 16:54:23,935 [tid:29220 pid:30204] ZZYY_interface.py[line:41] INFO your input:{} +2026-01-22 16:54:23,936 [tid:29220 pid:30204] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 16:54:23,937 [tid:29220 pid:30204] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 16:54:23,938 [tid:29220 pid:30204] runner.py[line:117] INFO 请求数据:{} +2026-01-22 16:54:23,939 [tid:29220 pid:30204] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 16:54:24,237 [tid:29220 pid:30204] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'c70554fb4bce4e15a657b5bf3183e763', 'tenant-id': '1', 'ssotoken': 'c70554fb4bce4e15a657b5bf3183e763', 'sso-token': 'c70554fb4bce4e15a657b5bf3183e763', 'Accesstoken': 'c70554fb4bce4e15a657b5bf3183e763', 'access-token': 'c70554fb4bce4e15a657b5bf3183e763', 'token': 'c70554fb4bce4e15a657b5bf3183e763'} +2026-01-22 16:54:24,417 [tid:29220 pid:30204] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 16:54:24,420 [tid:29220 pid:30204] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 16:54:26,316 [tid:29220 pid:30204] ZZYY_interface.py[line:58] INFO your input:{'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'} +2026-01-22 16:54:26,317 [tid:29220 pid:30204] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 16:54:26,318 [tid:29220 pid:30204] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-order/page +2026-01-22 16:54:26,318 [tid:29220 pid:30204] runner.py[line:117] INFO 请求数据:{'json': {'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'}} +2026-01-22 16:54:26,319 [tid:29220 pid:30204] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 16:54:26,588 [tid:29220 pid:30204] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '8907471869174423a3c418bac624c7e5', 'tenant-id': '1', 'ssotoken': '8907471869174423a3c418bac624c7e5', 'sso-token': '8907471869174423a3c418bac624c7e5', 'Accesstoken': '8907471869174423a3c418bac624c7e5', 'access-token': '8907471869174423a3c418bac624c7e5', 'token': '8907471869174423a3c418bac624c7e5'} +2026-01-22 16:54:26,809 [tid:29220 pid:30204] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'total': 314, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '21974228.52', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 16:54:26,811 [tid:29220 pid:30204] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'total': 314, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '21974228.52', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 16:56:22,419 [tid:14036 pid:29368] ZZYY_interface.py[line:41] INFO your input:{} +2026-01-22 16:56:22,420 [tid:14036 pid:29368] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 16:56:22,421 [tid:14036 pid:29368] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 16:56:22,421 [tid:14036 pid:29368] runner.py[line:117] INFO 请求数据:{} +2026-01-22 16:56:22,423 [tid:14036 pid:29368] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 16:56:22,687 [tid:14036 pid:29368] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'dc7307fbb9ef4210ba1723adf1f7d555', 'tenant-id': '1', 'ssotoken': 'dc7307fbb9ef4210ba1723adf1f7d555', 'sso-token': 'dc7307fbb9ef4210ba1723adf1f7d555', 'Accesstoken': 'dc7307fbb9ef4210ba1723adf1f7d555', 'access-token': 'dc7307fbb9ef4210ba1723adf1f7d555', 'token': 'dc7307fbb9ef4210ba1723adf1f7d555'} +2026-01-22 16:56:22,851 [tid:14036 pid:29368] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 16:56:22,852 [tid:14036 pid:29368] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 16:56:24,811 [tid:14036 pid:29368] ZZYY_interface.py[line:58] INFO your input:{'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'} +2026-01-22 16:56:24,812 [tid:14036 pid:29368] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 16:56:24,813 [tid:14036 pid:29368] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-order/page +2026-01-22 16:56:24,814 [tid:14036 pid:29368] runner.py[line:117] INFO 请求数据:{'json': {'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'}} +2026-01-22 16:56:24,815 [tid:14036 pid:29368] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 16:56:25,049 [tid:14036 pid:29368] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '7efc06258066450e92a8f12b9a93b2ec', 'tenant-id': '1', 'ssotoken': '7efc06258066450e92a8f12b9a93b2ec', 'sso-token': '7efc06258066450e92a8f12b9a93b2ec', 'Accesstoken': '7efc06258066450e92a8f12b9a93b2ec', 'access-token': '7efc06258066450e92a8f12b9a93b2ec', 'token': '7efc06258066450e92a8f12b9a93b2ec'} +2026-01-22 16:56:25,261 [tid:14036 pid:29368] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'total': 314, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '21974228.52', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 16:56:25,264 [tid:14036 pid:29368] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'total': 314, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '21974228.52', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 16:57:41,985 [tid:29772 pid:28120] ZZYY_interface.py[line:41] INFO your input:{} +2026-01-22 16:57:41,987 [tid:29772 pid:28120] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 16:57:41,988 [tid:29772 pid:28120] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 16:57:41,989 [tid:29772 pid:28120] runner.py[line:117] INFO 请求数据:{} +2026-01-22 16:57:41,990 [tid:29772 pid:28120] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 16:57:42,266 [tid:29772 pid:28120] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'eeff286c9c2f4137becc0a00f1795ed4', 'tenant-id': '1', 'ssotoken': 'eeff286c9c2f4137becc0a00f1795ed4', 'sso-token': 'eeff286c9c2f4137becc0a00f1795ed4', 'Accesstoken': 'eeff286c9c2f4137becc0a00f1795ed4', 'access-token': 'eeff286c9c2f4137becc0a00f1795ed4', 'token': 'eeff286c9c2f4137becc0a00f1795ed4'} +2026-01-22 16:57:42,437 [tid:29772 pid:28120] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 16:57:42,437 [tid:29772 pid:28120] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 16:57:44,405 [tid:29772 pid:28120] ZZYY_interface.py[line:58] INFO your input:{'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'} +2026-01-22 16:57:44,407 [tid:29772 pid:28120] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 16:57:44,409 [tid:29772 pid:28120] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-order/page +2026-01-22 16:57:44,410 [tid:29772 pid:28120] runner.py[line:117] INFO 请求数据:{'json': {'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'}} +2026-01-22 16:57:44,413 [tid:29772 pid:28120] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 16:57:44,709 [tid:29772 pid:28120] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '2ecd3345f6d24977b3145ddbd27362a7', 'tenant-id': '1', 'ssotoken': '2ecd3345f6d24977b3145ddbd27362a7', 'sso-token': '2ecd3345f6d24977b3145ddbd27362a7', 'Accesstoken': '2ecd3345f6d24977b3145ddbd27362a7', 'access-token': '2ecd3345f6d24977b3145ddbd27362a7', 'token': '2ecd3345f6d24977b3145ddbd27362a7'} +2026-01-22 16:57:44,914 [tid:29772 pid:28120] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'total': 314, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '21974228.52', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 16:57:44,916 [tid:29772 pid:28120] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'total': 314, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '21974228.52', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 17:01:23,374 [tid:30248 pid:29572] ZZYY_interface.py[line:41] INFO your input:{} +2026-01-22 17:01:23,376 [tid:30248 pid:29572] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 17:01:23,378 [tid:30248 pid:29572] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 17:01:23,379 [tid:30248 pid:29572] runner.py[line:117] INFO 请求数据:{} +2026-01-22 17:01:23,380 [tid:30248 pid:29572] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 17:01:23,760 [tid:30248 pid:29572] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '829ea1251db0459abe16f6188a116a71', 'tenant-id': '1', 'ssotoken': '829ea1251db0459abe16f6188a116a71', 'sso-token': '829ea1251db0459abe16f6188a116a71', 'Accesstoken': '829ea1251db0459abe16f6188a116a71', 'access-token': '829ea1251db0459abe16f6188a116a71', 'token': '829ea1251db0459abe16f6188a116a71'} +2026-01-22 17:01:23,958 [tid:30248 pid:29572] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 17:01:23,961 [tid:30248 pid:29572] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 17:01:26,218 [tid:30248 pid:29572] ZZYY_interface.py[line:58] INFO your input:{'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'} +2026-01-22 17:01:26,220 [tid:30248 pid:29572] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 17:01:26,221 [tid:30248 pid:29572] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-order/page +2026-01-22 17:01:26,222 [tid:30248 pid:29572] runner.py[line:117] INFO 请求数据:{'json': {'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'}} +2026-01-22 17:01:26,223 [tid:30248 pid:29572] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 17:01:26,516 [tid:30248 pid:29572] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'b9ea8c399dfa4af584da3bd66aadac85', 'tenant-id': '1', 'ssotoken': 'b9ea8c399dfa4af584da3bd66aadac85', 'sso-token': 'b9ea8c399dfa4af584da3bd66aadac85', 'Accesstoken': 'b9ea8c399dfa4af584da3bd66aadac85', 'access-token': 'b9ea8c399dfa4af584da3bd66aadac85', 'token': 'b9ea8c399dfa4af584da3bd66aadac85'} +2026-01-22 17:01:26,735 [tid:30248 pid:29572] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'total': 309, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '21854845.72', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 17:01:26,736 [tid:30248 pid:29572] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'total': 309, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '21854845.72', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 17:03:08,044 [tid:27412 pid:30452] ZZYY_interface.py[line:41] INFO your input:{} +2026-01-22 17:03:08,046 [tid:27412 pid:30452] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 17:03:08,047 [tid:27412 pid:30452] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 17:03:08,047 [tid:27412 pid:30452] runner.py[line:117] INFO 请求数据:{} +2026-01-22 17:03:08,048 [tid:27412 pid:30452] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 17:03:08,332 [tid:27412 pid:30452] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '15a538fa86284a5e95d73efadd9a2ae1', 'tenant-id': '1', 'ssotoken': '15a538fa86284a5e95d73efadd9a2ae1', 'sso-token': '15a538fa86284a5e95d73efadd9a2ae1', 'Accesstoken': '15a538fa86284a5e95d73efadd9a2ae1', 'access-token': '15a538fa86284a5e95d73efadd9a2ae1', 'token': '15a538fa86284a5e95d73efadd9a2ae1'} +2026-01-22 17:03:08,497 [tid:27412 pid:30452] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 17:03:08,498 [tid:27412 pid:30452] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 17:03:10,363 [tid:27412 pid:30452] ZZYY_interface.py[line:58] INFO your input:{'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'} +2026-01-22 17:03:10,364 [tid:27412 pid:30452] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 17:03:10,365 [tid:27412 pid:30452] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-order/page +2026-01-22 17:03:10,366 [tid:27412 pid:30452] runner.py[line:117] INFO 请求数据:{'json': {'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'}} +2026-01-22 17:03:10,367 [tid:27412 pid:30452] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 17:03:10,646 [tid:27412 pid:30452] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '362fdbd26846492cac3dae24a7c62673', 'tenant-id': '1', 'ssotoken': '362fdbd26846492cac3dae24a7c62673', 'sso-token': '362fdbd26846492cac3dae24a7c62673', 'Accesstoken': '362fdbd26846492cac3dae24a7c62673', 'access-token': '362fdbd26846492cac3dae24a7c62673', 'token': '362fdbd26846492cac3dae24a7c62673'} +2026-01-22 17:03:10,871 [tid:27412 pid:30452] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'total': 309, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '21854845.72', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 17:03:10,873 [tid:27412 pid:30452] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'total': 309, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '21854845.72', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 17:03:27,171 [tid:28460 pid:25588] ZZYY_interface.py[line:41] INFO your input:{} +2026-01-22 17:03:27,173 [tid:28460 pid:25588] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 17:03:27,174 [tid:28460 pid:25588] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 17:03:27,175 [tid:28460 pid:25588] runner.py[line:117] INFO 请求数据:{} +2026-01-22 17:03:27,176 [tid:28460 pid:25588] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 17:03:27,441 [tid:28460 pid:25588] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '965ddb49612e4f0bab166e831d4724d1', 'tenant-id': '1', 'ssotoken': '965ddb49612e4f0bab166e831d4724d1', 'sso-token': '965ddb49612e4f0bab166e831d4724d1', 'Accesstoken': '965ddb49612e4f0bab166e831d4724d1', 'access-token': '965ddb49612e4f0bab166e831d4724d1', 'token': '965ddb49612e4f0bab166e831d4724d1'} +2026-01-22 17:03:27,606 [tid:28460 pid:25588] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 17:03:27,608 [tid:28460 pid:25588] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 17:37:35,569 [tid:29468 pid:25288] ZZYY_interface.py[line:41] INFO your input:{} +2026-01-22 17:37:35,571 [tid:29468 pid:25288] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 17:37:35,571 [tid:29468 pid:25288] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 17:37:35,572 [tid:29468 pid:25288] runner.py[line:117] INFO 请求数据:{} +2026-01-22 17:37:35,574 [tid:29468 pid:25288] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 17:37:36,143 [tid:29468 pid:25288] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '93bdabe6007144b4afd6e7ad38aa9f3c', 'tenant-id': '1', 'ssotoken': '93bdabe6007144b4afd6e7ad38aa9f3c', 'sso-token': '93bdabe6007144b4afd6e7ad38aa9f3c', 'Accesstoken': '93bdabe6007144b4afd6e7ad38aa9f3c', 'access-token': '93bdabe6007144b4afd6e7ad38aa9f3c', 'token': '93bdabe6007144b4afd6e7ad38aa9f3c'} +2026-01-22 17:37:36,329 [tid:29468 pid:25288] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 17:37:36,334 [tid:29468 pid:25288] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 17:37:38,332 [tid:29468 pid:25288] ZZYY_interface.py[line:58] INFO your input:{'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'} +2026-01-22 17:37:38,334 [tid:29468 pid:25288] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 17:37:38,336 [tid:29468 pid:25288] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-order/page +2026-01-22 17:37:38,337 [tid:29468 pid:25288] runner.py[line:117] INFO 请求数据:{'json': {'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'}} +2026-01-22 17:37:38,339 [tid:29468 pid:25288] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 17:37:38,608 [tid:29468 pid:25288] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'aa7dec97b55f47169450cecd633b3a76', 'tenant-id': '1', 'ssotoken': 'aa7dec97b55f47169450cecd633b3a76', 'sso-token': 'aa7dec97b55f47169450cecd633b3a76', 'Accesstoken': 'aa7dec97b55f47169450cecd633b3a76', 'access-token': 'aa7dec97b55f47169450cecd633b3a76', 'token': 'aa7dec97b55f47169450cecd633b3a76'} +2026-01-22 17:37:38,824 [tid:29468 pid:25288] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'total': 309, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '21854845.72', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 17:37:38,825 [tid:29468 pid:25288] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'total': 309, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '21854845.72', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 17:40:33,443 [tid:25264 pid:25124] ZZYY_interface.py[line:41] INFO your input:{} +2026-01-22 17:40:33,444 [tid:25264 pid:25124] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 17:40:33,445 [tid:25264 pid:25124] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 17:40:33,446 [tid:25264 pid:25124] runner.py[line:117] INFO 请求数据:{} +2026-01-22 17:40:33,447 [tid:25264 pid:25124] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 17:40:33,755 [tid:25264 pid:25124] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '80e9d292b4bd46a28088a570e1e19e8d', 'tenant-id': '1', 'ssotoken': '80e9d292b4bd46a28088a570e1e19e8d', 'sso-token': '80e9d292b4bd46a28088a570e1e19e8d', 'Accesstoken': '80e9d292b4bd46a28088a570e1e19e8d', 'access-token': '80e9d292b4bd46a28088a570e1e19e8d', 'token': '80e9d292b4bd46a28088a570e1e19e8d'} +2026-01-22 17:40:33,971 [tid:25264 pid:25124] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 17:40:33,972 [tid:25264 pid:25124] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 17:40:35,860 [tid:25264 pid:25124] ZZYY_interface.py[line:58] INFO your input:{'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'} +2026-01-22 17:40:35,861 [tid:25264 pid:25124] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 17:40:35,862 [tid:25264 pid:25124] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-order/page +2026-01-22 17:40:35,863 [tid:25264 pid:25124] runner.py[line:117] INFO 请求数据:{'json': {'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'}} +2026-01-22 17:40:35,864 [tid:25264 pid:25124] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 17:40:36,134 [tid:25264 pid:25124] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'a83f49784cf343bda7ca07748e79e54f', 'tenant-id': '1', 'ssotoken': 'a83f49784cf343bda7ca07748e79e54f', 'sso-token': 'a83f49784cf343bda7ca07748e79e54f', 'Accesstoken': 'a83f49784cf343bda7ca07748e79e54f', 'access-token': 'a83f49784cf343bda7ca07748e79e54f', 'token': 'a83f49784cf343bda7ca07748e79e54f'} +2026-01-22 17:40:36,359 [tid:25264 pid:25124] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'total': 309, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '21854845.72', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 17:40:36,361 [tid:25264 pid:25124] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'total': 309, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '21854845.72', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 17:41:16,568 [tid:26996 pid:26804] ZZYY_interface.py[line:41] INFO your input:{} +2026-01-22 17:41:16,569 [tid:26996 pid:26804] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 17:41:16,570 [tid:26996 pid:26804] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 17:41:16,571 [tid:26996 pid:26804] runner.py[line:117] INFO 请求数据:{} +2026-01-22 17:41:16,572 [tid:26996 pid:26804] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 17:41:16,847 [tid:26996 pid:26804] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '4cffe9ccf0104f2480247ab755aad50d', 'tenant-id': '1', 'ssotoken': '4cffe9ccf0104f2480247ab755aad50d', 'sso-token': '4cffe9ccf0104f2480247ab755aad50d', 'Accesstoken': '4cffe9ccf0104f2480247ab755aad50d', 'access-token': '4cffe9ccf0104f2480247ab755aad50d', 'token': '4cffe9ccf0104f2480247ab755aad50d'} +2026-01-22 17:41:17,026 [tid:26996 pid:26804] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 17:41:17,027 [tid:26996 pid:26804] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 17:41:18,909 [tid:26996 pid:26804] ZZYY_interface.py[line:58] INFO your input:{'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'} +2026-01-22 17:41:18,910 [tid:26996 pid:26804] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 17:41:18,911 [tid:26996 pid:26804] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-order/page +2026-01-22 17:41:18,912 [tid:26996 pid:26804] runner.py[line:117] INFO 请求数据:{'json': {'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'}} +2026-01-22 17:41:18,912 [tid:26996 pid:26804] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 17:41:19,157 [tid:26996 pid:26804] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '23d2696fba68454b8d91f4d14ebf0b54', 'tenant-id': '1', 'ssotoken': '23d2696fba68454b8d91f4d14ebf0b54', 'sso-token': '23d2696fba68454b8d91f4d14ebf0b54', 'Accesstoken': '23d2696fba68454b8d91f4d14ebf0b54', 'access-token': '23d2696fba68454b8d91f4d14ebf0b54', 'token': '23d2696fba68454b8d91f4d14ebf0b54'} +2026-01-22 17:41:19,369 [tid:26996 pid:26804] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'total': 309, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '21854845.72', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 17:41:19,371 [tid:26996 pid:26804] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'total': 309, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '21854845.72', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 17:42:31,888 [tid:25516 pid:29772] ZZYY_interface.py[line:41] INFO your input:{} +2026-01-22 17:42:31,889 [tid:25516 pid:29772] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 17:42:31,890 [tid:25516 pid:29772] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 17:42:31,891 [tid:25516 pid:29772] runner.py[line:117] INFO 请求数据:{} +2026-01-22 17:42:31,892 [tid:25516 pid:29772] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 17:42:32,195 [tid:25516 pid:29772] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'de54e031fb0b4174b28531415574b459', 'tenant-id': '1', 'ssotoken': 'de54e031fb0b4174b28531415574b459', 'sso-token': 'de54e031fb0b4174b28531415574b459', 'Accesstoken': 'de54e031fb0b4174b28531415574b459', 'access-token': 'de54e031fb0b4174b28531415574b459', 'token': 'de54e031fb0b4174b28531415574b459'} +2026-01-22 17:42:32,370 [tid:25516 pid:29772] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 17:42:32,371 [tid:25516 pid:29772] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 17:42:37,306 [tid:25516 pid:29772] ZZYY_interface.py[line:58] INFO your input:{'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'} +2026-01-22 17:42:37,308 [tid:25516 pid:29772] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 17:42:37,310 [tid:25516 pid:29772] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-order/page +2026-01-22 17:42:37,311 [tid:25516 pid:29772] runner.py[line:117] INFO 请求数据:{'json': {'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'}} +2026-01-22 17:42:37,315 [tid:25516 pid:29772] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 17:42:37,606 [tid:25516 pid:29772] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '6a1ccc9beed44e1a8e6ca9bd70c106a4', 'tenant-id': '1', 'ssotoken': '6a1ccc9beed44e1a8e6ca9bd70c106a4', 'sso-token': '6a1ccc9beed44e1a8e6ca9bd70c106a4', 'Accesstoken': '6a1ccc9beed44e1a8e6ca9bd70c106a4', 'access-token': '6a1ccc9beed44e1a8e6ca9bd70c106a4', 'token': '6a1ccc9beed44e1a8e6ca9bd70c106a4'} +2026-01-22 17:42:37,834 [tid:25516 pid:29772] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'total': 309, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '21854845.72', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 17:42:37,835 [tid:25516 pid:29772] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'total': 309, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '21854845.72', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 17:45:22,475 [tid:25244 pid:4104] ZZYY_interface.py[line:41] INFO your input:{} +2026-01-22 17:45:22,476 [tid:25244 pid:4104] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 17:45:22,477 [tid:25244 pid:4104] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 17:45:22,478 [tid:25244 pid:4104] runner.py[line:117] INFO 请求数据:{} +2026-01-22 17:45:22,478 [tid:25244 pid:4104] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 17:45:22,792 [tid:25244 pid:4104] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '4294e9ca820740459358f961131f7a5f', 'tenant-id': '1', 'ssotoken': '4294e9ca820740459358f961131f7a5f', 'sso-token': '4294e9ca820740459358f961131f7a5f', 'Accesstoken': '4294e9ca820740459358f961131f7a5f', 'access-token': '4294e9ca820740459358f961131f7a5f', 'token': '4294e9ca820740459358f961131f7a5f'} +2026-01-22 17:45:22,957 [tid:25244 pid:4104] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 17:45:22,958 [tid:25244 pid:4104] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 17:45:24,878 [tid:25244 pid:4104] ZZYY_interface.py[line:58] INFO your input:{'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'} +2026-01-22 17:45:24,879 [tid:25244 pid:4104] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 17:45:24,881 [tid:25244 pid:4104] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-order/page +2026-01-22 17:45:24,882 [tid:25244 pid:4104] runner.py[line:117] INFO 请求数据:{'json': {'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'}} +2026-01-22 17:45:24,884 [tid:25244 pid:4104] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 17:45:25,187 [tid:25244 pid:4104] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'd7678d0a3f3b4ba88c73d81f9d6cfee6', 'tenant-id': '1', 'ssotoken': 'd7678d0a3f3b4ba88c73d81f9d6cfee6', 'sso-token': 'd7678d0a3f3b4ba88c73d81f9d6cfee6', 'Accesstoken': 'd7678d0a3f3b4ba88c73d81f9d6cfee6', 'access-token': 'd7678d0a3f3b4ba88c73d81f9d6cfee6', 'token': 'd7678d0a3f3b4ba88c73d81f9d6cfee6'} +2026-01-22 17:45:25,399 [tid:25244 pid:4104] runner.py[line:192] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'total': 309, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '21854845.72', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 17:45:25,401 [tid:25244 pid:4104] runner.py[line:225] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'total': 309, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '21854845.72', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 18:10:00,929 [tid:13336 pid:16304] ZZYY_interface.py[line:41] INFO your input:{} +2026-01-22 18:10:00,930 [tid:13336 pid:16304] runner.py[line:115] INFO 登录系统为None,用户名为手动输入:purchase +2026-01-22 18:10:00,932 [tid:13336 pid:16304] runner.py[line:116] INFO 请求地址:server not exist/erp/purchase-workbench/get-todo +2026-01-22 18:10:00,933 [tid:13336 pid:16304] runner.py[line:117] INFO 请求数据:{} +2026-01-22 18:10:00,934 [tid:13336 pid:16304] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 18:10:01,347 [tid:13336 pid:16304] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '9394c1f6166244d09dd2d51afe590990', 'tenant-id': '1', 'ssotoken': '9394c1f6166244d09dd2d51afe590990', 'sso-token': '9394c1f6166244d09dd2d51afe590990', 'Accesstoken': '9394c1f6166244d09dd2d51afe590990', 'access-token': '9394c1f6166244d09dd2d51afe590990', 'token': '9394c1f6166244d09dd2d51afe590990'} +2026-01-22 18:10:01,349 [tid:13336 pid:16304] runner.py[line:63] INFO 返回数据:Invalid URL 'server not exist/erp/purchase-workbench/get-todo': No schema supplied. Perhaps you meant http://server not exist/erp/purchase-workbench/get-todo? +2026-01-22 18:10:01,622 [tid:13336 pid:16304] ZZYY_interface.py[line:58] INFO your input:{'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'} +2026-01-22 18:10:01,623 [tid:13336 pid:16304] runner.py[line:115] INFO 登录系统为None,用户名为手动输入:purchase +2026-01-22 18:10:01,624 [tid:13336 pid:16304] runner.py[line:116] INFO 请求地址:server not exist/erp/purchase-order/page +2026-01-22 18:10:01,625 [tid:13336 pid:16304] runner.py[line:117] INFO 请求数据:{'json': {'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'}} +2026-01-22 18:10:01,626 [tid:13336 pid:16304] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 18:10:01,879 [tid:13336 pid:16304] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '4a3a7f4cda59456cb62b46d2769c614b', 'tenant-id': '1', 'ssotoken': '4a3a7f4cda59456cb62b46d2769c614b', 'sso-token': '4a3a7f4cda59456cb62b46d2769c614b', 'Accesstoken': '4a3a7f4cda59456cb62b46d2769c614b', 'access-token': '4a3a7f4cda59456cb62b46d2769c614b', 'token': '4a3a7f4cda59456cb62b46d2769c614b'} +2026-01-22 18:10:01,880 [tid:13336 pid:16304] runner.py[line:63] INFO 返回数据:Invalid URL 'server not exist/erp/purchase-order/page': No schema supplied. Perhaps you meant http://server not exist/erp/purchase-order/page? +2026-01-22 18:12:50,609 [tid:11832 pid:30004] ZZYY_interface.py[line:41] INFO your input:{} +2026-01-22 18:12:50,611 [tid:11832 pid:30004] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 18:12:50,611 [tid:11832 pid:30004] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 18:12:50,612 [tid:11832 pid:30004] runner.py[line:117] INFO 请求数据:{} +2026-01-22 18:12:50,613 [tid:11832 pid:30004] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 18:12:50,895 [tid:11832 pid:30004] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '361ae11fc6644a0dbff07be54d360d40', 'tenant-id': '1', 'ssotoken': '361ae11fc6644a0dbff07be54d360d40', 'sso-token': '361ae11fc6644a0dbff07be54d360d40', 'Accesstoken': '361ae11fc6644a0dbff07be54d360d40', 'access-token': '361ae11fc6644a0dbff07be54d360d40', 'token': '361ae11fc6644a0dbff07be54d360d40'} +2026-01-22 18:12:51,121 [tid:11832 pid:30004] runner.py[line:195] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769076000023, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 18:12:51,122 [tid:11832 pid:30004] runner.py[line:228] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769076000023, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 18:12:52,928 [tid:11832 pid:30004] ZZYY_interface.py[line:58] INFO your input:{'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'} +2026-01-22 18:12:52,930 [tid:11832 pid:30004] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 18:12:52,931 [tid:11832 pid:30004] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-order/page +2026-01-22 18:12:52,932 [tid:11832 pid:30004] runner.py[line:117] INFO 请求数据:{'json': {'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'}} +2026-01-22 18:12:52,933 [tid:11832 pid:30004] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 18:12:53,198 [tid:11832 pid:30004] runner.py[line:176] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'd90fba0295a64448bf4e09e6549f2a94', 'tenant-id': '1', 'ssotoken': 'd90fba0295a64448bf4e09e6549f2a94', 'sso-token': 'd90fba0295a64448bf4e09e6549f2a94', 'Accesstoken': 'd90fba0295a64448bf4e09e6549f2a94', 'access-token': 'd90fba0295a64448bf4e09e6549f2a94', 'token': 'd90fba0295a64448bf4e09e6549f2a94'} +2026-01-22 18:12:53,471 [tid:11832 pid:30004] runner.py[line:195] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'total': 308, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '21844061.26', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 18:12:53,473 [tid:11832 pid:30004] runner.py[line:228] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'total': 308, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '21844061.26', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 18:15:47,526 [tid:11168 pid:26872] ZZYY_interface.py[line:41] INFO your input:{} +2026-01-22 18:15:47,528 [tid:11168 pid:26872] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 18:15:47,530 [tid:11168 pid:26872] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 18:15:47,532 [tid:11168 pid:26872] runner.py[line:117] INFO 请求数据:{} +2026-01-22 18:15:47,533 [tid:11168 pid:26872] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 18:15:47,868 [tid:11168 pid:26872] runner.py[line:183] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '708c8b0c29cb4fb88d1ea2537d26d3a8', 'tenant-id': '1', 'ssotoken': '708c8b0c29cb4fb88d1ea2537d26d3a8', 'sso-token': '708c8b0c29cb4fb88d1ea2537d26d3a8', 'Accesstoken': '708c8b0c29cb4fb88d1ea2537d26d3a8', 'access-token': '708c8b0c29cb4fb88d1ea2537d26d3a8', 'token': '708c8b0c29cb4fb88d1ea2537d26d3a8'} +2026-01-22 18:15:48,049 [tid:11168 pid:26872] runner.py[line:202] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769076000023, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 18:15:48,050 [tid:11168 pid:26872] runner.py[line:235] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769076000023, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 18:15:49,889 [tid:11168 pid:26872] ZZYY_interface.py[line:58] INFO your input:{'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'} +2026-01-22 18:15:49,890 [tid:11168 pid:26872] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 18:15:49,891 [tid:11168 pid:26872] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-order/page +2026-01-22 18:15:49,892 [tid:11168 pid:26872] runner.py[line:117] INFO 请求数据:{'json': {'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'}} +2026-01-22 18:15:49,893 [tid:11168 pid:26872] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 18:15:50,159 [tid:11168 pid:26872] runner.py[line:183] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'c293551dfb6e46cfb3245139d174667c', 'tenant-id': '1', 'ssotoken': 'c293551dfb6e46cfb3245139d174667c', 'sso-token': 'c293551dfb6e46cfb3245139d174667c', 'Accesstoken': 'c293551dfb6e46cfb3245139d174667c', 'access-token': 'c293551dfb6e46cfb3245139d174667c', 'token': 'c293551dfb6e46cfb3245139d174667c'} +2026-01-22 18:15:50,367 [tid:11168 pid:26872] runner.py[line:202] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'total': 308, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '21844061.26', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 18:15:50,395 [tid:11168 pid:26872] runner.py[line:235] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'total': 308, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '21844061.26', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 18:16:38,065 [tid:23948 pid:20832] ZZYY_interface.py[line:41] INFO your input:{} +2026-01-22 18:16:38,066 [tid:23948 pid:20832] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 18:16:38,067 [tid:23948 pid:20832] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-workbench/get-todo +2026-01-22 18:16:38,068 [tid:23948 pid:20832] runner.py[line:117] INFO 请求数据:{} +2026-01-22 18:16:38,069 [tid:23948 pid:20832] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 18:16:38,404 [tid:23948 pid:20832] runner.py[line:183] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '02b6ecc7c6ec4c6198a477c96166cc53', 'tenant-id': '1', 'ssotoken': '02b6ecc7c6ec4c6198a477c96166cc53', 'sso-token': '02b6ecc7c6ec4c6198a477c96166cc53', 'Accesstoken': '02b6ecc7c6ec4c6198a477c96166cc53', 'access-token': '02b6ecc7c6ec4c6198a477c96166cc53', 'token': '02b6ecc7c6ec4c6198a477c96166cc53'} +2026-01-22 18:16:38,594 [tid:23948 pid:20832] runner.py[line:202] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769076000023, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 18:16:38,595 [tid:23948 pid:20832] runner.py[line:235] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'todoTask': [{'todoType': 0, 'title': 'HK采购单', 'businessId': 45779, 'businessSn': 'PO260120036', 'todoTime': 1769076000023, 'content': '包含SKU:1个,总数:200', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45781, 'businessSn': 'PO260120035', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:360', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45774, 'businessSn': 'PO260120038', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45777, 'businessSn': 'PO260120037', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:288', 'redundancy': None}, {'todoType': 0, 'title': 'HK采购单', 'businessId': 45783, 'businessSn': 'PO260120034', 'todoTime': 1769050801755, 'content': '包含SKU:1个,总数:359', 'redundancy': None}], 'inProcessTask': []}} +2026-01-22 18:16:40,741 [tid:23948 pid:20832] ZZYY_interface.py[line:58] INFO your input:{'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'} +2026-01-22 18:16:40,769 [tid:23948 pid:20832] runner.py[line:115] INFO 登录系统为smart-management-api-dev.best-envision.com,用户名为手动输入:purchase +2026-01-22 18:16:40,770 [tid:23948 pid:20832] runner.py[line:116] INFO 请求地址:https://smart-management-api-dev.best-envision.com/admin-api/erp/purchase-order/page +2026-01-22 18:16:40,771 [tid:23948 pid:20832] runner.py[line:117] INFO 请求数据:{'json': {'pageNo': 1, 'pageSize': 10, 'supplier_company_ids': ['334'], 'payment_status': '0', 'status': '0', 'order_sn': 'PO251209048'}} +2026-01-22 18:16:40,772 [tid:23948 pid:20832] runner.py[line:140] INFO 使用配置文件中的登录信息进行登录 +2026-01-22 18:16:41,044 [tid:23948 pid:20832] runner.py[line:183] INFO 请求头headers:{'User-Agent': 'python-requests/2.25.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'd1d822350e7c4c2dbd29873ed04fbfd2', 'tenant-id': '1', 'ssotoken': 'd1d822350e7c4c2dbd29873ed04fbfd2', 'sso-token': 'd1d822350e7c4c2dbd29873ed04fbfd2', 'Accesstoken': 'd1d822350e7c4c2dbd29873ed04fbfd2', 'access-token': 'd1d822350e7c4c2dbd29873ed04fbfd2', 'token': 'd1d822350e7c4c2dbd29873ed04fbfd2'} +2026-01-22 18:16:41,263 [tid:23948 pid:20832] runner.py[line:202] INFO ------状态码:200, 返回信息:{'code': 0, 'msg': '', 'data': {'total': 308, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '21844061.26', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} +2026-01-22 18:16:41,264 [tid:23948 pid:20832] runner.py[line:235] INFO 返回数据:{'code': 0, 'msg': '', 'data': {'total': 308, 'list': [{'id': 47489, 'orderSn': 'PO251209021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10780445, "name": "王焱"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 349, 'supplierName': '东莞市英爵实业有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1765260611000, 'totalQuantity': 470, 'totalAmount': 24681.8, 'totalPrice': 24681.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 470, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1765260128000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47465, 'orderSn': 'PO251223018', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 129, 'supplierName': '安芮健康科技(东莞)有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 700, 'totalAmount': 23100.0, 'totalPrice': 23100.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 700, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47463, 'orderSn': 'PO251223019', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 1232, 'totalAmount': 55264.0, 'totalPrice': 55264.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 1232, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47461, 'orderSn': 'PO251223020', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 409, 'supplierName': '深圳市享乐健康科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469720000, 'totalQuantity': 108, 'totalAmount': 6652.8, 'totalPrice': 6652.8, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 108, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47459, 'orderSn': 'PO251223021', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 355, 'supplierName': '惠州鸿晶科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 420, 'totalAmount': 14982.0, 'totalPrice': 14982.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 420, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 2}, {'id': 47457, 'orderSn': 'PO251223022', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 392, 'supplierName': '东莞市圣蓓电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 240, 'totalAmount': 11772.0, 'totalPrice': 11772.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': '', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 240, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462060000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47455, 'orderSn': 'PO251223023', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 236, 'supplierName': '东莞市梦马电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 160, 'totalAmount': 9766.4, 'totalPrice': 9766.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 160, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47453, 'orderSn': 'PO251223024', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10384696, "name": "胡利娟"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 419, 'supplierName': '东莞纵趣电子科技有限公司', 'warehouseId': 10152, 'warehouseName': '深圳中转仓-贸易FBA-JP', 'orderTime': 1766469719000, 'totalQuantity': 120, 'totalAmount': 10856.4, 'totalPrice': 10856.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 120, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462061000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47451, 'orderSn': 'PO251223025', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 10430026, "name": "余永艳"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 334, 'supplierName': '东莞市奕旭科技有限公司', 'warehouseId': 10156, 'warehouseName': '深圳中转仓-贸易FBA-EU', 'orderTime': 1766469719000, 'totalQuantity': 348, 'totalAmount': 34452.0, 'totalPrice': 34452.0, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 348, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 1}, {'id': 47449, 'orderSn': 'PO251223026', 'purchaserId': 143, 'purchaserName': '孙丽萍', 'principalIds': '[{"id": 104, "name": "方敏"}, {"id": 10430026, "name": "余永艳"}, {"id": 107, "name": "欧晓岚"}, {"id": 10378449, "name": "孙丽萍"}]', 'purchaseCompanyId': 101, 'purchaseCompanyName': '深圳易威行贸易有限公司', 'supplierId': 135, 'supplierName': '东莞市思你电子科技有限公司', 'warehouseId': 0, 'warehouseName': '多仓库采购', 'orderTime': 1766469719000, 'totalQuantity': 18362, 'totalAmount': 506158.4, 'totalPrice': 506158.4, 'settlementMethod': 'MONTHLY_SETTLEMENT', 'isTaxIncluded': 1, 'currencyCode': 'CNY', 'paymentMethod': 'ONLINE_BANKING', 'advanceRatio': 0.0, 'contractId': 0, 'relatedPlans': '[]', 'attachmentId': 0, 'inboundStatus': 0, 'inboundStatusText': '未入库', 'inboundQuantity': 0, 'pendingInboundQuantity': 18362, 'paymentStatus': 0, 'paymentStatusText': '未支付', 'status': 0, 'statusText': '待创建合同', 'cancelReason': None, 'remark': None, 'createTime': 1766462313000, 'userId': 143, 'deptId': 106, 'inboundTime': None, 'skuNum': 15}], 'redundancy1': '21844061.26', 'redundancy2': None, 'redundancy3': None, 'redundancy4': None}} diff --git a/base_framework/.gitignore b/base_framework/.gitignore new file mode 100644 index 0000000..eec981e --- /dev/null +++ b/base_framework/.gitignore @@ -0,0 +1,17 @@ +/.idea/workspace.xml +**/pz_all_server_ip.ini +**/*run.log* +**/.__run.lock +*/.idea +**/.idea/* +**/__pycache__/* +**/*.pyc +**/results/* +**/db_config.ini/* +*.pyc +**/interface.txt +**/case_result/* +*.project +*.txt +*.json +**/user.txt \ No newline at end of file diff --git a/base_framework/__init__.py b/base_framework/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/base_framework/base_config/config.ini b/base_framework/base_config/config.ini new file mode 100644 index 0000000..bc086cc --- /dev/null +++ b/base_framework/base_config/config.ini @@ -0,0 +1,18 @@ +# coding=utf-8 + +# QA环境 +[QA] +zhyy = https://smart-management-api-dev.best-envision.com/admin-api +zhyy_login = https://smart-management-api-dev.best-envision.com/admin-api/system/auth/login +ZZYY = https://smart-management-api-dev.best-envision.com/admin-api + +# uat环境 +[UAT] +zhyy = https://smart-management-api-pre.best-envision.com/admin-api/ +zhyy_login = https://smart-management-api-pre.best-envision.com/admin-api/system/auth/login + + +[team_user_list] +ASTWB_owner = 18202810506 +谯新久 = 18202810506 +ASTWB_to_dd = 2079cb3e311ab6a37138a6d4671181661d211c12a1532c2012ae7e6b397996c3 diff --git a/base_framework/base_config/config_hh_qa.ini b/base_framework/base_config/config_hh_qa.ini new file mode 100644 index 0000000..dca7458 --- /dev/null +++ b/base_framework/base_config/config_hh_qa.ini @@ -0,0 +1,33 @@ +# coding=utf-8 +[PostgreSQL] +db_test_host = 39.170.26.156 +db_test_port = 8566 +db_test_dbname = smart_management_st +db_test_user = sm_test_user +db_test_password = Test@736141 +db_charset = utf8 +db_min_cached = 10 +db_max_cached = 20 +db_max_shared = 20 +db_max_connecyions = 100 +db_blocking = True +db_max_usage = 5 +db_set_session = None + +[jwadmin] +tenant = 境外租户空间 +show_username = 超级管理员 +username = jwadmin +password = 1qaz@WSX + +[purchase] +tenant = 境内组织 +show_username = 孙丽萍 +username = sunliping01 +password = 1qaz@WSX + +[supply] +tenant = 境内组织 +show_username = 东莞艾斯保健用品有限公司 +username = SU00014 +password = 1qaz#WSX \ No newline at end of file diff --git a/base_framework/base_config/config_hh_sim.ini b/base_framework/base_config/config_hh_sim.ini new file mode 100644 index 0000000..3d4659e --- /dev/null +++ b/base_framework/base_config/config_hh_sim.ini @@ -0,0 +1,19 @@ +# coding=utf-8 + +[jwadmin] +tenant = 境外租户空间 +show_username = 超级管理员 +username = jwadmin +password = 1qaz@WSX + +[purchase] +tenant = 境内组织 +show_username = 孙丽萍 +username = sunliping01 +password = 1qaz@WSX + +[supply] +tenant = 境内组织 +show_username = 东莞艾斯保健用品有限公司 +username = SU00014 +password = 1qaz#WSX \ No newline at end of file diff --git a/base_framework/base_config/current_pth.py b/base_framework/base_config/current_pth.py new file mode 100644 index 0000000..0273821 --- /dev/null +++ b/base_framework/base_config/current_pth.py @@ -0,0 +1,31 @@ +# coding=utf-8 + +import os +import configparser + +# 配置文件路径 +HERE = os.path.dirname(os.path.abspath(__file__)) +ROOT_PATH = os.path.abspath(os.path.join(HERE, '../../')) +ASTWB_PATH = os.path.abspath(os.path.join(HERE, '../../ASTWB/library/Config/')) +env_choose_path = os.path.join(HERE, 'env_choose.ini') +config_choose_path = os.path.join(HERE, 'config.ini') +la_config_path = os.path.join(HERE, '../platform_tools/jira_tools/la_config.ini') +config_evn_path = os.path.join(HERE, 'current_pth.py') +server_ip_path = os.path.join(HERE, 'server_ip.ini') +log_path = os.path.join(ROOT_PATH, 'Log') +# 根据启动参数来加载对应的配置文件 +cof = configparser.ConfigParser() +cof.read(env_choose_path) +current_business = cof['run_evn_name']['current_business'].lower() +current_evn = cof['run_evn_name']['current_evn'].lower() +config_file_path = os.path.join(HERE, 'config_{0}_{1}.ini'.format(current_business, current_evn)) +db_config_path = config_file_path + +pz_all_server_ip_path = os.path.join(HERE, 'pz_all_server_ip.ini') +swagger_choose_path = os.path.join(HERE, 'swagger_url.ini') +astwb_config = os.path.join(ASTWB_PATH, 'team_config.ini') +uat_config_path = os.path.abspath(os.path.join(HERE, 'uat_config')) + + +if __name__ == '__main__': + print(config_file_path) diff --git a/base_framework/base_config/db_config.ini b/base_framework/base_config/db_config.ini new file mode 100644 index 0000000..2159a6b --- /dev/null +++ b/base_framework/base_config/db_config.ini @@ -0,0 +1,45 @@ +[Mysql] +db_test_host = mysql.qa.huohua.cn +db_all_school_test_host = qa-my-asc.mysql.rds.aliyuncs.com +db_hhi_test_host = qa-my-hhi.mysql.rds.aliyuncs.com +db_test_port = 3306 +db_test_dbname = crmthirdparty +db_all_school_test_dbname = hulk_content_audit +db_hhi_test_dbname = hhi_account +db_test_user = qa-dev +db_test_password = jaeg3SCQt0 +db_charset = utf8 +db_min_cached = 10 +db_max_cached = 20 +db_max_shared = 20 +db_max_connecyions = 100 +db_blocking = True +db_max_usage = 5 +db_set_session = None + + + +[MONGODB] +mongo_host = 10.250.100.106 +mongo_port = 20007 +mongo_user = hulk_teach_marketing_rw +mongo_passowrd = MmEzZmqatest + +[PgSql] +db_test_host = mysql.qa.huohua.cn +db_all_school_test_host = qa-my-asc.mysql.rds.aliyuncs.com +db_hhi_test_host = qa-my-hhi.mysql.rds.aliyuncs.com +db_test_port = 3306 +db_test_dbname = crmthirdparty +db_all_school_test_dbname = hulk_content_audit +db_hhi_test_dbname = hhi_account +db_test_user = qa-dev +db_test_password = jaeg3SCQt0 +db_charset = utf8 +db_min_cached = 10 +db_max_cached = 20 +db_max_shared = 20 +db_max_connecyions = 100 +db_blocking = True +db_max_usage = 5 +db_set_session = None \ No newline at end of file diff --git a/base_framework/base_config/env_choose.ini b/base_framework/base_config/env_choose.ini new file mode 100644 index 0000000..bad0079 --- /dev/null +++ b/base_framework/base_config/env_choose.ini @@ -0,0 +1,14 @@ +[run_evn_name] +current_business = hh +current_evn = qa +current_team = ZZYY + +[run_jira_id] +huohua-podenv = qa + +[is_ip_from_ini] +is_ip_from_ini = false + +[run_user_name] +default_user = jwadmin + diff --git a/base_framework/base_config/swagger_url.ini b/base_framework/base_config/swagger_url.ini new file mode 100644 index 0000000..8915080 --- /dev/null +++ b/base_framework/base_config/swagger_url.ini @@ -0,0 +1,2 @@ +[ZZYY] +zhyy = http://39.170.26.156:8380/swagger-ui/index.html#/ \ No newline at end of file diff --git a/base_framework/logs/database.log b/base_framework/logs/database.log new file mode 100644 index 0000000..4cf83e8 --- /dev/null +++ b/base_framework/logs/database.log @@ -0,0 +1 @@ +2026-01-22 11:35:43,802 - database - ERROR - ݿӳسʼʧ: δҵļԵ·: ['config/database.ini', '../config/database.ini', '../../config/database.ini', WindowsPath('C:/Users/hasee/.config/database.ini')] diff --git a/base_framework/main.py b/base_framework/main.py new file mode 100644 index 0000000..f6bd286 --- /dev/null +++ b/base_framework/main.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- + +from robotremoteserver import RobotRemoteServer +from library import KwLibrary +import platform +import os +import signal + +def main(): + RobotRemoteServer(library=KwLibrary(), host='0.0.0.0', port=9999) + +def kill_pid(): + """ + 功能:杀掉旧的进程 + """ + sys = platform.system() + if sys == "Windows": + with os.popen('netstat -aon|findstr "9999"') as res: + res = res.read().split('\n') + result = [] + for line in res: + temp = [i for i in line.split(' ') if i != ''] + if len(temp) > 4 and temp[1] == "0.0.0.0:9999": + result.append( + {'pid': temp[4], 'address': temp[1], 'state': temp[3]}) + os.popen("taskkill -pid {} -f".format(temp[4])) + else: + pid = os.popen( + "sudo -i netstat -nlp | grep :9999 | awk '{print $7}' | awk -F '/' '{ print $1 }'", + 'r', + 1).read().strip('\n') + if pid != "": + os.kill(int(pid), signal.SIGKILL) \ No newline at end of file diff --git a/base_framework/platform_tools/Jenkins/check_jenkins_build_results.py b/base_framework/platform_tools/Jenkins/check_jenkins_build_results.py new file mode 100644 index 0000000..20282b5 --- /dev/null +++ b/base_framework/platform_tools/Jenkins/check_jenkins_build_results.py @@ -0,0 +1,55 @@ +# -*- coding:utf-8 -*- +# 检查jenkins上的用例构建结果,并根据用例失败数量,将结果设置为通过或失败 +import sys +import os +HERE = os.path.dirname(os.path.abspath(__file__)) +WORKSPACE = os.path.abspath(os.path.join(HERE, '../../../')) +sys.path.append(WORKSPACE) +from base_framework.public_tools.xml_file_api import XmlFileApi +PROJECT_NAME = sys.argv[0] + +# 指定 Robot Framework 输出文件的路径 +HERE = os.path.dirname(os.path.abspath(__file__)) +output_file = os.path.abspath(os.path.join(HERE, '../../../Report/out/output.xml')) + +# 解析输出文件, 获取失败用例数量 +xf = XmlFileApi(file_path=output_file) +result = xf.get_contain_by_tag_names(tag_names='statistics/total/stat') +failed_count = int(result[0]['attrib']['fail']) + +# 按失败用例对应的qa人员标签, +if failed_count > 0: + result = xf.get_contain_by_tag_names(tag_names='statistics/tag/stat') + qa_info = {} + qa_failed_case_number = 0 + for item in result: + if 'qa-' in item['text'].lower(): + fail_case = int(item['attrib']['fail']) + if fail_case > 0: + qa_info[item['text'][3:]] = fail_case + qa_failed_case_number += fail_case + if len(qa_info) > 0: + print("失败用例对应的QA人员及失败用例数量:") + for key, value in qa_info.items(): + print(f"{key}: {value}个") + if failed_count > qa_failed_case_number: + print(f"无qa标签:{failed_count - qa_failed_case_number}个") + +# 根据失败用例数量设置构建结果 +if "-IT" in PROJECT_NAME: # it用例失败用例大于5个就标红 + if failed_count > 5: + print(f"测试用例执行失败,共有 {failed_count} 个失败的用例。") + sys.exit(1) # 退出码为1,表示构建失败,构建结果将展示为红色 + elif failed_count > 0: + print("测试用例小范围失败,但是不影响整体结果。") + sys.exit(0) # 退出码为0,表示构建成功,但此时受jenkins构建后步骤限制,将展示为黄色 + else: + print("测试用例执行成功,所有用例都通过了。") + sys.exit(0) # 退出码为0,表示构建成功,构建结果将展示为绿色 +else: # st和smoking有失败用例就标红 + if failed_count > 0: + print(f"测试用例执行失败,共有 {failed_count} 个失败的用例。") + sys.exit(1) # 退出码为1,表示构建失败,构建结果将展示为红色 + else: + print("测试用例执行成功,所有用例都通过了。") + sys.exit(0) # 退出码为0,表示构建成功,构建结果将展示为绿色 diff --git a/base_framework/platform_tools/Jenkins/jenkins_api.py b/base_framework/platform_tools/Jenkins/jenkins_api.py new file mode 100644 index 0000000..b9151ef --- /dev/null +++ b/base_framework/platform_tools/Jenkins/jenkins_api.py @@ -0,0 +1,68 @@ +# coding: utf-8 +from base_framework.public_tools.selenium_api import SeleniumWebUI +import time + + +class JenkinsAPI(SeleniumWebUI): + def __init__(self): + SeleniumWebUI.__init__(self) + self.project_info = {} + + def jenkins_login(self, u_name=None, u_pwd=None): + """ 登陆jenkins """ + if not u_name: + u_name = "admin" + u_pwd = "admin" + login_url = "http://39.170.26.156:8256/jenkins/login?from=%2Fjenkins%2F" + self.web_open_url(url=login_url) + self.web_send_keys(locator="//*[@id=\"j_username\"]", text=u_name) + self.web_send_keys(locator="//*[@name=\"j_password\"]", text=u_pwd) + self.web_click_element(locator="//*[@name=\"Submit\"]") + time.sleep(1) # 切换到构建墙 + self.web_click_element(locator="//*[text()='1-用户产品组']") + time.sleep(1) # 显示用例数量 + self.web_click_element(locator="//*[@title=\"Configure Build Monitor Settings\"]") + time.sleep(1) + self.web_click_checkbox(locator="//*[@id=\"settings-show-test-result\"]") + self.web_click_checkbox(locator="//button[text()=\"Done\"]") + + def jenkins_find_all_case_number(self): + """ 从构建墙上读取用例数量 """ + elements = self.web_find_elements(locator="//li[@ng-repeat=\"project in jobs track by project.hashCode\"]") + for element in elements: + item = element.text.split('\n') + case_number = item[1].split('/') + self.project_info[item[0]] = case_number[1] + + def jenkins_edit_project_config(self, **kwargs): + """ 编辑工程配置信息 """ + for jp in self.project_info: + case_number = int(self.project_info[jp]) + if jp == "TO-IT": + jp = "TO-IT-TEST" + elif jp == "TO-SMOKING": + jp = "TO-SMOKING_NEW" + project_url = "http://10.250.200.1:8080/jenkins/view/1-%E7%94%A8%E6%88%B7%E4%BA%A7%E5%93%81%E7%BB%84/" \ + "job/{}/configure".format(jp) + if "SMOKING" in jp or "-ST" in jp: + threshold = 99.999999 # 冒烟和ST用例需全部构建成功 + else: + threshold = round((1-5.5/case_number)*100, 4) # 阈值 + self.web_open_url(url=project_url) # 进入设置页面 + self.web_send_keys(locator="//input[@name=\"_.unstableThreshold\"]", text=str(threshold)) + self.web_click_checkbox(locator="//button[text()=\"保存\"]") + print("{}设置阈值{}完毕....".format(jp, threshold)) + time.sleep(1) + + +if __name__ == '__main__': + ja = JenkinsAPI() + ja.jenkins_login() + time.sleep(3) + ja.jenkins_find_all_case_number() + ja.jenkins_edit_project_config() + time.sleep(3) + ja.web_close_browser() + + + diff --git a/base_framework/platform_tools/Keywords_service/__init__.py b/base_framework/platform_tools/Keywords_service/__init__.py new file mode 100644 index 0000000..f0035fb --- /dev/null +++ b/base_framework/platform_tools/Keywords_service/__init__.py @@ -0,0 +1 @@ +special_team = ["EN", "PZ", "ASTOP", "ASOPE", "ASTWB", "ASORG", "UBRD", "UBJ", "ES", "ASTEA", 'GUE', "TMO", "PB"] diff --git a/base_framework/platform_tools/Keywords_service/keyword_service.py b/base_framework/platform_tools/Keywords_service/keyword_service.py new file mode 100644 index 0000000..9facc3e --- /dev/null +++ b/base_framework/platform_tools/Keywords_service/keyword_service.py @@ -0,0 +1,684 @@ +# -*- coding:utf-8 -*- +import copy +import importlib +import importlib.util as i_util +import inspect +import json +import os +import re +import sys +import traceback +from base_framework.platform_tools.Keywords_service import special_team + +token_false_server = ["scm-server", "scm-biz-server", "peppa-qi-api", "lggzt", "cti-manage", "ccwx-api", "peppa-la-core-server"] +add_api_list = ["peppa-teach-api"] +host_servers = ["HULK-ORG-API", "HULK-TEACHER-API", "PEPPA-LA-CORE-SERVER"] +host_servers_except_api = ["HULK-CONTENT-AUDIT-SERVER"] +header_servers = ["hulk-operation-api-server", "hulk-teach-supply-cli-api", "hulk-teach-backend-api"] +uid_server = ['SPARKEDU-API', 'SPARKEDU-SITE-API', 'HUOHUA-SERVICE-API'] +eid_server = ['SPARKEDU-SITE-MANAGE', 'SPARKEDU-SITE-SCHEDULER'] + +def check_server_host(servers, host): + """ + 检查不需要token的server是否存在url中 + """ + for server in servers: + if server in host: + return True + else: + return False + + +def get_project_root_path(path="base_framework"): + """ + 获取项目目录 + """ + o_path = os.getcwd() + try: + project_path = re.search(r"(.*%s)" % path, o_path).group(1) + return os.path.abspath(os.path.join(project_path, os.path.pardir)) + except: + pass + + +Project_Path = get_project_root_path() + +from base_framework.public_tools.log import get_logger +from base_framework.public_tools.sqlhelper import MySqLHelper + +db = MySqLHelper() +log = get_logger() + + +def get_classes(module): + classes = [] + cls_members = inspect.getmembers(module, inspect.isclass) + for (name, _) in cls_members: + classes.append(name) + return classes + + +def get_module_by_file_path(file_path): + spec = importlib.util.spec_from_file_location("module_name", file_path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +class SetAttr(object): + + def __init__(self, paras): + for key, values in paras.items(): + self._add_attr(key, values) + + def _add_attr(self, key, value): + self.__setattr__(key, value) + + +class SwaggerDBInfo(object): + + def __init__(self): + pass + + @staticmethod + def get_team_swagger_info_by_team(teamname): + sql = "SELECT * FROM sparkatp.swagger_info where team = '%s'" % teamname + return db.select_all(sql, choose_db="huohua") + + @staticmethod + def get_team_swagger_info_by_id(swagger_id): + sql = "SELECT * FROM sparkatp.swagger_info where id = '%s'" % swagger_id + return db.select_one(sql, choose_db="huohua") + + @staticmethod + def get_interface_info_by_url_and_type(request_url, request_type, interface_id): + sql = '''select * from sparkatp.interface_info where in_url = "%s" and type = "%s" and + id = %s''' % (request_url, request_type, interface_id) + return db.select_all(sql, choose_db="huohua") + + @staticmethod + def get_interface_parameters_by_interface_id(interface_id): + sql = """select * from sparkatp.request_parameters a right join sparkatp.parameters_relation b on + a.id = b.parameter_id where a.interface_id = {} and a.is_need != 2 and b.type = 'request'""".format( + interface_id) + return db.select_all(sql=sql, choose_db="huohua") + + @staticmethod + def get_interface_request_demo_info_by_interface_id(interface_id): + sql = 'SELECT * FROM sparkatp.parameters_demo where in_id = "%s" order by id DESC' % interface_id + return db.select_all(sql, choose_db="huohua") + + +class KWFileOperation(object): + """ + 操作xx_interface.py文件 + """ + + def __init__(self, team): + self.kw_file_path = os.path.abspath(os.path.join(Project_Path, + "{team}".format(team=team), "library", + "{team}_interface.py".format(team=team.upper()))) + self.all_keywords = [] + self.all_url = {} + self.kw_name_with_url = {} + self.runner = self._get_obj_runner() + self.team = team + self.kw_class = self._get_interface_class_name(self.kw_file_path) + + def _clean_demo_to_less_key(self, demo): + demo_result = copy.deepcopy(demo) + for key, value in demo.items(): + if not value: + demo_result.pop(key) + lenth = len(demo_result) + paras = "kwargs" + if lenth == 1 and isinstance(list(demo_result.values())[0], list): + paras = "args" + return lenth, demo_result, paras + + def _get_interface_class_name(self, file_path): + module = get_module_by_file_path(file_path) + classes = get_classes(module) + for class_ in classes: + if "interface" in class_.lower() and class_.lower().startswith(self.team.lower()): + return class_ + + def _get_obj_runner(self): + spec = i_util.spec_from_file_location("module_name", self.kw_file_path) + module = i_util.module_from_spec(spec) + spec.loader.exec_module(module) + runner = inspect.getmembers(module) + for module in runner: + if module[0] == "obj_runner": + return module[1] + return runner + + def _get_file_content(self): + with open(self.kw_file_path, "r", encoding="UTF-8") as f: + for line in f: + yield line.strip("\n") + + def re_write_all_keyword_to_py_file(self): + with open(self.kw_file_path, "r", encoding="UTF-8") as file: + all_content = file.readlines() + index = len(all_content) + + with open(self.kw_file_path, "r", encoding="UTF-8") as file1: + for line in file1: + if "if __name__" in line and "__main__" in line: + index = all_content.index(line) + break + + for i in range(index - 1, 0, -1): + s = all_content[i] + if all_content[i] != "\n": + index = i + all_content.insert(i + 1, "\n") + break + a = all_content[i + 1] + with open(self.kw_file_path, "w", encoding="UTF-8") as file: + file.writelines(all_content[:index + 1]) + + def get_all_exist_keywords(self): + for item in self._get_file_content(): + kw_match = re.search(r"def\s+(kw_in{1}_\w+)\(", item) + if kw_match: + self.all_keywords.append(kw_match.group(1)) + return self.all_keywords + + def get_all_exist_keywords_info(self): + for item in self._get_file_content(): + kw_match = re.search(r"(kw_in{1}_\w+)\(", item) + if kw_match: + self.all_keywords.append(kw_match.group(1)) + return self.all_keywords + + def write_content_to_interface_file(self, content, file_path=None): + if not file_path: + file_path = self.kw_file_path + with open(file_path, "a+", encoding="UTF-8") as f: + f.write(content) + + def generate_all_keyword_info(self): + """ + 获取interface文件内已有关键字的url请求地址和请求方法 + """ + temp_dict = {} + all_content = self._get_file_content() + server = "" + for line in all_content: + line_new = line + if "def kw_in_" in line: + kw_name = re.search("(kw_in_[\w+_]+)\(", line).group(1) + if "tools.get_container_ip_from_eureka" in line or "tool.get_container_ip_from_eureka" in line: + server = list(re.search("\([\"\']?([\w\-_]+)[\"\']?|\=[\"\']?([\w\-_]+)[\"\']?", line).groups()) + server.remove(None) + server = server[0] + if "self." in line and "=" in line: + temp_dict[line.split("=")[0].strip()] = line.split("=")[1].strip().strip("\"") + if " url =" in line or "url=" in line: + log.info(line) + try: + # urls = list(re.search(r"%s(/[\w+-_\}\{]+/?)\??|\}(/[\w+-_\}\{]+/?)\??", line).groups()) + match = re.search(r"%s(/[\w+-_\}\{]+/?)\??|\}(/[\w+-_\}\{]+/?)\??", line) + urls = list(match.groups()) if match else [] + except Exception as err: + traceback.print_exc() + continue + # urls.remove(None) + urls = [url for url in urls if url is not None] + if not urls: + continue + url = urls[0] + if "\\" in line: + line_new = all_content.__next__() + try: + host = re.search("obj_runner\.([\w\-_]+)", line_new).group(1).strip().strip(")").strip(",") + except: + if "self." in line: + if self.team.upper() in ["EN", "ASTWB", "ASOPE"]: + host_header = "https://" + else: + host_header = "http://" + server = re.search("self\.([\w\-_]+)", line_new).group(1).strip().strip(")").strip(",") + server = server.replace("_", "-").lower() + if self.team.upper() == 'TMO': + host = host_header + "%s" % 'swagger' + ".qa.huohua.cn" + elif server.upper() in host_servers: + if server.upper() == "HULK-ORG-API": + host = host_header + "%s" % server + ".qa.huohua.cn" + else: + host = host_header + "%s" % "api" + ".qa.allschool.com" + else: + if server.upper() in host_servers_except_api: + host = host_header + "%s" % server + ".qa.allschool.com" + else: + host = host_header + "%s" % server + ".qa.huohua.cn" + + if self.team.upper() == "TO": + host = "teach_opt_host" + if re.search("\{\}/\{\}", line) and "self." in line: + para = re.search("(self\.[\w_]+)", line).group(1).strip().strip(")").strip(",") + url = temp_dict[para] + urls[0] + if "?" in url: + url = url.split("?")[0] + # if url.endswith("/"): + # url = url[:len(url) - 1] + + if server: + if self.team.upper() in special_team and self.team.upper() != 'TMO': + if url.startswith("/"): + url = host + "%s" % url + else: + url = host + "/%s" % url + elif self.team.upper() == 'TMO': + if url.startswith("/"): + url = '%s/%s%s' % (host, server, url) + else: + url = '%s/%s/%s' % (host, server, url) + else: + try: + if url.startswith("/"): + url = eval("self.runner.%s" % host) + "/%s" % server + url + else: + url = eval("self.runner.%s" % host) + "/%s/" % server + url + except Exception as err: + url = "empty_url" + else: + try: + if url.startswith("/"): + url1 = eval("self.runner.%s" % host) + url = url1 + url + else: + url = eval("self.runner.%s" % host) + url + except Exception as err: + url = "empty_url" + + if "req_type=" in line: + method = re.search('req_type="(\w+)"|req_type=\'(\w+)\'', line.replace("\'", "\"")).group(1) + self.all_url[url] = [method, kw_name] + server = "" + return self.all_url + + +class KWOperation(object): + """ + 根据数据库内容生成对应的keywords + """ + + def __init__(self, team, kw_instance, server=None): + self.team = team + self.doc_string_table_content_temp = " |{}|{}|{}|{}|{}|" + self.function_name = "" + self.help_doc = None + self.function_content = None + self.has_kw_list = [] + self.function_list = [] + self.file = kw_instance + self.all_keywords = self.file.get_all_exist_keywords() + self.all_url = self.file.generate_all_keyword_info() + self.kw_name = None + self.kw_string = None + self.kw_array = None + self.server = server + + def get_interface_demo(self, interface_id): + return SwaggerDBInfo.get_interface_request_demo_info_by_interface_id(interface_id) + + def get_interface_parameters(self, interface_id): + return SwaggerDBInfo.get_interface_parameters_by_interface_id(interface_id) + + def get_swagger_info_by_id(self, swagger_id): + return SwaggerDBInfo.get_team_swagger_info_by_id(swagger_id) + + def _check_kw_exist(self, keywordname): + if keywordname in self.all_keywords: + return False + else: + return True + + def generate_kw_name(self, interface_info, demo): + """ + 生成对应关键字方法名称如kw_in_to_get_leads_get(**kwargs) + """ + lenth, demo_result, paras_d = self.file._clean_demo_to_less_key(demo) + _, name_list, _, _ = self._generate_all_url_paramaters(interface_info.in_url) + if "" in name_list: + name_list.remove("") + name_list.append(interface_info.name) + for item in add_api_list: + if item in interface_info.in_url: + name_list[-1] = 'api_' + name_list[-1] + count = -1 + self.kw_name = "kw_in_{}_{}_{}".format(self.team.lower(), name_list[-1], interface_info.type.lower()) + while abs(count) <= len(name_list): + if self._check_kw_exist(self.kw_name): + if lenth > 1: + self.function_name = " def {kw_name}(self, **kwargs):".format(kw_name=self.kw_name) + else: + if self.team.upper() in ["TMO"]: + self.function_name = " def {kw_name}(self, **kwargs):".format(kw_name=self.kw_name) + else: + if demo_result: + if isinstance(list(demo_result.values())[0], list): + self.function_name = " def {kw_name}(self, *args, **kwargs):".format( + kw_name=self.kw_name) + else: + self.function_name = " def {kw_name}(self, **kwargs):".format( + kw_name=self.kw_name) + else: + self.function_name = " def {kw_name}(self, **kwargs):".format( + kw_name=self.kw_name) + self.has_kw_list.append(interface_info.id) + self.all_keywords.append(self.kw_name) + return True + else: + count -= 1 + self.kw_name += "_" + name_list[count] + else: + self.has_kw_list.append(interface_info.id) + return False + + def generate_kw_doc(self, interface_info, parameters_list): + """ + 根据接口参数信息生成对应的帮助文档 + """ + + def set_lenth(para): + return " " + str(para).strip() + " " + def get_normal_value(normal_values): + return normal_values.split(",")[0] if normal_values else 0 + + doc = [] + doc_string_head = " \"\"\"" + doc_string_table_head = " | {} | {} | {} | {} | {} |".format("请求参数名".ljust(30), "说明".ljust(15), + "类型".ljust(12), "是否必填".ljust(8), + "如无要求时的值".ljust(8)) + doc.append(doc_string_head) + doc.append(" | 功能说明:| {} |".format(interface_info.interface_describe)) + doc.append(doc_string_table_head) + # if self.team.upper() not in ["TMO"]: + # doc.append(self.doc_string_table_content_temp.format(set_lenth("is_check"), + # set_lenth("is_check默认空不校验返回,有值就校验返回"), + # set_lenth("string"), + # set_lenth("业务case的时候需要传入值"), + # set_lenth("False"))) + for parameter in parameters_list: + attr = SetAttr(parameter) + if int(attr.in_body) != 0 and int(attr.is_need) != 2: + doc.append(self.doc_string_table_content_temp.format(set_lenth(attr.name), set_lenth(attr.note), + set_lenth(attr.type), set_lenth(attr.is_need), + set_lenth(get_normal_value(attr.normal_values)))) + doc.append(doc_string_head) + return "\n".join(doc) + + def _generate_all_url_paramaters(self, url): + """ + 拆解url生成host, 生成keyword名的相关名称和url中带{}的参数 + """ + head_list = [] + para_list = [] + host, url_last = re.search(r"(\S+\.cn|\S+\.com)(\S+)", url).groups() + search = re.findall("[\w+\-_]+|[\{\w+\-_\}]+", url_last) + if self.team.upper() in ["TMO"]: + search.pop(0) + url_lasr_temp = url_last.split("/") + url_lasr_temp.pop(1) + url_last = '/'.join(url_lasr_temp) + for item in search: + if "{" in item: + temp = item.replace("{", "").replace("}", "") + para_list.append(temp) + else: + head_list.append(item) + return host, head_list, para_list, url_last + + def _generate_url_host(self, host): + """ + 根据host生成对应的url后缀,比如teach_opt_host + 如果需要将host替换成IP则生成对应的IP + """ + url_host = "" + if 'peppa-cc-manage' in host: + url_host = "cc_host" + elif 'crmv2' in host or 'smm' in host or 'xxljob' in host: + url_host = "crm_host" + elif 'scm' in host: + url_host = "scm_host" + elif "la-gate" in host: + url_host = "insights_host" + elif 'la-ai-api-bg' in host or 'la-api-ai' in host: + url_host = "spark_land_host" + elif 'manage' in host: + url_host = "manage_host" + elif 'teach' in host: + url_host = "teach_host" + elif 'teach-opt-api' in host or 'sparkle-manage' in host: + url_host = "teach_opt_host" + elif 'opengalaxy' in host: + url_host = "opengalaxy_host" + elif 'employee-manage' in host: + url_host = "ehr_host" + elif 'peppa-agent-manage' in host: + url_host = "hhr_host" + elif 'peppa-agent-api' in host: + url_host = "hhr_api_host" + elif "teach-message-api" in host: + url_host = "teacher_message_host" + elif "swagger" in host: + url_host = "swagger_host" + elif "peppa-qi-api" in host: + url_host = "qi_host" + elif "scm-server" in host: + url_host = "scm_server_host" + elif "scm-biz-server" in host: + url_host = "scm_biz_server_host" + elif "cti-manage" in host: + url_host = "cti_host" + elif "peppa-conversion-api" in host: + url_host = "uc_host" + elif "la-api" in host: + url_host = "la_api_host" + elif "hulk_content_audit_server" in host: + url_host = "hulk_content_audit" + elif "ccwx-api" in host: + url_host = "cc_weixin_host" + + return url_host + + def generate_function_url(self, interface_info, demo): + """ + 生成对应的url + """ + lenth, demo_result, paras_d = self.file._clean_demo_to_less_key(demo) + url_list = [] + user_kwargs = " user, kwargs = get_user(kwargs)" + url_list.append(user_kwargs) + check_json = " kwargs = convert_json(kwargs)" + if paras_d == "args": + check_json = " args = convert_json(args)" + url_list.append(check_json) + host, _, parameters, url_last = self._generate_all_url_paramaters(interface_info.in_url) + host = re.findall("[\w+-]+", host) + url_host = self._generate_url_host(host) + server = "" + # if self.team.upper() in ["TMO"]: + # url_list.append(" try:\n kwargs = eval(args[0])\n except:") + # url_list.append(" try:\n if args[0] and isinstance(args[0], dict):") + # url_list.append("kwargs = args[0]".rjust(20 + len("kwargs = args[0]"), " ")) + # url_list.append("except:".rjust(12 + len("except:"), " ")) + # url_list.append("kwargs = kwargs".rjust(16 + len("kwargs = kwargs"), " ")) + # url_last_tmp = url_last.split("/") + # server = url_last_tmp.pop(1) + # url_last = "/".join(url_last_tmp) + # ip = " ip = tools.get_container_ip_from_eureka(\"%s\", need_jira_id=True)" % server + # url_list.append(ip) + # host = " obj_runner." + url_host + " = \"http://\" + " + "ip[\"container_ip\"]" + " + \":8080\"" + # url_list.append(host) + if self.team.upper() in special_team: + if self.server.upper() in host_servers or self.team.upper() in ["TMO"]: + host_server = self.server.lower().replace("-", "_") + else: + host_server = host[1].replace("-", "_") + url_host = "self.%s" % host_server + else: + if self.server.upper() in host_servers or self.team.upper() in ["TMO"]: + host_server = self.server.lower().replace("-", "_") + url_host = "self.%s" % host_server + # if self.team.upper() == 'LALIVE' and 'la-api' in host: + # url_last = url_last.replace("smart", "api/smart") + if "PEPPA-QI-API".lower() in host: + ip = " obj_runner.%s = \"http://{}:8080\".format(self.get_container_ip('PEPPA-QI-API'))" % url_host + url_list.append(ip) + if parameters: + if lenth > 1: + temp = 'kwargs.get("u")' + else: + temp = 'kwargs' + url = " url = \"%s" + url_last + "\".format(" + for item in parameters: + url += "%s=%s, " % (item, "%s.get(\"%s\", \"\")" % (temp, item)) + url += ") % " + else: + url = " url = \"%s" + url_last + "\" % " + + if self.team.upper() in special_team or self.server.upper() in host_servers: + url += url_host + else: + url += "obj_runner.%s" % url_host + url_list.append(url) + return "\n".join(url_list), server + + def generate_function_body(self, interface_info, demo, parameters_all): + """ + 生成关键字的body内容 + """ + lenth, demo_result, paras_d = self.file._clean_demo_to_less_key(demo) + host, _, parameters, url_last = self._generate_all_url_paramaters(interface_info.in_url) + func_body = [] + if re.search(r"\*(\w+)", self.function_name): + paras = re.search(r"\*(\w+)", self.function_name).group(1) + if self.team.upper() in ["TMO"]: + paras = "kwargs" + content = ' obj_log.info("your input:{0}".format(%s))' % paras + func_body.append(content) + if self.kw_string: + func_body.append(" kwargs = kwargs.get('%s', '')" % parameters_all[0].get("name")) + if self.kw_array: + func_body.append(" kwargs = eval(kwargs.pop('%s'))" % parameters_all[0].get("name")) + else: + paras = None + if lenth > 1: + resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", ' % interface_info.type + if "d" in demo_result: + resp = resp + 'json=kwargs.get("d"), ' + if "p" in demo_result: + resp = resp + 'params=kwargs.get("p"), ' + if "h" in demo_result: + resp = resp + 'headers=kwargs.get("h"), ' + resp = resp + 'user=user)' + else: + if demo_result: + if "d" in demo_result: + resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", json=%s, user=user)' % ( + interface_info.type, paras) + elif "h" in demo_result: + resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", headers=%s, user=user)' % ( + interface_info.type, paras) + elif "p" in demo_result: + resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", params=%s, user=user)' % ( + interface_info.type, paras) + elif "u" in demo_result: + resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", user=user)' % ( + interface_info.type) + else: + resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", user=user)' % ( + interface_info.type) + + if self.team.upper() in special_team or check_server_host(token_false_server, host) or \ + self.team.upper() in ["TMO"]: + resp = resp.replace("user=user", "token=False") + try: + if self.server.upper() in host_servers: + resp = resp.replace("token=False", "token=False, user=user") + if self.server.upper() == "HULK-ORG-API": + resp = resp.replace("user=user", "user=user, as_login_type=2") + if self.server.upper() in host_servers_except_api: + resp = resp.replace("token=False", "") + except: + pass + + if str(self.server).lower() in header_servers: + header = " header = {'debug-param': 'huangliye@huohua.cn'}" + if "h" in demo_result: + resp = resp.replace(' headers=kwargs.get("h"),', "") + header = " header = kwargs.get('h')\n header.update({'debug-param': 'huangliye@huohua.cn'})" + func_body.append(header) + resp = resp.replace("token=False", "token=False, headers=header") + if str(self.server).upper() in eid_server: + header = " header = {'debug-param': 'eid:%s' % self.eid}" + if "h" in demo_result: + resp = resp.replace(' headers=kwargs.get("h"),', "") + header = " header = kwargs.get('h')\n header.update({'debug-param': 'eid:%s' % self.eid})" + func_body.append(header) + resp = resp.replace("token=False", "token=False, headers=header") + if str(self.server).upper() in uid_server: + header = " header = {'debug-param': 'uid:%s' % self.uid}" + if "h" in demo_result: + resp = resp.replace(' headers=kwargs.get("h"),', "") + header = " header = kwargs.get('h')\n header.update({'debug-param': 'uid:%s' % self.uid})" + func_body.append(header) + resp = resp.replace("token=False", "token=False, headers=header") + func_body.append(resp) + # if self.team.upper() not in ["TMO"] and paras_d == "kwargs": + # func_body.append(" check_resp(is_check, resp)") + func_body.append(" return resp\n") + + return "\n".join(func_body) + + def get_has_keyword_id_in_db(self): + """ + 获取所有已有 + """ + keyword_list = [] + for key in self.all_url.keys(): + interface_name = key.split("/")[-1] + sql = "select * from sparkatp.interface_info where name = '%s' and type = '%s'" % ( + interface_name, self.all_url[key][0]) + interface_infos = SwaggerDBInfo.get_db_info_by_sql(sql=sql) + for interface_info in interface_infos: + interface_attr = SetAttr(interface_info) + if key.lower() in interface_attr.in_url.lower(): + keyword_list.append(interface_attr.id) + + return keyword_list + + +def run_keyword_generage(result_path, interface, demo_info, parameters, keyword): + if demo_info.request_demo: + demo_info = json.loads(demo_info.request_demo) + else: + demo_info = {} + if keyword.all_url.get(interface.in_url) and keyword.all_url.get(interface.in_url)[ + 0].lower() == interface.type.lower(): + msg = "接口url:%s,%s,已存在关键字,继续生成用例!" % (interface.type, interface.in_url) + log.warning(msg) + keyword.file.write_content_to_interface_file(msg + "\n", result_path) + return True + else: + exist = keyword.generate_kw_name(interface, demo_info) + if exist: + keyword.function_list = [] + doc = keyword.generate_kw_doc(interface, parameters) + url, temp = keyword.generate_function_url(interface, demo_info) + content = keyword.generate_function_body(interface, demo_info, parameters) + keyword.function_list.append("\n".join(("", keyword.function_name, doc, url, content))) + keyword.file.re_write_all_keyword_to_py_file() + keyword.file.write_content_to_interface_file("\n".join(keyword.function_list)) + keyword.file.all_url[interface.in_url] = [interface.type, keyword.kw_name] + return True + else: + msg = "接口url:%s,%s,无法生成关键字,请检查!" % (interface.type, interface.in_url) + log.warning(msg) + keyword.file.write_content_to_interface_file(msg + "\n", result_path) + return False diff --git a/base_framework/platform_tools/Keywords_service/keyword_service1.py b/base_framework/platform_tools/Keywords_service/keyword_service1.py new file mode 100644 index 0000000..bba9c6f --- /dev/null +++ b/base_framework/platform_tools/Keywords_service/keyword_service1.py @@ -0,0 +1,666 @@ +# -*- coding:utf-8 -*- +import copy +import importlib +import importlib.util as i_util +import inspect +import json +import os +import re +import sys +import traceback +from base_framework.platform_tools.Keywords_service import special_team + +token_false_server = ["scm-server", "scm-biz-server", "peppa-qi-api", "lggzt", "cti-manage", "ccwx-api"] +add_api_list = ["peppa-teach-api"] +host_servers = ["HULK-ORG-API", "HULK-TEACHER-API"] +host_servers_except_api = ["HULK-CONTENT-AUDIT-SERVER"] +header_servers = ["hulk-operation-api-server", "hulk-teach-supply-cli-api", "hulk-teach-backend-api"] +uid_server = ['SPARKEDU-API', 'SPARKEDU-SITE-API'] +eid_server = ['SPARKEDU-SITE-MANAGE', 'SPARKEDU-SITE-SCHEDULER'] + +def check_server_host(servers, host): + """ + 检查不需要token的server是否存在url中 + """ + for server in servers: + if server in host: + return True + else: + return False + + +def get_project_root_path(path="base_framework"): + """ + 获取项目目录 + """ + o_path = os.getcwd() + try: + project_path = re.search(r"(.*%s)" % path, o_path).group(1) + return os.path.abspath(os.path.join(project_path, os.path.pardir)) + except: + pass + + +Project_Path = get_project_root_path() + +from base_framework.public_tools.log import get_logger +from base_framework.public_tools.sqlhelper import MySqLHelper + +db = MySqLHelper() +log = get_logger() + + +def get_classes(module): + classes = [] + cls_members = inspect.getmembers(module, inspect.isclass) + for (name, _) in cls_members: + classes.append(name) + return classes + + +def get_module_by_file_path(file_path): + spec = importlib.util.spec_from_file_location("module_name", file_path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +class SetAttr(object): + + def __init__(self, paras): + for key, values in paras.items(): + self._add_attr(key, values) + + def _add_attr(self, key, value): + self.__setattr__(key, value) + + +class SwaggerDBInfo(object): + + def __init__(self): + pass + + @staticmethod + def get_team_swagger_info_by_team(teamname): + sql = "SELECT * FROM sparkatp.swagger_info where team = '%s'" % teamname + return db.select_all(sql, choose_db="huohua") + + @staticmethod + def get_team_swagger_info_by_id(swagger_id): + sql = "SELECT * FROM sparkatp.swagger_info where id = '%s'" % swagger_id + return db.select_one(sql, choose_db="huohua") + + @staticmethod + def get_interface_info_by_url_and_type(request_url, request_type, team): + sql = 'select * from sparkatp.interface_info where in_url = "%s" and type = "%s" and swagger_id in (select id from sparkatp.swagger_info where team = "%s")' % (request_url, request_type, team) + return db.select_all(sql, choose_db="huohua") + + @staticmethod + def get_interface_parameters_by_interface_id(interface_id): + sql = "select * from sparkatp.request_parameters a right join sparkatp.parameters_relation b on a.id = b.parameter_id where a.interface_id = {} and b.type = 'request' and a.offline=0".format( + interface_id) + return db.select_all(sql=sql, choose_db="huohua") + + @staticmethod + def get_interface_request_demo_info_by_interface_id(interface_id): + sql = 'SELECT * FROM sparkatp.parameters_demo where in_id = "%s" order by id DESC' % interface_id + return db.select_all(sql, choose_db="huohua") + + @staticmethod + def get_swagger_url_by_team_and_server(team, server): + sql = "select * from sparkatp.swagger_info where team='%s' and server_name = '%s';" % (team, server) + return db.select_one(sql, choose_db="huohua") + + +class KWFileOperation(object): + """ + 操作xx_interface.py文件 + """ + + def __init__(self, team, server): + self.kw_file_path = os.path.abspath(os.path.join(Project_Path, + "{team}".format(team=team), "library", + "{team}_interface.py".format(team=team.upper()))) + self.team = team + self.server = server + self.all_url = {} + self.all_keywords = [] + self.runner = self._get_obj_runner() + self.file_content = self.re_write_all_keyword_to_py_file() + self.check_server_and_rewrite_server() + self.get_all_exist_keywords() + self.generate_all_keyword_info() + self.kw_class = self._get_interface_class_name(self.kw_file_path) + + def check_server_and_rewrite_server(self): + """ + 检查server是否在interface.py文件中配置获取IP + """ + start_index = 0 + empty_index = 0 + server_host = self.server.lower().replace('-', '_') + server_upper = self.server.upper() + if "self.%s =" % server_host in ''.join(self.file_content) or '': + return + for line in self.file_content: + if "def __init__(self):" in line: + start_index = self.file_content.index(line) + 1 + log.info(start_index) + if start_index == 0: + continue + if " def " in line and "__init__" not in line: + end_index = self.file_content.index(line) + log.info(end_index) + break + if "pass" in self.file_content[start_index]: + self.file_content[ + start_index] = ' self.need_jira_id = True if ReadConfig(env_choose_path).get_value("run_jira_id", "huohua-podenv") else False\n' + elif 'self.need_jira_id = True if ' not in self.file_content[start_index] and "pass" in self.file_content[ + start_index]: + self.file_content.insert(start_index, + ' self.need_jira_id = True if ReadConfig(env_choose_path).get_value("run_jira_id", "huohua-podenv") else False\n') + for item in range(start_index, end_index): + if self.file_content[item] == "\n": + self.file_content.insert(item, + " self.%s_ip = obj_tool.get_container_ip_from_eureka('%s', need_jira_id=self.need_jira_id)\n" % ( + server_host, server_upper)) + + self.file_content.insert(item + 1, + ' self.%s = "http://" + self.%s_ip["container_ip"] + ":8080"\n' % ( + server_host, server_host)) + break + with open(self.kw_file_path, "w", encoding="UTF-8") as file: + for line1 in self.file_content: + file.writelines(line1) + + + def _clean_demo_to_less_key(self, demo): + demo_result = copy.deepcopy(demo) + for key, value in demo.items(): + if not value: + demo_result.pop(key) + lenth = len(demo_result) + paras = "kwargs" + if lenth == 1 and isinstance(list(demo_result.values())[0], list): + paras = "args" + return lenth, demo_result, paras + + def _get_interface_class_name(self, file_path): + module = get_module_by_file_path(file_path) + classes = get_classes(module) + for class_ in classes: + if "interface" in class_.lower(): + return class_ + + def _get_obj_runner(self): + spec = i_util.spec_from_file_location("module_name", self.kw_file_path) + module = i_util.module_from_spec(spec) + spec.loader.exec_module(module) + runner = inspect.getmembers(module) + for module in runner: + if module[0] == "obj_runner": + return module[1] + return runner + + def _get_file_content(self): + with open(self.kw_file_path, "r", encoding="UTF-8") as file: + all_content = file.readlines() + + return all_content + + def re_write_all_keyword_to_py_file(self): + with open(self.kw_file_path, "r", encoding="UTF-8") as file: + all_content = file.readlines() + index = len(all_content) + + with open(self.kw_file_path, "r", encoding="UTF-8") as file1: + for line in file1: + if "if __name__" in line and "__main__" in line: + index = all_content.index(line) + break + + for i in range(index - 1, 0, -1): + s = all_content[i] + if all_content[i] != "\n": + index = i + all_content.insert(i + 1, "\n") + break + a = all_content[i + 1] + with open(self.kw_file_path, "w", encoding="UTF-8") as file: + file.writelines(all_content[:index + 1]) + return all_content[:index + 1] + + def get_all_exist_keywords(self): + for item in self.file_content: + kw_match = re.search(r"def\s+(kw_in{1}_\w+)\(", item) + if kw_match: + self.all_keywords.append(kw_match.group(1)) + return self.all_keywords + + def write_content_to_interface_file(self, content, file_path=None): + if not file_path: + file_path = self.kw_file_path + with open(file_path, "a+", encoding="UTF-8") as f: + f.write(content) + + def generate_all_keyword_info(self): + """ + 获取interface文件内已有关键字的url请求地址和请求方法 + """ + temp_dict = {} + all_content = self.file_content + server = False + kw_line = False + url_line = False + host = False + for line in all_content: + if "def kw_in_" in line: + kw_line = True + kw_name = re.search("(kw_in_[\w+_]+)\(", line).group(1) + if kw_line: + try: + if "get_container_ip" in line: + server = list(re.search(r'get_container_ip.*\([\'\"]([\w\-_]+)[\'\"]', line).groups()) + server = server[0] + except Exception as err: + pass + if "self." in line and "=" in line: + temp_dict[line.split("=")[0].strip()] = line.split("=")[1].strip().strip("\"") + if (" url =" in line or "url=" in line) and 'url +' not in line: + log.info(line) + try: + urls = list(re.search(r"%s(/[\w+-_\}\{]+/?)\??|\}(/[\w+-_\}\{]+/?)\??", line).groups()) + urls.remove(None) + except Exception as err: + log.info("error: %s" % line) + url_line = True + if url_line: + if 'url = "%s/api/customer/{customerId}/give_up"' in line: + print() + if not host: + try: + host_key = re.search("obj_runner\.([\w\-_]+)", line).group(1).strip().strip(")").strip(",") + host = eval("self.runner.%s" % host_key) + except: + if not server: + if "self." in line: + server = re.search("self\.([\w\-_]+)", line).group(1).strip().strip(")").strip(",") + server = server.replace("_", "-").lower() + if server: + try: + host_temp = SwaggerDBInfo.get_swagger_url_by_team_and_server(self.team, server)['sw_url'] + host = host_temp.split("/v2")[0] + except Exception as err: + host = "" + if host: + url = host + urls[0] + + if "req_type=" in line: + method = re.search('req_type="(\w+)"|req_type=\'(\w+)\'', line.replace("\'", "\"")).group(1) + self.all_url[url] = [method, kw_name] + server = False + kw_line = False + url_line = False + host = False + return self.all_url + + +class KWOperation(object): + """ + 根据数据库内容生成对应的keywords + """ + + def __init__(self, team, kw_instance): + self.team = team + self.doc_string_table_content_temp = " |{}|{}|{}|{}|{}|" + self.function_name = "" + self.help_doc = None + self.function_content = None + self.has_kw_list = [] + self.function_list = [] + self.file = kw_instance + self.all_keywords = self.file.all_keywords + self.all_url = self.file.all_url + self.kw_name = None + self.kw_string = None + self.server = kw_instance.server + + def get_interface_demo(self, interface_id): + return SwaggerDBInfo.get_interface_request_demo_info_by_interface_id(interface_id) + + def get_interface_parameters(self, interface_id): + return SwaggerDBInfo.get_interface_parameters_by_interface_id(interface_id) + + def get_swagger_info_by_id(self, swagger_id): + return SwaggerDBInfo.get_team_swagger_info_by_id(swagger_id) + + def _check_kw_exist(self, keywordname): + if keywordname in self.all_keywords: + return False + else: + return True + + def generate_kw_name(self, interface_info, demo): + """ + 生成对应关键字方法名称如kw_in_to_get_leads_get(is_check='', **kwargs) + """ + lenth, demo_result, paras_d = self.file._clean_demo_to_less_key(demo) + _, name_list, _, _ = self._generate_all_url_paramaters(interface_info.in_url) + if "" in name_list: + name_list.remove("") + name_list.append(interface_info.name) + for item in add_api_list: + if item in interface_info.in_url: + name_list[-1] = 'api_' + name_list[-1] + count = -1 + self.kw_name = "kw_in_{}_{}_{}".format(self.team.lower(), name_list[-1], interface_info.type.lower()) + while abs(count) <= len(name_list): + if self._check_kw_exist(self.kw_name): + if lenth > 1: + self.function_name = " def {kw_name}(self, is_check='', **kwargs):".format(kw_name=self.kw_name) + else: + if self.team.upper() in ["TMO"]: + self.function_name = " def {kw_name}(self, *args, **kwargs):".format(kw_name=self.kw_name) + else: + if demo_result: + if isinstance(list(demo_result.values())[0], list): + self.function_name = " def {kw_name}(self, *args, **kwargs):".format( + kw_name=self.kw_name) + else: + self.function_name = " def {kw_name}(self, is_check='', **kwargs):".format( + kw_name=self.kw_name) + else: + self.function_name = " def {kw_name}(self, is_check='', **kwargs):".format( + kw_name=self.kw_name) + self.has_kw_list.append(interface_info.id) + self.all_keywords.append(self.kw_name) + return True + else: + count -= 1 + self.kw_name += "_" + name_list[count] + else: + self.has_kw_list.append(interface_info.id) + return False + + def generate_kw_doc(self, interface_info, parameters_list): + """ + 根据接口参数信息生成对应的帮助文档 + """ + + def set_lenth(para): + return " " + str(para).strip() + " " + + doc = [] + doc_string_head = " \"\"\"" + doc_string_table_head = " | {}| {}| {}| {}| {}|".format("请求参数名".ljust(30), "说明".ljust(15), + "类型".ljust(12), "条件".ljust(10), + "是否必填".ljust(8)) + doc.append(doc_string_head) + doc.append(" {} + {} + interface id: {}".format(interface_info.interface_describe, interface_info.type, + interface_info.id)) + doc.append(" url: " + interface_info.in_url) + doc.append(doc_string_table_head) + if self.team.upper() not in ["TMO"]: + doc.append(self.doc_string_table_content_temp.format(set_lenth("is_check"), + set_lenth("is_check默认空不校验返回,有值就校验返回"), + set_lenth("string"), + set_lenth("业务case的时候需要传入值"), + set_lenth("False"))) + for parameter in parameters_list: + attr = SetAttr(parameter) + if int(attr.in_body) != 0 and int(attr.is_need) != 2: + doc.append(self.doc_string_table_content_temp.format(set_lenth(attr.name), set_lenth(attr.note), + set_lenth(attr.type), set_lenth(attr.p_condition), + set_lenth(attr.is_need))) + doc.append(doc_string_head) + return "\n".join(doc) + + def _generate_all_url_paramaters(self, url): + """ + 拆解url生成host, 生成keyword名的相关名称和url中带{}的参数 + """ + head_list = [] + para_list = [] + host, url_last = re.search(r"(\S+\.cn|\S+\.com)(\S+)", url).groups() + search = re.findall("[\w+\-_]+|[\{\w+\-_\}]+", url_last) + for item in search: + if "{" in item: + temp = item.replace("{", "").replace("}", "") + para_list.append(temp) + else: + head_list.append(item) + return host, head_list, para_list, url_last + + def _generate_url_host(self): + """ + 根据host生成对应的url后缀,比如teach_opt_host + 如果需要将host替换成IP则生成对应的IP + """ + server_temp = self.server.lower().replace('-', '_') + url_host = 'self.%s' % server_temp + # if self.server: + # url_host = "" + # url_host = "" + # if 'peppa-cc-manage' in host: + # url_host = "cc_host" + # elif 'crmv2' in host or 'smm' in host or 'xxljob' in host: + # url_host = "crm_host" + # elif 'scm' in host: + # url_host = "scm_host" + # elif "la-gate" in host: + # url_host = "insights_host" + # elif 'la-ai-api-bg' in host or 'la-api-ai' in host: + # url_host = "spark_land_host" + # elif 'manage' in host: + # url_host = "manage_host" + # elif 'teach' in host: + # url_host = "teach_host" + # elif 'teach-opt-api' in host or 'sparkle-manage' in host: + # url_host = "teach_opt_host" + # elif 'opengalaxy' in host: + # url_host = "opengalaxy_host" + # elif 'employee-manage' in host: + # url_host = "ehr_host" + # elif 'peppa-agent-manage' in host: + # url_host = "hhr_host" + # elif 'peppa-agent-api' in host: + # url_host = "hhr_api_host" + # elif "teach-message-api" in host: + # url_host = "teacher_message_host" + # elif "swagger" in host: + # url_host = "swagger_host" + # elif "peppa-qi-api" in host: + # url_host = "qi_host" + # elif "scm-server" in host: + # url_host = "scm_server_host" + # elif "scm-biz-server" in host: + # url_host = "scm_biz_server_host" + # elif "cti-manage" in host: + # url_host = "cti_host" + # elif "peppa-conversion-api" in host: + # url_host = "uc_host" + # elif "la-api" in host: + # url_host = "la_api_host" + # elif "hulk_content_audit_server" in host: + # url_host = "hulk_content_audit" + + return url_host + + def generate_function_url(self, interface_info, demo): + """ + 生成对应的url + """ + lenth, demo_result, paras_d = self.file._clean_demo_to_less_key(demo) + url_list = [] + user_kwargs = " user, kwargs = get_user(kwargs)" + url_list.append(user_kwargs) + check_json = " kwargs = convert_json(kwargs)" + if paras_d == "args": + check_json = " args = convert_json(args)" + url_list.append(check_json) + host, _, parameters, url_last = self._generate_all_url_paramaters(interface_info.in_url) + host = re.findall("[\w+-]+", host) + url_host = self._generate_url_host() + server = "" + if self.team.upper() in ["TMO"]: + url_list.append(" try:\n kwargs = eval(args[0])\n except:") + url_list.append(" try:\n if args[0] and isinstance(args[0], dict):") + url_list.append("kwargs = args[0]".rjust(20 + len("kwargs = args[0]"), " ")) + url_list.append("except:".rjust(12 + len("except:"), " ")) + url_list.append("kwargs = kwargs".rjust(16 + len("kwargs = kwargs"), " ")) + url_last_tmp = url_last.split("/") + server = url_last_tmp.pop(1) + url_last = "/".join(url_last_tmp) + + if parameters: + if lenth > 1: + temp = 'kwargs.get("u")' + else: + temp = 'kwargs' + url = " url = \"%s" + url_last + "\".format(" + for item in parameters: + url += "%s=%s, " % (item, "%s.get(\"%s\", \"\")" % (temp, item)) + url += ") % " + else: + url = " url = \"%s" + url_last + "\" % " + + url += url_host + url_list.append(url) + return "\n".join(url_list), server + + def generate_function_body(self, interface_info, demo, parameters_all): + """ + 生成关键字的body内容 + """ + lenth, demo_result, paras_d = self.file._clean_demo_to_less_key(demo) + host, _, parameters, url_last = self._generate_all_url_paramaters(interface_info.in_url) + func_body = [] + if re.search(r"\*(\w+)", self.function_name): + paras = re.search(r"\*(\w+)", self.function_name).group(1) + if self.team.upper() in ["TMO"]: + paras = "kwargs" + content = ' obj_log.info("your input:{0}".format(%s))' % paras + func_body.append(content) + if self.kw_string: + func_body.append(" kwargs = kwargs.get('%s', '')" % parameters_all[0].get("name")) + else: + paras = None + if lenth > 1: + resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", ' % interface_info.type + if "d" in demo_result: + resp = resp + 'json=kwargs.get("d"), ' + if "p" in demo_result: + resp = resp + 'params=kwargs.get("p"), ' + if "h" in demo_result: + resp = resp + 'headers=kwargs.get("h"), ' + resp = resp + 'user=user)' + else: + if demo_result: + if "d" in demo_result: + resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", json=%s, user=user)' % ( + interface_info.type, paras) + elif "h" in demo_result: + resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", headers=%s, user=user)' % ( + interface_info.type, paras) + elif "p" in demo_result: + resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", params=%s, user=user)' % ( + interface_info.type, paras) + elif "u" in demo_result: + resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", user=user)' % ( + interface_info.type) + else: + resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", user=user)' % ( + interface_info.type) + + if self.team.upper() in special_team or check_server_host(token_false_server, host) or self.team.upper() in [ + "TMO"]: + resp = resp.replace("user=user", "token=False") + try: + if self.server.upper() in host_servers: + resp = resp.replace("token=False", "token=False, user=user") + if self.server.upper() == "HULK-ORG-API": + resp = resp.replace("user=user", "user=user, as_login_type=2") + if self.server.upper() in host_servers_except_api: + resp = resp.replace("token=False", "") + except: + pass + + if str(self.server).lower() in header_servers: + header = " header = {'debug-param': 'huangliye@huohua.cn'}" + if "h" in demo_result: + resp = resp.replace(' headers=kwargs.get("h"),', "") + header = " header = kwargs.get('h')\n header.update({'debug-param': 'huangliye@huohua.cn'})" + func_body.append(header) + resp = resp.replace("token=False", "token=False, headers=header") + if str(self.server).upper() in eid_server: + header = " header = {'debug-param': 'eid:%s' % self.eid}" + if "h" in demo_result: + resp = resp.replace(' headers=kwargs.get("h"),', "") + header = " header = kwargs.get('h')\n header.update({'debug-param': 'eid:%s' % self.eid})" + func_body.append(header) + resp = resp.replace("token=False", "token=False, headers=header") + if str(self.server).upper() in uid_server: + header = " header = {'debug-param': 'uid:%s' % self.uid}" + if "h" in demo_result: + resp = resp.replace(' headers=kwargs.get("h"),', "") + header = " header = kwargs.get('h')\n header.update({'debug-param': 'uid:%s' % self.uid})" + func_body.append(header) + resp = resp.replace("token=False", "token=False, headers=header") + func_body.append(resp) + if self.team.upper() not in ["TMO"] and paras_d == "kwargs": + func_body.append(" check_resp(is_check, resp)") + func_body.append(" return resp\n") + + return "\n".join(func_body) + + def get_has_keyword_id_in_db(self): + """ + 获取所有已有 + """ + keyword_list = [] + for key in self.all_url.keys(): + interface_name = key.split("/")[-1] + sql = "select * from sparkatp.interface_info where name = '%s' and type = '%s'" % ( + interface_name, self.all_url[key][0]) + interface_infos = SwaggerDBInfo.get_db_info_by_sql(sql=sql) + for interface_info in interface_infos: + interface_attr = SetAttr(interface_info) + if key.lower() in interface_attr.in_url.lower(): + keyword_list.append(interface_attr.id) + + return keyword_list + + +def run_keyword_generage(result_path, interface, demo_info, parameters, keyword): + if demo_info.request_demo: + demo_info = json.loads(demo_info.request_demo) + else: + demo_info = {} + if keyword.all_url.get(interface.in_url) and keyword.all_url.get(interface.in_url)[ + 0].lower() == interface.type.lower(): + msg = "接口url:%s,%s,已存在关键字,继续生成用例!" % (interface.type, interface.in_url) + log.warning(msg) + keyword.file.write_content_to_interface_file(msg + "\n", result_path) + return True + else: + exist = keyword.generate_kw_name(interface, demo_info) + if exist: + keyword.function_list = [] + doc = keyword.generate_kw_doc(interface, parameters) + url, temp = keyword.generate_function_url(interface, demo_info) + content = keyword.generate_function_body(interface, demo_info, parameters) + keyword.function_list.append("\n".join(("", keyword.function_name, doc, url, content))) + keyword.file.re_write_all_keyword_to_py_file() + keyword.file.write_content_to_interface_file("\n".join(keyword.function_list)) + keyword.file.all_url[interface.in_url] = [interface.type, keyword.kw_name] + return True + else: + msg = "接口url:%s,%s,无法生成关键字,请检查!" % (interface.type, interface.in_url) + log.warning(msg) + keyword.file.write_content_to_interface_file(msg + "\n", result_path) + return False + + +if __name__ == "__main__": + kw = KWFileOperation("CC", "peppa-qi-api") + kw.check_server_and_rewrite_server() diff --git a/base_framework/platform_tools/Keywords_service/readme b/base_framework/platform_tools/Keywords_service/readme new file mode 100644 index 0000000..5f5897f --- /dev/null +++ b/base_framework/platform_tools/Keywords_service/readme @@ -0,0 +1,3 @@ +目录结构说明: + 使用者:王刚 + 用途:存放自动生成py文件关键字的脚本 \ No newline at end of file diff --git a/base_framework/platform_tools/Message_service/Feishu_api.py b/base_framework/platform_tools/Message_service/Feishu_api.py new file mode 100644 index 0000000..0c89cf8 --- /dev/null +++ b/base_framework/platform_tools/Message_service/Feishu_api.py @@ -0,0 +1,141 @@ +# -*- coding:utf-8 -*- +""" +功能:发送飞书消息接口 +""" + +import requests +import json +import logging +import time +import urllib3 +urllib3.disable_warnings() +import os +from base_framework.public_tools.read_config import ReadConfig + +try: + JSONDecodeError = json.decoder.JSONDecodeError +except AttributeError: + JSONDecodeError = ValueError +HERE = os.path.dirname(os.path.abspath(__file__)) +msg_config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'msg_config.ini') +read_config = ReadConfig(filename=msg_config_path) + + +def get_feishu_config_options(): + """ + 功能:获取飞书配置文件信息 + option_key:配置文件中的key + """ + return read_config.get_options('feishu_user_list') + + +def get_feishu_config_value(option_key): + """ + 功能:获取飞书配置文件信息 + option_key:配置文件中的key + """ + return read_config.get_value(sections='feishu_user_list', options=option_key) + +def get_user_name_by_email_prefix(email_prefix): + """ + 功能:获取飞书配置文件中的邮箱前缀与中文姓名的对应关系 + email_prefix:邮箱前缀 + 返回:对应的中文姓名 + """ + return read_config.get_value(sections='user_name', options=email_prefix) + + +class FeiShuMessage(object): + def __init__(self, team='TO', secret=None, pc_slide=False, fail_notice=False): + """ + 机器人初始化 + :param team: 业务组名,用于从msg_config配置文件中读取对应群组的webhook地址 + :param secret: 机器人安全设置页面勾选“加签”时需要传入的密钥 + :param pc_slide: 消息链接打开方式,默认False为浏览器打开,设置为True时为PC端侧边栏打开 + :param fail_notice: 消息发送失败提醒,默认为False不提醒,开发者可以根据返回的消息发送结果自行判断和处理 + """ + super(FeiShuMessage, self).__init__() + self.headers = {'Content-Type': 'application/json; charset=utf-8'} + team_name = team.lower() + '_webhook_token' + if team_name in get_feishu_config_options(): + token = get_feishu_config_value(option_key=team_name) + self.webhook = "https://open.feishu.cn/open-apis/bot/v2/hook/{0}".format(token) + else: + self.webhook = None + logging.error("Team: {} not in msg_config.ini".format(team)) + return + self.secret = secret + self.pc_slide = pc_slide + self.fail_notice = fail_notice + + def send_text(self, msg): + """ + 消息类型为text类型 + :param msg: 消息内容 + :return: 返回消息发送结果 + """ + data = {"msg_type": "text"} + if msg and self.webhook: # 传入msg非空 + data["content"] = {"text": msg} + if "全部通过" in msg: + # 不用发飞书消息 + logging.info("+++++++++ 全部构建成功,不发消息 ++++++++") + return False + return self.post(data) + + def post(self, data): + """ + 发送消息(内容UTF-8编码) + :param data: 消息数据(字典) + :return: 返回消息发送结果 + """ + try: + post_data = json.dumps(data) + response = requests.post(self.webhook, headers=self.headers, data=post_data, verify=False) + except requests.exceptions.HTTPError as exc: + logging.error("消息发送失败, HTTP error: %d, reason: %s" % (exc.response.status_code, exc.response.reason)) + raise + except requests.exceptions.ConnectionError: + logging.error("消息发送失败,HTTP connection error!") + raise + except requests.exceptions.Timeout: + logging.error("消息发送失败,Timeout error!") + raise + except requests.exceptions.RequestException: + logging.error("消息发送失败, Request Exception!") + raise + else: + try: + result = response.json() + except JSONDecodeError: + logging.error("服务器响应异常,状态码:%s,响应内容:%s" % (response.status_code, response.text)) + return {'errcode': 500, 'errmsg': '服务器响应异常'} + else: + logging.debug('发送结果:%s' % result) + # 消息发送失败提醒(errcode 不为 0,表示消息发送异常),默认不提醒,开发者可以根据返回的消息发送结果自行判断和处理 + if self.fail_notice and result.get('errcode', True): + time_now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())) + error_data = { + "msgtype": "text", + "text": { + "content": "[注意-自动通知]飞书机器人消息发送失败,时间:%s,原因:%s,请及时跟进,谢谢!" % ( + time_now, result['errmsg'] if result.get('errmsg', False) else '未知异常') + }, + "at": { + "isAtAll": False + } + } + logging.error("消息发送失败,自动通知:%s" % error_data) + requests.post(self.webhook, headers=self.headers, data=json.dumps(error_data)) + return result + + +if __name__ == '__main__': + fs = FeiShuMessage(team='TO') + 吴勇刚 = "吴勇刚" + 刘明浩 = "刘明浩" + 陈慧宗 = "陈慧宗" + message = "【TO-SMOKING_NEW】 第【1314】次构建结果:失败【5】个 \n|--{}: 1个\n|--{}: 2个\n|--{}: 3个" \ + "构建报告:...........test..........".format(吴勇刚, 刘明浩, 陈慧宗) + resp = fs.send_text(msg=message) + print(resp) \ No newline at end of file diff --git a/base_framework/platform_tools/Message_service/check_jira.py b/base_framework/platform_tools/Message_service/check_jira.py new file mode 100644 index 0000000..255bac5 --- /dev/null +++ b/base_framework/platform_tools/Message_service/check_jira.py @@ -0,0 +1,101 @@ +# encoding: utf-8 +# @Time : 2021/12/20 18:14 +# @Author : yk +# @Site : +# @File : jira_message_by_ding.py + +from jira import JIRA +import json +import requests +import datetime +import urllib.parse + + +class JiraObj: + def __init__(self, user, pwd, jira_addr): + if not hasattr(JiraObj, 'jira'): + JiraObj.jira_obj(user, pwd, jira_addr) + + @staticmethod + def jira_obj(user, pwd, jira_addr): + JiraObj.jira = JIRA(auth=(user, pwd), options={'server': jira_addr}) + + @staticmethod + def search_by_jql(jql, fields=None): + return JiraObj.jira.search_issues(jql, fields=fields, maxResults=-1, json_result='true') + + + +def get_delay_sub_task_creator(jira_obj, now_date): + name_list="chengpu,fengtian,dengzhenbo,guosongchao,handongtang,huanghaifeng,lidaijun,wanglushun,wangyujie02,yangwenlei01,yangwenlei01,zhanghaodong,zhengxin01,zhuzipeng" + # name_list="zhanghaodong" + jql = 'issuetype = 缺陷 and createdDate < \'{} 18:00\' AND status not in (QA测试,SIM验证,Done,关闭,待测试) and assignee in ({})'.format( + now_date,name_list) + fields = 'assignee' + print(jql) + issue_list = jira_obj.search_by_jql(jql, fields).get('issues') + print(issue_list) + if issue_list: + return list(map(lambda x: x.get('fields').get('assignee').get('name'), issue_list)) + else: + return None +def send_message_by_feishu(name, message): + data={"list":[name]} + a=requests.post(url="http://sqe.qc.huohua.cn:8082/feishu/getUserIds",data=data) + print(a.text) + open_id=a.text.split("open_id")[1].split('"')[2].split("\\")[0] + + data={ + "app_id": "cli_a2d926427f39900d", + "app_secret": "CvETCGh3rHu6CtcnxzaWK7rMnVgcSLED" + } + a=requests.post(url="https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",json=data) + + tenant_access_token=eval(a.text)["tenant_access_token"] + data ={"msg_type": "text","content": { "text": "{}".format(message) }, "open_ids": [open_id]} + + header={"Authorization":"Bearer {}".format(tenant_access_token),"Content-Type":"application/json; charset=utf-8"} + + a=requests.post(url="https://open.feishu.cn/open-apis/message/v4/batch_send/", json=data,headers=header) + + print(a.text) +if __name__ == '__main__': + + now_date = datetime.datetime.now().strftime('%Y-%m-%d') + + jira_obj = JiraObj('yaokun', 'Cyjayk1314', 'https://jira.bg.huohua.cn') + creator = get_delay_sub_task_creator(jira_obj, now_date) + + if creator: + creator=list(set(creator)) + message="你还有未解决的bug,请及时解决,若已解决请及时更新jira状态" + for i in creator: + jql = 'issuetype = 缺陷 and createdDate < \'{} 18:00\' AND status not in (QA测试,SIM验证,Done,关闭,待测试) and assignee in ({})'.format( + now_date, i) + print(jql) + i=i+"@sparkedu.com" + + a=jira_obj.search_by_jql(jql, "assignee").get('issues') + for j in a: + print(j["key"]) + send_message_by_feishu(i,"你有未修复的bug请及时修复https://jira.bg.huohua.cn/browse/{}".format(j["key"])) + # send_message_by_feishu("yaokun@sparkedu.com","https://jira.bg.huohua.cn/browse/HHC-50790") +# data={"list":["yaokun@sparkedu.com"]} +# a=requests.post(url="http://sqe.qc.huohua.cn:8082/feishu/getUserIds",data=data) +# +# print(a.text.split("open_id")[1].split('"')[2].split("\\")[0]) +# open_id="ou_95feb664191332a7916bb3b0d886f553" +# print(a.text) +# data={ +# "app_id": "cli_a2d926427f39900d", +# "app_secret": "CvETCGh3rHu6CtcnxzaWK7rMnVgcSLED" +# } +# a=requests.post(url="https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",json=data) +# print(a.text) +# +# data ={"msg_type": "text","content": { "text": "https://jira.bg.huohua.cn/browse/HHC-50790" }, "open_ids": [open_id]} +# +# header={"Authorization":"Bearer t-719d260f474e6a1a6b4a5d990a6bccec6fc3b17f","Content-Type":"application/json; charset=utf-8"} +# print(data) +# a = requests.post(url="https://open.feishu.cn/open-apis/message/v4/batch_send/", json=data,headers=header) +# print(a.text) \ No newline at end of file diff --git a/base_framework/platform_tools/Message_service/dingding_api.py b/base_framework/platform_tools/Message_service/dingding_api.py new file mode 100644 index 0000000..98cd473 --- /dev/null +++ b/base_framework/platform_tools/Message_service/dingding_api.py @@ -0,0 +1 @@ +# to be add 。。。。 \ No newline at end of file diff --git a/base_framework/platform_tools/Message_service/msg_config.ini b/base_framework/platform_tools/Message_service/msg_config.ini new file mode 100644 index 0000000..8780125 --- /dev/null +++ b/base_framework/platform_tools/Message_service/msg_config.ini @@ -0,0 +1,236 @@ +# 此文档中配置钉钉,飞书群组和群用户信息 +[feishu_user_list] +JENKINS_webhook_token = 6de2e886-10de-4a85-aa92-2d7446d0c315 +CDQA_webhook_token = 30e400ed-b802-4afb-b38c-2a0690c1a2a6 +TO_webhook_token = 2109acc2-6ffe-4038-8bf1-4bfcfcd4cb01 +TMO_webhook_token = 2109acc2-6ffe-4038-8bf1-4bfcfcd4cb01 +HHI_TO_webhook_token = 2109acc2-6ffe-4038-8bf1-4bfcfcd4cb01 +HHI_TMO_webhook_token = 2109acc2-6ffe-4038-8bf1-4bfcfcd4cb01 +GUE_webhook_token = a46a610c-4c52-4abf-bcad-5fde5cd545e4 +LALIVE_webhook_token = 3f9379c7-ebcd-4844-9c61-3f2642f64df4 +H2R_webhook_token = 3f9379c7-ebcd-4844-9c61-3f2642f64df4 +CC_webhook_token = 3f9379c7-ebcd-4844-9c61-3f2642f64df4 +ASTWB_webhook_token = 31ab2e27-df7c-46d6-8294-afb98a68b6f0 +ASOPE_webhook_token = 31ab2e27-df7c-46d6-8294-afb98a68b6f0 +ASTOP_webhook_token = 31ab2e27-df7c-46d6-8294-afb98a68b6f0 +ES_webhook_token = 84b165e1-160f-4ebd-a36d-d1873cca0d20 +SCM_webhook_token = 84b165e1-160f-4ebd-a36d-d1873cca0d20 +UBRD_webhook_token = a46a610c-4c52-4abf-bcad-5fde5cd545e4 +DBSYNC_webhook_token = 054f4c3c-b38b-46f8-9cc4-d9660e205fc7 +ODS_webhook_token = 054f4c3c-b38b-46f8-9cc4-d9660e205fc7 +AUTOMATION_webhook_token = ceb41bf3-f3ce-433f-bbea-2960bf46a819 +; TO-RD_webhook_token = d8d9ac4b-e5f9-47a6-8810-0ae6b621a7cb +TO-RD_webhook_token = 2387b345-3675-49dd-971a-2f81b98e3ed1 +TO-FE_webhook_token = 1d0dac58-dd68-412d-a2eb-b445927339a +GUE-RD_webhook_token = 8951949c-e300-4c96-a725-53346309851a +USER-FE_webhook_token = 4f946e2b-0b58-4eb5-aa22-3d3516aee1ba +SCM-RD_webhook_token = 74ceedc8-510f-4efd-bf64-4c5e6e87dc0f +SCM-monitor_webhook_token = b821c1e5-63b0-42c9-b7ff-ab89c6dcd076 +PB_webhook_token = 84b165e1-160f-4ebd-a36d-d1873cca0d20 +INFO_webhook_token = 20114e9a-9f09-44f9-bfbe-0da382745cde +CODING_webhook_token = 20114e9a-9f09-44f9-bfbe-0da382745cde +OFFLINE_webhook_token = 20114e9a-9f09-44f9-bfbe-0da382745cde + +陈林 = ou_69254b2555a2c6257d5ca45d885f785e +吴勇刚 = ou_94f57439f58bde9376189a3cabb0b11a +陈慧宗 = ou_bc2b266b05a428959bfcff3378af2d36 +胥雯筠 = ou_04e4a3ccf680acc17f1039dd57349a00 +陈江 = ou_02ce6ebcb8a510c0b4102e15aaf59d05 +谯新久 = ou_c3c6f7ac7997000dcb23e634a830851d +罗洪 = ou_f6981cb788d066accae770fda5fa7ee5 +刘睿权 = ou_218303e87f4c45ff81ed16a1e1d0b1ee +陈典模 = ou_0fe84f418974af89e89148e2380427dd +李聪 = ou_711ebaac08461ae9a2726fcbe393d7de +左其灵 = ou_cc27134156714d88da23e159ba8390e1 +宋飞飞 = ou_ccafa6ebe0194e42ea55f8804734e6d4 + +付文龙 = ou_589b7151ac583ca3d94f0fb2d579f282 +赵晓放 = ou_a62ef7960b9c57e1946f55a6bb5d8b35 +张浩东 = ou_04ba397251c63036e9d75096f3c28704 +王玉杰 = ou_e3198cbf59327bac7dd686a94af30c2a +冯天 = ou_3522b9c4015645ea2d4c36aa6c260e90 +郭松超 = ou_6a8e6ccaceb51167c4745b347085edd9 +张雄 = ou_1ab263e2e7386fd5c5ecd67c1c6f2b6d +刘学刚 = ou_a17ade671a3030f12ae0fa13cc4f82bc +朱亮 = ou_0cc7a4c1a97bb58dd188b6aac6c7569b +高志军 = ou_5df6937cc109565f92868f0cdca4906c +马锦程 = ou_d35f2668f960300ee2d9a7768c7de9ce +刘英杰 = ou_d62d587ce3960add08214726897c228d +张晓刚 = ou_320976aaeb1e50976932f2eb446918c7 + + +赵飞 = ou_17fdcc65d985a21a225034db318308bf +刘信林 = ou_dec1cf51c8b3e63823e7262928b20dbb +叶飞 = ou_3559e23c390a60a59f4b10e7053e5551 +向明 = ou_4c26b3066017f57f6f2658b49e16b8a5 +姜浩 = ou_8014499bb65adda6399ebc82e1a46724 +姜灵敏 = ou_e4e113c845e88a43125c8a994710ff94 +徐长乐 = ou_54c5d0f98d26ab7500a5af64d9022b1b +卿晨 = ou_3e517c22dbcdfce4b4e75af20a7e5d8c +李柏成 = ou_e40510cf340bc5ac0f860ac01844f1a1 +苟宇恒 = ou_d82652fb433a470d7ac72a542292cd3b +董吉祥 = ou_4d9d1b8cf8c743f75313863a3ac336ad +白杨 = ou_ac886a59ffd1a4a0da4cb3d06c1e7b03 + +李明泽 = ou_0fc5af64989a1677fa4c34b7542e4c2c +沈佳坤 = ou_36ed516d812a1bacc3f978179e32910d +朱乾元 = ou_31a10ecba1074d45df6c6cbb13ec16e9 +顾洋 = ou_543fe7b50e1cadf6974e93ac8663b09d +吴优 = ou_117b8d43a8122021d290ef89cbcb9c88 + + +王亚超 = ou_649286a2b30bfe81bb4041a2b617fd80 +徐佳林 = ou_f382da52d42932870a0ea122753f795a +彭霞 = ou_9ef2d9e4a5c098ab27933208dd95ff8a +通用 = ou_abcdefghijklmnopqrstuvwxyz123456 +韩东堂 = ou_4ff52d71cb3cbe7cd1f26b2ac7da5eb0 +邓振博 = ou_994af7ca097ea1b63a5bec13b0b9a42c +朱子朋 = ou_8d13121890e1de3851c0616b8392cb1f +李代军 = ou_279bbc6d368cffa743c959d7461d8481 +黄海峰 = ou_8a4cf96dfc173dbed704d44fdc8fc5c1 +杨文磊 = ou_885d10cea3c3ae72c380971a7082da06 +卢纪霖 = ou_eaaf66ef5c53d3977a0bd3961376beae +杨远宁 = ou_8693a6f29fe174b4d18155e6d4da0396 +崔瑞 = ou_7bbb7b678b07e74fd1c245bafc6877f9 +李振宇 = ou_82a619af1d592dfb994ee28389815ee9 +王同刚 = ou_ca8ea6ff6bf6258f49932dc4b0c98fbc +张瑞涛 = ou_ca355f3fad90fe934087d225bb5794b4 +左钊 = ou_ea9a364cffa235aec88d37d075e300bb +田翔 = ou_75796572af14c996f853f015ecb788e0 +赵加会 = ou_33ed201f4e7f0f8919bc7021130c066e +刘欣畅 = ou_6247db29c46ac536d2aa10fab9711a82 +田文昌 = ou_60e01001facaadd78b89ef05281e0763 +郑宇翔 = ou_4e8d677a2389a8f8a5517961f2423970 +王国静 = ou_862b663bac7be9762eb78d71a9e6defb +李其 = ou_cfb094d4d15f7da729eea8b1491ffd13 +尚万中 = ou_53e6edbf2ef8a1620cbadb4b7efeb966 +张景峰 = ou_48c88a2ff129e5d23215d245c6b82fc0 + +[user_name] +fuwenlong = 付文龙 +fengtian = 冯天 +guosongchao = 郭松超 +wangyujie02 = 王玉杰 +zhanghaodong = 张浩东 +zhuliang = 朱亮 +liuxuegang = 刘学刚 +gaozhijun = 高志军 +majincheng = 马锦程 +zhaoxiaofang = 赵晓放 +liuyingjie = 刘英杰 +zhangxiaogang = 张晓刚 + +zhaofei = 赵飞 +liuxinlin = 刘信林 +yefei = 叶飞 +xiangming = 向明 +jianghao = 姜浩 +jianglingmin = 姜灵敏 +xuchangle = 徐长乐 +qingchen = 卿晨 +libaicheng = 李柏成 +gouyuheng = 苟宇恒 +jixiang.dong = 董吉祥 +baiyang01 = 白杨 + +wuyonggang = 吴勇刚 +xuwenjun = 胥雯筠 +songfeifei = 宋飞飞 +wanggang02 = 王刚 +lichao04 = 李超 +xiexiangyi = 谢祥益 +denghaiou = 邓海鸥 +yaokun = 姚坤 +chenhuizong = 陈慧宗 +yuanzhengqi = 袁正旗 +luohong = 罗洪 +chendianmo = 陈典模 +licong = 李聪 +zuoqiling = 左其灵 +zhengxin01 = 郑新 +handongtang = 韩东堂 +dengzhenbo = 邓振博 +zhuzipeng = 朱子朋 +lidaijun = 李代军 +huanghaifeng =黄海峰 +yangwenlei01 =杨文磊 +lujilin = 卢纪霖 +yangyuanning = 杨远宁 +cuirui = 崔瑞 +lizhenyu = 李振宇 +wangtonggang = 王同刚 +zhangruitao01 = 张瑞涛 +zuozhao = 左钊 +tianxiang = 田翔 +zhaojiahui = 赵加会 +liuxinchang = 刘欣畅 +tianwenchang = 田文昌 +zhengyuxiang = 郑宇翔 +zhangxiong = 张雄 +wangguojing = 王国静 +liqi03 = 李其 +shangwanzhong = 尚万中 +zhangjingfeng = 张景峰 + +limingze = 李明泽 +zhuqianyuan = 朱乾元 +guyang = 顾洋 +shenjiakun = 沈佳坤 +wuyou = 吴优 + +[dingding_user_list] +SCM_owner = 13350956802 +景虎成 = 13350956802 +文妮 = 17302811505 +罗洪 = 13550629276 +TO_owner = 18215530124 +李超 = 18011454607 +吴勇刚 = 13540133074 +王刚 = 19141999584 +刘明浩 = 18380450039 +谢祥益 = 18010623985 +唐其麟 = 18011454607 +肖淇迈 = 19141999584 +唐浩 = 18380450039 +TMO_owner = 18215530124 +姚坤 = 18215530124 +罗志鹏 = 18582482272 +刘涛婷 = 18328504751 +陈江 = 13458500234 +ASTWB_owner = 18202810506 +谯新久 = 18202810506 +刘鹏 = 13547858402 +杨雨 = 13608006659 +华学敏 = 13708231975 +ASORG_owner = 18202810506 +PZ_owner = 18202810506 +肖亮 = 18108096240 +ASTOP_owner = 18780106567 +ASOPE_owner = 18180956201 +蒋安龙 = 18180956201 +杨中莲 = 13882105134 +CC_owner = 18215530124 +林于棚 = 18782019436 +黄业宏 = 18603267203 +EN_owner = 18180956201 +LALIVE_owner = 18215530124 +H2R_owner = 18380448416 +胥雯筠 = 18780106567 +周仁华 = 18281029023 +袁正旗 = 18030895120 +刘睿权 = 15681935823 +文青 = 18584851102 +刘德全 = 13402829590 +包利 = 18380448416 +ASTOP_to_dd = 495fc7e0171e829acda91ffa08eaf37307e123fafc090249b633eb651c31dd79 +ASTWB_to_dd = 2079cb3e311ab6a37138a6d4671181661d211c12a1532c2012ae7e6b397996c3 +ASOPE_to_dd = 147617cea7e1b480e54cecb6dfd33edb5a9ed872e1bab52007d488673ad8ab0f +ASORG_to_dd = 2079cb3e311ab6a37138a6d4671181661d211c12a1532c2012ae7e6b397996c3 +CC_to_dd = ccceb513dbc7120dc301fa38a2f634b861009ec54d424d293653dc15c6e4963f +H2R_to_dd = ccceb513dbc7120dc301fa38a2f634b861009ec54d424d293653dc15c6e4963f +LALIVE_to_dd = ccceb513dbc7120dc301fa38a2f634b861009ec54d424d293653dc15c6e4963f +TMO_to_dd = ccceb513dbc7120dc301fa38a2f634b861009ec54d424d293653dc15c6e4963f +TO_to_dd = ccceb513dbc7120dc301fa38a2f634b861009ec54d424d293653dc15c6e4963f +UBRD_to_dd = ccceb513dbc7120dc301fa38a2f634b861009ec54d424d293653dc15c6e4963f +ULS_to_dd = ccceb513dbc7120dc301fa38a2f634b861009ec54d424d293653dc15c6e4963f +SCM_to_dd = 38fc76120cb204e2c1da53ec9921805670466da64c37aed8bc6fba0513f5688b \ No newline at end of file diff --git a/base_framework/platform_tools/Swagger_scanner/__init__.py b/base_framework/platform_tools/Swagger_scanner/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/base_framework/platform_tools/Test_report/__init__.py b/base_framework/platform_tools/Test_report/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/base_framework/platform_tools/Testcase_service/__init__.py b/base_framework/platform_tools/Testcase_service/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/base_framework/platform_tools/Testcase_service/case_service.py b/base_framework/platform_tools/Testcase_service/case_service.py new file mode 100644 index 0000000..9932979 --- /dev/null +++ b/base_framework/platform_tools/Testcase_service/case_service.py @@ -0,0 +1,851 @@ +# -*- coding:utf-8 -*- +import copy +import importlib +import json +import os +import random +import re +import sys +import string +import time +import traceback + + +def get_project_root_path(path="base_framework"): + """ + 获取项目目录 + """ + o_path = os.getcwd() + try: + project_path = re.search(r"(.*%s)" % path, o_path).group(1) + return os.path.abspath(os.path.join(project_path, os.path.pardir)) + except: + pass + + +Project_Path = get_project_root_path() +sys.path.append(Project_Path) + +from base_framework.platform_tools.Keywords_service.keyword_service import SetAttr, log + + +def load_module_from_file(file_path, team): + module_file = re.search(r"\\(%s\\.*)\.py" % team, file_path) + # if not module_file: + # module_file = re.search(r"\\(%s\\.*)\.py" % team.lower(), file_path) + temp = module_file.group(1) + temp = temp.replace(os.sep, ".") + # try: + # module = importlib.import_module(temp) + # except Exception as err: + # temp = re.sub(r"%s\." % team, "%s." % team.lower(), temp) + # module = importlib.import_module(temp) + return importlib.import_module(temp) + + +class ITCaseFileOperation(object): + def __init__(self, team, kw_instance): + self.kw = kw_instance + self.all_url = kw_instance.all_url + self.kw_class_name = kw_instance.kw_class + self.case_dir = os.path.abspath(os.path.join(Project_Path, team, "test_case", "TestCase", "1.接口")) + self.env_path = os.path.abspath( + os.path.join(Project_Path, team, "test_case", "Resource", "AdapterKws", "env.robot")) + + def _check_case_file_exist(self, file_path): + if os.path.exists(file_path) and os.path.isfile(file_path): + return True + else: + return False + + def check_case_exist(self, case_path, case_name): + if self._check_case_file_exist(case_path): + with open(case_path, "r", encoding="utf-8") as f: + for line in f.readlines(): + if line.startswith(case_name): + return True + else: + return False + else: + return False + + def generate_case_file(self, file_path): + dirname = file_path[:-len(os.path.basename(file_path))] + if not self._check_case_file_exist(file_path): + if not os.path.exists(dirname): + os.makedirs(dirname) + self.write_content_to_case_file(file_path, self.generate_env_releate_path(dirname)) + else: + with open(file_path, "r", encoding="utf-8") as f: + content = f.readlines() + if content: + if "\n" != content[-1]: + self.write_content_to_case_file(file_path, "\n") + else: + self.write_content_to_case_file(file_path, self.generate_env_releate_path(dirname)) + + def generate_env_releate_path(self, file_path): + return "*** Settings ***\nResource " + os.path.relpath(self.env_path, + file_path).replace("\\", + "/") + "\n\n*** Test Cases ***\n" + + def _check_case_file_resource(self, file_path): + """ + 确定case文件中是否有关键字 + """ + with open(file_path, "a+", encoding="utf-8") as f: + content_list = f.readlines() + if "*** Keywords ***\n" not in content_list: + return True + else: + kw_index = content_list.rindex("*** Keywords ***\n") + for index in range(kw_index, len(content_list) + 1): + if "*** Test Cases ***\n" == content_list[index]: + status = True + if "*** Keywords ***\n" == content_list[index]: + status = False + return status + + def write_content_to_case_file(self, file_path, content): + status = self._check_case_file_resource(file_path) + with open(file_path, "a+", encoding="utf-8") as f: + if status: + f.write(content) + else: + content = "*** Test Cases ***\n" + content + f.write(content) + + +class ITCaseContentOperation(object): + def __init__(self, team, kw_instance): + self.team = team + self.file = ITCaseFileOperation(team, kw_instance) + self.doc_content_row_temp = " ... | {} | {} | {} |" + self.normal_1001 = None + self.un_expect_list = None + self.case_string = None + + def _generate_case_name(self, interface_name, case_type, case_des): + return "-".join([interface_name, case_type, case_des]) + + def _check_interface_confirm(self, interface_info): + if not interface_info.confirmed: + raise Exception("接口%s未确认,请先确认接口参数。" % interface_info.name) + + def generate_case_body_all_1001_array(self, request_demo, parameters): + temp_list = list() + demo_copy = copy.deepcopy(request_demo) + for key, value in request_demo.items(): + if not value: + demo_copy.pop(key) + if len(demo_copy.keys()) == 1: + demo_copy = demo_copy.pop(list(demo_copy.keys())[0]) + dict_para, self.un_expect_list, _, _ = self._analysis_case_parameters_to_dict(parameters) + demo_add_values = self._analysis_case_parameters_to_value(demo_copy, dict_para) + data_list = self._analysis_dict_to_body_value(demo_copy, demo_add_values) + if isinstance(demo_copy, list): + for item in data_list: + temp_list.append([item]) + data_list = temp_list + return data_list + + def _generate_parameter_to_dict(self, parameters): + dict_temp = dict() + for parameter in parameters: + parameter_attr = SetAttr(parameter) + dict_temp[parameter_attr.name] = parameter_attr + return dict_temp + + def _analysis_case_parameters_to_value_empty_dict(self, request_demo, parameters, type=None): + """ + 循环生成缺少某个参数的值,其它正确的参数组合列表 + """ + parameter_expect_list = list() + expect_lenth = 0 + un_expect_lenth = 0 + parameter_expect_dict = dict() + parameter_un_expect_dict = dict() + for key, parameter_attr in parameters.items(): + if parameter_attr.normal_values: + parameter_expect_dict[parameter_attr.name] = parameter_attr.normal_values.split(",") + else: + parameter_expect_dict[parameter_attr.name] = [ + self._generate_body_expect_type_value(parameter_attr.type)] + + if len(parameter_expect_dict[parameter_attr.name]) > expect_lenth: + expect_lenth = len(parameter_expect_dict[parameter_attr.name]) + + if parameter_attr.exception_values: + parameter_un_expect_dict[parameter_attr.name] = parameter_attr.exception_values.split(",") + else: + parameter_un_expect_dict[parameter_attr.name] = [ + self._generate_body_un_expect_type_value(parameter_attr.type)] + + if len(parameter_un_expect_dict[parameter_attr.name]) > un_expect_lenth: + un_expect_lenth = len(parameter_un_expect_dict[parameter_attr.name]) + + for key in parameter_expect_dict.keys(): + if key not in json.dumps(request_demo): + continue + value = copy.copy(parameter_expect_dict) + value[key] = ['NULL'] + if parameters.get(key).is_need == 1: + status = "FAIL" + else: + status = "PASS" + if type == "1102": + name = "缺少参数%s" % key + else: + name = "缺少参数%s的值" % key + parameter_expect_list.append([value, status, name]) + return parameter_expect_list + + def _distinguish_parameters_by_is_needed(self, parameters): + """ + 将必填参数和非必须参数分开成2个列表 + """ + need_parameters = list() + no_need_parameters = list() + for parameter in parameters: + parameter_attr = SetAttr(parameter) + if parameter_attr.is_need != 0: + need_parameters.append(parameter_attr) + else: + no_need_parameters.append(parameter_attr) + return need_parameters, no_need_parameters + + def _replace_null_value_no_need_parameters(self, demo, no_need_parameter_attrs): + """ + 将所有非必填参数置空 + """ + really_body = copy.deepcopy(demo) + for parameter_attr in no_need_parameter_attrs: + if re.search(r"\"%s\"" % parameter_attr.name, demo): + check_para = r"\"%s\":\s[\[\{]" % parameter_attr.name + if re.search(check_para, demo): + continue + try: + expect_value = parameter_attr.normal_values.split(",")[0] + except Exception as err: + log.error(err) + raise Exception("接口参数%s无正常值,请先确认接口参数。" % parameter_attr.name) + patten1 = r"\"%s\":\s\"%s\"" % (parameter_attr.name, expect_value) + replace1 = "\"%s\": \"NULL\"" % parameter_attr.name + really_body = re.sub(patten1, replace1, really_body) + return really_body + + def generate_case_body_all_1101_array(self, request_demo, parameters): + """ + 生成1101用例对应的所有请求参数组合 + """ + body_array = list() + need, no_need = self._distinguish_parameters_by_is_needed(parameters) + demo_json = json.dumps(request_demo, ensure_ascii=False) + if no_need: + no_need_demo_json = self._replace_null_value_no_need_parameters(demo_json, no_need) + body_array.append([json.loads(no_need_demo_json), "PASS", "所有非必填参数为空"]) + for item in need: + parameter_attr = copy.copy(item) + demo_json_copy = copy.copy(demo_json) + if re.search("\"%s\"" % parameter_attr.name, demo_json_copy): + check_para = r"\"%s\":\s\{|\"%s\":\s\[\{" % (parameter_attr.name, parameter_attr.name) + if re.search(check_para, demo_json_copy): + continue + name = "缺少参数%s" % parameter_attr.name + log.info("1101:%s" % name) + try: + expect_value = parameter_attr.normal_values.split(",")[0] + except Exception as err: + log.error(err) + raise Exception("接口参数%s无正常值,请先确认接口参数。" % parameter_attr.name) + if parameter_attr.type == "array": + patten1 = r"\"%s\":\s\[\"%s\"\]" % (parameter_attr.name, expect_value) + replace1 = "\"%s\": []" % parameter_attr.name + else: + patten1 = r"\"%s\":\s\"%s\"" % (parameter_attr.name, expect_value) + patten1 = patten1.replace("{", r"\{") + patten1 = patten1.replace("}", r"\}") + patten1 = patten1.replace("[", r"\[") + patten1 = patten1.replace("]", r"\]") + patten1 = patten1.replace("$", r"\$") + replace1 = "\"%s\": \"NULL\"" % parameter_attr.name + really_body = re.sub(patten1, replace1, demo_json_copy) + if parameter_attr.is_need == 1: + status = "FAIL" + else: + status = "PASS" + body_array.append([json.loads(really_body), status, name]) + return body_array + + def _clean_all_none_json_char(self, really_body): + """ + 清除1102场景删除缺少参数后不规则的字符串内容 + """ + really_body = re.sub(",\s,", ",", really_body) + really_body = re.sub("\[\s?,", "[", really_body) + really_body = re.sub("\{\s?,", "{", really_body) + really_body = re.sub(",\s\]", "]", really_body) + really_body = re.sub(",\s\}", "}", really_body) + really_body = re.sub("\s,", "", really_body) + return really_body + + def _replace_null_all_no_need_parameters(self, demo, no_need_parameter_attrs): + """ + 将所有非必填参数置空 + """ + really_body = copy.deepcopy(demo) + for parameter_attr in no_need_parameter_attrs: + if re.search(r"\"%s\"" % parameter_attr.name, demo): + check_para = r"\"%s\":\s\{|\"%s\":\s\[\{" % (parameter_attr.name, parameter_attr.name) + if re.search(check_para, demo): + continue + try: + expect_value = parameter_attr.normal_values.split(",")[0] + except Exception as err: + log.error(err) + raise Exception("接口参数%s无正常值,请先确认接口参数。" % parameter_attr.name) + if parameter_attr.type == "array": + patten1 = r"\"%s\":\s\[\"%s\"\]" % (parameter_attr.name, expect_value) + else: + patten1 = r"\"%s\":\s\"%s\"" % (parameter_attr.name, expect_value) + replace1 = "" + really_body = re.sub(patten1, replace1, really_body) + really_body = self._clean_all_none_json_char(really_body) + return really_body + + def generate_case_body_all_1102_array(self, request_demo, parameters): + """ + 生成1102用例对应的所有参数组合 + """ + body_array = list() + need, no_need = self._distinguish_parameters_by_is_needed(parameters) + demo_json = json.dumps(request_demo, ensure_ascii=False) + if no_need: + no_need_demo_json = self._replace_null_all_no_need_parameters(demo_json, no_need) + body_array.append([json.loads(no_need_demo_json), "PASS", "所有非必填参数均缺失"]) + for item in need: + parameter_attr = copy.copy(item) + demo_json_copy = copy.copy(demo_json) + if re.search(r"\"%s\"" % parameter_attr.name, demo_json_copy): + check_para = r"\"%s\":\s\{|\"%s\":\s\[\{" % (parameter_attr.name, parameter_attr.name) + if re.search(check_para, demo_json_copy): + continue + name = "缺少参数%s" % parameter_attr.name + log.info("1102:%s" % name) + expect_value = parameter_attr.normal_values.split(",")[0] + if parameter_attr.type == "array": + patten1 = r"\"%s\":\s\[\"%s\"\]" % (parameter_attr.name, expect_value) + else: + patten1 = r"\"%s\":\s\"%s\"" % (parameter_attr.name, expect_value) + patten1 = patten1.replace("{", r"\{") + patten1 = patten1.replace("}", r"\}") + patten1 = patten1.replace("[", r"\[") + patten1 = patten1.replace("]", r"\]") + patten1 = patten1.replace("$", r"\$") + replace1 = "" + really_body = re.sub(patten1, replace1, demo_json_copy) + really_body = self._clean_all_none_json_char(really_body) + if parameter_attr.is_need == 1: + status = "FAIL" + else: + status = "PASS" + body_array.append([json.loads(really_body), status, name]) + return body_array + + def generate_case_body_all_1201_array(self, request_demo, parameters): + """ + 生成1201用例对应的所有参数组合 + """ + body_array = list() + demo_json = json.dumps(request_demo, ensure_ascii=False) + for item in parameters: + parameter_attr = SetAttr(item) + if re.search(r"\"%s\"" % parameter_attr.name, demo_json): + check_para = r"\"%s\":\s\{|\"%s\":\s\[\{" % (parameter_attr.name, parameter_attr.name) + if re.search(check_para, demo_json): + continue + log.info("参数%s开始替换异常值" % parameter_attr.name) + for un_expect_item in self.un_expect_list.get(parameter_attr.name, []): + name = "参数%s使用异常值%s" % (parameter_attr.name, un_expect_item) + demo_json_copy = copy.copy(demo_json) + expect_value = parameter_attr.normal_values.split(",")[0] + if parameter_attr.type == "array": + patten1 = r"\"%s\":\s\[\"%s\"\]" % (parameter_attr.name, expect_value) + replace1 = '"%s": ["%s"]' % (parameter_attr.name, un_expect_item) + else: + patten1 = r"\"%s\":\s\"%s\"" % (parameter_attr.name, expect_value) + patten1 = patten1.replace("{", r"\{") + patten1 = patten1.replace("}", r"\}") + patten1 = patten1.replace("[", r"\[") + patten1 = patten1.replace("]", r"\]") + patten1 = patten1.replace("$", r"\$") + replace1 = '"%s": "%s"' % (parameter_attr.name, un_expect_item) + really_body = re.sub(patten1, replace1, demo_json_copy) + status = "FAIL" + + body_array.append([json.loads(really_body), status, name]) + return body_array + + def create_case_content(self, result_path, interface_info, parameters, demo, case_type, tags=None): + """ + 生成用例的所有内容 + """ + self._check_interface_confirm(interface_info) + body_content = list() + doc = self.generate_case_doc(interface_info, tags=tags, demo=demo) + case_file, _ = self.generate_case_file_path(interface_info) + interface_name = interface_info.name + if not parameters: + body = self.generate_case_body_content(interface_info.in_url, doc, interface_name, case_type, + interface_info.interface_describe, None, status="PASS") + + else: + try: + body_array = self.generate_case_body_all_1001_array(demo, parameters) + self.normal_1001 = body_array[0] + except TypeError: + log.warning(traceback.print_exc()) + body_array = [] + self.normal_1001 = {} + if case_type == "1001": + try: + body = self.generate_case_body_content(interface_info.in_url, doc, interface_name, case_type, + interface_info.interface_describe, body_array, status="PASS") + except Exception as err: + msg = "生成用例%s,%s失败原因如下:%s" % (interface_info.type, interface_info.in_url, traceback.print_exc()) + log.error(msg) + self.file.write_content_to_case_file(result_path, msg + "\n") + + elif case_type == "1101" or case_type == "1102" or case_type == "1201": + body_temp_list = [] + # parameters_temp = self._generate_parameter_to_dict(parameters) + try: + body_array = eval( + "self.generate_case_body_all_%s_array(self.normal_1001, parameters)" % case_type) + if case_type == "1101": + name = "缺少参数值" + elif case_type == "1102": + name = "缺少参数key和值" + elif case_type == "1201": + name = "参数使用异常值" + else: + name = "" + body_tmp = self.generate_case_body_content(interface_info.in_url, doc, interface_name, + case_type, + name, body_array, status="PASS") + body_temp_list.append(body_tmp) + body = "".join(body_temp_list) + except Exception as err: + msg = "生成用例%s,%s失败原因如下:%s" % (interface_info.type, interface_info.in_url, traceback.print_exc()) + log.error(msg) + self.file.write_content_to_case_file(result_path, msg + "\n") + raise err + body_content.append(body) + self.file.write_content_to_case_file(case_file, "\n".join(body_content)) + msg = "接口url:%s,%s,生成case%s成功!路径为:%s" % (interface_info.type, interface_info.in_url, case_type, case_file) + log.info(msg) + self.file.write_content_to_case_file(result_path, msg + "\n") + return msg + + def generate_case_body_content(self, url, doc, interface_name, case_type, description, body_para, status="PASS"): + body_content = list() + case_name = self._generate_case_name(interface_name, case_type, description) + body_content.append(case_name) + body_content.append(doc) + # 如何需要自动生成1001类型用例的请求body数据,请将304/305行放开,并把306~310注释 + request = self._generate_case_body_content(url, body_content=body_para, status=status) + body_content.append(request) + # if case_type != "1001": + # request = self._generate_case_body_content(url, body_content=body_para, status=status) + # body_content.append(request) + # else: + # body_content.append(" #请自行添加用例请求数据!\n") + return "\n".join(body_content) + + def _generate_case_body_content(self, url, body_content=None, status="PASS"): + body_list = [] + if body_content is None: + try: + body_request = " ${resp} %s" % (self.file.all_url.get(url)[1]) + except Exception as err: + log.error(traceback.print_exc()) + body_request = " #url: %s 无法找到对应的关键字,请检查并更新!" % url + body_list.append(body_request) + body_list.append(" Log ${resp}") + if self.team.upper() in ["TMO"]: + body_validate_success = " should be equal as json ${resp} %s" % '请自行添加检查结果' + else: + body_validate_code = " Should Be Equal ${resp['code']} ${200}" + body_validate_success = " Should Be True ${resp['success']}" + body_list.append(body_validate_code) + body_list.append(body_validate_success) + else: + if not body_content: + body_create = " #request_demo无内容或者parameters在数据表中不全,请先检查再生成 \n ${body} Evaluate %s" % body_content + body_list.append(body_create) + count = 0 + for real_body in body_content: + body_error = None + result = {"message": "", "code": 200, "success": True} + temp = copy.copy(real_body) + if len(real_body) == 3 and isinstance(real_body, list): + if real_body[1] == "PASS" or real_body[1] == "FAIL": + real_body = temp[0] + status = temp[1] + case_description = temp[2] + body_list.append(" #%s" % case_description) + try: + module = load_module_from_file(self.file.kw.kw_file_path, self.team) + importlib.reload(module) + if isinstance(real_body, list): + temp_type = "*" + else: + temp_type = "**" + result = eval( + "%s.%s().%s(%sreal_body)" % ( + "module", self.file.kw_class_name, self.file.all_url.get(url)[1], temp_type)) + except: + msg = "#url: %s 无法通过参数得到请求结果!" % url + log.warning(real_body) + log.warning(msg) + log.warning(traceback.print_exc()) + if "${time}" in str(real_body): + body_list.append(" ${time} get time epoch") + body_create = " ${body_%s} Evaluate %s" % (count, real_body) + try: + if isinstance(real_body, list): + body_request = " ${resp_%s} %s %s" % ( + count, self.file.all_url.get(url)[1], "@{body_%s}" % count) + elif isinstance(real_body, dict): + body_request = " ${resp_%s} %s %s" % ( + count, self.file.all_url.get(url)[1], "&{body_%s}" % count) + except Exception as err: + log.error(traceback.print_exc()) + body_request = " #url: %s 无法找到对应的关键字,请检查并更新!" % url + body_list.append(body_create) + body_list.append(body_request) + body_list.append(" Log ${resp_%s}" % count) + if self.team.upper() in ["TMO"]: + body_validate_resp = " Should Be Equal as json ${resp_%s} %s" % (count, result) + else: + try: + body_validate_code = " Should Be Equal as strings ${resp_%s['code']} ${%s}" + # body_validate_success = " %s ${resp_%s['success']}" + body_validate_success = " %s ${resp_%s['success']} %s" + body_validate_resp = " %s ${resp_%s[\"message\"]} %s" + if isinstance(result, str): + body_validate_code = " #特殊接口返回!" + body_validate_success = " #返回为bytes类型的字符码。仅检查结果是否相等!" + body_validate_resp = " Should Be Equal as strings ${resp_%s} %s" % (count, result) + + elif status == "PASS": + body_validate_code = body_validate_code % (count, result['code']) + body_validate_success = body_validate_success % ("Should Be True", count, "") + try: + if result["message"]: + body_validate_resp = body_validate_resp % ( + "Should Be Equal as strings", count, result["message"]) + else: + body_validate_resp = body_validate_resp % ( + "Should Be Equal as strings", count, '${EMPTY}') + except KeyError as err: + body_validate_resp = " #resp中没有message,请后续自行添加!" + log.warning(err) + log.warning("resp中没有message,请后续自行添加!") + else: + try: + body_validate_code = body_validate_code % (count, result['code']) + except Exception as e: + if "status" in result: + body_validate_code = body_validate_code % (count, result['status']) + body_validate_code = body_validate_code.replace("code", "status") + else: + raise KeyError("接口不中无code返回,请与开发确认!!!") + try: + body_validate_success = body_validate_success % ( + "Should Be Equal As Strings", count, result["success"]) + except Exception as err: + if "error" in result: + body_validate_success = body_validate_success % ( + "Should Be Equal As Strings", count, result["error"]) + body_validate_success = body_validate_success.replace("success", "error") + else: + raise KeyError("接口不中无success返回,请与开发确认!!!") + try: + if result.get("message"): + if re.search("[a-zA-Z0-9]{32}", result.get("message", "")): + body_validate_resp = body_validate_resp % ("should contain", count, + re.sub("[a-zA-Z0-9]{32}", "", result["message"])) + else: + body_validate_resp = body_validate_resp % ( + "should be equal as strings", count, result["message"]) + else: + body_validate_resp = body_validate_resp % ( + "Should Be Equal as strings", count, '${EMPTY}') + except KeyError as err: + body_validate_resp = " #resp中没有message,请后续自行添加!" + log.warning(err) + log.warning("resp中没有message,请后续自行添加!") + except Exception as error: + log.error(error) + body_validate_code = None + body_validate_success = None + body_validate_resp = None + body_error = " #无法正确生成异常用例返回值,请检查并手动生成!" + if body_validate_code: + body_list.append(body_validate_code) + if body_validate_success: + body_list.append(body_validate_success) + if body_validate_resp: + body_list.append(body_validate_resp) + if body_error: + body_list.append(body_error) + # body_list.append(" #请自行添加data校验!") + count += 1 + body_list.append("\n") + return "\n".join(body_list) + + def _generate_body_expect_type_value(self, type): + if type.lower() == 'integer': + value = random.randint(0, 100) + elif type.lower() == "string": + value = "".join([random.choice(string.digits + string.ascii_letters) for i in range(5)]) + elif type.lower() == "date": + value = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + elif type.lower() == "boolean": + value = True + elif type.lower() == "array": + value = [1] + else: + value = "123" + return value + + def _generate_body_un_expect_type_value(self, type): + value = "" + if type.lower() == 'integer': + value = "".join([random.choice(string.digits + string.ascii_letters) for i in range(5)]) + elif type.lower() == "string": + value = random.randint(0, 10000) + else: + value = False + return value + + def _analysis_dict_to_body_value(self, dict_none, dict_data): + """ + 生成所有请求参数的组合列表 + """ + data_list = [] + if isinstance(dict_none, list): + if dict_none: + for index in range(len(dict_none)): + data = self._analysis_dict_to_body_value(dict_none[index], dict_data[index]) + data_list.extend(data) + else: + data_list = dict_data + elif isinstance(dict_none, dict): + try: + len_num = self._get_nums(dict_data) + except Exception as e: + # log.error(e) + log.error("数据表request_parameters中参数不全,请使用interface_hunter补全参数") + raise e + for i in range(0, len_num): + copy_dict = copy.copy(dict_none) + for key, value in copy_dict.items(): + data = self._analysis_dict_to_body_value(copy_dict[key], dict_data[key]) + if not data: + copy_dict[key] = None + elif i >= len(data): + copy_dict[key] = data[0] + if isinstance(value, list): + copy_dict[key] = [data[0]] + else: + copy_dict[key] = data[i] + if isinstance(value, list): + copy_dict[key] = [data[i]] + + data_list.append(copy_dict) + + else: + data_list = dict_data + + return data_list + + def _get_nums(self, dict_data): + """ + 获取所有参数对应值的最大长度 + """ + nums_list = [0] + if isinstance(dict_data, list): + for item in dict_data: + if isinstance(item, dict): + data = self._get_nums(dict_data[0]) + nums_list.append(data) + else: + nums_list.append(len(dict_data)) + + elif isinstance(dict_data, dict): + for value in dict_data.values(): + data = self._get_nums(value) + nums_list.append(data) + elif dict_data == None: + nums_list.append(0) + else: + nums_list.append(len(dict_data)) + return max(nums_list) + + def _analysis_case_parameters_to_value(self, interface_demo, parameters): + """ + 根据demo将每个参数的对应值的列表写回demo + """ + body = copy.copy(interface_demo) + if isinstance(body, list): + for index in range(len(body)): + item = body[index] + body[index] = self._analysis_case_parameters_to_value(item, parameters) + + elif isinstance(body, dict): + for key, value in body.items(): + if isinstance(value, dict) or isinstance(value, list): + if value: + body[key] = self._analysis_case_parameters_to_value(body.get(key), parameters) + else: + body[key] = parameters.get(key) + else: + body[key] = parameters.get(key) + return body + + def _analysis_case_parameters_to_dict(self, parameters): + """ + 根据所有参数生成正常值 和异常值的列表 + """ + expect_lenth = 0 + un_expect_lenth = 0 + parameter_expect_dict = dict() + parameter_un_expect_dict = dict() + for parameter in parameters: + parameter_attr = SetAttr(parameter) + if parameter_attr.normal_values: + if parameter_attr.normal_values.startswith("["): + parameter_expect_dict[parameter_attr.name] = parameter_attr.normal_values.split(",") + else: + if parameter_attr.type == "array": + parameter_expect_dict[parameter_attr.name] = [[x] for x in parameter_attr.normal_values.split(",")] + else: + parameter_expect_dict[parameter_attr.name] = parameter_attr.normal_values.split(",") + else: + continue + if len(parameter_expect_dict[parameter_attr.name]) > expect_lenth: + expect_lenth = len(parameter_expect_dict[parameter_attr.name]) + + if parameter_attr.exception_values: + parameter_un_expect_dict[parameter_attr.name] = parameter_attr.exception_values.split(",") + else: + # raise Exception("接口参数%s无异常值,请先确认接口参数。" % parameter_attr.name) + parameter_un_expect_dict[parameter_attr.name] = [] + + if len(parameter_un_expect_dict[parameter_attr.name]) > un_expect_lenth: + un_expect_lenth = len(parameter_un_expect_dict[parameter_attr.name]) + + return parameter_expect_dict, parameter_un_expect_dict, expect_lenth, un_expect_lenth + + def generate_case_doc(self, interface_info, tags=None, demo=None): + if demo: + lenth, demo_json, paras_d = self.file.kw._clean_demo_to_less_key(demo) + doc_list = list() + doc_list.append( + " [Documentation] {} 接口路径:{}, {}".format(interface_info.interface_describe, + interface_info.type, interface_info.in_url)) + # doc_list.append(self.doc_content_row_temp.format("输入参数名", "参数类型", "说明")) + # for parameters_info in parameters: + # sub_doc = self._generate_case_doc(parameters_info) + # doc_list.append(sub_doc) + if tags: + tag_list = tags.split(",") + tag = " [Tags]" + for tag_ in tag_list: + tag += " %s" % tag_ + doc_list.append(tag) + if lenth > 1: + doc_list.append( + " # 请求参数说明 h:放header里的参数 u:放url路径节点中的参数 d:不放在url中在请求参数 p:放在url问号后在key-value结构参数") + return "\n".join(doc_list) + + def _generate_case_doc(self, request_parameter): + doc_content = list() + parameter_attr = SetAttr(request_parameter) + row = self.doc_content_row_temp.format(parameter_attr.name, parameter_attr.type, parameter_attr.note) + doc_content.append(row) + return "\n".join(doc_content) + + def generate_case_file_path(self, interface_info): + path_cell_list = self._generate_case_file_path_info(interface_info.in_url) + path_cell_list.insert(0, self.file.case_dir) + return os.path.abspath(os.path.join(*path_cell_list[:-1]) + ".robot"), path_cell_list[-1] + + def _generate_case_file_path_info(self, url): + head_list = [] + para_list = [] + host, url_last = re.search(r"(\S+\.cn|\S+\.com)(\S+)", url).groups() + path_first = re.search("//([\w+\-]+)\.", host).group(1) + head_list.append(path_first) + search = re.findall("[\w+\-_]+|[\{\w+\-_\}]+", url_last) + for item in search: + if "{" in item: + temp = item.replace("{", "").replace("}", "") + para_list.append(temp) + else: + head_list.append(item) + return head_list + + +def run_interface_case_generate(result_path, interface, demo_info, parameters, case_instance, tags=None, normal=None): + case_type_list = ["1001", "1101", "1102", "1201"] + not_exist_case_type = list() + exist_case_name = list() + case_path, _ = case_instance.generate_case_file_path(interface) + case_name_head = interface.name + if parameters: + for case_type in case_type_list: + case_name = "-".join([case_name_head, case_type]) + if not case_instance.file.check_case_exist(case_path, case_name): + not_exist_case_type.append(case_type) + else: + exist_case_name.append(case_name) + else: + case_name = "-".join([case_name_head, "1001"]) + if not case_instance.file.check_case_exist(case_path, case_name): + not_exist_case_type.append("1001") + else: + exist_case_name.append(case_name) + for item in exist_case_name: + msg = "接口url:%s,%s,已存在用例%s,请确认!" % (interface.type, interface.in_url, item) + log.warning(msg) + case_instance.file.write_content_to_case_file(result_path, msg + "\n") + if not_exist_case_type: + if normal: + not_exist_case_type = ["1001"] + case_instance.file.generate_case_file(case_path) + try: + demo = json.loads(demo_info.request_demo) + except Exception as err: + log.error(traceback.print_exc()) + log.warning("the demo of interface(%s) is empty,please check." % interface.id) + demo = dict() + try: + result = list() + result.append("") + result.append("-" * 60) + result.append("用例生成成功!") + for case_type in not_exist_case_type: + body = case_instance.create_case_content(result_path, interface, parameters, demo, case_type, tags=tags) + # result.append(body) + result.append(body.split("!")[1]) + log.info("\n".join(result)) + except: + log.error("could not generate case for interface id: %s, url: %s" % (interface.id, interface.in_url)) + log.error(traceback.print_exc()) + else: + msg = "接口url:%s,%s,已存在用例,请确认!" % (interface.type, interface.in_url) + log.warning(msg) + case_instance.file.write_content_to_case_file(result_path, msg + "\n") diff --git a/base_framework/platform_tools/Testcase_service/case_service1.py b/base_framework/platform_tools/Testcase_service/case_service1.py new file mode 100644 index 0000000..41d8834 --- /dev/null +++ b/base_framework/platform_tools/Testcase_service/case_service1.py @@ -0,0 +1,833 @@ +# -*- coding:utf-8 -*- +import copy +import importlib +import json +import os +import random +import re +import sys +import string +import time +import traceback + + +def get_project_root_path(path="base_framework"): + """ + 获取项目目录 + """ + o_path = os.getcwd() + try: + project_path = re.search(r"(.*%s)" % path, o_path).group(1) + return os.path.abspath(os.path.join(project_path, os.path.pardir)) + except: + pass + + +Project_Path = get_project_root_path() +sys.path.append(Project_Path) + +from base_framework.platform_tools.Keywords_service.keyword_service1 import SetAttr, log + + +def load_module_from_file(file_path, team): + module_file = re.search(r"(%s\\.*)\.py" % team, file_path) + if not module_file: + module_file = re.search(r"(%s.*)\.py" % team.lower(), file_path) + temp = module_file.group(1) + temp = temp.replace(os.sep, ".") + # try: + # module = importlib.import_module(temp) + # except Exception as err: + # temp = re.sub(r"%s\." % team, "%s." % team.lower(), temp) + # module = importlib.import_module(temp) + return importlib.import_module(temp) + + +class ITCaseFileOperation(object): + def __init__(self, team, kw_instance): + self.kw = kw_instance + self.all_url = kw_instance.all_url + self.kw_class_name = kw_instance.kw_class + self.case_dir = os.path.abspath(os.path.join(Project_Path, team, "test_case", "TestCase", "1.接口")) + self.env_path = os.path.abspath( + os.path.join(Project_Path, team, "test_case", "Resource", "AdapterKws", "env.robot")) + + def _check_case_file_exist(self, file_path): + if os.path.exists(file_path) and os.path.isfile(file_path): + return True + else: + return False + + def check_case_exist(self, case_path, case_name): + if self._check_case_file_exist(case_path): + with open(case_path, "r", encoding="utf-8") as f: + for line in f.readlines(): + if line.startswith(case_name): + return True + else: + return False + else: + return False + + def generate_case_file(self, file_path): + if not self._check_case_file_exist(file_path): + dirname = file_path[:-len(os.path.basename(file_path))] + if not os.path.exists(dirname): + os.makedirs(dirname) + self.write_content_to_case_file(file_path, self.generate_env_releate_path(dirname)) + else: + with open(file_path, "r", encoding="utf-8") as f: + if f.readlines(): + if "\n" != f.readlines()[-1]: + self.write_content_to_case_file(file_path, "\n") + + def generate_env_releate_path(self, file_path): + return "*** Settings ***\nResource " + os.path.relpath(self.env_path, + file_path).replace("\\", + "/") + "\n\n*** Test Cases ***\n" + + def _check_case_file_resource(self, file_path): + """ + 确定case文件中是否有关键字 + """ + with open(file_path, "a+", encoding="utf-8") as f: + content_list = f.readlines() + if "*** Keywords ***\n" not in content_list: + return True + else: + kw_index = content_list.rindex("*** Keywords ***\n") + for index in range(kw_index, len(content_list) + 1): + if "*** Test Cases ***\n" == content_list[index]: + status = True + if "*** Keywords ***\n" == content_list[index]: + status = False + return status + + def write_content_to_case_file(self, file_path, content): + status = self._check_case_file_resource(file_path) + with open(file_path, "a+", encoding="utf-8") as f: + if status: + f.write(content) + else: + content = "*** Test Cases ***\n" + content + f.write(content) + + +class ITCaseContentOperation(object): + def __init__(self, team, kw_instance): + self.team = team + self.file = ITCaseFileOperation(team, kw_instance) + self.doc_content_row_temp = " ... | {} | {} | {} |" + self.normal_1001 = None + self.un_expect_list = None + self.case_string = None + + def _generate_case_name(self, interface_name, case_type, case_des): + return "-".join([interface_name, case_type, case_des]) + + def _check_interface_confirm(self, interface_info): + if not interface_info.confirmed: + raise Exception("接口%s未确认,请先确认接口参数。" % interface_info.name) + + def generate_case_body_all_1001_array(self, request_demo, parameters): + temp_list = list() + demo_copy = copy.deepcopy(request_demo) + for key, value in request_demo.items(): + if not value: + demo_copy.pop(key) + if len(demo_copy.keys()) == 1: + demo_copy = demo_copy.pop(list(demo_copy.keys())[0]) + dict_para, self.un_expect_list, _, _ = self._analysis_case_parameters_to_dict(parameters) + demo_add_values = self._analysis_case_parameters_to_value(demo_copy, dict_para) + data_list = self._analysis_dict_to_body_value(demo_copy, demo_add_values) + if isinstance(demo_copy, list): + for item in data_list: + temp_list.append([item]) + data_list = temp_list + return data_list + + def _generate_parameter_to_dict(self, parameters): + dict_temp = dict() + for parameter in parameters: + parameter_attr = SetAttr(parameter) + dict_temp[parameter_attr.name] = parameter_attr + return dict_temp + + def _analysis_case_parameters_to_value_empty_dict(self, request_demo, parameters, type=None): + """ + 循环生成缺少某个参数的值,其它正确的参数组合列表 + """ + parameter_expect_list = list() + expect_lenth = 0 + un_expect_lenth = 0 + parameter_expect_dict = dict() + parameter_un_expect_dict = dict() + for key, parameter_attr in parameters.items(): + if parameter_attr.normal_values: + parameter_expect_dict[parameter_attr.name] = parameter_attr.normal_values.split(",") + else: + parameter_expect_dict[parameter_attr.name] = [ + self._generate_body_expect_type_value(parameter_attr.type)] + + if len(parameter_expect_dict[parameter_attr.name]) > expect_lenth: + expect_lenth = len(parameter_expect_dict[parameter_attr.name]) + + if parameter_attr.exception_values: + parameter_un_expect_dict[parameter_attr.name] = parameter_attr.exception_values.split(",") + else: + parameter_un_expect_dict[parameter_attr.name] = [ + self._generate_body_un_expect_type_value(parameter_attr.type)] + + if len(parameter_un_expect_dict[parameter_attr.name]) > un_expect_lenth: + un_expect_lenth = len(parameter_un_expect_dict[parameter_attr.name]) + + for key in parameter_expect_dict.keys(): + if key not in json.dumps(request_demo): + continue + value = copy.copy(parameter_expect_dict) + value[key] = ['NULL'] + if parameters.get(key).is_need == 1: + status = "FAIL" + else: + status = "PASS" + if type == "1102": + name = "缺少参数%s" % key + else: + name = "缺少参数%s的值" % key + parameter_expect_list.append([value, status, name]) + return parameter_expect_list + + def _distinguish_parameters_by_is_needed(self, parameters): + """ + 将必填参数和非必须参数分开成2个列表 + """ + need_parameters = list() + no_need_parameters = list() + for parameter in parameters: + parameter_attr = SetAttr(parameter) + if parameter_attr.is_need != 0: + need_parameters.append(parameter_attr) + else: + no_need_parameters.append(parameter_attr) + return need_parameters, no_need_parameters + + def _replace_null_value_no_need_parameters(self, demo, no_need_parameter_attrs): + """ + 将所有非必填参数置空 + """ + really_body = copy.deepcopy(demo) + for parameter_attr in no_need_parameter_attrs: + if re.search(r"\"%s\"" % parameter_attr.name, demo): + check_para = r"\"%s\":\s[\[\{]" % parameter_attr.name + if re.search(check_para, demo): + continue + try: + expect_value = parameter_attr.normal_values.split(",")[0] + except Exception as err: + log.error(err) + raise Exception("接口参数%s无正常值,请先确认接口参数。" % parameter_attr.name) + patten1 = r"\"%s\":\s\"%s\"" % (parameter_attr.name, expect_value) + replace1 = "\"%s\": \"NULL\"" % parameter_attr.name + really_body = re.sub(patten1, replace1, really_body) + return really_body + + def generate_case_body_all_1101_array(self, request_demo, parameters): + """ + 生成1101用例对应的所有请求参数组合 + """ + body_array = list() + need, no_need = self._distinguish_parameters_by_is_needed(parameters) + demo_json = json.dumps(request_demo, ensure_ascii=False) + if no_need: + no_need_demo_json = self._replace_null_value_no_need_parameters(demo_json, no_need) + body_array.append([json.loads(no_need_demo_json), "PASS", "所有非必填参数为空"]) + for item in need: + parameter_attr = copy.copy(item) + demo_json_copy = copy.copy(demo_json) + if re.search("\"%s\"" % parameter_attr.name, demo_json_copy): + check_para = r"\"%s\":\s\{|\"%s\":\s\[\{" % (parameter_attr.name, parameter_attr.name) + if re.search(check_para, demo_json_copy): + continue + name = "缺少参数%s" % parameter_attr.name + try: + expect_value = parameter_attr.normal_values.split(",")[0] + except Exception as err: + log.error(err) + raise Exception("接口参数%s无正常值,请先确认接口参数。" % parameter_attr.name) + if parameter_attr.type == "array": + patten1 = r"\"%s\":\s\[\"%s\"\]" % (parameter_attr.name, expect_value) + replace1 = "\"%s\": []" % parameter_attr.name + else: + patten1 = r"\"%s\":\s\"%s\"" % (parameter_attr.name, expect_value) + patten1 = patten1.replace("{", r"\{") + patten1 = patten1.replace("}", r"\}") + patten1 = patten1.replace("[", r"\[") + patten1 = patten1.replace("]", r"\]") + patten1 = patten1.replace("$", r"\$") + replace1 = "\"%s\": \"NULL\"" % parameter_attr.name + really_body = re.sub(patten1, replace1, demo_json_copy) + if parameter_attr.is_need == 1: + status = "FAIL" + else: + status = "PASS" + body_array.append([json.loads(really_body), status, name]) + return body_array + + def _clean_all_none_json_char(self, really_body): + """ + 清除1102场景删除缺少参数后不规则的字符串内容 + """ + really_body = re.sub(",\s,", ",", really_body) + really_body = re.sub("\[\s?,", "[", really_body) + really_body = re.sub("\{\s?,", "{", really_body) + really_body = re.sub(",\s\]", "]", really_body) + really_body = re.sub(",\s\}", "}", really_body) + really_body = re.sub("\s,", "", really_body) + return really_body + + def _replace_null_all_no_need_parameters(self, demo, no_need_parameter_attrs): + """ + 将所有非必填参数置空 + """ + really_body = copy.deepcopy(demo) + for parameter_attr in no_need_parameter_attrs: + if re.search(r"\"%s\"" % parameter_attr.name, demo): + check_para = r"\"%s\":\s\{|\"%s\":\s\[\{" % (parameter_attr.name, parameter_attr.name) + if re.search(check_para, demo): + continue + try: + expect_value = parameter_attr.normal_values.split(",")[0] + except Exception as err: + log.error(err) + raise Exception("接口参数%s无正常值,请先确认接口参数。" % parameter_attr.name) + if parameter_attr.type == "array": + patten1 = r"\"%s\":\s\[\"%s\"\]" % (parameter_attr.name, expect_value) + else: + patten1 = r"\"%s\":\s\"%s\"" % (parameter_attr.name, expect_value) + replace1 = "" + really_body = re.sub(patten1, replace1, really_body) + really_body = self._clean_all_none_json_char(really_body) + return really_body + + def generate_case_body_all_1102_array(self, request_demo, parameters): + """ + 生成1102用例对应的所有参数组合 + """ + body_array = list() + need, no_need = self._distinguish_parameters_by_is_needed(parameters) + demo_json = json.dumps(request_demo, ensure_ascii=False) + if no_need: + no_need_demo_json = self._replace_null_all_no_need_parameters(demo_json, no_need) + body_array.append([json.loads(no_need_demo_json), "PASS", "所有非必填参数均缺失"]) + for item in need: + parameter_attr = copy.copy(item) + demo_json_copy = copy.copy(demo_json) + if re.search(r"\"%s\"" % parameter_attr.name, demo_json_copy): + check_para = r"\"%s\":\s\{|\"%s\":\s\[\{" % (parameter_attr.name, parameter_attr.name) + if re.search(check_para, demo_json_copy): + continue + name = "缺少参数%s" % parameter_attr.name + expect_value = parameter_attr.normal_values.split(",")[0] + if parameter_attr.type == "array": + patten1 = r"\"%s\":\s\[\"%s\"\]" % (parameter_attr.name, expect_value) + else: + patten1 = r"\"%s\":\s\"%s\"" % (parameter_attr.name, expect_value) + patten1 = patten1.replace("{", r"\{") + patten1 = patten1.replace("}", r"\}") + patten1 = patten1.replace("[", r"\[") + patten1 = patten1.replace("]", r"\]") + patten1 = patten1.replace("$", r"\$") + replace1 = "" + really_body = re.sub(patten1, replace1, demo_json_copy) + really_body = self._clean_all_none_json_char(really_body) + if parameter_attr.is_need == 1: + status = "FAIL" + else: + status = "PASS" + body_array.append([json.loads(really_body), status, name]) + return body_array + + def generate_case_body_all_1201_array(self, request_demo, parameters): + """ + 生成1201用例对应的所有参数组合 + """ + body_array = list() + demo_json = json.dumps(request_demo, ensure_ascii=False) + for item in parameters: + parameter_attr = SetAttr(item) + if re.search(r"\"%s\"" % parameter_attr.name, demo_json): + check_para = r"\"%s\":\s\{|\"%s\":\s\[\{" % (parameter_attr.name, parameter_attr.name) + if re.search(check_para, demo_json): + continue + for un_expect_item in self.un_expect_list.get(parameter_attr.name): + name = "参数%s使用异常值%s" % (parameter_attr.name, un_expect_item) + demo_json_copy = copy.copy(demo_json) + expect_value = parameter_attr.normal_values.split(",")[0] + if parameter_attr.type == "array": + patten1 = r"\"%s\":\s\[\"%s\"\]" % (parameter_attr.name, expect_value) + replace1 = '"%s": ["%s"]' % (parameter_attr.name, un_expect_item) + else: + patten1 = r"\"%s\":\s\"%s\"" % (parameter_attr.name, expect_value) + patten1 = patten1.replace("{", r"\{") + patten1 = patten1.replace("}", r"\}") + patten1 = patten1.replace("[", r"\[") + patten1 = patten1.replace("]", r"\]") + patten1 = patten1.replace("$", r"\$") + replace1 = '"%s": "%s"' % (parameter_attr.name, un_expect_item) + really_body = re.sub(patten1, replace1, demo_json_copy) + status = "FAIL" + + body_array.append([json.loads(really_body), status, name]) + return body_array + + def create_case_content(self, result_path, interface_info, parameters, demo, case_type, tags=None): + """ + 生成用例的所有内容 + """ + self._check_interface_confirm(interface_info) + body_content = list() + doc = self.generate_case_doc(interface_info, tags=tags, demo=demo) + case_file, _ = self.generate_case_file_path(interface_info) + interface_name = interface_info.name + if not parameters: + body = self.generate_case_body_content(interface_info.in_url, doc, interface_name, case_type, + interface_info.interface_describe, None, status="PASS") + + else: + try: + body_array = self.generate_case_body_all_1001_array(demo, parameters) + self.normal_1001 = body_array[0] + except TypeError: + log.warning(traceback.print_exc()) + body_array = [] + self.normal_1001 = {} + if case_type == "1001": + try: + body = self.generate_case_body_content(interface_info.in_url, doc, interface_name, case_type, + interface_info.interface_describe, body_array, status="PASS") + except Exception as err: + msg = "生成用例%s,%s失败原因如下:%s" % (interface_info.type, interface_info.in_url, traceback.print_exc()) + log.error(msg) + self.file.write_content_to_case_file(result_path, msg + "\n") + + elif case_type == "1101" or case_type == "1102" or case_type == "1201": + body_temp_list = [] + # parameters_temp = self._generate_parameter_to_dict(parameters) + try: + body_array = eval( + "self.generate_case_body_all_%s_array(self.normal_1001, parameters)" % case_type) + if case_type == "1101": + name = "缺少参数值" + elif case_type == "1102": + name = "缺少参数key和值" + elif case_type == "1201": + name = "参数使用异常值" + else: + name = "" + body_tmp = self.generate_case_body_content(interface_info.in_url, doc, interface_name, + case_type, + name, body_array, status="PASS") + body_temp_list.append(body_tmp) + body = "".join(body_temp_list) + except Exception as err: + msg = "生成用例%s,%s失败原因如下:%s" % (interface_info.type, interface_info.in_url, traceback.print_exc()) + log.error(msg) + self.file.write_content_to_case_file(result_path, msg + "\n") + raise err + body_content.append(body) + self.file.write_content_to_case_file(case_file, "\n".join(body_content)) + msg = "接口url:%s,%s,生成case%s成功!路径为:%s" % (interface_info.type, interface_info.in_url, case_type, case_file) + log.info(msg) + self.file.write_content_to_case_file(result_path, msg + "\n") + return msg + + def generate_case_body_content(self, url, doc, interface_name, case_type, description, body_para, status="PASS"): + body_content = list() + case_name = self._generate_case_name(interface_name, case_type, description) + body_content.append(case_name) + body_content.append(doc) + # 如何需要自动生成1001类型用例的请求body数据,请将304/305行放开,并把306~310注释 + request = self._generate_case_body_content(url, body_content=body_para, status=status) + body_content.append(request) + # if case_type != "1001": + # request = self._generate_case_body_content(url, body_content=body_para, status=status) + # body_content.append(request) + # else: + # body_content.append(" #请自行添加用例请求数据!\n") + return "\n".join(body_content) + + def _generate_case_body_content(self, url, body_content=None, status="PASS"): + body_list = [] + if body_content is None: + try: + body_request = " ${resp} %s" % (self.file.all_url.get(url)[1]) + except Exception as err: + log.error(traceback.print_exc()) + body_request = " #url: %s 无法找到对应的关键字,请检查并更新!" % url + body_list.append(body_request) + body_list.append(" Log ${resp}") + if self.team.upper() in ["TMO"]: + body_validate_success = " check_rf ${resp} %s" % '请自行添加检查结果' + else: + body_validate_code = " Should Be Equal ${resp['code']} ${200}" + body_validate_success = " Should Be True ${resp['success']}" + body_list.append(body_validate_code) + body_list.append(body_validate_success) + else: + if not body_content: + body_create = " #request_demo无内容或者parameters在数据表中不全,请先检查再生成 \n ${body} Evaluate %s" % body_content + body_list.append(body_create) + count = 0 + for real_body in body_content: + result = {"message": "", "code": 200, "success": True} + temp = copy.copy(real_body) + if len(real_body) == 3 and isinstance(real_body, list): + if real_body[1] == "PASS" or real_body[1] == "FAIL": + real_body = temp[0] + status = temp[1] + case_description = temp[2] + body_list.append(" #%s" % case_description) + try: + module = load_module_from_file(self.file.kw.kw_file_path, self.team) + importlib.reload(module) + if isinstance(real_body, list): + temp_type = "*" + else: + temp_type = "**" + result = eval( + "%s.%s().%s(%sreal_body)" % ( + "module", self.file.kw_class_name, self.file.all_url.get(url)[1], temp_type)) + except: + msg = "#url: %s 无法通过参数得到请求结果!" % url + log.warning(real_body) + log.warning(msg) + log.warning(traceback.print_exc()) + if "${time}" in str(real_body): + body_list.append(" ${time} get time epoch") + body_create = " ${body_%s} Evaluate %s" % (count, real_body) + try: + if isinstance(real_body, list): + body_request = " ${resp_%s} %s %s" % ( + count, self.file.all_url.get(url)[1], "@{body_%s}" % count) + elif isinstance(real_body, dict): + body_request = " ${resp_%s} %s %s" % ( + count, self.file.all_url.get(url)[1], "&{body_%s}" % count) + except Exception as err: + log.error(traceback.print_exc()) + body_request = " #url: %s 无法找到对应的关键字,请检查并更新!" % url + body_list.append(body_create) + body_list.append(body_request) + body_list.append(" Log ${resp_%s}" % count) + if self.team.upper() in ["TMO"]: + body_validate_resp = " check_rf ${resp_%s} %s" % (count, result) + else: + body_validate_code = " Should Be Equal as strings ${resp_%s['code']} ${%s}" + # body_validate_success = " %s ${resp_%s['success']}" + body_validate_success = " %s ${resp_%s['success']} %s" + body_validate_resp = " %s ${resp_%s[\"message\"]} %s" + if isinstance(result, str): + body_validate_code = " #特殊接口返回!" + body_validate_success = " #返回为bytes类型的字符码。仅检查结果是否相等!" + body_validate_resp = " Should Be Equal as strings ${resp_%s} %s" % (count, result) + + elif status == "PASS": + body_validate_code = body_validate_code % (count, result['code']) + body_validate_success = body_validate_success % ("Should Be True", count, "") + try: + if result["message"]: + body_validate_resp = body_validate_resp % ( + "Should Be Equal as strings", count, result["message"]) + else: + body_validate_resp = body_validate_resp % ( + "Should Be Equal as strings", count, '${EMPTY}') + except KeyError as err: + body_validate_resp = " #resp中没有message,请后续自行添加!" + log.warning(err) + log.warning("resp中没有message,请后续自行添加!") + else: + try: + body_validate_code = body_validate_code % (count, result['code']) + except Exception as e: + if "status" in result: + body_validate_code = body_validate_code % (count, result['status']) + body_validate_code = body_validate_code.replace("code", "status") + else: + raise KeyError("接口不中无code返回,请与开发确认!!!") + try: + body_validate_success = body_validate_success % ( + "Should Be Equal As Strings", count, result["success"]) + except Exception as err: + if "error" in result: + body_validate_success = body_validate_success % ( + "Should Be Equal As Strings", count, result["error"]) + body_validate_success = body_validate_success.replace("success", "error") + else: + raise KeyError("接口不中无success返回,请与开发确认!!!") + try: + if result.get("message"): + if re.search("[a-zA-Z0-9]{32}", result.get("message", "")): + body_validate_resp = body_validate_resp % ("should contain", count, + re.sub("[a-zA-Z0-9]{32}", "", result["message"])) + else: + body_validate_resp = body_validate_resp % ( + "should be equal as strings", count, result["message"]) + else: + body_validate_resp = body_validate_resp % ( + "Should Be Equal as strings", count, '${EMPTY}') + except KeyError as err: + body_validate_resp = " #resp中没有message,请后续自行添加!" + log.warning(err) + log.warning("resp中没有message,请后续自行添加!") + + body_list.append(body_validate_code) + body_list.append(body_validate_success) + body_list.append(body_validate_resp) + # body_list.append(" #请自行添加data校验!") + count += 1 + body_list.append("\n") + return "\n".join(body_list) + + def _generate_body_expect_type_value(self, type): + if type.lower() == 'integer': + value = random.randint(0, 100) + elif type.lower() == "string": + value = "".join([random.choice(string.digits + string.ascii_letters) for i in range(5)]) + elif type.lower() == "date": + value = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + elif type.lower() == "boolean": + value = True + elif type.lower() == "array": + value = [1] + else: + value = "123" + return value + + def _generate_body_un_expect_type_value(self, type): + value = "" + if type.lower() == 'integer': + value = "".join([random.choice(string.digits + string.ascii_letters) for i in range(5)]) + elif type.lower() == "string": + value = random.randint(0, 10000) + else: + value = False + return value + + def _analysis_dict_to_body_value(self, dict_none, dict_data): + """ + 生成所有请求参数的组合列表 + """ + data_list = [] + if isinstance(dict_none, list): + if dict_none: + for index in range(len(dict_none)): + data = self._analysis_dict_to_body_value(dict_none[index], dict_data[index]) + data_list.extend(data) + else: + data_list = dict_data + elif isinstance(dict_none, dict): + try: + len_num = self._get_nums(dict_data) + except Exception as e: + # log.error(e) + log.error("数据表request_parameters中参数不全,请使用interface_hunter补全参数") + raise e + for i in range(0, len_num): + copy_dict = copy.copy(dict_none) + for key, value in copy_dict.items(): + data = self._analysis_dict_to_body_value(copy_dict[key], dict_data[key]) + if not data: + copy_dict[key] = None + elif i >= len(data): + copy_dict[key] = data[0] + if isinstance(value, list): + copy_dict[key] = [data[0]] + else: + copy_dict[key] = data[i] + if isinstance(value, list): + copy_dict[key] = [data[i]] + + data_list.append(copy_dict) + + else: + data_list = dict_data + + return data_list + + def _get_nums(self, dict_data): + """ + 获取所有参数对应值的最大长度 + """ + nums_list = [0] + if isinstance(dict_data, list): + for item in dict_data: + if isinstance(item, dict): + data = self._get_nums(dict_data[0]) + nums_list.append(data) + else: + nums_list.append(len(dict_data)) + + elif isinstance(dict_data, dict): + for value in dict_data.values(): + data = self._get_nums(value) + nums_list.append(data) + elif dict_data == None: + nums_list.append(0) + else: + nums_list.append(len(dict_data)) + return max(nums_list) + + def _analysis_case_parameters_to_value(self, interface_demo, parameters): + """ + 根据demo将每个参数的对应值的列表写回demo + """ + body = copy.copy(interface_demo) + if isinstance(body, list): + for index in range(len(body)): + item = body[index] + body[index] = self._analysis_case_parameters_to_value(item, parameters) + + elif isinstance(body, dict): + for key, value in body.items(): + if isinstance(value, dict) or isinstance(value, list): + if value: + body[key] = self._analysis_case_parameters_to_value(body.get(key), parameters) + else: + body[key] = parameters.get(key) + else: + body[key] = parameters.get(key) + return body + + def _analysis_case_parameters_to_dict(self, parameters): + """ + 根据所有参数生成正常值 和异常值的列表 + """ + expect_lenth = 0 + un_expect_lenth = 0 + parameter_expect_dict = dict() + parameter_un_expect_dict = dict() + for parameter in parameters: + parameter_attr = SetAttr(parameter) + if parameter_attr.normal_values: + if parameter_attr.normal_values.startswith("["): + parameter_expect_dict[parameter_attr.name] = parameter_attr.normal_values.split(",") + else: + if parameter_attr.type == "array": + parameter_expect_dict[parameter_attr.name] = [[x] for x in parameter_attr.normal_values.split(",")] + else: + parameter_expect_dict[parameter_attr.name] = parameter_attr.normal_values.split(",") + else: + continue + if len(parameter_expect_dict[parameter_attr.name]) > expect_lenth: + expect_lenth = len(parameter_expect_dict[parameter_attr.name]) + + if parameter_attr.exception_values: + parameter_un_expect_dict[parameter_attr.name] = parameter_attr.exception_values.split(",") + else: + # raise Exception("接口参数%s无异常值,请先确认接口参数。" % parameter_attr.name) + parameter_un_expect_dict[parameter_attr.name] = [] + + if len(parameter_un_expect_dict[parameter_attr.name]) > un_expect_lenth: + un_expect_lenth = len(parameter_un_expect_dict[parameter_attr.name]) + + return parameter_expect_dict, parameter_un_expect_dict, expect_lenth, un_expect_lenth + + def generate_case_doc(self, interface_info, tags=None, demo=None): + if demo: + lenth, demo_json, paras_d = self.file.kw._clean_demo_to_less_key(demo) + doc_list = list() + doc_list.append( + " [Documentation] {} 接口路径:{}, {}".format(interface_info.interface_describe, + interface_info.type, interface_info.in_url)) + # doc_list.append(self.doc_content_row_temp.format("输入参数名", "参数类型", "说明")) + # for parameters_info in parameters: + # sub_doc = self._generate_case_doc(parameters_info) + # doc_list.append(sub_doc) + if tags: + tag_list = tags.split(",") + tag = " [Tags]" + for tag_ in tag_list: + tag += " %s" % tag_ + doc_list.append(tag) + if lenth > 1: + doc_list.append( + " # 请求参数说明 h:放header里的参数 u:放url路径节点中的参数 d:不放在url中在请求参数 p:放在url问号后在key-value结构参数") + return "\n".join(doc_list) + + def _generate_case_doc(self, request_parameter): + doc_content = list() + parameter_attr = SetAttr(request_parameter) + row = self.doc_content_row_temp.format(parameter_attr.name, parameter_attr.type, parameter_attr.note) + doc_content.append(row) + return "\n".join(doc_content) + + def generate_case_file_path(self, interface_info): + path_cell_list = self._generate_case_file_path_info(interface_info.in_url) + path_cell_list.insert(0, self.file.case_dir) + return os.path.abspath(os.path.join(*path_cell_list[:-1]) + ".robot"), path_cell_list[-1] + + def _generate_case_file_path_info(self, url): + head_list = [] + para_list = [] + host, url_last = re.search(r"(\S+\.cn|\S+\.com)(\S+)", url).groups() + path_first = re.search("//([\w+\-]+)\.", host).group(1) + head_list.append(path_first) + search = re.findall("[\w+\-_]+|[\{\w+\-_\}]+", url_last) + for item in search: + if "{" in item: + temp = item.replace("{", "").replace("}", "") + para_list.append(temp) + else: + head_list.append(item) + return head_list + + +def run_interface_case_generate(result_path, interface, demo_info, parameters, case_instance, tags=None, normal=None): + case_type_list = ["1001", "1101", "1102", "1201"] + not_exist_case_type = list() + exist_case_name = list() + case_path, _ = case_instance.generate_case_file_path(interface) + case_name_head = interface.name + if parameters: + for case_type in case_type_list: + case_name = "-".join([case_name_head, case_type]) + if not case_instance.file.check_case_exist(case_path, case_name): + not_exist_case_type.append(case_type) + else: + exist_case_name.append(case_name) + else: + case_name = "-".join([case_name_head, "1001"]) + if not case_instance.file.check_case_exist(case_path, case_name): + not_exist_case_type.append("1001") + else: + exist_case_name.append(case_name) + for item in exist_case_name: + msg = "接口url:%s,%s,已存在用例%s,请确认!" % (interface.type, interface.in_url, item) + log.warning(msg) + case_instance.file.write_content_to_case_file(result_path, msg + "\n") + if not_exist_case_type: + if normal: + not_exist_case_type = ["1001"] + case_instance.file.generate_case_file(case_path) + try: + demo = json.loads(demo_info.request_demo) + except Exception as err: + log.error(traceback.print_exc()) + log.warning("the demo of interface(%s) is empty,please check." % interface.id) + demo = dict() + try: + result = list() + result.append("") + result.append("-" * 60) + result.append("用例生成成功!") + for case_type in not_exist_case_type: + body = case_instance.create_case_content(result_path, interface, parameters, demo, case_type, tags=tags) + # result.append(body) + result.append(body.split("!")[1]) + log.info("\n".join(result)) + except: + log.error("could not generate case for interface id: %s, url: %s" % (interface.id, interface.in_url)) + log.error(traceback.print_exc()) + else: + msg = "接口url:%s,%s,已存在用例,请确认!" % (interface.type, interface.in_url) + log.warning(msg) + case_instance.file.write_content_to_case_file(result_path, msg + "\n") diff --git a/base_framework/platform_tools/Testcase_service/readme b/base_framework/platform_tools/Testcase_service/readme new file mode 100644 index 0000000..74fa507 --- /dev/null +++ b/base_framework/platform_tools/Testcase_service/readme @@ -0,0 +1,3 @@ +目录结构说明: + 使用者:王刚 + 用途:存放自动生成RF层用例的脚本 \ No newline at end of file diff --git a/base_framework/platform_tools/__init__.py b/base_framework/platform_tools/__init__.py new file mode 100644 index 0000000..94dda3c --- /dev/null +++ b/base_framework/platform_tools/__init__.py @@ -0,0 +1,7 @@ +# -*- coding:utf-8 -*- + +""" +Author: linyupeng +Email: linyupeng@huohua.cn +Create Data: 2021/4/9 19:08 +""" \ No newline at end of file diff --git a/base_framework/platform_tools/case_runner.py b/base_framework/platform_tools/case_runner.py new file mode 100644 index 0000000..8d6f87f --- /dev/null +++ b/base_framework/platform_tools/case_runner.py @@ -0,0 +1,266 @@ +# -*- coding:utf-8 -*- +import argparse +import copy +import datetime +import json +import os +import sys +import time +from base_framework.platform_tools.Keywords_service.keyword_service import SetAttr, SwaggerDBInfo, \ + KWFileOperation, log, KWOperation, run_keyword_generage, Project_Path +# from base_framework.platform_tools.Swagger_scanner.scanner import SwaggerOnlineDebug +from base_framework.platform_tools.Testcase_service.case_service import ITCaseContentOperation, \ + run_interface_case_generate, load_module_from_file +from base_framework.base_config.current_pth import * +from base_framework.public_tools.read_config import ReadConfig +from base_framework.platform_tools.Swagger_scanner.scanner_add import GenerateBody + +dir_name = os.path.dirname(__file__) +interface_path = os.path.join(dir_name, "Swagger_scanner/interface.txt") +token_header = ["accesstoken", "user-token"] + + +def get_all_interface_info_from_file(path): + with open(path, encoding="UTF-8") as f: + content = f.readlines() + return content[1:] + + +def write_interface_info_to_file(content, path=interface_path): + with open(path, mode="r", encoding="UTF-8") as f: + line = f.readline().strip("\n") + with open(path, mode="w", encoding="UTF-8") as f1: + f1.write(line + "\n" + content) + + +def generate_result_file(): + file_name = "reslut_%s.txt" % time.strftime("%Y_%m_%d_%H_%M_%S", time.localtime()) + result_dir = os.path.join(dir_name, "case_result") + result_file = os.path.abspath(os.path.join(result_dir, file_name)) + if not os.path.exists(result_dir): + os.makedirs(result_dir) + with open(result_file, "w+", encoding="UTF-8") as f: + f.write("开始自动生成keyword和case! 开始时间:%s\n" % time.strftime("%Y-%m-%d_%H:%M:%S", time.localtime())) + return result_file + + +def write_case_result_to_case_file(file_path, content): + with open(file_path, "a+", encoding="UTF-8") as f: + lines = f.readlines() + text = content + "\n" + if lines: + if lines[-1] != "\n": + text = "\n" + "\n" + content + "\n" + + f.write(text) + + +def clean_the_interface_info(result_file, interface_info): + interface = tuple(interface_info.strip("\n").split(",")) + interface_infos = SwaggerDBInfo.get_interface_info_by_url_and_type(interface[1], interface[0], interface[2]) + if len(interface_infos) > 1: + msg = "接口url:%s,%s,存在多条数据,请确认!" % interface + log.warning(msg) + write_case_result_to_case_file(result_file, msg) + elif len(interface_infos) == 0: + msg = "接口url:%s,%s,在数据库中不存在,请确认!" % interface + log.warning(msg) + write_case_result_to_case_file(result_file, msg) + else: + interface_info_attr = SetAttr(interface_infos[0]) + if interface_info_attr.is_used == 0: + msg = "接口url:%s,%s,%s,已不在使用,请确认!" % interface + log.warning(msg) + write_case_result_to_case_file(result_file, msg) + elif interface_info_attr.at_numbers > 0: + msg = "接口url:%s,%s,%s,已存在用例,请确认!" % interface + log.warning(msg) + write_case_result_to_case_file(result_file, msg) + else: + msg = "接口url:%s,%s,%s,开始生成接口关键字和用例!" % interface + log.info(msg) + write_case_result_to_case_file(result_file, msg) + return interface_info_attr + return msg + + +def generate_swagger_url_and_option(swagger_info): + swagger_info_attr = SetAttr(swagger_info) + url = swagger_info_attr.sw_url + temp_list = url.split("v2") + swagger_url = temp_list[0] + 'swagger-ui.html' + option = '/v2' + temp_list[1] + return swagger_url, option, temp_list[0] + + +def get_demo_by_interface_info(interface_id): + body_demo = GenerateBody(interface_id) + request_data = body_demo.combine_request_parameters() + if request_data["d"]: + request_demo = body_demo.combine_request_data(request_data["d"][0], request_data["d"][1], key=0) + request_data["d"] = request_demo + return request_data + + +def clear_request_parameters(parameters): + """ + 清除参数中作为token的参数 + """ + parameters_result = copy.deepcopy(parameters) + for item in parameters: + if item.get("name").lower() in token_header: + parameters_result.remove(item) + return parameters_result + + +def clear_header_token_demo(demo_info): + """ + 清除请求头中带有token这种无用参数 + """ + demo_copy = json.loads(copy.deepcopy(demo_info.request_demo)) + header = copy.deepcopy(demo_copy.get('h')) + for key, _ in demo_copy.get('h').items(): + if key in token_header: + header.pop(key) + if not header: + demo_copy['h'] = {} + else: + demo_copy['h'] = header + demo_info.request_demo = json.dumps(demo_copy) + return demo_info + + +def generate_parameter_to_dict(parameters): + """ + 将数据库查询到的参数以参数名组成字典 + """ + dict_temp = dict() + for parameter in parameters: + parameter_attr = SetAttr(parameter) + dict_temp[parameter_attr.name] = parameter_attr + return dict_temp + + +def clean_un_use_parameter(demo, parameters): + """ + 删除不再使用的参数 + """ + if isinstance(demo, SetAttr): + demo_item = json.loads(demo.request_demo) + demo_copy = copy.deepcopy(demo_item) + else: + demo_item = copy.deepcopy(demo) + demo_copy = copy.deepcopy(demo) + parameters_dict = generate_parameter_to_dict(parameters) + if isinstance(demo, list): + demo_copy.clear() + for index in range(len(demo_item)): + demo_copy.append(clean_un_use_parameter(demo_item[index], parameters)) + elif isinstance(demo_item, dict): + for key, values in demo_item.items(): + if parameters_dict.get(key, None) and int(parameters_dict.get(key).is_need) == 2: + demo_copy.pop(key) + continue + demo_copy[key] = clean_un_use_parameter(values, parameters) + else: + demo_copy = demo_copy + return demo_copy + + +def confirm_demo_value_string(kw, case, demo_string, parameters): + """ + 确认demo中的请求参数是不是纯值,并非json说格式 + """ + demo = copy.deepcopy(demo_string.request_demo) + demo_value = json.loads(demo) + if (not demo_value["d"]) and (demo_value["d"] != 0): + return demo_value + if (isinstance(demo_value["d"], str) or isinstance(demo_value["d"], int)): + demo_value["d"] = {parameters[0].get("name"): demo_value["d"]} + kw.kw_string = True + case.case_string = True + elif isinstance(demo_value["d"], list): + if not isinstance(demo_value["d"][0], dict): + demo_value["d"] = {parameters[0].get("name"): demo_value["d"]} + kw.kw_string = True + case.case_string = True + elif 'empty' in demo_value['d']: + kw.kw_array = demo_value['d'].pop('empty') + return demo_value + + +def confirm_correct_team_case(team): + interface_file_path = os.path.abspath(os.path.join(Project_Path, + "{team}".format(team=team), "library", + "{team}_interface.py".format(team=team.upper()))) + try: + module = load_module_from_file(interface_file_path, team.upper()) + real_team = team.upper() + except Exception as err: + real_team = team.lower() + return real_team + + +def run_interface_case(team, tags=None, platform=None, server=None, driver=None, normal=None): + team = confirm_correct_team_case(team) + kw_instance = KWFileOperation(team) + result_file = generate_result_file() + interface_contents = get_all_interface_info_from_file(interface_path) + keyword = KWOperation(team, kw_instance, server) + case = ITCaseContentOperation(team, kw_instance) + for interface_temp in interface_contents: + if interface_temp == "\n" or interface_temp.startswith("#"): + continue + interface = clean_the_interface_info(result_file, interface_temp) + if not isinstance(interface, str): + parameters = keyword.get_interface_parameters(interface.id) + parameters = clear_request_parameters(parameters) + if parameters: + demo = get_demo_by_interface_info(interface.id) + else: + demo = {"d": {}, "h": {}, "p": {}, "u": {}} + demo = [{"request_demo": json.dumps(demo)}] + demo_info = SetAttr(demo[0]) + demo_info = clear_header_token_demo(demo_info) + demo_info.request_demo = json.dumps(clean_un_use_parameter(demo_info, parameters)) + demo_info.request_demo = json.dumps(confirm_demo_value_string(keyword, case, demo_info, parameters)) + kw_status = run_keyword_generage(result_file, interface, demo_info, parameters, keyword) + if kw_status: + msg = "接口url:%s,%s,生成keyword成功!接下来继续生成用例!" % (interface.type, interface.in_url) + log.info(msg) + write_case_result_to_case_file(result_file, msg) + run_interface_case_generate(result_file, interface, demo_info, parameters, case, tags, normal) + return True + else: + return interface + + +def print_usage(): + parser_inst.print_usage() + parser_inst.exit(1, "生成用例失败,请根据使用帮助传入正确参数。") + + +def write_platform_to_running_env(platform, team): + if not platform: + platform = "" + cfg_opt = ReadConfig(env_choose_path) + cfg_opt.set_section("run_jira_id", "huohua-podenv", platform) + cfg_opt.set_section("run_evn_name", "current_team", team) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="请输入组名!") + parser.add_argument("-team", dest='team', help="TO or TMO") + parser.add_argument("-tag", dest='tags', help="标签", default=None) + parser.add_argument("-p", dest='platform', help="独立环境", default=None) + parser.add_argument("-s", dest='server', help="服务名", default=None) + parser.add_argument("-d", dest='driver', help="驱动路径", default=None) + parser.add_argument("-n", dest='normal', help="是否只生成正常用例", default=None) + args = parser.parse_args() + global parser_inst + parser_inst = parser + if not args.team: + print_usage() + else: + write_platform_to_running_env(args.platform, args.team) + run_interface_case(args.team, args.tags, args.platform, args.server, args.driver, args.normal) diff --git a/base_framework/platform_tools/case_runner1.py b/base_framework/platform_tools/case_runner1.py new file mode 100644 index 0000000..82dcae6 --- /dev/null +++ b/base_framework/platform_tools/case_runner1.py @@ -0,0 +1,264 @@ +# -*- coding:utf-8 -*- +import argparse +import copy +import datetime +import json +import os +import sys +import time +from base_framework.platform_tools.Keywords_service.keyword_service1 import SetAttr, SwaggerDBInfo, \ + KWFileOperation, log, KWOperation, run_keyword_generage, Project_Path +# from base_framework.platform_tools.Swagger_scanner.scanner import SwaggerOnlineDebug +from base_framework.platform_tools.Testcase_service.case_service1 import ITCaseContentOperation, \ + run_interface_case_generate, load_module_from_file +from base_framework.base_config.current_pth import * +from base_framework.public_tools.read_config import ReadConfig +from base_framework.platform_tools.Swagger_scanner.scanner_add import GenerateBody + +dir_name = os.path.dirname(__file__) +interface_path = os.path.join(dir_name, "Swagger_scanner/interface.txt") +token_header = ["accesstoken", "user-token"] + + +def get_all_interface_info_from_file(path): + with open(path, encoding="UTF-8") as f: + content = f.readlines() + return content[1:] + + +def write_interface_info_to_file(content, path=interface_path): + with open(path, mode="r", encoding="UTF-8") as f: + line = f.readline().strip("\n") + with open(path, mode="w", encoding="UTF-8") as f1: + f1.write(line + "\n" + content) + + +def generate_result_file(): + file_name = "reslut_%s.txt" % time.strftime("%Y_%m_%d_%H_%M_%S", time.localtime()) + result_dir = os.path.join(dir_name, "case_result") + result_file = os.path.abspath(os.path.join(result_dir, file_name)) + if not os.path.exists(result_dir): + os.makedirs(result_dir) + with open(result_file, "w+", encoding="UTF-8") as f: + f.write("开始自动生成keyword和case! 开始时间:%s\n" % time.strftime("%Y-%m-%d_%H:%M:%S", time.localtime())) + return result_file + + +def write_case_result_to_case_file(file_path, content): + with open(file_path, "a+", encoding="UTF-8") as f: + lines = f.readlines() + text = content + "\n" + if lines: + if lines[-1] != "\n": + text = "\n" + "\n" + content + "\n" + + f.write(text) + + +def clean_the_interface_info(result_file, interface_info, team): + interface = tuple(interface_info.strip("\n").split(",")) + interface_infos = SwaggerDBInfo.get_interface_info_by_url_and_type(interface[1], interface[0], team) + if len(interface_infos) > 1: + msg = "接口url:%s,%s,存在多条数据,请确认!" % interface + log.warning(msg) + write_case_result_to_case_file(result_file, msg) + elif len(interface_infos) == 0: + msg = "接口url:%s,%s,在数据库中不存在,请确认!" % interface + log.warning(msg) + write_case_result_to_case_file(result_file, msg) + else: + interface_info_attr = SetAttr(interface_infos[0]) + if interface_info_attr.is_used == 0: + msg = "接口url:%s,%s,已不在使用,请确认!" % interface + log.warning(msg) + write_case_result_to_case_file(result_file, msg) + elif interface_info_attr.at_numbers > 0: + msg = "接口url:%s,%s,已存在用例,请确认!" % interface + log.warning(msg) + write_case_result_to_case_file(result_file, msg) + else: + msg = "接口url:%s,%s,开始生成接口关键字和用例!" % interface + log.info(msg) + write_case_result_to_case_file(result_file, msg) + return interface_info_attr + return msg + + +def generate_swagger_url_and_option(swagger_info): + swagger_info_attr = SetAttr(swagger_info) + url = swagger_info_attr.sw_url + temp_list = url.split("v2") + swagger_url = temp_list[0] + 'swagger-ui.html' + option = '/v2' + temp_list[1] + return swagger_url, option, temp_list[0] + + +def get_demo_by_interface_info(interface_id): + body_demo = GenerateBody(interface_id) + request_data = body_demo.combine_request_parameters() + if request_data["d"]: + request_demo = body_demo.combine_request_data(request_data["d"][0], request_data["d"][1], key=0) + request_data["d"] = request_demo + return request_data + + +def clear_request_parameters(parameters): + """ + 清除参数中作为token的参数 + """ + parameters_result = copy.deepcopy(parameters) + for item in parameters: + if item.get("name").lower() in token_header: + parameters_result.remove(item) + return parameters_result + + +def clear_header_token_demo(demo_info): + """ + 清除请求头中带有token这种无用参数 + """ + demo_copy = json.loads(copy.deepcopy(demo_info.request_demo)) + header = copy.deepcopy(demo_copy.get('h')) + for key, _ in demo_copy.get('h').items(): + if key in token_header: + header.pop(key) + if not header: + demo_copy['h'] = {} + else: + demo_copy['h'] = header + demo_info.request_demo = json.dumps(demo_copy) + return demo_info + + +def generate_parameter_to_dict(parameters): + """ + 将数据库查询到的参数以参数名组成字典 + """ + dict_temp = dict() + for parameter in parameters: + parameter_attr = SetAttr(parameter) + dict_temp[parameter_attr.name] = parameter_attr + return dict_temp + + +def clean_un_use_parameter(demo, parameters): + """ + 删除不再使用的参数 + """ + if isinstance(demo, SetAttr): + demo_item = json.loads(demo.request_demo) + demo_copy = copy.deepcopy(demo_item) + else: + demo_item = copy.deepcopy(demo) + demo_copy = copy.deepcopy(demo) + parameters_dict = generate_parameter_to_dict(parameters) + if isinstance(demo, list): + demo_copy.clear() + for index in range(len(demo_item)): + demo_copy.append(clean_un_use_parameter(demo_item[index], parameters)) + elif isinstance(demo_item, dict): + for key, values in demo_item.items(): + if parameters_dict.get(key, None) and int(parameters_dict.get(key).is_need) == 2: + demo_copy.pop(key) + continue + demo_copy[key] = clean_un_use_parameter(values, parameters) + else: + demo_copy = demo_copy + return demo_copy + + +def confirm_demo_value_string(kw, case, demo_string, parameters): + """ + 确认demo中的请求参数是不是纯值,并非json说格式 + """ + demo = copy.deepcopy(demo_string.request_demo) + demo_value = json.loads(demo) + if (not demo_value["d"]) and (demo_value["d"] != 0): + return demo_value + if (isinstance(demo_value["d"], str) or isinstance(demo_value["d"], int)): + demo_value["d"] = {parameters[0].get("name"): demo_value["d"]} + kw.kw_string = True + case.case_string = True + elif isinstance(demo_value["d"], list): + if not isinstance(demo_value["d"][0], dict): + demo_value["d"] = {parameters[0].get("name"): demo_value["d"]} + kw.kw_string = True + case.case_string = True + return demo_value + + +def confirm_correct_team_case(team): + interface_file_path = os.path.abspath(os.path.join(Project_Path, + "{team}".format(team=team), "library", + "{team}_interface.py".format(team=team.upper()))) + try: + module = load_module_from_file(interface_file_path, team.upper()) + real_team = team.upper() + except Exception as err: + real_team = team.lower() + return real_team + + +def run_interface_case(team, tags=None, server=None, normal=None): + team = confirm_correct_team_case(team) + kw_instance = KWFileOperation(team, server) + result_file = generate_result_file() + interface_contents = get_all_interface_info_from_file(interface_path) + keyword = KWOperation(team, kw_instance) + case = ITCaseContentOperation(team, kw_instance) + for interface_temp in interface_contents: + if interface_temp == "\n" or interface_temp.startswith("#"): + continue + interface = clean_the_interface_info(result_file, interface_temp, team) + if not isinstance(interface, str): + parameters = keyword.get_interface_parameters(interface.id) + parameters = clear_request_parameters(parameters) + if parameters: + demo = get_demo_by_interface_info(interface.id) + else: + demo = {"d": {}, "h": {}, "p": {}, "u": {}} + demo = [{"request_demo": json.dumps(demo)}] + demo_info = SetAttr(demo[0]) + demo_info = clear_header_token_demo(demo_info) + demo_info.request_demo = json.dumps(clean_un_use_parameter(demo_info, parameters)) + demo_info.request_demo = json.dumps(confirm_demo_value_string(keyword, case, demo_info, parameters)) + kw_status = run_keyword_generage(result_file, interface, demo_info, parameters, keyword) + if kw_status: + msg = "接口url:%s,%s,生成keyword成功!接下来继续生成用例!" % (interface.type, interface.in_url) + log.info(msg) + write_case_result_to_case_file(result_file, msg) + run_interface_case_generate(result_file, interface, demo_info, parameters, case, tags, normal) + return True + else: + return interface + + +def print_usage(): + parser_inst.print_usage() + parser_inst.exit(1, "生成用例失败,请根据使用帮助传入正确参数。") + + +def write_platform_to_running_env(platform, team): + if not platform: + platform = "" + cfg_opt = ReadConfig(env_choose_path) + cfg_opt.set_section("run_jira_id", "huohua-podenv", platform) + cfg_opt.set_section("run_evn_name", "current_team", team) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="请输入组名!") + parser.add_argument("-team", dest='team', help="TO or TMO") + parser.add_argument("-tag", dest='tags', help="标签", default=None) + parser.add_argument("-p", dest='platform', help="独立环境", default=None) + parser.add_argument("-s", dest='server', help="服务名", default=None) + parser.add_argument("-d", dest='driver', help="驱动路径", default=None) + parser.add_argument("-n", dest='normal', help="是否只生成正常用例", default=None) + args = parser.parse_args() + global parser_inst + parser_inst = parser + if not args.team: + print_usage() + else: + write_platform_to_running_env(args.platform, args.team) + run_interface_case(args.team, args.tags, args.platform, args.server, args.driver, args.normal) diff --git a/base_framework/platform_tools/case_runner10.py b/base_framework/platform_tools/case_runner10.py new file mode 100644 index 0000000..afbbdcc --- /dev/null +++ b/base_framework/platform_tools/case_runner10.py @@ -0,0 +1,105 @@ +# -*- coding: utf-8 -*- +__author__ = 'huaxuemin' + +import argparse +from runner9 import CaseRunner +usage_info = ''' +usage: +$ case_runner.py -w %WORKSPACE% -c %case_path% -t tc1,tc2 -i p0,p1,p2 -e norun,del -r True +run tag cases in workspace +''' + + +def usage(): + print(usage_info) + exit(-1) + + +def get_test_case_name(build_id): + try: + if build_id: + import pymysql + db = pymysql.connect(host="mysql.qa.huohua.cn", user="qa-dev", password="jaeg3SCQt0", database="sparkatp", + charset='utf8') + cursor = db.cursor() + get_sql = "SELECT test_case_id FROM sparkatp.build_params WHERE build_info_id={}".format(build_id) + cursor.execute(get_sql) + res = cursor.fetchone() + test_case_name = "" + if res: + test_case_name = res[0].replace("(", "*").replace(")", "*").replace(" ", "*") + cursor.close() + db.close() + return test_case_name + except Exception as e: + print(e) + return "" + + +def main(): + parser = argparse.ArgumentParser(description="This is for introduction parameter") + parser.add_argument("-w", dest='workspace', help="case_runner workspace") + parser.add_argument("-c", dest='case_path', help="case path") + parser.add_argument("-t", dest='test_case', default='', help="test case") + parser.add_argument("-i", dest='include', default='', help="include tag") + parser.add_argument("-e", dest='exclude', default='', help="exclude tag") + parser.add_argument("-r", dest='rerun', default='true', help="rerun failed case") + parser.add_argument("-env", dest='special_env', default='', help="special env") + parser.add_argument("-team", dest='team', default='', help="job name with team ") + parser.add_argument("-penv", dest='physics_env', help="QA or SIM", default='QA') + parser.add_argument("-b", dest='business', help="hh or hhi", default='hh') + parser.add_argument("-b_id", dest='build_info_id', help="build_info_id", default='') + parser.add_argument("-b_url", dest='build_url', help="build_url", default='') + parser.add_argument("-is_db", dest='is_use_db', help="is_use_db", default='0') + args = parser.parse_args() + + if args.workspace is None: + usage() + + workspace = args.workspace + case_path = args.case_path + test_case = args.test_case + include = args.include + exclude = args.exclude + rerun = args.rerun + special_env = args.special_env + team = args.team.split("-")[0] + physics_env = args.physics_env + business = args.business + build_info_id = args.build_info_id + build_url = args.build_url + is_use_db = args.is_use_db + if str(is_use_db) == "1" and build_info_id: + test_case_name = get_test_case_name(build_info_id) + test_case = test_case_name if test_case_name else test_case + + # if team not in ["CC", "EN", "H2R", "LaLive", "SCM", "TMO", "TO", "ASOP", "ASTWB", "ASORG"]: + # team = "" + + case_runner = CaseRunner(workspace, case_path, test_case, include, exclude, rerun, special_env, team, physics_env, + business) + try: + # 更新独立环境代号到配置文件 + case_runner.update_platform() + # 检查工作目录名字对应的端口,有就使用,没有就分配 + case_runner.assign_port() + # 查找对应端口进程并杀掉 + case_runner.check_port() + # 更新KWL和RF中的端口 + case_runner.update_port() + # 更新重名测试套 + case_runner.update_suite() + # 启动KWL + case_runner.start_kwl() + # 构建用例 + case_runner.run_cases() + # 构建完成后,停止KWL + case_runner.check_port() + except Exception as e: + print(e) + # 构建完成后,入库build_url + case_runner.record_build_url(build_info_id, build_url) + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/base_framework/platform_tools/case_runner9.py b/base_framework/platform_tools/case_runner9.py new file mode 100644 index 0000000..56006dc --- /dev/null +++ b/base_framework/platform_tools/case_runner9.py @@ -0,0 +1,683 @@ +# -*- coding: utf-8 -*- +__author__ = 'huaxuemin' + +from logbook import Logger +import shutil +import errno +import os +import sqlite3 +from pathlib import Path +import random +import socket +import configparser +import codecs +import time +import json +import re +import requests + +logging = Logger(__name__) +HERE = os.path.dirname(os.path.abspath(__file__)) +ROBOT_LOG_LEVEL = 'INFO' +from requests.packages.urllib3.exceptions import InsecureRequestWarning + +requests.packages.urllib3.disable_warnings(InsecureRequestWarning) + + +class OSType: + WIN, LINUX, UNKNOWN = range(3) + + def __init__(self): + pass + + @staticmethod + def get_type(): + import platform + system_name = platform.system() + if system_name.lower() == 'windows': + return OSType.WIN + elif system_name.lower() == 'linux': + return OSType.LINUX + else: + return OSType.UNKNOWN + + +def run_process(cmd_str, out_p=False): + """ + run command + cmd_str unicode string. + """ + if OSType.WIN == OSType.get_type(): + # cmd_str = cmd_str.encode('gbk') + cmd_str = cmd_str + elif OSType.LINUX == OSType.get_type(): + cmd_str = cmd_str.encode('utf-8') + else: + raise RuntimeError("your os is not support.") + + logging.info('cmd: %s' % cmd_str) + print(cmd_str) + import subprocess + from subprocess import PIPE + close_fds = False if OSType.WIN == OSType.get_type() else True + if out_p: + p = subprocess.Popen(cmd_str, shell=True, close_fds=close_fds, stdout=PIPE) + p.wait() + return p.returncode, p.stdout.read() + else: + p = subprocess.Popen(cmd_str, shell=True, close_fds=close_fds, stdout=subprocess.DEVNULL) + p.wait() + return p.returncode, None + + +class CaseRunner: + + def __init__(self, workspace, case_path, test_case, include, exclude, rerun="true", special_env="", team="", + physics_env="QA", business="hh"): + self.workspace = workspace + self.case_path = case_path + self.test_case = test_case + self.include = include + self.exclude = exclude + self.rerun = rerun + self.special_env = special_env + self.team = team + self.physics_env = physics_env + self.business = business + self.con = sqlite3.connect(HERE + "/case_runner.db") + self.cur = self.con.cursor() + self.def_port = set("9" + "".join(map(str, random.choices(range(10), k=3))) for i in range(500)) + self.env_port = None + self.pid = None + self.wait_time = 120 + self.all_dir_name = set() + self.galaxy_server_name_to_swagger = {"peppa-teach-opt-cms-api": "teach-opt-cms-api", + "peppa-teach-biz-server": "peppa-teach-biz", + "peppa-market-server": "peppa-market", + "peppa-teach-parker-server": "peppa-teach-parker", + "peppa-course-server": "peppa-course"} + self.swagger_name_to_galaxy = {"teach-opt-cms-api": "peppa-teach-opt-cms-api", + "peppa-teach-biz": "peppa-teach-biz-server", + "peppa-market": "peppa-market-server", + "peppa-teach-parker": "peppa-teach-parker-server", + "peppa-course": "peppa-course-server"} + # 获取opengalaxy ssotoken相关变量 + self.ops_uri = "http://opengalaxy.bg.huohua.cn" + self.showUsername = "luohong" + self.username = "luohong" + self.password = "Lh123456789@" + self.sso_login_url = "https://sso.huohua.cn/authentication/form" + self.redirect_url = "https://sso.huohua.cn/oauth/authorize?client_id=open-galaxy&response_type=code&redirect_uri=http://opengalaxy.bg.huohua.cn/api/v1/users/user/ssologin/" + + def getSsoToken(self): + session = requests.session() + post_data = dict() + post_data['showUsername'] = self.showUsername + post_data['username'] = self.username + post_data['password'] = self.password + session.post(url=self.sso_login_url, data=post_data, allow_redirects=True, verify=False) + resp = session.get( + url=self.redirect_url, + allow_redirects=False, + verify=False) + resp1 = session.get( + url=resp.headers['Location'], + allow_redirects=False, + verify=False) + ssoToken = resp1.headers["Set-Cookie"].split(";")[4].split("=")[2] + return ssoToken + + def update_platform(self): + if len(self.special_env) > 0: + config_file = str(Path(self.workspace) / "base_framework/base_config/env_choose.ini") + fd = open(config_file, encoding='utf-8') + data = fd.read() + if data[:3] == codecs.BOM_UTF8: + data = data[3:] + files = codecs.open(config_file, "w") + files.write(data) + files.close() + fd.close() + + cf = configparser.ConfigParser(allow_no_value=True) + cf.read(config_file, encoding='utf-8') + cf.set("run_jira_id", "huohua-podenv", self.special_env) + with open(config_file, 'w') as fw: # 循环写入 + cf.write(fw) + + def assign_port(self): + workspace_base_name = Path(self.workspace).name + query_port = "SELECT PORT FROM ENV_PORT WHERE NAME='{}'".format(workspace_base_name) + res_port = self.cur.execute(query_port).fetchall() + query_all_port = "SELECT PORT FROM ENV_PORT" + res_all_port = self.cur.execute(query_all_port).fetchall() + not_use_port = (self.def_port - set(str(item[0]) for item in res_all_port)).pop() + print("not use port is {}".format(not_use_port)) + self.env_port = not_use_port + if len(res_port) == 0: + insert_name_port_sql = "INSERT INTO ENV_PORT(NAME,PORT)VALUES(?,?)" + self.cur.execute(insert_name_port_sql, (workspace_base_name, not_use_port)).fetchall() + self.con.commit() + else: + update_name_port_sql = "UPDATE ENV_PORT SET PORT='{}' WHERE NAME='{}'".format(self.env_port, + workspace_base_name) + self.cur.execute(update_name_port_sql).fetchall() + self.con.commit() + + self.cur.close() + self.con.close() + print("use port is {}".format(self.env_port)) + + # def assign_port(self): + # workspace_base_name = Path(self.workspace).name + # query_port = "SELECT PORT FROM ENV_PORT WHERE NAME='{}'".format(workspace_base_name) + # res = self.cur.execute(query_port).fetchall() + # if len(res) == 0: + # query_all_port = "SELECT PORT FROM ENV_PORT" + # res_all_port = self.cur.execute(query_all_port).fetchall() + # not_use_port = (self.def_port - set(str(item[0]) for item in res_all_port)).pop() + # print("not use port is {}".format(not_use_port)) + # insert_name_port_sql = "INSERT INTO ENV_PORT(NAME,PORT)VALUES(?,?)" + # self.cur.execute(insert_name_port_sql, (workspace_base_name, not_use_port)).fetchall() + # self.con.commit() + # self.env_port = not_use_port + # else: + # self.env_port = str(res[0][0]) + # self.cur.close() + # self.con.close() + # print("use port is {}".format(self.env_port)) + + def _get_not_used_port(self): + find_pid_linux_cmd = "lsof -i:{}".format(self.env_port) + res_code, res_context = run_process(find_pid_linux_cmd, out_p=True) + if len(res_context) > 0: + return True + else: + return False + + def check_port(self): + if OSType.WIN == OSType.get_type(): + find_pid_win_cmd = 'netstat -ano | findstr {} | findstr LISTENING'.format(self.env_port) + res_code, res_context = run_process(find_pid_win_cmd, out_p=True) + if res_code == 0: + print(res_context) + if len(res_context) > 0: + try: + self.pid = str(res_context).split()[-1].replace("\\r\\n'", "") + self._kill_pid() + except IndexError: + pass + + elif OSType.LINUX == OSType.get_type(): + find_pid_linux_cmd = "lsof -i:{}".format(self.env_port) + res_code, res_context = run_process(find_pid_linux_cmd, out_p=True) + if res_code == 0: + print(res_context) + # 获取pid + if len(res_context) > 0: + try: + self.pid = str(res_context).split("\\n")[1].split()[1] + self._kill_3_pid() + except IndexError: + pass + else: + raise RuntimeError("your os is not support.") + + def _kill_3_pid(self): + self._kill_pid() + count = 3 + while count > 0: + find_pid_linux_cmd = "lsof -i:{}".format(self.env_port) + res_code, res_context = run_process(find_pid_linux_cmd, out_p=True) + + if len(res_context) > 0: + time.sleep(2) + self.pid = str(res_context).split("\\n")[1].split()[1] + self._kill_pid() + count -= 1 + continue + else: + break + + def _kill_pid(self): + if OSType.WIN == OSType.get_type(): + kill_pid_cmd = "taskkill /f /pid {}".format(self.pid) + elif OSType.LINUX == OSType.get_type(): + kill_pid_cmd = "kill -9 {}".format(self.pid) + else: + raise RuntimeError("your os is not support.") + + res_code, res_context = run_process(kill_pid_cmd) + if res_code: + raise RuntimeError("kill pid: {} failed. error: {}".format(self.pid, res_context)) + + def update_port(self): + main_py = Path(self.workspace) / "base_framework/main.py" + tmp_py = Path(self.workspace) / "base_framework/tmp.py" + shutil.copy(main_py, tmp_py) + src_context = "port=9999" + dst_context = "port={}".format(self.env_port) + self._replace_file_context(tmp_py, main_py, src_context, dst_context) + env_robot = Path(self.workspace) / "{}/test_case/Resource/AdapterKws/env.robot".format(self.team) + tmp_robot = Path(self.workspace) / "{}/test_case/Resource/AdapterKws/tmp.robot".format(self.team) + shutil.copy(env_robot, tmp_robot) + src_context = "127.0.0.1:9999" + dst_context = "127.0.0.1:{}".format(self.env_port) + self._replace_file_context(tmp_robot, env_robot, src_context, dst_context) + + def update_suite(self): + case_dir = Path(self.workspace) / "{}/test_case".format(self.team) + self._set_path(case_dir) + + case_src_path = Path(self.case_path) + if case_src_path.is_file(): + base_name = case_src_path.name.split(case_src_path.suffix)[0] + if base_name.upper() in self.all_dir_name: + self.case_path = str(case_src_path.with_name(base_name + "9" + case_src_path.suffix)) + + self._replace_path(case_dir) + + def _set_path(self, src_path): + for p in src_path.iterdir(): + if p.is_dir(): + self.all_dir_name.add(p.name.upper()) + self._set_path(p) + + def _replace_path(self, src_path): + for p in src_path.iterdir(): + if p.is_dir(): + self._replace_path(p) + elif p.is_file(): + base_name = p.name.split(p.suffix)[0] + if base_name.upper() in self.all_dir_name: + p.rename(p.with_name(base_name + "9" + p.suffix)) + + def start_kwl(self): + startup_path = Path(self.workspace) / "base_framework" + startup = "startup.py" + exec_cmd_params = "" + if len(self.special_env) > 0: + exec_cmd_params = exec_cmd_params + "-j {} ".format(self.special_env) + if len(self.team) > 0: + exec_cmd_params = exec_cmd_params + "-t {} ".format(self.team) + if len(self.business) > 0: + exec_cmd_params = exec_cmd_params + "-b {} ".format(self.business) + if OSType.WIN == OSType.get_type(): + exec_cmd_path = "cd {} && python {} ".format(startup_path, startup) + exec_cmd = exec_cmd_path + exec_cmd_params + elif OSType.LINUX == OSType.get_type(): + exec_cmd_path = "cd {} && python3 {} ".format(startup_path, startup) + exec_cmd = exec_cmd_path + exec_cmd_params + else: + raise RuntimeError("your os is not support.") + exec_cmd = exec_cmd[:-1] + # import subprocess + # from subprocess import PIPE + # close_fds = False if OSType.WIN == OSType.get_type() else True + # subprocess.Popen(exec_cmd, shell=True, close_fds=close_fds, stdout=subprocess.DEVNULL) + try: + pro = self._start_kwl_server(exec_cmd) + if not pro.is_alive(): + print("--------pro.exec_status: ", pro.is_alive()) + pro.kill() + time.sleep(5) + self._kill_3_pid() + self._start_kwl_server(exec_cmd) + except Exception as e: + print("--------error: ", e) + + @staticmethod + def _start_kwl_server(exec_cmd): + from multiprocessing import Process + p = Process(target=run_process, args=(exec_cmd,)) + p.daemon = True + p.start() + time.sleep(30) + return p + + @staticmethod + def _replace_file_context(src_file, dst_file, src_context, dst_context): + with open(src_file, "r") as f_src: + with open(dst_file, "w") as f_dst: + for line in f_src: + if src_context in line: + f_dst.writelines(line.replace(src_context, dst_context)) + else: + f_dst.writelines(line) + + def _get_report_dir(self): + """ + Create %WORKSPACE%/Report 用于存放对应构建的构建日志 + """ + report_dir = os.path.join(self.workspace, 'Report') + if not os.path.exists(report_dir): + logging.info(u'create report directory: %s' % report_dir) + os.makedirs(report_dir) + + return report_dir + + def _get_report_ci_out_dir(self): + """ + Create %WORKSPACE%/Report/ci_out,用于存放构建日志 + """ + ci_out_dir = os.path.join(self.workspace, 'Report', 'ci_out') + if os.path.exists(ci_out_dir): + logging.info(u'create ci output directory: %s' % ci_out_dir) + shutil.rmtree(ci_out_dir) + + return ci_out_dir + + @staticmethod + def copy_any_thing(src, dst): + try: + shutil.copytree(src, dst) + except OSError as exc: + if exc.errno == errno.ENOTDIR: + shutil.copy(src, dst) + else: + raise + + def _wait_kwl_run(self): + i = 0 + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + while i < self.wait_time: + try: + s.connect(("127.0.0.1", int(self.env_port))) + s.shutdown(2) + s.close() + return + except socket.error: + i += 1 + s.close() + + def run_cases(self): + """ + run cases by workspace include exclude. + """ + self._wait_kwl_run() + report_dir = os.path.join(self.workspace, 'Report') + if os.path.exists(report_dir): + logging.info(u'clean report directory: %s' % report_dir) + shutil.rmtree(report_dir) + os.makedirs(report_dir) + + if self.rerun == 'true' or self.rerun == '': + self._rerun_failed_cases() + else: + self._default_run_cases() + + out_dir = os.path.join(self._get_report_dir(), 'out') + if os.path.exists(out_dir): + # copy report to ci_out. + self.copy_any_thing(self._get_report_dir(), self._get_report_ci_out_dir()) + else: + raise RuntimeError(u'%s directory is not exist.' % out_dir) + + def _default_run_cases(self): + """ + exec robot and output log to {report_dir}/out directory. + return command execute exit code. + """ + + test_case_cmdstr = [] + if len(self.test_case) > 0: + for tc in self.test_case.split(','): + test_case_cmdstr.append(u'--test %s' % tc) + + includes_cmdstr = [] + if len(self.include) > 0: + for include in self.include.split(','): + includes_cmdstr.append(u'--include %s' % include) + + excludes_cmdstr = [] + if len(self.exclude) > 0: + for exclude in self.exclude.split(','): + excludes_cmdstr.append(u'--exclude %s' % exclude) + output_directory = os.path.join(self._get_report_dir(), 'out') + Path(output_directory).mkdir(exist_ok=True) + + # Set ROBOT_SYSLOG_FILE and ROBOT_SYSLOG_LEVEL environment variable. + os.environ['ROBOT_SYSLOG_FILE'] = os.path.join(self._get_report_dir(), 'robot_syslog.txt') + os.environ['ROBOT_SYSLOG_LEVEL'] = ROBOT_LOG_LEVEL + + logging.info(u'building cases...') + res_code, _ = run_process(' '.join( + [u'robot'] + test_case_cmdstr + excludes_cmdstr + includes_cmdstr + [u'-d', output_directory, + self.case_path])) + + # clear ROBOT_SYSLOG_FILE(NONE) and ROBOT_SYSLOG_LEVEL environment variable. + os.environ['ROBOT_SYSLOG_FILE'] = 'NONE' + + return res_code + + def _rerun_failed_cases(self): + """ + execute robot twice(the second execute is only failed in first.) + and remerge output to {report_dir}/out directory. + """ + + logging.info('rerunfailed cases mode...') + + return_code = self._default_run_cases() + logging.info('first run exit code: %s' % return_code) + if not return_code: + logging.info('first cases built successfully') + return + + logging.info('the first cases to build there is failure cases') + output_directory = os.path.join(self._get_report_dir(), u'out') + + if not os.path.exists(output_directory): + raise RuntimeError(u'the first cases to built throw exception.') + + output_directory_r1 = os.path.join(self._get_report_dir(), u'first_out') + output_directory_r2 = os.path.join(self._get_report_dir(), u'second_out') + logging.info('rename the first cases to build the output directory') + cur_dir = os.getcwd() + os.chdir(self._get_report_dir()) + if OSType.WIN == OSType.get_type(): + cmd_str = 'ren out first_out' + elif OSType.LINUX == OSType.get_type(): + cmd_str = 'cp -R out first_out' + else: + raise RuntimeError("your os is not support.") + os.system(cmd_str) + os.chdir(cur_dir) + + output_directory_cmdstr = [u'-d', output_directory_r2] + rerun_failed_cmdstr = [u'--rerunfailed', os.path.join(output_directory_r1, u'output.xml')] + + logging.info('rerunfailed test cases...') + run_process(' '.join([u'robot'] + output_directory_cmdstr + rerun_failed_cmdstr + [self.case_path])) + + if not os.path.exists(output_directory_r2): + raise RuntimeError(u'the second cases to built throw throw exception.') + + # Set ROBOT_SYSLOG_FILE and ROBOT_SYSLOG_LEVEL environment variable. + os.environ['ROBOT_SYSLOG_FILE'] = os.path.join(self._get_report_dir(), 'robot_syslog2.txt') + os.environ['ROBOT_SYSLOG_LEVEL'] = ROBOT_LOG_LEVEL + + logging.info('merge report...') + run_process(' '.join([u'rebot', u'-d', output_directory, u'-o', u'output.xml', u'--merge', + os.path.join(output_directory_r1, u'output.xml'), + os.path.join(output_directory_r2, u'output.xml')])) + # clear ROBOT_SYSLOG_FILE(NONE) and ROBOT_SYSLOG_LEVEL environment variable. + os.environ['ROBOT_SYSLOG_FILE'] = 'NONE' + + def record_build_url(self, build_id, report_url): + try: + if build_id: + import pymysql + db = pymysql.connect(host="mysql.qa.huohua.cn", user="qa-dev", password="jaeg3SCQt0", + database="sparkatp", charset='utf8') + cursor = db.cursor() + if build_id and report_url: + update_sql = "UPDATE sparkatp.build_info set report_url='{}',status=2 WHERE id={}".format( + report_url, build_id) + cursor.execute(update_sql) + cursor.fetchall() + try: + self.get_jacoco_report(cursor, build_id) + except Exception as e: + print(e) + db.commit() + cursor.close() + db.close() + + except Exception as e: + print(e) + + def get_tester_by_project_id(self): + """ + 获取project_id和tester + :return: + """ + try: + import pymysql + db = pymysql.connect(host="10.250.200.53", user="root", password="peppa@test", database="tools", + charset='utf8') + cursor = db.cursor() + + db.commit() + cursor.close() + db.close() + except Exception as e: + print(e) + + def get_project_id_by_build_id(self, cursor, build_id): + try: + if build_id: + get_scene_id_sql = "SELECT scene_id FROM build_info where id='{}'".format( + build_id) + cursor.execute(get_scene_id_sql) + scene_id_info = cursor.fetchone() + if scene_id_info: + scene_id = scene_id_info[0] + get_project_id_sql = "SELECT project_id FROM scene_new where id='{}'".format( + scene_id) + cursor.execute(get_project_id_sql) + project_id_info = cursor.fetchone() + if project_id_info: + project_id = project_id_info[0] + return project_id + return 0 + return 0 + except Exception as e: + print(e) + + def get_jacoco_report(self, cursor, build_id): + get_server_name_sql = "SELECT run_server_list FROM build_info where id='{}' and is_jacoco=1".format(build_id) + cursor.execute(get_server_name_sql) + server_name_info = cursor.fetchone() + if server_name_info: + server_name_list = eval(server_name_info[0]) + for server_name in server_name_list: + if server_name == "PEPPA-TEACH-API" or server_name == "peppa-teach-api" or "-EXECUTOR" in server_name.upper(): + continue + self._do_jacoco_report(server_name, build_id, cursor) + + def _do_jacoco_report(self, project_name, build_id, cursor): + if not self.special_env: + insert_data = "INSERT INTO `sparkatp`.`build_jacoco`(`build_info_id`, `jacoco_report_id`, `team`, `server_name`, `now_version`, `base_version`, `env_name`, `status`, `report_url`, `remark`) VALUES ('{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}');".format( + build_id, "", self.team, project_name, self.special_env, "master", + self.special_env, "1", "", "QA环境构建,不需要收集增量覆盖率") + cursor.execute(insert_data) + return + elif self.special_env.upper() == "NONE" or self.special_env.upper() == "QA": + insert_data = "INSERT INTO `sparkatp`.`build_jacoco`(`build_info_id`, `jacoco_report_id`, `team`, `server_name`, `now_version`, `base_version`, `env_name`, `status`, `report_url`, `remark`) VALUES ('{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}');".format( + build_id, "", self.team, project_name, self.special_env, "master", + self.special_env, "1", "", "QA环境构建,不需要收集增量覆盖率") + cursor.execute(insert_data) + return + # 收集覆盖率报告 + import requests + import json + if self.team.upper() in ["CC", "LALIVE", "H2R"]: + self.team = "CC" + if self.team.upper() in ["SCM", "ES"]: + self.team = "ES" + if self.team.upper() in ["TO", "TMO"]: + self.team = "TTS" + current_version = self.get_branch_from_open_galaxy(self.special_env, project_name) + if current_version == "master": + insert_data = "INSERT INTO `sparkatp`.`build_jacoco`(`build_info_id`, `jacoco_report_id`, `team`, `server_name`, `now_version`, `base_version`, `env_name`, `status`, `report_url`, `remark`) VALUES ('{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}');".format( + build_id, "", self.team, project_name, current_version, current_version, + self.special_env, "1", "", "master不需要收集增量覆盖率") + cursor.execute(insert_data) + return + if not current_version: + print("-----------------: {}, 独立环境: {}, 服务名: {}".format("未在独立环境中服务,不收集覆盖率", self.special_env, project_name)) + return + if project_name.lower() in self.galaxy_server_name_to_swagger.keys(): + project_name = self.galaxy_server_name_to_swagger[project_name.lower()] + base_version = "master" + url = "http://10.250.0.252:8989/cov/syncCollectionCov" + params = {"baseVersion": base_version, "businessName": self.team, "currentVersion": current_version, + "departmentName": "质量保障中心", + "envName": self.special_env, "isBranch": 1, "isDiff": 2, "projectName": project_name} + res = requests.post(url, json=params) + if res.status_code == 200: + if json.loads(res.text)["msg"] != "success": + logging.error("{}收集覆盖率报告失败,err: {}".format(project_name, res.text)) + insert_data = "INSERT INTO `sparkatp`.`build_jacoco`(`build_info_id`, `jacoco_report_id`, `team`, `server_name`, `now_version`, `base_version`, `env_name`, `status`, `report_url`, `remark`) VALUES ('{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}');".format( + build_id, "", self.team, project_name, current_version, base_version, + self.special_env, "3", "", res.text) + cursor.execute(insert_data) + else: + jacoco_report_id = json.loads(res.text)["data"] + insert_data = "INSERT INTO `sparkatp`.`build_jacoco`(`build_info_id`, `jacoco_report_id`, `team`, `server_name`, `now_version`, `base_version`, `env_name`, `status`, `report_url`, `remark`) VALUES ('{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}');".format( + build_id, int(jacoco_report_id), self.team, project_name, current_version, base_version, + self.special_env, "0", "", "") + cursor.execute(insert_data) + else: + logging.error("{}收集覆盖率报告失败,status:{},err: {}".format(project_name, str(res.status_code), res.text)) + insert_data = "INSERT INTO `sparkatp`.`build_jacoco`(`build_info_id`, `jacoco_report_id`, `team`, `server_name`, `now_version`, `base_version`, `env_name`, `status`, `report_url`, `remark`) VALUES ('{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}');".format( + build_id, "", self.team, project_name, current_version, base_version, + self.special_env, "3", "", res.text) + cursor.execute(insert_data) + # print(res.text) + + def get_branch_from_open_galaxy(self, special_env, server_name): + if server_name.lower() in self.swagger_name_to_galaxy.keys(): + server_name = self.swagger_name_to_galaxy[server_name.lower()] + try: + headers = { + "Api-Token": "2a1cbc25e0d183e1ec9fe5872c1433617c96da9e2905c4371d8f979479910aa1"} + open_galaxy_url = "http://opengalaxy.bg.huohua.cn/api/v1/hcloud/tree/node/sec/application?sec_name={}".format( + special_env) + res = requests.get(open_galaxy_url, headers=headers) + if res.status_code == 200: + result = json.loads(res.text) + for item in result["data"]["results"]: + if item["name"].lower() == server_name.lower(): + branch = item["branch"] + return branch + return "" + else: + print("获取运维服务列表失败,status: {}, res: {}".format(res.status_code, res.text)) + return "" + except Exception as e: + print(e) + return "" + + +if __name__ == '__main__': + # workspace = "/root/workspaces/huaxuemin-dev" + workspace = r"E:\huohua\auto\huaxuemin-dev" + # case_path = "/root/workspaces/huaxuemin-dev/HuoHuaTestCase/EN/1.接口/Peppa-Eng-Live/play_back.robot" + case_path = r"E:\huohua\auto\huaxuemin-dev\HuoHuaTestCase\EN\1.接口\Peppa-Eng-Live\play_back.robot" + test_case = "" + include = "" + exclude = "" + test = CaseRunner(workspace, case_path, test_case, include, exclude) + # test.assign_port() + # test.check_port() + # test.update_port() + # test.start_kwl() + # test.run_cases() + # test.check_port() + # build_info_id = '16329' + # build_url = 'http://10.250.200.1:8080/jenkins/view/QE_JOB/job/qe_job1/63/' + # test.record_build_url(build_info_id, build_url) + res = test.get_branch_from_open_galaxy("HHC-92692", "peppa-asset-server") + print(res) diff --git a/base_framework/platform_tools/feishu/feishu_document_api.py b/base_framework/platform_tools/feishu/feishu_document_api.py new file mode 100644 index 0000000..95653d7 --- /dev/null +++ b/base_framework/platform_tools/feishu/feishu_document_api.py @@ -0,0 +1,194 @@ +# -*- coding:utf-8 -*- +import json +import requests +import logging +from base_framework.public_tools.utils import Tools +obj_tool = Tools() + + +class FeiShuMultidimensionalTableOperations: + def __init__(self, app_token, table_id, view_id): + """ + 初始化飞书多维表格操作类 + Args: + app_token: 多维表格token,登录飞书多维表格,按F12,右侧接口列表中找带token的接口,复制token + table_id: 多维表格id,登录飞书多维表格,在浏览器地址栏中找到table=后面的值 + view_id: 多维表格视图id,登录飞书多维表格,在浏览器地址栏中找到view=后面的值 + Note: + 如果返回显示禁止访问的话,说明你的飞书表格没有给应用开通权限,需要在更多-添加应用中添加此应用,并开通权限 + 这里我们使用的应用名是:QualityAssurance-Customer-CD-QA + 如果你搜多不到这个应用,说明你没有此应用的使用权限,找陈江或刚哥开通权限 + """ + self.app_token = app_token # 表格token + self.table_id = table_id # 表格id + self.view_id = view_id # 表格视图id + self.app_id = 'cli_a53b456397bdd00c' # 飞书应用的app_id,使用前,多维表格需要在更多-添加应用中添加此应用,并开通权限 + self.app_secret = 's3U4tXp9lTz7imMDQsHDNcoO4AgzXWk7' # 飞书应用的app_secret + self.fs_login_url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal" + self.http_session = requests.session() + self.tenant_access_token = ( # 以应用身份请求api的鉴权凭证,每次获取后有效期为2小时 + self.__get_tenant_access_token_by_feishu().get("tenant_access_token")) + self.http_session.headers.update({"Content-Type": "application/json; charset=utf-8", + "Authorization": f"Bearer {self.tenant_access_token}", + "User-Agent": "lark-api-explorer/v1"}) + + def __get_tenant_access_token_by_feishu(self): + """功能:获取飞书应用的tenant_access_token:以应用身份请求api的鉴权凭证,每次获取后有效期为2小时""" + headers = {'Content-Type': 'application/json; charset=utf-8'} + data = {'app_id': self.app_id, 'app_secret': self.app_secret} + r = requests.post(self.fs_login_url, data=json.dumps(data), headers=headers, verify=False) + r_json = r.json() + if r_json.get('code') == 0: + return {"expire": r_json.get('expire'), "tenant_access_token": r_json.get('tenant_access_token')} + else: + raise Exception(f"获取原始tenant_access_token失败{r_json}") + + def fsmt_search_from_m_table(self, filter_dict=None, sort=None, automatic_fields=False, field_names=None): + """ + 功能:从多维表格中查询数据,详细说明见:https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/search + Args: + filter_dict: 过滤条件,类型:list,格式:[{"conjunction": "string", "conditions": "list"] + conjunction: 过滤条件之间的关系,取值范围:and、or,当仅有一个条件时,也需要填写此字段 + conditions: 过滤条件,格式:[{"field_name": "string", "operator": "string", "value": "list"}] + field_name: 筛选条件的左值,值为字段的名称,类型:string + operator: 筛选条件的运算符, + 取值范围:is:等于 + isNot:不等于 + contains:包含 + doesNotContain:不包含 + isEmpty:为空 + isNotEmpty:不为空 + isGreater:大于 + isGreaterEqual:大于等于 + isLess:小于 + isLessEqual:小于等于 + like:LIKE 运算符。暂未支持 + in:IN 运算符。暂未支持 + value: 筛选条件的右值,值为字段的值,类型:list + 若查询条件是时间格式,且为具体时间或日期时,格式为:["ExactDate", "13位的时间戳"] + 同时也支持时间段查询,格式为:["具体的时间段描述字符"],如: + ["Today"]:今天 + ["Tomorrow"]: 明天 + ["Yesterday"]:昨天 + ["CurrentWeek"]:本周 + ["LastWeek"]:上周 + ["CurrentMonth"]:本月 + ["LastMonth"]:上月 + ["TheLastWeek"]:过去七天内 + ["TheNextWeek"]:未来七天内 + ["TheLastMonth"]:过去三十天内 + ["TheNextMonth"]:未来三十天内 + sort: 排序条件,类型:list,格式:[{"field_name": "string", "desc": "bool"}] + field_name: 排序字段的名称,类型:string + desc: 是否倒序排序,类型:bool, 默认值:False + automatic_fields: 控制是否返回自动计算的字段, true 表示返回 + field_names: 指定本次查询返回记录中包含哪些字段 + Returns: {"code": 0, "msg": "", + "data": {"items": "入参中自定义的返回字段", + "has_more": "bool,是否还有更多项", + "page_token": "boolean, 分页标记,当 has_more 为 true 时,会同时返回新的 page_token,否则不返回 page_token", + "total": "int, 总数"}} + """ + url = ("https://open.feishu.cn/open-apis/bitable/v1/apps/{}/tables/{}/records/search" + .format(self.app_token, self.table_id)) + request_data = {"view_id": self.view_id, + "filter": filter_dict, + "automatic_fields": automatic_fields, + "sort": sort, + "field_names": field_names} + try: + rs = self.http_session.post(url, data=json.dumps(request_data), verify=False) + except Exception as e: + raise Exception(f"调用飞书接口{url}失败:{e}") + rj_json = rs.json() + if rj_json.get('code') == 0: + return rj_json + else: + raise Exception(f"调用飞书接口{url}失败:\n{rs.status_code}, {rs.text}") + + def fsmt_insert_data_to_m_table(self, m_table_column_dict): + """ + https://open.feishu.cn/open-apis/bitable/v1/apps/:app_token/tables/:table_id/records/batch_create + api文档地址:https://open.feishu.cn/document/server-docs/docs/bitable-v1/app-table-record/batch_create + """ + url = ("https://open.feishu.cn/open-apis/bitable/v1/apps/{}/tables/{}/records/batch_create" + .format(self.app_token, self.table_id)) + request_data = {"records": []} + for column_dict in m_table_column_dict: + request_data["records"].append({"fields": column_dict}) + try: + rs = self.http_session.post(url, data=json.dumps(request_data), verify=False) + except Exception as e: + raise Exception(f"调用飞书接口{url}失败:{e}") + rj_json = rs.json() + if rj_json.get('code') == 0: + return rj_json + else: + raise Exception(f"调用飞书接口{url}失败:\n{rs.status_code}, {rs.text}") + + def fsmt_update_one_data_to_m_table(self, record_id, m_table_column_dict): + """ + https://open.feishu.cn/open-apis/bitable/v1/apps/:app_token/tables/:table_id/records/:record_id/update + api文档:https://open.feishu.cn/document/server-docs/docs/bitable-v1/app-table-record/update + """ + url = f"""https://open.feishu.cn/open-apis/bitable/v1/apps/{self.app_token}/tables/{self.table_id}/records/{record_id}""" + data = {"fields": m_table_column_dict} + logging.info(f"更新数据请求参数为:{data}") + rs = self.http_session.put(url, data=json.dumps(data), verify=False) + rj_json = rs.json() + if rj_json.get('code') == 0: + return rj_json + else: + logging.info(f"更新异常,异常返回结果为:{rs}") + + + def batch_update_data(self, table_id, update_records_data_list): + """ + https://open.feishu.cn/open-apis/bitable/v1/apps/:app_token/tables/:table_id/records/batch_update + api文档:https://open.feishu.cn/document/server-docs/docs/bitable-v1/app-table-record/batch_update + """ + url = f"""https://open.feishu.cn/open-apis/bitable/v1/apps/{self.app_token}/tables/{table_id}/records/batch_update""" + data = {"records": update_records_data_list} + logging.info(f"批量更新数据请求参数为:{data}") + rs = self.http_session.post(url, data=json.dumps(data), verify=False) + rj_json = rs.json() + if rj_json.get('code') == 0: + return rj_json + else: + logging.info(f"批量更新异常,异常返回结果为:{rs}") + raise Exception(f"调用飞书接口{url}失败:{rs}") + + def delete_data(self): + pass + + +if __name__ == '__main__': + + fs = FeiShuMultidimensionalTableOperations(app_token="HssibyRf4anbpxsA3G2c1IPwnic", + table_id="tblVbNoZE1RI3Hdo", + view_id="vewx9lK9hI") + filter_dict = {"conjunction": "and", + "conditions": [{"field_name": "时间", "operator": "is", "value": ["Today"]}]} + sort = [{"field_name": "时间", "desc": True}] + field_names = ["时间", "入班", "换班", "补课", "月份"] + + rsp = fs.fsmt_search_from_m_table(filter_dict=filter_dict, sort=sort, field_names=field_names) + + if rsp.get("code") == 0: + if int(rsp.get("data").get("total")) == 0: + ci_data = [['2024-07-24', 7022, 2040, 105]] + request_data = [] + for item in ci_data: + ci_date = obj_tool.get_format_date(r_time="{} 00:00:00".format(item[0]), r_type=13) + request_data.append({"时间": ci_date, "入班": item[1], "换班": item[2], "补课": item[3], + "月份": "{}月".format(int(item[0].split("-")[1]))}) + for item in request_data: + print(item) + # request_data = [{"时间": 1721664000000, "入班": 123, "换班": 124, "补课": 125, "月份": "7月"}, + # {"时间": 1721750400000, "入班": 223, "换班": 224, "补课": 225, "月份": "7月"}] + rsp = fs.fsmt_insert_data_to_m_table(m_table_column_dict=request_data) + print(rsp) + else: + print("数据已存在,本次不写入...") + else: + print("查询失败:{}".format(rsp)) diff --git a/base_framework/platform_tools/kibana/logstashlog_kibana.py b/base_framework/platform_tools/kibana/logstashlog_kibana.py new file mode 100644 index 0000000..a8bed80 --- /dev/null +++ b/base_framework/platform_tools/kibana/logstashlog_kibana.py @@ -0,0 +1,204 @@ +# -*- coding:utf-8 -*- +""" +功能:通过kibana查询logstash日志 +进度:待完善.... +""" + +import requests +import json +from datetime import datetime, timedelta, timezone + + +class LogstashLogKibana: + def __init__(self, k_user="wuyonggang", k_pwd="Mima@123"): + self.kibana_host = "https://logstashlog-kibana.qc.huohua.cn/internal/bsearch" + self.kibana_user = k_user + self.kibana_pwd = k_pwd + self.r_header = {'kbn-version': '7.14.2', + 'Content-Type': 'application/json; charset=gbk', + 'sec-ch-ua-mobile': r'?0'} + self.request = requests.session() + self._login_kibana() + + def _login_kibana(self): + url = "https://logstashlog-kibana.qc.huohua.cn/internal/security/login" + payload = json.dumps({"providerType": "basic", + "providerName": "basic", + "currentURL": "https://logstashlog-kibana.qc.huohua.cn/login?msg=LOGGED_OUT", + "params": {"username": self.kibana_user, "password": self.kibana_pwd} + }) + resp = self.request.post(url, headers=self.r_header, data=payload) + print(resp) + + def _format_message(self, message): + """ + 格式化消息 + :param message: 消息 + :return: 格式化后的消息 + """ + + + def _get_time_tamp(self, minute=10): + + # 获取当前时间 + current_time = datetime.utcnow().replace(tzinfo=timezone.utc) + # 计算十分钟前的时间 + ten_minutes_ago = current_time - timedelta(minutes=minute) + # 格式化时间为 Elasticsearch 时间戳格式 + gte_timestamp = ten_minutes_ago.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z' + lte_timestamp = current_time.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z' + return gte_timestamp, lte_timestamp + + def lk_query_kibana_log(self, minutes=15, app_name=None, querys=None): + + if querys is None: + querys = ['java.lang.NullPointerException'] + if not (isinstance(querys, list) or isinstance(querys, dict)): + raise ValueError('querys:{},必须为列表或者字典'.format(querys)) + gte_timestamp, lte_timestamp = self._get_time_tamp(minute=minutes) + query_con = [] + if isinstance(querys, dict): + query_cons = {"bool": {"should": [{"match_phrase": querys}], "minimum_should_match": 1}} + else: + for query in querys: + if len(querys) == 1: + query_cons = { + "multi_match": { + "type": "phrase", + "query": query, + "lenient": True + }} + else: + query_con.append({ + "multi_match": { + "type": "phrase", + "query": query, + "lenient": True + } + }) + query_cons = { + "bool": { + "filter": query_con}} + + url = "https://logstashlog-kibana.qc.huohua.cn/internal/bsearch" + payload = json.dumps({ + "batch": [ + { + "request": { + "params": { + "index": "logstash-qc-logstashlog*", + "body": { + "size": 10000, + "sort": [ + { + "@timestamp": { + "order": "desc", + "unmapped_type": "boolean" + } + } + ], + "version": True, + "fields": [ + { + "field": "*", + "include_unmapped": "true" + }, + { + "field": "@timestamp", + "format": "strict_date_optional_time" + }, + { + "field": "end_data", + "format": "strict_date_optional_time" + }, + { + "field": "end_date", + "format": "strict_date_optional_time" + }, + { + "field": "start_date", + "format": "strict_date_optional_time" + } + ], + "aggs": { + "2": { + "date_histogram": { + "field": "@timestamp", + "fixed_interval": "30m", + "time_zone": "Asia/Shanghai", + "min_doc_count": 1 + } + } + }, + "script_fields": {}, + "stored_fields": [ + "*" + ], + "runtime_mappings": {}, + "_source": False, + "query": { + "bool": { + "must": [], + "filter": [ + query_cons, + { + "range": { + "@timestamp": { + "gte": gte_timestamp, + "lte": lte_timestamp, + "format": "strict_date_optional_time" + } + } + }, + { + "match_phrase": { + "APP_NAME": app_name + } + } + ], + "should": [], + "must_not": [] + } + }, + "highlight": { + "pre_tags": [ + "@kibana-highlighted-field@" + ], + "post_tags": [ + "@/kibana-highlighted-field@" + ], + "fields": { + "*": {} + }, + "fragment_size": 2147483647 + } + }, + "track_total_hits": True, + "preference": 1708481407352 + } + }, + "options": { + # "sessionId": "473ee7d3-be00-411e-a925-71e2f669a230", + "isRestore": False, + "strategy": "ese", + "isStored": False + } + } + ] + }) + print(payload) + response = self.request.post(url, headers=self.r_header, data=payload) + # binary_data = response.text + # 解码为字符串并解析为 JSON 对象 + json_string = response.text + json_data = json.loads(json_string) + print(json_data) + # print(response.json()) + # path = os.path.join(self.data_path_list, file_name) + # with open(path, 'w+', encoding='utf-8') as f: + # f.write(response.text) + +if __name__ == '__main__': + lk = LogstashLogKibana() + lk.lk_query_kibana_log(minutes=15, app_name="peppa-sparkle-scheduler", + querys=['java.lang.NullPointerException']) \ No newline at end of file diff --git a/base_framework/platform_tools/wyg_bingfa.py b/base_framework/platform_tools/wyg_bingfa.py new file mode 100644 index 0000000..b7e8cd7 --- /dev/null +++ b/base_framework/platform_tools/wyg_bingfa.py @@ -0,0 +1,55 @@ +import concurrent.futures +import requests +import time +import json + +cost_time = [] +ALL_TIMES = 10000 + +def make_request(url, method='GET', data=None): + try: + start_time = int(time.time() * 1000) + if method.upper() == 'POST': + response = requests.post(url, json=data, headers={"content-type": "application/json;charset=UTF-8"}) + else: # 默认使用GET方法 + response = requests.get(url, params=data) + end_time = int(time.time() * 1000) + elapsed_time = end_time - start_time + cost_time.append(elapsed_time) + return response.text, elapsed_time + except requests.RequestException as e: + return f"Request failed: {e}", None + +def main(): + r_url = {'url': 'https://swagger.qa.huohua.cn/peppa-teach-timetable-server/timetableStudentServiceApi/queryListByIds', + 'method': 'POST', 'data': [225838679]} + requests_list = [r_url] * ALL_TIMES + + m_start_time = int(time.time() * 1000) + success_times = 0 + with concurrent.futures.ThreadPoolExecutor(max_workers=50) as executor: + future_to_req = {executor.submit(make_request, req['url'], req['method'], req['data']): req for req in requests_list} + for future in concurrent.futures.as_completed(future_to_req): + req = future_to_req[future] + try: + data, elapsed_time = future.result() + if data and json.loads(data).get('code') == 200: + success_times += 1 + # print(f"URL: {req['url']}\nMethod: {req['method']}\nData: {req['data']}\nResponse:\n{data}\nElapsed time: {elapsed_time:.2f} seconds\n") + print("-----:本次耗时{}ms".format(elapsed_time)) + except Exception as e: + print(f"Error fetching {req['url']}: {e}") + m_end_time = int(time.time() * 1000) + + all_times = (m_end_time - m_start_time)/1000 + tps = ALL_TIMES / all_times + print("共计访问接口:{}次,成功访问接口:{}次,持续时长:{}秒,tps: {}" + .format(len(cost_time), success_times, all_times, tps)) + print("成功访问接口:{}次".format(success_times)) + print("单次最大耗时:{}ms".format(max(cost_time))) + print("单次最小耗时:{}ms".format(min(cost_time))) + print("单次平均耗时:{}ms".format(sum(cost_time) / len(cost_time))) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/base_framework/platform_tools/zendao_tools/__init__.py b/base_framework/platform_tools/zendao_tools/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/base_framework/platform_tools/zendao_tools/handle_bug.py b/base_framework/platform_tools/zendao_tools/handle_bug.py new file mode 100644 index 0000000..4356836 --- /dev/null +++ b/base_framework/platform_tools/zendao_tools/handle_bug.py @@ -0,0 +1,403 @@ +import requests +import hashlib +import json +from bs4 import BeautifulSoup +from datetime import datetime +import time + + +def send_to_feishu(webhook_url, content, keyword="bug"): + """ + 发送消息到飞书机器人 + + Args: + webhook_url: 飞书机器人的webhook地址 + content: 要发送的消息内容 + keyword: 关键词,需要包含在消息中 + """ + try: + # 构建消息体 + message = { + "msg_type": "text", + "content": { + "text": f"{keyword}\n{content}" + } + } + + # 发送请求 + headers = {'Content-Type': 'application/json'} + response = requests.post(webhook_url, + data=json.dumps(message), + headers=headers, + timeout=10) + + if response.status_code == 200: + print(f"✅ 消息发送成功到飞书") + return True + else: + print(f"❌ 飞书消息发送失败: {response.status_code}") + print(f"响应: {response.text}") + return False + + except Exception as e: + print(f"❌ 发送飞书消息异常: {e}") + return False + + +def get_all_bugs_and_send(): + """获取所有产品的Bug并发送到飞书""" + base_url = "http://39.170.26.156:8888" + username = "qiaoxinjiu" + password = "Qiao123456" + + # 飞书配置 + feishu_webhook = "https://open.feishu.cn/open-apis/bot/v2/hook/c7288ada-1c0c-472a-b652-a475a9586302" + keyword = "bug" + + print("获取禅道Bug列表") + print("=" * 50) + + # 登录 + session = requests.Session() + password_md5 = hashlib.md5(password.encode()).hexdigest() + + login_resp = session.post(f"{base_url}/api.php/v1/tokens", + json={"account": username, "password": password_md5}, + headers={'Content-Type': 'application/json'}) + + if login_resp.status_code not in [200, 201]: + error_msg = f"❌ 禅道登录失败: {login_resp.status_code}" + print(error_msg) + send_to_feishu(feishu_webhook, error_msg, keyword) + return + + token = login_resp.json().get('token') + session.headers.update({'Token': token}) + + print("✅ 登录成功") + + # 获取所有产品 + products_resp = session.get(f"{base_url}/api.php/v1/products") + if products_resp.status_code != 200: + error_msg = f"❌ 获取产品列表失败: {products_resp.status_code}" + print(error_msg) + send_to_feishu(feishu_webhook, error_msg, keyword) + return + + products = products_resp.json().get('products', []) + print(f"📦 共 {len(products)} 个产品") + + all_bugs = [] + + # 遍历每个产品获取Bug(只获取产品ID为2的) + for product in products: + product_id = product.get('id') + product_name = product.get('name') + + # 只获取产品ID为2的 + if product_id != 2: + continue + + print(f"\n获取产品 '{product_name}' 的Bug...") + + # 获取HTML页面 + bugs_url = f"{base_url}/bug-browse-{product_id}.html" + bugs_resp = session.get(bugs_url, params={'product': product_id, 'limit': 100}) + + if bugs_resp.status_code == 200: + try: + html = bugs_resp.text + soup = BeautifulSoup(html, "html.parser") + + # 找 bug 列表的 table + table = ( + soup.find("table", id="bugList") + or soup.find("table", class_="table") + or soup.find("table") + ) + if not table: + print(" ❌ 未找到 bug 列表 table") + continue + + # 解析表头 + header_tr = table.find("tr") + if not header_tr: + print(" ❌ 未找到表头行") + continue + + header_map = {} + for idx, th in enumerate(header_tr.find_all(["th", "td"])): + text = th.get_text(strip=True) + if not text: + continue + if "ID" == text or text == "编号": + header_map["id"] = idx + elif "标题" in text: + header_map["title"] = idx + elif "状态" in text: + header_map["status"] = idx + elif "严重" in text: + header_map["severity"] = idx + elif "优先" in text or "优先级" in text: + header_map["pri"] = idx + elif "指派" in text: + header_map["assignedTo"] = idx + elif "创建" in text or "打开" in text: + header_map["openedDate"] = idx + + bugs = [] + + # 遍历表体行 + for tr in table.find_all("tr")[1:]: + tds = tr.find_all("td") + if not tds: + continue + + # id + bug_id = tr.get("data-id") + if not bug_id and "id" in header_map and header_map["id"] < len(tds): + bug_id = tds[header_map["id"]].get_text(strip=True) + if not bug_id: + continue + + # 标题 + title = "" + if "title" in header_map and header_map["title"] < len(tds): + cell = tds[header_map["title"]] + link = cell.find("a") + title = (link or cell).get_text(strip=True) + + # 状态 + status = "" + if "status" in header_map and header_map["status"] < len(tds): + status = tds[header_map["status"]].get_text(strip=True) + + # 严重程度 + severity = "" + if "severity" in header_map and header_map["severity"] < len(tds): + sev_cell = tds[header_map["severity"]] + severity = sev_cell.get_text(strip=True) + if not severity: + sev_span = sev_cell.find("span") + if sev_span and sev_span.get("title"): + severity = sev_span.get("title").strip() + + # 优先级 + pri = "" + if "pri" in header_map and header_map["pri"] < len(tds): + pri = tds[header_map["pri"]].get_text(strip=True) + + # 指派给 + assigned_to = "" + if "assignedTo" in header_map and header_map["assignedTo"] < len(tds): + assigned_to = tds[header_map["assignedTo"]].get_text(strip=True) + + # 创建/打开日期 + opened_date = "" + if "openedDate" in header_map and header_map["openedDate"] < len(tds): + opened_date = tds[header_map["openedDate"]].get_text(strip=True) + + bug = { + "id": bug_id, + "title": title, + "statusName": status, + "severity": severity, + "pri": pri, + "assignedToName": assigned_to, + "openedDate": opened_date, + "product_name": product_name, + } + bugs.append(bug) + + print(f" ✅ 解析出 {len(bugs)} 个Bug") + all_bugs.extend(bugs) + + except Exception as e: + print(f" ❌ 解析失败: {e}") + else: + print(f" ❌ 获取失败: {bugs_resp.status_code}") + + # 处理并发送统计信息 + if all_bugs: + # 计算统计数据 + today_str = datetime.today().strftime("%Y-%m-%d") + today_md_str = datetime.today().strftime("%m-%d") + + total_bugs = len(all_bugs) + + # 今日新增Bug + today_bugs = [ + b for b in all_bugs + if ( + today_str in str(b.get("openedDate", "")).strip() + or today_md_str in str(b.get("openedDate", "")).strip() + ) + ] + + # 未关闭Bug(状态不包含"已关闭"、"已解决"等) + open_bugs = [ + b for b in all_bugs + if not any(kw in str(b.get("statusName", "")).lower() for kw in ["closed", "resolved", "done", "cancel"]) + and not any(kw in str(b.get("statusName", "")) for kw in ["已关闭", "已解决", "已完成", "已取消"]) + ] + + # 按指派人员统计 + assigned_stats = {} + for bug in all_bugs: + assigned = bug.get("assignedToName", "未指派") + assigned_stats[assigned] = assigned_stats.get(assigned, 0) + 1 + + # 按状态统计 + status_stats = {} + for bug in all_bugs: + status = bug.get("statusName", "未知") + status_stats[status] = status_stats.get(status, 0) + 1 + + # 构建飞书消息 + current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + + message = f"🐛 禅道Bug统计报告\n" + message += f"⏰ 时间: {current_time}\n" + message += f"📊 产品: 智慧运营平台V1.0\n" + message += f"================================\n" + message += f"📈 Bug统计概览:\n" + message += f" • 总Bug数: {total_bugs}\n" + message += f" • 今日新增: {len(today_bugs)}\n" + message += f" • 未关闭Bug: {len(open_bugs)}\n" + message += f"\n📋 状态分布:\n" + + # 添加状态统计 + for status, count in sorted(status_stats.items()): + message += f" • {status}: {count}个\n" + + message += f"\n👥 指派人员统计:\n" + # 添加指派统计(前5名) + sorted_assigned = sorted(assigned_stats.items(), key=lambda x: x[1], reverse=True)[:5] + for person, count in sorted_assigned: + message += f" • {person}: {count}个\n" + + # 今日新增Bug详情 + if today_bugs: + message += f"\n🆕 今日新增Bug详情 ({len(today_bugs)}个):\n" + today_bugs.sort(key=lambda x: int(x.get('id', 0)), reverse=True) + for bug in today_bugs[:10]: # 只显示前10个 + bug_url = f"http://39.170.26.156:8888/bug-view-{bug.get('id')}.html" + message += f" [#{bug.get('id')}] {bug.get('title', '')[:30]}...\n" + message += f" 状态: {bug.get('statusName', '未知')} | 严重: {bug.get('severity', '未知')} | 指派: {bug.get('assignedToName', '未指派')}\n" + + # 未关闭Bug详情(前5个) + if open_bugs: + open_bugs.sort(key=lambda x: int(x.get('id', 0)), reverse=True) + message += f"\n⚠️ 最新未关闭Bug (前5个):\n" + for bug in open_bugs[:5]: + bug_url = f"http://39.170.26.156:8888/bug-view-{bug.get('id')}.html" + message += f" [#{bug.get('id')}] {bug.get('title', '')[:30]}...\n" + message += f" 状态: {bug.get('statusName', '未知')} | 严重: {bug.get('severity', '未知')} | 指派: {bug.get('assignedToName', '未指派')}\n" + + message += f"\n🔗 禅道地址: {base_url}" + + # 打印到控制台 + print(f"\n{'=' * 80}") + print(message) + print(f"{'=' * 80}") + + # 发送到飞书 + print("\n发送消息到飞书...") + success = send_to_feishu(feishu_webhook, message, keyword) + + if success: + print(f"✅ Bug统计报告已发送到飞书") + else: + print(f"❌ 飞书消息发送失败") + else: + message = f"📭 禅道Bug统计报告\n" + message += f"⏰ 时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n" + message += f"📊 产品: 智慧运营平台V1.0\n" + message += f"================================\n" + message += f"✅ 当前没有Bug数据\n" + message += f"🔗 禅道地址: {base_url}" + + print(message) + send_to_feishu(feishu_webhook, message, keyword) + + +def send_simple_report(): + """发送简化的Bug统计报告到飞书""" + base_url = "http://39.170.26.156:8888" + username = "qiaoxinjiu" + password = "Qiao123456" + + # 飞书配置 + feishu_webhook = "https://open.feishu.cn/open-apis/bot/v2/hook/c7288ada-1c0c-472a-b652-a475a9586302" + keyword = "bug" + + print("获取禅道Bug统计") + print("=" * 50) + + try: + # 登录 + session = requests.Session() + password_md5 = hashlib.md5(password.encode()).hexdigest() + + login_resp = session.post(f"{base_url}/api.php/v1/tokens", + json={"account": username, "password": password_md5}, + headers={'Content-Type': 'application/json'}) + + if login_resp.status_code not in [200, 201]: + error_msg = f"❌ 禅道登录失败" + print(error_msg) + send_to_feishu(feishu_webhook, error_msg, keyword) + return + + token = login_resp.json().get('token') + session.headers.update({'Token': token}) + print("✅ 登录成功") + + # 获取产品ID=2的Bug页面 + bug_url = f"{base_url}/bug-browse-2.html" + response = session.get(bug_url) + + if response.status_code != 200: + error_msg = f"❌ 无法获取Bug页面" + print(error_msg) + send_to_feishu(feishu_webhook, error_msg, keyword) + return + + # 简单统计Bug数量 + import re + bug_matches = re.findall(r'bug-view-(\d+)\.html', response.text) + total_bugs = len(set(bug_matches)) # 去重 + + # 构建简单消息 + current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + + message = f"🐛 禅道Bug速报\n" + message += f"⏰ 时间: {current_time}\n" + message += f"📊 产品: 智慧运营平台V1.0\n" + message += f"================================\n" + message += f"📈 当前总Bug数: {total_bugs}\n" + message += f"🔗 查看详情: {bug_url}\n" + message += f"\n💡 提示: 详细统计请查看禅道系统" + + print(f"发现 {total_bugs} 个Bug") + + # 发送到飞书 + success = send_to_feishu(feishu_webhook, message, keyword) + + if success: + print(f"✅ 简版Bug报告已发送到飞书") + else: + print(f"❌ 飞书消息发送失败") + + except Exception as e: + error_msg = f"❌ 获取Bug统计异常: {str(e)}" + print(error_msg) + send_to_feishu(feishu_webhook, error_msg, keyword) + + +if __name__ == "__main__": + # 运行完整版本(带详细统计) + get_all_bugs_and_send() + + # 或者运行简化版本(只发速报) + # send_simple_report() \ No newline at end of file diff --git a/base_framework/platform_tools/zendao_tools/models.py b/base_framework/platform_tools/zendao_tools/models.py new file mode 100644 index 0000000..e69de29 diff --git a/base_framework/platform_tools/zendao_tools/zendao_api.py b/base_framework/platform_tools/zendao_tools/zendao_api.py new file mode 100644 index 0000000..ece5daa --- /dev/null +++ b/base_framework/platform_tools/zendao_tools/zendao_api.py @@ -0,0 +1,237 @@ +from __future__ import annotations + +""" +ZenDao bug list crawler. + +Design overview: +1. Read credentials + crawl settings from zendao_config.ini. +2. Use ZenDao REST API (`api.php/v1/accessTokens`) to obtain a short-lived token. +3. Pull paginated bug records under the configured product via `/api.php/v1/products/{product_id}/bugs`. +4. Export normalized bug data to CSV (default) or JSON, usable by other automation. +""" + +import argparse +import configparser +import csv +import hashlib +import json +import sys +from dataclasses import dataclass +from pathlib import Path +from typing import Dict, Iterable, List, Sequence + +import requests +from requests import Response, Session + + +BUG_EXPORT_FIELDS: Sequence[str] = ( + "id", + "title", + "status", + "severity", + "pri", + "openedBy", + "openedDate", + "assignedTo", + "resolvedBy", + "resolution", + "lastEditedDate", +) +DEFAULT_CONFIG_PATH = Path(__file__).with_name("zendao_config.ini") + + +class ZenDaoAPIError(RuntimeError): + """Raised when ZenDao returns an unexpected payload.""" + + +@dataclass +class ZenDaoSettings: + base_url: str + username: str + password: str + product_id: int + per_page: int = 50 + max_pages: int = 10 + verify_ssl: bool = False + timeout: int = 10 + auth_mode: str = "password" # password | token + api_token: str | None = None + auth_endpoint: str = "api.php/v1/tokens" + token_header: str = "Token" + password_hash: str = "md5" # md5 | plain + + @classmethod + def from_file(cls, path: Path, section: str = "zendao") -> "ZenDaoSettings": + parser = configparser.ConfigParser() + if not path.exists(): + raise FileNotFoundError(f"Config file not found: {path}") + + parser.read(path, encoding="utf-8") + if section not in parser: + raise KeyError(f"Section [{section}] not found in {path}") + + cfg = parser[section] + return cls( + base_url=cfg.get("base_url", "").rstrip("/"), + username=cfg.get("username", ""), + password=cfg.get("password", ""), + product_id=cfg.getint("product_id", fallback=0), + per_page=cfg.getint("per_page", fallback=50), + max_pages=cfg.getint("max_pages", fallback=10), + verify_ssl=cfg.getboolean("verify_ssl", fallback=False), + timeout=cfg.getint("timeout", fallback=10), + auth_mode=cfg.get("auth_mode", "password").lower(), + api_token=cfg.get("api_token"), + auth_endpoint=cfg.get("auth_endpoint", "api.php/v1/tokens"), + token_header=cfg.get("token_header", "Token"), + password_hash=cfg.get("password_hash", "md5").lower(), + ) + + def validate(self) -> None: + missing = [ + name + for name, value in ( + ("base_url", self.base_url), + ("username", self.username), + ("password", self.password), + ) + if not value + ] + if missing: + raise ValueError(f"Missing config values: {', '.join(missing)}") + if self.product_id <= 0: + raise ValueError("product_id must be > 0") + if self.auth_mode not in {"password", "token"}: + raise ValueError("auth_mode must be 'password' or 'token'") + if self.password_hash not in {"md5", "plain"}: + raise ValueError("password_hash must be 'md5' or 'plain'") + if self.auth_mode == "token" and not self.api_token: + raise ValueError("api_token required when auth_mode=token") + + +class ZenDaoClient: + def __init__(self, settings: ZenDaoSettings, session: Session | None = None) -> None: + self.settings = settings + self.session: Session = session or requests.Session() + self._token: str | None = None + + def authenticate(self) -> str: + if self.settings.auth_mode == "token": + if not self.settings.api_token: + raise ValueError("api_token missing in config") + self.session.headers.update({self.settings.token_header: self.settings.api_token}) + self._token = self.settings.api_token + return self._token + + payload = { + "account": self.settings.username, + "password": self._format_password(self.settings.password), + } + response = self._request("post", self.settings.auth_endpoint, json=payload) + data = response.json() + token = data.get("token") or data.get("accessToken") + if not token: + raise ZenDaoAPIError(f"Unexpected token response: {data}") + + self._token = token + self.session.headers.update({self.settings.token_header: token}) + return token + + def _format_password(self, password: str) -> str: + if self.settings.password_hash == "md5": + return hashlib.md5(password.encode()).hexdigest() + return password + + def fetch_bugs_page(self, page: int = 1) -> List[Dict]: + params = {"page": page, "limit": self.settings.per_page} + endpoint = f"api.php/v1/products/{self.settings.product_id}/bugs" + response = self._request("get", endpoint, params=params) + payload = response.json() + bugs = payload.get("bugs") or payload.get("data") + if bugs is None: + raise ZenDaoAPIError(f"Unexpected bug payload: {payload}") + return list(bugs) + + def fetch_all_bugs(self) -> List[Dict]: + all_bugs: List[Dict] = [] + for page in range(1, self.settings.max_pages + 1): + page_bugs = self.fetch_bugs_page(page) + if not page_bugs: + break + all_bugs.extend(page_bugs) + if len(page_bugs) < self.settings.per_page: + break + return all_bugs + + def _request(self, method: str, path: str, **kwargs) -> Response: + url = f"{self.settings.base_url}/{path.lstrip('/')}" + kwargs.setdefault("timeout", self.settings.timeout) + kwargs.setdefault("verify", self.settings.verify_ssl) + response = self.session.request(method=method, url=url, **kwargs) + response.raise_for_status() + return response + + +def normalize_bug(bug: Dict, fields: Sequence[str] = BUG_EXPORT_FIELDS) -> Dict[str, str]: + normalized = {} + for field in fields: + value = bug.get(field, "") + if isinstance(value, dict): + value = value.get("realname") or value.get("account") or value + normalized[field] = value if isinstance(value, str) else str(value) + return normalized + + +def write_json(path: Path, bugs: Iterable[Dict]) -> None: + path.write_text(json.dumps(list(bugs), ensure_ascii=False, indent=2), encoding="utf-8") + + +def write_csv(path: Path, bugs: Iterable[Dict], fields: Sequence[str]) -> None: + bugs = list(bugs) + if not bugs: + path.write_text("", encoding="utf-8") + return + + with path.open("w", encoding="utf-8", newline="") as csv_file: + writer = csv.DictWriter(csv_file, fieldnames=fields) + writer.writeheader() + for bug in bugs: + writer.writerow(normalize_bug(bug, fields)) + + +def run_crawler(config: Path, output: Path) -> Path: + settings = ZenDaoSettings.from_file(config) + settings.validate() + + client = ZenDaoClient(settings) + client.authenticate() + bugs = client.fetch_all_bugs() + + if output.suffix.lower() == ".json": + write_json(output, bugs) + else: + write_csv(output, bugs, BUG_EXPORT_FIELDS) + return output + + +def parse_args(argv: Sequence[str]) -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Fetch bug list from ZenDao.") + parser.add_argument("--config", type=Path, default=DEFAULT_CONFIG_PATH, help="Path to zendao_config.ini") + parser.add_argument( + "--output", + type=Path, + default=Path("bugs.csv"), + help="Output file path (.csv or .json)", + ) + return parser.parse_args(argv) + + +def main(argv: Sequence[str] | None = None) -> None: + args = parse_args(argv or sys.argv[1:]) + output = run_crawler(args.config, args.output) + print(f"Bug list saved to: {output}") + + +if __name__ == "__main__": + main() + diff --git a/base_framework/platform_tools/zendao_tools/zendao_config.ini b/base_framework/platform_tools/zendao_tools/zendao_config.ini new file mode 100644 index 0000000..a7ea353 --- /dev/null +++ b/base_framework/platform_tools/zendao_tools/zendao_config.ini @@ -0,0 +1,14 @@ +[zendao] +base_url = http://39.170.26.156:8888 +username = qiaoxinjiu +password = Qiao123456 +product_id = 1 +per_page = 50 +max_pages = 10 +verify_ssl = false +timeout = 10 +auth_mode = password +api_token = +auth_endpoint = api.php/v1/tokens +token_header = Token +password_hash = md5 diff --git a/base_framework/public_tools/__init__.py b/base_framework/public_tools/__init__.py new file mode 100644 index 0000000..38ee221 --- /dev/null +++ b/base_framework/public_tools/__init__.py @@ -0,0 +1,41 @@ +# -*- coding:utf-8 -*- + +""" +Author: qiaoxinjiu +Create Data: 2020/9/21 18:39 +""" +import json + + +def check_resp(is_check, resp): + if is_check: + assert resp["code"] == 200, "{}, exp code: {}".format(resp, 200) + assert resp["success"] is True, "{}, exp code: {}".format(resp, True) + assert resp["message"] == "", "{}, exp code: {}".format(resp, "") + assert resp["data"] != "", "{}, exp code: {}".format(resp, "") + + +def custom_check_resp(is_check, assert_list): + """传参数例子:asert_list = [(f"{resp}['code']", 0), (f"{resp}['message']", ""), (f"{resp}['success']", "True")]""" + if is_check: + for al in assert_list: + assert str(eval(al[0])) == str(al[1]), "{}, exp code: {}".format(eval(al[0]), al[1]) + else: + pass + + +def convert_json(parames): + temp_json = json.dumps(parames) + temp_json1 = temp_json.replace("\"NULL\"", "null") + kwargs = json.loads(temp_json1) + + return kwargs + + +def get_user(kwargs): + if kwargs.get("user", None): + user = kwargs.pop("user") + else: + user = None + + return user, kwargs diff --git a/base_framework/public_tools/apollo.py b/base_framework/public_tools/apollo.py new file mode 100644 index 0000000..ef7f89c --- /dev/null +++ b/base_framework/public_tools/apollo.py @@ -0,0 +1,157 @@ +# -*-coding:utf-8-*- +import requests, time + +from base_framework.public_tools import log +from base_framework.public_tools.get_token import LazyProperty +from base_framework.public_tools.read_config import InitConfig + +obj_log = log.get_logger() + + +class Apollo: + def __init__(self): + self.server = None + self.session = None + self.config = InitConfig() + self.show_username = self.config.show_username + self.password = self.config.password + self.apollo_url = self.config.apollo_url + self.apollo_host = self.config.apollo_host + self.current_evn = self.config.current_evn + + # @LazyProperty + def apollo_login(self): + post_data = dict() + post_data['login-submit'] = '登录' + post_data['username'] = self.show_username + post_data['password'] = self.password + req_session = requests.Session() + resp = req_session.post(url=self.apollo_url, data=post_data) + return req_session + + def __get_project__(self): + pass + + def apollo_get_config_value(self, app_id, name_space, key, cluster='default'): + """ + 功能:读取apollo上的配置 + Args: + app_id: url地址里的appid + name_space: 页面分组的命名空间,如:application,dict.config,teach.common等 + key: 具体的配置key + cluster: 集群名,如:default,hhi等 + Returns: 具体的配置值 + """ + return self.get_apollo_config_by_project_key(server=app_id, project=name_space, key=key, cluster=cluster) + + def get_apollo_config_by_project_key(self, project, key, cluster='default', server=""): + """ + 返回{id: 8812, namespaceId: 378, key: "", value: "", comment: "#线上不要此配置", lineNum: 1,…} + """ + self.session = self.apollo_login() + if server: + self.server = server + req_url = "%s/apps/%s/envs/%s/clusters/%s/namespaces" % ( + self.apollo_host, self.server, self.current_evn, cluster) + obj_log.info("get apollo url is : %s" % req_url) + resp = self.session.get(url=req_url).json() + # obj_log.info("apollo value is : %s" % resp) + for item in resp: + base_info = item.get('baseInfo') + items = item.get('items') + if not base_info['namespaceName'].lower() == project.lower(): + continue + for key_info in items: + item_detail = key_info["item"] + if item_detail["key"] == key: + return key_info["item"] + else: + obj_log.info("无法找到key:%s" % key) + return None + + def set_apollo_config_by_project_key(self, project, key, value, cluster='default', server="", set_type='append', + evn_name=""): + """ + 功能:设置apollo配置项 + Args: + server: 项目名,也称appid + project: 属性名 + key: 键值key + value: 具体要设置的值 + cluster: 集群,如:default,hhi等 + set_type: 设置类型:append-追加,new-重置 + Returns: + """ + if evn_name: + self.current_evn = evn_name + self.session = self.apollo_login() + if server: + self.server = server + req_url = "%s/apps/%s/envs/%s/clusters/%s/namespaces/%s/item" % ( + self.apollo_host, self.server, self.current_evn, cluster, project) + obj_log.info("set apollo url is : %s" % req_url) + items = self.get_apollo_config_by_project_key(project, key, cluster) + if items is None: + items = dict() + items["value"] = value + items["key"] = key + items["tableViewOperType"] = 'create' + items["addItemBtnDisabled"] = True + + else: + if set_type == 'new': + items["value"] = value + elif set_type == 'append': + if str(value) in str(items["value"]): + obj_log.info("apollo配置已存在,本次跳过....") + else: + items["value"] = items["value"] + ",{}".format(value) + items["tableViewOperType"] = 'update' + obj_log.info("set apollo parameters is : %s" % items) + update_resp = self.session.put(url=req_url, json=items) + obj_log.info("set apollo resp is : %s" % update_resp) + + assert update_resp.status_code == 200 + obj_log.info("为key:%s设置value:%s成功" % (key, value)) + release_body = dict() + release_time_stamp = time.localtime() + release_time = '%s-release' % time.strftime("%Y%m%d%H%M%S", release_time_stamp) + release_body["isEmergencyPublish"] = False + release_body["releaseComment"] = "" + release_body["releaseTitle"] = release_time + release_url = "%s/apps/%s/envs/%s/clusters/%s/namespaces/%s/releases" % ( + self.apollo_host, self.server, self.current_evn, cluster, project) + resp = self.session.post(url=release_url, json=release_body) + assert resp.status_code == 200 + time.sleep(5) + + def add_new_apollo_config_by_project_key(self, project, key, value, cluster='default', server=""): + self.session = self.apollo_login() + if server: + self.server = server + items = dict() + items["value"] = value + items["key"] = key + items["tableViewOperType"] = 'create' + items["addItemBtnDisabled"] = True + req_url = "%s/apps/%s/envs/%s/clusters/%s/namespaces/%s/item" % ( + self.apollo_host, self.server, self.current_evn, cluster, project) + update_resp = self.session.put(url=req_url, json=items) + assert update_resp.status_code == 200 + release_body = dict() + release_time_stamp = time.localtime() + release_time = '%s-release' % time.strftime("%Y%m%d%H%M%S", release_time_stamp) + release_body["isEmergencyPublish"] = False + release_body["releaseComment"] = "" + release_body["releaseTitle"] = release_time + release_url = "%s/apps/%s/envs/%s/clusters/%s/namespaces/%s/releases" % ( + self.apollo_host, self.server, self.current_evn, cluster, project) + resp = self.session.post(url=release_url, json=release_body) + assert resp.status_code == 200 + time.sleep(5) + + +if __name__ == '__main__': + ap = Apollo() + print(ap.apollo_get_config_value(app_id='peppa-teach-api', name_space='application', + key='new_ticket_leave_logic_switch')) diff --git a/base_framework/public_tools/custom_error.py b/base_framework/public_tools/custom_error.py new file mode 100644 index 0000000..74c84dd --- /dev/null +++ b/base_framework/public_tools/custom_error.py @@ -0,0 +1,8 @@ +# -*- coding:utf-8 -*- +# 存放自定义异常类 + +class BusinessError(Exception): + """功能:用于AITA项目识别是业务异常,需返给前端做展示""" + def __init__(self, message="这里是自定义的业务异常"): + self.message = message + super().__init__(self.message) diff --git a/base_framework/public_tools/db_config.py b/base_framework/public_tools/db_config.py new file mode 100644 index 0000000..966e0ef --- /dev/null +++ b/base_framework/public_tools/db_config.py @@ -0,0 +1,31 @@ +# -*- coding:utf-8 -*- + +""" +Author: qiaoxinjiu +Create Data: 2020/11/10 10:26 +""" +import pymysql + +DB_TEST_HOST = "mysql.qa.huohua.cn" +DB_TEST_PORT = 3306 +DB_TEST_DBNAME = "crmthirdparty" +DB_TEST_USER = "qa-dev" +DB_TEST_PASSWORD = "jaeg3SCQt0" +# 数据库连接编码 +DB_CHARSET = "utf8" +# mincached : 启动时开启的闲置连接数量(缺省值 0 开始时不创建连接) +DB_MIN_CACHED = 10 +# maxcached : 连接池中允许的闲置的最多连接数量(缺省值 0 代表不闲置连接池大小) +DB_MAX_CACHED = 20 +# maxshared : 共享连接数允许的最大数量(缺省值 0 代表所有连接都是专用的)如果达到了最大数量,被请求为共享的连接将会被共享使用 +DB_MAX_SHARED = 20 +# maxconnecyions : 创建连接池的最大数量(缺省值 0 代表不限制) +DB_MAX_CONNECYIONS = 100 +# blocking : 设置在连接池达到最大数量时的行为(缺省值 0 或 False 代表返回一个错误 其他代表阻塞直到连接数减少,连接被分配) +DB_BLOCKING = True +# maxusage : 单个连接的最大允许复用次数(缺省值 0 或 False 代表不限制的复用).当达到最大数时,连接会自动重新连接(关闭和重新打开) +DB_MAX_USAGE = 0 +# setsession : 一个可选的SQL命令列表用于准备每个会话,如["set datestyle to german", ...] +DB_SET_SESSION = None +# creator : 使用连接数据库的模块 +DB_CREATOR = pymysql diff --git a/base_framework/public_tools/db_dbutils_init.py b/base_framework/public_tools/db_dbutils_init.py new file mode 100644 index 0000000..e87d514 --- /dev/null +++ b/base_framework/public_tools/db_dbutils_init.py @@ -0,0 +1,300 @@ +# -*- coding:utf-8 -*- + +""" +Author: qiaoxinjiu +Create Data: 2020/11/6 17:34 +""" +import pymysql +import pymongo +# from DBUtils.PooledDB import PooledDB +from dbutils.pooled_db import PooledDB +import psycopg2 +from psycopg2 import pool +from psycopg2.extras import RealDictCursor +from base_framework.public_tools.read_config import InitConfig +from base_framework.public_tools.read_config import ReadConfig, get_current_config +# from base_framework.public_tools import db_config as config +from base_framework.base_config.current_pth import * + +""" +@功能:创建数据库连接池 +""" +as_db = ['ZZYY'] + + +class PgConnectionPool(InitConfig): + pool = None + pool_cache = dict() + + def __init__(self): + try: + super().__init__() + self.DB_SSL = False + except Exception as e: + print(e) + + # 创建数据库连接conn和游标cursor + def __enter__(self): + self.conn = self.__getconn() + self.cursor = self.conn.cursor() + + def __getconn(self, choose_db=None): + # 如果未指定数据库,使用默认配置 + if not choose_db: + choose_db = 'default' + + try: + self.pool = self.pool_cache[choose_db] + except KeyError: + rc = ReadConfig(config_file_path) + + # 根据choose_db值获取对应的PostgreSQL配置 + if choose_db == 'default': + db_host = rc.get_value(sections='PostgreSQL', options='db_test_host') + db_port = rc.get_value(sections='PostgreSQL', options='db_test_port') + db_name = rc.get_value(sections='PostgreSQL', options='db_test_dbname') + db_user = rc.get_value(sections='PostgreSQL', options='db_test_user') + db_password = rc.get_value(sections='PostgreSQL', options='db_test_password') + db_min_cached = rc.get_value(sections='PostgreSQL', options='db_min_cached') + db_max_cached = rc.get_value(sections='PostgreSQL', options='db_max_cached') + db_max_shared = rc.get_value(sections='PostgreSQL', options='db_max_shared') + db_max_connecyions = rc.get_value(sections='PostgreSQL', options='db_max_connecyions') + db_max_usage = rc.get_value(sections='PostgreSQL', options='db_max_usage') + else: + # 可以扩展其他数据库连接 + db_host = rc.get_value(sections='PostgreSQL', options=f'db_{choose_db}_host') + db_port = rc.get_value(sections='PostgreSQL', options=f'db_{choose_db}_port') + db_name = rc.get_value(sections='PostgreSQL', options=f'db_{choose_db}_name') + db_user = rc.get_value(sections='PostgreSQL', options=f'db_{choose_db}_user') + db_password = rc.get_value(sections='PostgreSQL', options=f'db_{choose_db}_password') + db_min_cached = rc.get_value(sections='PostgreSQL', options=f'db_{choose_db}_min_cached') + db_max_cached = rc.get_value(sections='PostgreSQL', options=f'db_{choose_db}_max_cached') + db_max_shared = rc.get_value(sections='PostgreSQL', options=f'db_{choose_db}_max_shared') + db_max_connecyions = rc.get_value(sections='PostgreSQL', options=f'db_{choose_db}_max_connecyions') + db_max_usage = rc.get_value(sections='PostgreSQL', options=f'db_{choose_db}_max_usage') + + # PostgreSQL连接池配置 + try: + print("=" * 80) + print("PostgreSQL连接池配置信息:") + print(" 主机(Host): {}".format(db_host)) + print(" 端口(Port): {}".format(db_port)) + print(" 数据库名(Database): {}".format(db_name)) + print(" 用户名(User): {}".format(db_user)) + print(" 密码(Password): {} (已隐藏)".format('*' * len(db_password) if db_password else 'None')) + print(" 最小缓存连接数(MinCached): {}".format(db_min_cached)) + print(" 最大缓存连接数(MaxCached): {}".format(db_max_cached)) + print(" 最大共享连接数(MaxShared): {}".format(db_max_shared)) + print(" 最大连接数(MaxConnections): {}".format(db_max_connecyions)) + print(" 最大使用次数(MaxUsage): {}".format(db_max_usage)) + print(" SSL模式(SSLMode): {}".format('require' if self.DB_SSL else 'disable')) + print(" 连接超时(ConnectTimeout): 30秒") + print("=" * 80) + + self.pool = PooledDB( + creator=psycopg2, + host=db_host, + port=int(db_port), + user=db_user, + password=db_password, + database=db_name, + mincached=int(db_min_cached), + maxcached=int(db_max_cached), + maxshared=int(db_max_shared), + maxconnections=int(db_max_connecyions), + blocking=True, + maxusage=int(db_max_usage), + setsession=None, + # PostgreSQL特定参数 + sslmode='require' if self.DB_SSL else 'disable', + connect_timeout=30, + keepalives=1, + keepalives_idle=30, + keepalives_interval=10, + keepalives_count=5 + ) + self.pool_cache[choose_db] = self.pool + print("PostgreSQL连接池创建成功") + except Exception as e: + error_msg = """ + PostgreSQL连接池创建失败! + 连接配置信息: + 主机(Host): {} + 端口(Port): {} + 数据库名(Database): {} + 用户名(User): {} + 密码(Password): {} (已隐藏) + SSL模式(SSLMode): {} + 连接超时(ConnectTimeout): 30秒 + 错误详情: {} + """.format( + db_host, db_port, db_name, db_user, + '*' * len(db_password) if db_password else 'None', + 'require' if self.DB_SSL else 'disable', + str(e) + ) + print(error_msg) + raise Exception(error_msg) + + try: + return self.pool.connection() + except Exception as e: + # 尝试获取连接配置信息用于错误提示 + try: + rc = ReadConfig(config_file_path) + db_host = rc.get_value(sections='PostgreSQL', options='db_test_host') + db_port = rc.get_value(sections='PostgreSQL', options='db_test_port') + db_name = rc.get_value(sections='PostgreSQL', options='db_test_dbname') + db_user = rc.get_value(sections='PostgreSQL', options='db_test_user') + except: + db_host = db_port = db_name = db_user = 'unknown' + error_msg = """ + PostgreSQL连接获取失败! + 连接配置信息: + 主机(Host): {} + 端口(Port): {} + 数据库名(Database): {} + 用户名(User): {} + 错误详情: {} + """.format(db_host, db_port, db_name, db_user, str(e)) + print(error_msg) + raise Exception(error_msg) + + # 释放连接池资源 + def __exit__(self, exc_type, exc_val, exc_tb): + if self.cursor: + self.cursor.close() + if self.conn: + self.conn.close() + + # 获取连接和游标(返回字典形式的结果) + def getconn(self, choose_db=None): + conn = self.__getconn(choose_db=choose_db) + # 使用RealDictCursor返回字典形式的游标 + cursor = conn.cursor(cursor_factory=RealDictCursor) + return cursor, conn + + +class MyConnectionPool(InitConfig): + pool = None + pool_cache = dict() + + def __init__(self): + try: + super().__init__() + except Exception as e: + print(e) + self.current_business = get_current_config(section='run_evn_name', key='current_business') + + # 创建数据库连接conn和游标cursor + def __enter__(self): + self.conn = self.__getconn() + self.cursor = self.conn.cursor() + + def __getconn(self, choose_db=None): + current_team = ReadConfig(env_choose_path).get_value(sections='run_evn_name', options='current_team') + if not choose_db: # 没有指定,则按小组默认设置 + if current_team.upper() in as_db and choose_db is None: + choose_db = 'as' + elif current_team.upper() == "SE" and choose_db is None: + choose_db = 'se' + elif current_team.upper() == "XUEDAU" and choose_db is None: + choose_db = 'xdu' + elif current_team.upper() not in as_db and choose_db is None and self.current_business == 'hh': + choose_db = 'hh' + elif current_team.upper() not in as_db and choose_db is None and self.current_business == 'hhi': + choose_db = 'hhi' + try: + self.pool = self.pool_cache[choose_db] + except Exception as e: + rc = ReadConfig(config_file_path) + if choose_db == 'as': + db_host = rc.get_value(sections='Mysql', options='db_as_svr') + elif choose_db == 'se': + db_host = rc.get_value(sections='Mysql', options='db_se_svr') + elif choose_db == 'xdu': + db_host = rc.get_value(sections='Mysql', options='db_xdu_svr') + self.DB_TEST_USER = rc.get_value(sections='Mysql', options='db_xdu_user') + self.DB_TEST_PASSWORD = rc.get_value(sections='Mysql', options='db_xdu_password') + elif choose_db == 'hh' or choose_db == 'huohua': + db_host = rc.get_value(sections='Mysql', options='db_hh_svr') + elif choose_db == 'hhi': + db_host = rc.get_value(sections='Mysql', options='db_hhi_svr') + elif choose_db == 'hh.qa': # 自动化和信息化的数据都走huohua + db_host = 'mysql.qa.huohua.cn' + elif not choose_db: # 没有传入则默认走huohua + choose_db = 'hh.qa' + db_host = 'mysql.qa.huohua.cn' + else: + raise Exception("当前仅支持hh,hhi,as,se四个数据库服务器,而你选择的是:{}".format(choose_db)) + default_db = 'sys' + self.pool = PooledDB( + creator=pymysql, + host=db_host, + port=int(self.DB_TEST_PORT), + user=self.DB_TEST_USER, + passwd=self.DB_TEST_PASSWORD, + db=default_db, + mincached=int(self.DB_MIN_CACHED), + maxcached=int(self.DB_MAX_CACHED), + maxshared=int(self.DB_MAX_SHARED), + maxconnections=int(self.DB_MAX_CONNECYIONS), + blocking=True, + maxusage=int(self.DB_MAX_USAGE), + setsession=None, + use_unicode=True, + charset=self.DB_CHARSET + ) + self.pool_cache[choose_db] = self.pool + return self.pool.connection() + + # 释放连接池资源 + def __exit__(self, exc_type, exc_val, exc_tb): + self.cursor.close() + self.conn.close() + + # 关闭连接归还给链接池 + # def close(self): + # self.cursor.close() + # self.conn.close() + + # 从连接池中取出一个连接 + def getconn(self, choose_db=None): + conn = self.__getconn(choose_db=choose_db) + # 字典形式返回 + cursor = conn.cursor(pymysql.cursors.DictCursor) + return cursor, conn + + +# 获取连接池,实例化 +def get_my_connection(): + return MyConnectionPool() + + +def get_pg_connection(): + return PgConnectionPool() + + +class MongoConnectionPool(InitConfig): + def __init__(self): + try: + # super().__init__() + super(MongoConnectionPool, self).__init__() + except Exception as e: + print(e) + + def mongo_connect(self): + try: + self.connect_ = pymongo.MongoClient(host=self.MONGO_HOST, + port=int(self.MONGO_PORT), + username=self.MONGO_USER, + password=self.MONGO_PASSWORD, + authSource="hulk_teach_marketing" + ) + except Exception as e: + raise Exception("mongdb连接失败:{}".format(e)) + return self.connect_ + + +def get_my_mongo_connection(): + return MongoConnectionPool() diff --git a/base_framework/public_tools/edu_user_helper.py b/base_framework/public_tools/edu_user_helper.py new file mode 100644 index 0000000..aca0636 --- /dev/null +++ b/base_framework/public_tools/edu_user_helper.py @@ -0,0 +1,168 @@ +# encoding: utf-8 +# @Time : 2022/4/18 上午10:36 +# @Author : chenjiang +# @Site : +# @File : edu_user_helper.py + +import requests +# import py_eureka_client.eureka_client as eureka_client + +from base_framework.public_tools.sqlhelper import MySqLHelper +from base_framework.public_tools.my_faker import MyFaker +from base_framework.public_tools import log +from base_framework.public_tools.utils import Tools + +obj_log = log.get_logger() +obj_my_faker = MyFaker() +obj_my_sql_helper = MySqLHelper() +obj_tools = Tools() + + +# def get_ip_by_server_name(env_name, service_name, type): +# """ +# 根据服务名称,环境,as/hh +# :param env_name: 环境信息 +# :param service_name: 服务名称 +# :param type: as/hh +# :return: +# """ +# all_school_eureka_server= 'http://eureka.qa.allschool.com/eureka/' +# hh_eureka_server = 'http://eureka.qa.huohua.cn/eureka/' +# try: +# if type.lower() == 'as': +# eureka_server = all_school_eureka_server +# else: +# eureka_server = hh_eureka_server +# +# eureka_client.init( +# eureka_server=eureka_server, +# app_name="ASC--", +# instance_ip="127.0.0.1", +# instance_port=8080) +# client = eureka_client.get_client() +# app = client.applications.get_application(service_name) +# ip_list = [] +# for app_in in app.up_instances: +# if app_in.metadata.get('ver').lower() == env_name.lower() and app_in.ipAddr: +# ip_list.append(app_in.ipAddr) +# else: +# pass +# if ip_list: +# return ip_list +# else: +# obj_log.info("未获取到ip") +# return False +# except Exception as e: +# obj_log.info(e) +# return False + + +class EDUUserHelper: + """ + 用户中心用户相关操作 + """ + + def __init__(self): + pass + + def get_unregistered_phone(self): + """ + | 功能说明: | 获取未注册的手机号 | + | 输入参数: | | + | 返回参数: | phone | + | 作者信息: | 陈江 | 2022/4/18 | + """ + phone = obj_my_faker.gen_phone_number() + if phone: + is_exit = obj_my_sql_helper.select_one( + 'SELECT id FROM `ucenter`.`user_profile` WHERE `phone` = \'{}\''.format(phone[0])) + if is_exit: + obj_log.info('该{}手机号码已经存在,正在重新获取'.format(phone)) + self.get_unregistered_phone() + else: + return phone[0] + else: + obj_log.error('生成手机号码失败') + return False + + def get_unused_email(self): + """ + | 功能说明: | 获取未使用的邮箱 | + | 输入参数: | | + | 返回参数: | email| + | 作者信息: | 陈江 | 2022/4/18 | + """ + email = obj_my_faker.gen_email() + if email: + is_exit = obj_my_sql_helper.select_one( + 'SELECT id FROM `ucenter`.`user_contact` WHERE `contact_info` = \'{}\''.format(email)) + if is_exit: + obj_log.info('该{}邮箱已经存在,正在重新获取'.format(email)) + self.get_unused_email() + else: + return email + else: + obj_log.error('生成邮箱失败') + return False + + def get_sms_code_by_phone(self, phone): + """ + | 功能说明: | 根据手机号码获取短信验证码 | + | 输入参数: | phone | 手机号| + | 返回参数: | 验证码 | + | 作者信息: | 陈江 | 2022/4/18 | + """ + + # 根据phone获取phone_code + phone_server_ip = obj_tools.get_container_ip_from_eureka('PHONE-SERVER', need_jira_id='qa', + eureka_url='http://eureka.qa.huohua.cn') + if phone_server_ip.get('container_ip'): + url = 'http://{}:8080/encrypt/regdata?biztype=phone&uid=123456&sourceData={}'.format( + phone_server_ip.get('container_ip'), phone) + response = requests.post(url=url) # 三个参数 + response_json = response.json() + else: + obj_log.info('未获取到phone-server的ip') + return False + if response_json.get('data'): + msg = obj_my_sql_helper.select_all( + 'SELECT msg FROM `push_service`.`sms` WHERE `phone_code` = \'{}\' ORDER BY id DESC LIMIT 10 '.format( + response_json.get('data'))) + if msg: + for m in msg: + try: + if ',' in m.get('msg'): + msg_split = m.get('msg').split(',')[0] + elif ',' in m.get('msg'): + msg_split = m.get('msg').split(',')[0] + else: + return False + if ':' in msg_split: + code = msg_split.split(':')[1].strip(' ') + elif ':' in msg_split: + code = msg_split.split(':')[1].strip(' ') + else: + import re + pattern = r'\b(\d{4,6})\b.*?verification code' + match = re.search(pattern, msg_split) + if match: + verification_code = match.group(1) + return verification_code + return False + return code + except Exception as e: + return e + else: + obj_log.info('未找到发送的短信记录') + return False + else: + obj_log.info('获取{}该手机号的phonecode失败'.format(phone)) + return False + + +if __name__ == '__main__': + a = EDUUserHelper() + print(a.get_sms_code_by_phone('13563963497')) + # c = {'msg': 'SMS verification code: 6378, valid within 10 minutes, please ignore if you are not operating by yourself.'} + # + # print(c.get('msg').split(',')[0].split(':')[1].strip(' ')) diff --git a/base_framework/public_tools/es_api.py b/base_framework/public_tools/es_api.py new file mode 100644 index 0000000..76db422 --- /dev/null +++ b/base_framework/public_tools/es_api.py @@ -0,0 +1,85 @@ +# coding: utf-8 +import json + +import requests +from base_framework.public_tools.custom_error import BusinessError + +class ElasticsearchApi: + """ + | 功能说明: | 查询Elasticsearch | + | 作者信息: | 作者 huaxuemin | + | 修改时间: | 2022-12-05 | + """ + + def __init__(self): + self.es_url = "http://es-cn-zvp2bgn8a004cbosn.elasticsearch.aliyuncs.com:9200" + self.headers = {"Authorization": "Basic ZWxhc3RpYzpMa3N3aV5hbEl3"} + + def kw_es_query(self, es_query_body, index="user_personas_index_qa", es_url=None, headers=None): + """ + | 功能说明: | 查询es | + | 输入参数: | + | | es_query_body | es请求参数,符合es语法 | + | | index | 索引 | + | | es_url | es_url | + | 返回参数: | json | + | 作者信息: | 作者 huaxuemin | 修改时间 2022-12-05 | + 说明:根据es语法查询es + """ + es_url = es_url if es_url else self.es_url + req_es_url = es_url + "/" + index + "/" + "_search" + headers = self.headers.update(headers) if headers else self.headers + try: + res = requests.post(req_es_url, json=es_query_body, headers=headers) + return json.loads(res.text) + except Exception as e: + return e + + def kw_es_replace(self, es_id, es_replace_body, index="user_personas_index_qa", es_url=None, headers=None): + """ + | 功能说明: | 根据id替换es数据 | + | 输入参数: | + | | es_id | es_id | + | | es_query_body | es请求参数,符合es语法 | + | | index | 索引 | + | | es_url | es_url | + | 返回参数: | json | + | 作者信息: | 作者 huaxuemin | 修改时间 2022-12-05 | + 说明:根据es语法替换es数据 + """ + es_url = es_url if es_url else self.es_url + req_es_url = es_url + "/" + index + "/" + "_doc" + "/" + es_id + headers = self.headers.update(headers) if headers else self.headers + try: + res = requests.post(req_es_url, json=es_replace_body, headers=headers) + return json.loads(res.text) + except Exception as e: + return e + + def kw_dbs_to_es(self, index_name, env="qa", es_svr="db-to-es"): + """ + | 功能说明 | 同步某个索引的es数据 | + | 请求参数名 | 说明 | 类型 | 是否必填 | 如无要求时的值 | + | index_name | 索引名称 | string | 0 | peppa-classes-supply | + | env | 环境,qa,sim | string | 0 | qa | + | es_svr | ES服务,db-to-es,teach-to-es等 | string | 0 | db-to-es | + :return + 详见: https://tm.huohua.cn/162891389180100609/articles/215540479291179010 + 教务接口切换ES详见apollo配置:peppa-teach-common teach.common change.to.es + """ + if env not in ("qa", "sim"): + raise BusinessError("只支持qa和sim") + resp = requests.get(url="http://{}.{}.huohua.cn?index={}".format(es_svr, env, index_name)) + if resp.status_code != 200: + raise Exception('同步失败,错误信息:{}'.format(resp.text)) + return True + + +if __name__ == '__main__': + es = ElasticsearchApi() + # print(es.kw_dbs_to_es(index_name='peppa-classes-supply')) + q_sql = {"query":{"bool":{"must":[{"term":{"classes.business_region":"101"}},{"term":{"classes.year":"2023"}}],"must_not":[],"should":[]}},"from":0,"size":10,"sort":[{"classes.id":{"order":"desc"}}],"aggs":{}} + q_url = "http://10.250.200.194:9200" + rsp = es.kw_es_query(es_query_body=q_sql, es_url=q_url, index="peppa-classes-supply2") + print(rsp) + diff --git a/base_framework/public_tools/eureka_api.py b/base_framework/public_tools/eureka_api.py new file mode 100644 index 0000000..47f683e --- /dev/null +++ b/base_framework/public_tools/eureka_api.py @@ -0,0 +1,243 @@ +# -*-coding:utf-8-*- +import requests +import re +import configparser +import time +import os + +from base_framework.platform_tools.Message_service.Feishu_api import FeiShuMessage +from base_framework.public_tools.read_config import get_current_config,get_zhyy_config +from base_framework.public_tools.sqlhelper import MySqLHelper +from base_framework.base_config.current_pth import HERE +from bs4 import BeautifulSoup +from base_framework.public_tools.utils import Tools +from lxml import etree + +from base_framework.public_tools import log + +obj_log = log.get_logger() +obj_tool = Tools() + + +class EurekaAPI: + def __init__(self): + self.business = get_current_config(section="run_evn_name", key="current_business") + self.team = get_current_config(section="run_evn_name", key="current_team") + self.evn = get_current_config(section="run_evn_name", key="current_evn") + self.jira_id = get_current_config(section="run_jira_id", key="huohua-podenv") + self.is_ip_from_ini = get_current_config(section="is_ip_from_ini", key="is_ip_from_ini") + + self.server_list = list() + self.message = FeiShuMessage(team='AUTOMATION') + if self.jira_id == '': + self.jira_id = 'qa' + self.server_to_domain = {"PEPPA-CORE-API": "https://core-api.qa.huohua.cn/"} + self.sim_server_to_domain = {"PEPPA-TEACH-API": "https://teach-api.sim.huohua.cn/"} + + def __get_eureka_url_from_db(self, team): + """ + | 功能 | 从DB中获取服务对应的eureka_url,供get_container_ip_from_eureka函数使用 | + """ + sql_str = "select eureka_url as hh, eureka_url_hhi as hhi " \ + "from sparkatp.swagger_info where team='{}';".format(team) + res = MySqLHelper().select_one(sql=sql_str) + return res[self.business] + + def __get_eureka_info_by_type(self, eureka_type): + """ + | 功能 | 根据eureka类型,返回对应的url和配置文件名 | + """ + if eureka_type.lower() == "hh": + eureka_url = "http://eureka.{}.huohua.cn/".format(self.evn.lower()) + eureka_file_name = "eureka_{}_huohua_cn.ini".format(self.evn.lower()) + elif eureka_type.lower() == "vsl": + eureka_url = "http://eureka.{}.visparklearning.com/".format(self.evn.lower()) + eureka_file_name = "eureka_{}_visparklearning_com.ini".format(self.evn.lower()) + elif eureka_type.lower() == "as": + eureka_url = "http://eureka.{}.allschool.com/".format(self.evn.lower()) + eureka_file_name = "eureka_{}_allschool_com.ini".format(self.evn.lower()) + elif eureka_type.lower() == "ec": + eureka_url = "http://eureka-core.{}.huohua.cn/".format(self.evn.lower()) + eureka_file_name = "eureka_core_{}_huohua_cn.ini".format(self.evn.lower()) + elif eureka_type.lower() == "xdu": + eureka_url = "http://eureka.{}.xuedau.com/".format(self.evn.lower()) + eureka_file_name = "eureka_{}_xuedau_com.ini".format(self.evn.lower()) + else: + raise Exception("eureka类型仅支持HH,VSL,AS,EC和XDU,但你的输入的是:{}".format(eureka_type)) + return eureka_url, eureka_file_name + + def get_all_server_ip_from_eureka(self, eureka="HH"): + """ + | 功能说明: | 获取Eureka全部服务的IP,并写入对应文件中文件中 | + | 输入参数: | eureka | 类型:HH | http://eureka.qa.huohua.cn/ | + | | | 类型:VSL | http://eureka.qa.visparklearning.com/ | + | | | 类型:AS | http://eureka.qa.allschool.com/ | + | | | 类型:EC | http://eureka-core.qa.huohua.cn/ | + | | | 类型:XDU | http://eureka.qa.xuedau.com/ | + | 返回参数: | 无 | | + | 作者信息: | 谯新久 | 2022.03.27 | + """ + eureka_url, eureka_file_name = self.__get_eureka_info_by_type(eureka_type=eureka) + server_ip_path = os.path.join(HERE, eureka_file_name) + + temp_text = requests.get(url=eureka_url).content + soup = BeautifulSoup(temp_text.decode('utf-8'), "html.parser") + all_container_ip = soup.find_all(href=re.compile("actuator/info")) + tree_dict = dict() + for temp in all_container_ip: + server = list(temp.parent.parent)[1].get_text() + server_ip = list(temp) + if server in tree_dict.keys(): + tree_dict[server].extend(server_ip) + else: + tree_dict[server] = server_ip + cof = configparser.ConfigParser() + cof.read(server_ip_path, encoding='utf-8') + eureka_section = self.business + if eureka_section not in cof.sections(): + cof.add_section(section=eureka_section) + for svr_name in tree_dict: + cof.set(section=eureka_section, option=svr_name, value=str(tree_dict[svr_name])) + with open(server_ip_path, 'w') as fw: # 循环写入 + cof.write(fw) + time.sleep(2) # 等待5s,让ip写入到文件中去 + + def get_server_ip_from_config(self, server_name, eureka="HH"): + """ + | 功能说明: | 获取server_ip.ini文件中获取server_name对应的IP | + | 输入参数: | server_name | 服务名 | + | | eureka | 类型:HH | http://eureka.qa.huohua.cn/ | + | | | 类型:VSL | http://eureka.qa.visparklearning.com/ | + | | | 类型:AS | http://eureka.qa.allschool.com/ | + | | | 类型:EC | http://eureka-core.qa.huohua.cn/ | + | 返回参数: | string | 如:10.10.10.10:8080 | + | 作者信息: | 谯新久 | 2022.03.27 | + 特别说明: + | 1 | 独立环境取startup.py启动时传入的值,没有对应的独立环境则取qa的ip返回 | + | 2 | 当同一独立环境存在多个ip时,打印error日志后,返回第一个ip | + """ + eureka_url, eureka_file_name = self.__get_eureka_info_by_type(eureka_type=eureka) + server_ip_path = os.path.join(HERE, eureka_file_name) + ip_list = get_current_config(file_path=server_ip_path, section=self.business, key=server_name.lower()) + if self.evn.lower() == "sim": + ip_list = ip_list.replace("${server.port}", "8080") # 替换成8080端口 + if self.jira_id in ip_list: + jira = self.jira_id + elif "sim" in ip_list: + jira = "sim" + elif "no" in ip_list: + jira = "no" + else: + jira = "" + elif self.jira_id in ip_list: + jira = self.jira_id + elif 'qa' in ip_list: + jira = 'qa' + elif 'groot' in ip_list: + jira = 'groot' + else: + jira = 'qa' + if ip_list != 'server not exist': + if jira and jira not in ip_list: + if server_name.lower() not in self.server_list: + self.server_list.append(server_name.lower()) + message = "{}在启动startup时发现服务{}在独立环境{}未部署成功,请确认。".format(self.team, server_name, jira) + self.message.send_text(message) + + obj_ip = [] + ip_str = ip_list.replace('[', '').replace(']', '').replace('\'', '').replace(' ', '') + ip_list = ip_str.split(',') + for ip in ip_list: + if jira and jira in ip: # 找到对应独立环境的ip + if ip.startswith('10.'): # 跳过非 + obj_ip.append(ip) + if self.evn.lower() == "sim": # sim环境只有一个ip时,直接返回次ip + if not obj_ip and len(ip_list) == 1: + ips = ip_list[0].split(":") + return "{}:{}".format(ips[0], ips[1]) + if self.evn.lower() != "sim" and len(obj_ip) > 1: # sim环境不判断是否有多个部署 + obj_log.error("eureka中含有{}个{}的{}环境,请检查.........".format(len(obj_ip), server_name, jira)) + elif len(obj_ip) == 0: + obj_log.error("eureka中未找到{}的{}相关的ip信息,请检查.........".format(server_name, jira)) + return "server not exist" + # return obj_ip[0].rstrip("{}".format(jira))[:-1] + return obj_ip[0].split("{}".format(jira))[0].rstrip(":") + else: + obj_log.error("eureka中未找到{}的{}相关的ip信息,请检查.........".format(server_name, jira)) + return "server not exist" + + def get_server_url_from_config(self, server_name, eureka="HH", is_domain=True): + """ + | 功能说明: | 从对应文件中获取server_name对应的IP,组装成url后返回 | + | 输入参数: | server_name | 服务名 | + | | eureka | 类型:HH | http://eureka.qa.huohua.cn/ | + | | | 类型:VSL | http://eureka.qa.visparklearning.com/ | + | | | 类型:AS | http://eureka.qa.allschool.com/ | + | | | 类型:EC | http://eureka-core.qa.huohua.cn/ | + | | | 类型:XDU | http://eureka.qa.xuedau.com/ | + | | is_domain | 是否域名,True域名,False IP | + | 返回参数: | string | 如:http://10.10.10.10:8080/ | + | 作者信息: | 谯新久 | 2022.03.27 | + 特别说明: + | 1 | 独立环境取startup.py启动时传入的值,没有对应的独立环境则取qa的ip返回 | + | 2 | 当同一独立环境存在多个ip时,打印error日志后,返回第一个ip | + """ + if is_domain: + if self.evn == "SIM": + if server_name.upper() in self.sim_server_to_domain.keys(): + return self.sim_server_to_domain[server_name.upper()] + return "https://swagger.sim.huohua.cn/{}/".format(server_name.lower()) + if server_name.upper() in self.server_to_domain.keys(): + return self.server_to_domain[server_name.upper()] + return "https://swagger.qa.huohua.cn/{}/".format(server_name.lower()) + else: + # eureka_url, _ = self.__get_eureka_info_by_type(eureka_type=eureka) + # ip_res = obj_tool.get_container_ip_from_eureka(server_name=server_name, jira_id_dev=self.jira_id, + # eureka_url=eureka_url) + # if ip_res: + # ip_info = ip_res["container_ip"] + # url_info = "http://" + ip_info + ":8080/" + # return url_info + + # # if self.is_ip_from_ini == "true": + ip_info = self.get_server_ip_from_config(server_name=server_name.lower(), eureka=eureka) + if ip_info != 'server not exist': + url_info = "http://" + ip_info + "/" + return url_info + else: + raise Exception("eureka中未找到{}的{}相关的ip信息,请检查.........".format(server_name, self.jira_id)) + + def get_url_from_config(self, is_domain=True): + """ + | 功能说明: | 从对应文件中获取对应环境的域名,组装成url后返回 | + | 输入参数: | 环境 | 服务名 | + | | is_domain | 是否域名,True域名,False IP | + | 返回参数: | string | 如:http://10.10.10.10:8080/ | + | 作者信息: | 谯新久 | 2026.01.15 | + """ + if is_domain: + env = self.evn.upper() + team = self.team.lower() + domain_url = get_zhyy_config(section=env, key=team) + return domain_url + else: + #todo 暂无ip + return + + # # if self.is_ip_from_ini == "true": + # ip_info = self.get_server_ip_from_config(server_name=server_name.lower(), eureka=eureka) + # if ip_info != 'server not exist': + # url_info = "http://" + ip_info + "/" + # return url_info + # else: + # raise Exception("eureka中未找到{}的{}相关的ip信息,请检查.........".format(server_name, self.jira_id)) + + +if __name__ == '__main__': + er = EurekaAPI() + # er.get_all_server_ip_from_eureka + # er.get_all_server_ip_from_eureka(eureka="VSL") + res = er.get_server_url_from_config(server_name='peppa-scm-server', is_domain=False) + print(res) + # res = er.get_server_url_from_config(server_name='sparkedu-api', eureka="VSL") + # print(res) diff --git a/base_framework/public_tools/excel_api.py b/base_framework/public_tools/excel_api.py new file mode 100644 index 0000000..bd3b5ed --- /dev/null +++ b/base_framework/public_tools/excel_api.py @@ -0,0 +1,272 @@ +# -*- coding: UTF-8 -*- +# @File: operation_Xlsx.py +# @Description: excle的基本操作 +# @Author: WenQing +# @Date: 2021-08-26 11:15:08 +import os +import xlrd, xlwt +import pandas as pd +from xlutils.copy import copy +import openpyxl +from base_framework.base_config.current_pth import env_choose_path +from base_framework.public_tools.read_config import ReadConfig + + +class ExcelApi: + def __init__(self): + self.p_here = os.path.dirname(os.path.abspath(__file__)) + self.c_team = ReadConfig(env_choose_path).get_value(sections='run_evn_name', options='current_team') + self.up_file_path = os.path.abspath(os.path.join(self.p_here, '../../{}/library/UpFile/'.format(self.c_team))) + + def excel_create_excel_file(self, file_name, file_dict_data=None, file_path=None): + """ + 功能:按列将数据写入excel文件,没有文件时就新建文件 + Args: + file_name: 文件名 + file_dict_data: 文件内容,字典类型,格式:{'title1':[], 'title2':[],..... 'titleN':[],} + file_path: 文件路径,没有时默认放在小组目录下的UpFile文件夹中 + Returns: + """ + if not file_path: + full_path = self.up_file_path + os.sep + file_name + else: + full_path = file_path + os.sep + file_name + if not file_dict_data: + file_dict_data = {} + df = pd.DataFrame(file_dict_data) + # 将 DataFrame 的数据保存到 Excel 文件中 + df.to_excel(full_path, index=False) + + def excel_create_file_by_column(self, file_name, file_dict_data=None, file_path=None): + """ + 功能:按列将数据写入excel文件,没有文件时就新建文件 + Args: + file_name: 文件名 + file_dict_data: 文件内容,字典类型,格式:{'title1':[], 'title2':[],..... 'titleN':[],} + file_path: 文件路径,没有时默认放在小组目录下的UpFile文件夹中 + Returns: + """ + if not file_path: + full_path = self.up_file_path + os.sep + file_name + else: + full_path = file_path + os.sep + file_name + if not file_dict_data: + file_dict_data = {} + df = pd.DataFrame(file_dict_data) + # 将 DataFrame 的数据保存到 Excel 文件中 + df.to_excel(full_path, index=False) + + def excel_write_file_by_line(self, file_name, line_data=None, file_path=None, w_type='new'): + """ + 功能:按行将数据写入excel文件,没有文件时就新建文件 + Args: + file_name: 文件名 + line_data: 文件内容,列表类型,格式:[[字段1,字段2],[字段1,字段2]] + file_path: 文件路径,没有时默认放在小组目录下的UpFile文件夹中 + w_type: 写入类型:new-重写,append-追加 + """ + if not file_path: + full_path = self.up_file_path + os.sep + file_name + else: + full_path = file_path + os.sep + file_name + if w_type == 'new': + self.write_xlsx(path=full_path, sheet_name="Sheet1", value=line_data) + elif w_type == 'append': + self.append_xlsx(path=full_path, value=line_data) + else: + raise Exception("目前仅支持new和append两种模式,而当前传入的是;{}".format(w_type)) + + def excel_replace_cell_value(self, file_name, replace_dict, sheet_name=None, skip_first_row=True): + """ + 功能:替换excel文件中的指定单元格的值 + Args: + file_name: 文件名,如果文件不在UpFile文件夹中,需要传入完整路径 + sheet_name: 工作表名称,默认为全部工作表 + replace_dict: 替换的数据,字典类型,格式:{'old_txt_1': 'new_txt_1', 'old_txt_2': 'new_txt_2',..... } + skip_first_row: 是否跳过第一行,默认跳过,用于第一行是标题的情况 + Returns: + """ + if "/" not in file_name: # 如果没有路径,就默认在UpFile文件夹中 + file_name = self.up_file_path + os.sep + file_name + # 读取指定工作表的数据 + wb = openpyxl.load_workbook(file_name) + # 遍历所有要替换的数据 + for old_text, new_text in replace_dict.items(): + # 遍历所有工作表 + for sheet in wb.worksheets: + # 遍历工作表中的所有行和列 + for row in sheet.iter_rows(): + if skip_first_row: # 跳过第一行 + skip_first_row = False + continue + for cell in row: + if cell.value == old_text: + cell.value = new_text + wb.save(file_name) + + def excel_read_columns(self, file_name, sheet_name, column_names): + """ + 功能:按列读取excel文件的数据 + Args: + file_name: 文件名,如果文件不在UpFile文件夹中,需要传入完整路径 + sheet_name: 工作表名称 + column_names: 列名列表,格式:['title1', 'title2',..... 'titleN'] + Returns: + 返回指定列的数据,格式为:[[字段1,字段2],[字段1,字段2]] + """ + if "/" not in file_name: # 如果没有路径,就默认在UpFile文件夹中 + file_name = self.up_file_path + os.sep + file_name + # 读取指定工作表的数据 + df = pd.read_excel(file_name, sheet_name=sheet_name) + + # 检查指定列是否存在 + missing_columns = [col for col in column_names if col not in df.columns] + if missing_columns: + raise ValueError(f"Columns {missing_columns} do not exist in the sheet '{sheet_name}'.") + + # 返回指定列的数据 + return df[column_names].values.tolist() + + def read_asDict(self, path): + """ + | 功能说明: | 读取excle,输出字典格式| + | 传入参数: | 读取文件路径 | + | 返回数据: | 表头字段作为key,单元格值作为value | + | 作者信息: | 作者 文青 | 修改时间 | + 举例说明: + """ + self.table = pd.read_excel(path) + data = [] + for i in self.table.index.values: + data_dict = self.table.loc[i].to_dict() + data.append(data_dict) + return data + + def read_asList(self, path, sheetname=None): + """ + | 功能说明: | 读取excle和sheetname,输出列表格式| + | 传入参数: | 读取excle和sheetname | + | 返回数据: | 列表[] | + | 作者信息: | 作者 文青 | 修改时间 | + 举例说明: + """ + wb = openpyxl.load_workbook(path) + if not sheetname: + sheets = wb.sheetnames + sheetname = sheets[0] + ws = wb[sheetname] + rows = ws.rows + columns = ws.columns + data = [] + for row in rows: + line = [col.value for col in row] + data.append(line) + return data + + def read_xls_txt(self, path): + """ + | 功能说明: | 读取excle的第一列放在一个列表中| + | 传入参数: | 读取excle路径 | + | 返回数据: | 列表[] | + | 作者信息: | 作者 文青 | 修改时间 | + 举例说明: + """ + data = pd.read_excel(path, header=None) + data_list = [] + nrows = data.shape[0] + for irow in range(nrows): + data_list.append(data.iloc[irow, 0]) + string = '' + for i in range(len(data_list)): + string += data_list[i] + ',' + return string + + def write_xls(self, value, sheetname, path): + """ + | 功能说明: | 创建一个xls的文件并且写入数据 | + | 传入参数: | value:传入列表,格式:[[字段1,字段2],[字段1,字段2],[字段1,字段2],[字段1,字段2]] | + |sheetname:sheet名称 | + |path:写入文件路径 | + | 返回数据: | | + | 作者信息: | 作者 文青 | 修改时间 | + 举例说明: + """ + index = len(value) # 获取需要写入数据的行数 + workbook = xlwt.Workbook() # 新建一个工作簿 + sheet = workbook.add_sheet(sheetname=sheetname) # 在工作簿中新建一个sheetname的表格 + for i in range(0, index): + for j in range(0, len(value[i])): + sheet.write(i, j, value[i][j]) + workbook.save(path) + # return "写入成功!" + + def append_xls(self, value, path): + """ + | 功能说明: | 对xls文件第一个sheet内容进行追加 | + | 传入参数: | value:传入列表,格式:[[字段1,字段2],[字段1,字段2],[字段1,字段2],[字段1,字段2]] | + |path:写入文件路径 | + | 返回数据: | | + | 作者信息: | 作者 文青 | 修改时间 | + 举例说明: + """ + index = len(value) # 获取需要写入数据的行数 + workbook = xlrd.open_workbook(path) # 打开工作簿 + sheets = workbook.sheet_names() # 获取所有表格 + worksheet = workbook.sheet_by_name(sheets[0]) # 获取所有表格中的第一个表格 + rows_old = worksheet.nrows # 获取表格的总行数 + new_workbook = copy(workbook) # 把原文件复制一份 + new_worksheet = new_workbook.get_sheet(0) # 获取副本第一个表格 + for i in range(0, index): + for j in range(0, len(value[i])): + new_worksheet.write(i + rows_old, j, value[i][j]) + new_workbook.save(path) + # return "追加成功!!" + + def write_xlsx(self, path, sheet_name, value): + """ + | 功能说明: | 创建一个xls的文件并且写入数据 | + | 传入参数: | value:传入列表,格式:[[字段1,字段2],[字段1,字段2],[字段1,字段2],[字段1,字段2]] | + |sheetname:sheet名称 | + |path:写入文件路径 | + | 返回数据: | | + | 作者信息: | 作者 文青 | 修改时间 | + 举例说明: + """ + index = len(value) + workbook = openpyxl.Workbook() + sheet = workbook.active + sheet.title = sheet_name + for i in range(0, index): + for j in range(0, len(value[i])): + sheet.cell(row=i + 1, column=j + 1, value=str(value[i][j])) + workbook.save(path) + return "写入成功!!" + + def append_xlsx(self, path, value): + """ + | 功能说明: | 对xls文件第一个sheet内容进行追加 | + | 传入参数: | value:传入列表,格式:[[字段1,字段2],[字段1,字段2],[字段1,字段2],[字段1,字段2]] | + |path:写入文件路径 | + | 返回数据: | | + | 作者信息: | 作者 文青 | 修改时间 | + 举例说明: + """ + data = openpyxl.load_workbook(path) + sheets = data.sheetnames + table = data[sheets[0]] + for i in value: + table.append(i) + data.save(path) + return "追加成功!!" + + +if __name__ == '__main__': + ea = ExcelApi() + f_data = {"教师ID": [1, 2, 3, 4], + "教师姓名": [11, 22, 33, 44], + "课程ID": [111, 222, 333, 444], + "课程名称": [1111, 2222, 3333, 4444], + "更新类型(1.新增;0.删除,填写数字)": [0, 1, 0, 1] + } + ea.excel_create_excel_file(file_name="测试大盘A标签.xlsx", file_dict_data=f_data) + diff --git a/base_framework/public_tools/get_token.py b/base_framework/public_tools/get_token.py new file mode 100644 index 0000000..31f62f0 --- /dev/null +++ b/base_framework/public_tools/get_token.py @@ -0,0 +1,911 @@ +import json +from urllib import parse + +import requests +from retrying import retry +from base_framework.public_tools.read_config import InitConfig +from base_framework.public_tools.read_config import ReadConfig, get_current_config, get_current_env +from base_framework.base_config.current_pth import * +from requests.packages.urllib3.connectionpool import InsecureRequestWarning +from base_framework.public_tools import log +from Crypto.Cipher import PKCS1_v1_5 +from Crypto.PublicKey import RSA +import base64 + + +requests.packages.urllib3.disable_warnings(InsecureRequestWarning) +import time +import re +# get_cfg = ReadConfig(env_choose_path) +token_dict = dict() +student_token_cache = {} +obj_log = log.get_logger() + + +class LazyProperty: + def __init__(self, fun): + self.fun = fun + + def __get__(self, instance, owner): + if instance is None: + return self + value = self.fun(instance) + setattr(instance, self.fun.__name__, value) + return value + + +class LoginSys(InitConfig): + def __init__(self, host=None, curt_user=None, current_evn=None): + try: + InitConfig.__init__(self, run_user_name=curt_user, current_evn=current_evn) + except Exception as e: + print(e) + self.host = host + self.curt_user = self.current_user + self.env = get_current_env() + + @LazyProperty + def get_session(self): + """ + 获取SSO session + :return: + """ + sys_type = self.host.split(".") + if 'visparklearning' in sys_type and ('cc-manage' or 'cti-manage' in sys_type): + api_url = self.curt_sso_url + elif 'hulk-content-audit-server' in sys_type or 'hulk-content-manage-gateway' in sys_type or 'hulk-class-help-manager' in sys_type or 'hulk-ark-gateway' in sys_type or 'manage-api' in sys_type : + api_url = self.all_school_sso_url + else: + api_url = self.curt_sso_url + post_data = dict() + post_data['showUsername'] = self.show_username + post_data['username'] = self.username + post_data['password'] = self.password + req_session = requests.session() + resp = req_session.post( + url=api_url, + data=post_data, + allow_redirects=False, + verify=False) + return req_session + + @LazyProperty + def get_code(self): + """ + 获取code + :return: + """ + post_data = dict() + sys_type = self.host.split(".") + if 'visparklearning' in sys_type and ('cc-manage' or 'cti-manage' in sys_type): + post_data['client_id'] = "vispark-crm" + post_data['response_type'] = 'code' + post_data['redirect_uri'] = self.crm_vispark_url + authorization = self.cc_auth + # elif sys_type == "smm": + # post_data['client_id'] = "sms-manage" + # post_data['response_type'] = 'code' + # post_data['redirect_uri'] = self.sms_url + if 'visparklearning' in sys_type and ('cc-manage' or 'cti-manage' in sys_type): + api_url = self.get_code_url + elif 'hulk-content-audit-server' in sys_type or 'hulk-content-manage-gateway' in sys_type or 'hulk-class-help-manager' in sys_type or 'hulk-ark-gateway' in sys_type or 'manage-api' in sys_type: + api_url = self.get_all_school_code_url + else: + api_url = self.get_code_url + req_session = self.get_session + if 'mq-console' in sys_type: + allow_redirects = True + else: + allow_redirects = False + obj_log.info(api_url) + obj_log.info(post_data) + resp = req_session.get( + url=api_url, + params=post_data, + allow_redirects=allow_redirects, + verify=False) + if 'mq-console' in sys_type: + return [req_session, 'MQ'] + code_temp = resp.headers + obj_log.info(code_temp) + code = code_temp.get('location') + obj_log.info(code) + if 'client_id=gmp' in code: + return code + try: + code = code.split("=")[-1] + except Exception: + raise IndexError("获取token失败,请检查环境或者登录信息是否正确!") + resp_list = [code, post_data['redirect_uri'], authorization] + return resp_list + + # @LazyProperty + def get_token(self, type_name=None): + """ + 获取token + :return: + """ + try: + token = token_dict[self.host].get(self.curt_user, None) + except KeyError as e: + token = False + if token: + return token + else: + token_dict_temp = dict() + sys_type = self.host.split(".") + + post_data = dict() + temp_code = self.get_code + if isinstance(temp_code, str): + session = self.get_session + resp = session.get(url=temp_code) + for key, value in session.cookies.items(): + if key == 'peppa_sso_token': + token = value + break + token_header = {"accesstoken": token} + session.headers.update(token_header) + if self.jira_id: + podenv = {"huohua-podenv": self.jira_id} + session.headers.update(podenv) + token_dict_temp[self.curt_user] = session + token_dict[self.host] = token_dict_temp + return session + + if temp_code[1] == 'MQ': # MQ特殊处理 + return temp_code[0] + post_data['code'] = temp_code[0] + post_data['redirect_uri'] = temp_code[1] + post_data['grant_type'] = 'authorization_code' + req_session = self.get_session + authorization = temp_code[2] + if authorization: + header = {'authorization': authorization} + if 'visparklearning' in sys_type and ('cc-manage' or 'cti-manage' in sys_type): + resp = req_session.get( + url=api_url+'&code='+temp_code[0], data=post_data, headers=header, allow_redirects=False, verify=False) + elif 'visparklearning' in sys_type and 'manage-gw' in sys_type: + resp = req_session.get(url=api_url + '&code=' + temp_code[0], data=post_data, headers=header, + allow_redirects=False, verify=False) + else: + resp = req_session.post( + url=api_url, data=post_data, headers=header, allow_redirects=False, verify=False) + if 'visparklearning' in sys_type and ('cc-manage' or 'cti-manage' in sys_type): + token_temp = parse.parse_qs(parse.urlparse(resp.headers['location']).query) + elif 'visparklearning' in sys_type and 'manage-gw' in sys_type: + token_temp = parse.parse_qs(parse.urlparse(resp.headers['location']).query) + else: + token_temp = resp.json() + if type_name == 'BFF': # bff架构使用 + token = {"token": token_temp['access_token']} + elif 'visparklearning' in sys_type and ('cc-manage' or 'cti-manage' in sys_type): + token = {"accesstoken": token_temp['peppa_sso_token'][0]} + elif 'visparklearning' in sys_type and 'manage-gw' in sys_type: + token = {"accesstoken": token_temp['peppa_sso_token'][0]} + else: + if "access_token" not in token_temp: + obj_log.warning("---------获取token失败!-------") + obj_log.warning("type_name:{}\ntoken_temp:{}".format(type_name, token_temp)) + obj_log.warning("-----------------------------") + token = {"accesstoken": token_temp['access_token']} + req_session.headers.update(token) + if self.jira_id: + podenv = {"huohua-podenv": self.jira_id} + req_session.headers.update(podenv) + token_dict_temp[self.curt_user] = req_session + token_dict[self.host] = token_dict_temp + return req_session + else: + if 'kunpeng' in post_data['redirect_uri']: # 鲲鹏/mp系统登录 + req_session.get(url=post_data['redirect_uri'] + '&code=' + temp_code[0], verify=False) + return req_session + elif 'allschool-mp' in post_data['redirect_uri']: + if self.jira_id: + podenv = {"huohua-podenv": self.jira_id} + req_session.headers.update(podenv) + req_session.get(url=post_data['redirect_uri'] + '&code=' + temp_code[0], verify=False) + access_token = req_session.cookies.get("peppa_sso_token") + req_session.headers.update({"accesstoken": access_token}) + return req_session + run_session = self.sso_oauth_login(session=req_session, code=temp_code[0]) + if self.jira_id: + podenv = {"huohua-podenv": self.jira_id} + run_session.headers.update(podenv) + token_dict_temp[self.curt_user] = run_session + token_dict[self.host] = token_dict_temp + return run_session + + def sso_oauth_login(self, type='manage', session=None, code=None): + """ + | 功能说明: | | + | 输入参数: | | | + | 返回参数: | XXX | + | 作者信息: | 作者 | 修改时间 | + 举例说明: + | sso_oauth_login | | + """ + if type == 'manage': + url = self.manage_url + '?code=' + code + elif type == 'opengalaxy': # 独立环境发布系统 + url = self.opengalaxy_url + '?code=' + code + session.get(url=url, allow_redirects=True, verify=False) + return session + + def ug_hhi_sso_login_by_session_id(self): + """ + ug的manage后台登录获取sessionId + :return: + """ + req_session = requests.session() + headers = {"Content-Type": "application/json"} + + # sso登录 + url_form = self.curt_sso_url + obj_log.info(url_form) + post_data = dict() + post_data['showUsername'] = self.show_username + post_data['username'] = self.username + post_data['password'] = self.password + res_form = req_session.post(url=url_form, data=post_data, allow_redirects=False, verify=False) + obj_log.info("ug_hhi_sso_login_by_session_id_url_form:{}".format(url_form)) + obj_log.info("ug_hhi_sso_login_by_session_id_res_form:{}".format(res_form.headers)) + + + # 进行sso.qa.sparkedu.com/oauth/authorize + url_authorize = "{}?client_id=manage&response_type=code&redirect_uri={}".format( + self.get_code_url, self.manage_url) + res_authorize = req_session.get(url_authorize, allow_redirects=False) + obj_log.info("ug_hhi_sso_login_by_session_id_url_authorize:{}".format(url_authorize)) + obj_log.info("ug_hhi_sso_login_by_session_id_res_authorize:{}".format(res_authorize.headers)) + + # 获取到res_authorize的location地址(主要是用到code) + url_sso_oauth_login = res_authorize.headers.get('location') + obj_log.info("ug_hhi_sso_login_by_session_id_url_sso_oauth_login:{}".format(url_sso_oauth_login)) + res_url_sso_oauth_login = req_session.get(url_sso_oauth_login, allow_redirects=False) + obj_log.info("ug_hhi_sso_login_by_session_id_res_url_sso_oauth_login:{}".format(res_url_sso_oauth_login.headers)) + + # 因为环境问题所以多请求一次域名 + res_manage_host = req_session.get(self.manage_host) + obj_log.info("ug_hhi_sso_login_by_session_id_url_manage:{}".format(self.manage_host)) + obj_log.info( + "ug_hhi_sso_login_by_session_id_res_manage_host:{}".format(res_manage_host.headers)) + + token_dict_temp = dict() + token_dict_temp[self.curt_user] = req_session + token_dict[self.host] = token_dict_temp + + return req_session + + def ug_sso_login_by_starlight(self): + """ + ug的starlight.qa.huohua.cn登录 + :return: + """ + req_session = requests.session() + headers = {"Content-Type": "application/json"} + + # 访问starlight.qa.huohua.cn/生成JSESSION_STARLIGHT + starlight_url = self.starlight_url + obj_log.info(starlight_url) + req_session.get(starlight_url, allow_redirects=False) + + # sso登录 + url_form = self.curt_sso_url + obj_log.info(url_form) + post_data = dict() + post_data['showUsername'] = self.show_username + post_data['username'] = self.username + post_data['password'] = self.password + res_form = req_session.post(url=url_form, data=post_data, allow_redirects=False, verify=False) + obj_log.info(f'res_form的header:{res_form.headers}') + + # 进行sso.qa.sparkedu.com/oauth/authorize + url_authorize = "{}?client_id=starlight&response_type=code&redirect_uri={}".format( + self.get_code_url, self.suyang_manage_sso_login_url) + res_authorize = req_session.get(url_authorize, allow_redirects=False) + obj_log.info(f"res_authorize的header:{res_authorize.headers.get('location')}") + + # 获取到res_authorize的location地址(主要是用到code) + req_session.get(res_authorize.headers.get('location'), allow_redirects=False) + + # 访问https://starlight.qa.huohua.cn/ + req_session.get(self.starlight_url, allow_redirects=False) + + token_dict_temp = dict() + token_dict_temp[self.curt_user] = req_session + token_dict[self.host] = token_dict_temp + return req_session + +class SparkleLogin(InitConfig): + def __init__(self, host=None, curt_user=None, current_evn=None): + # super().__init__(run_user_name=None, current_evn=current_evn) + InitConfig.__init__(self, run_user_name=None, current_evn=current_evn) + self.pc_token = None + self.session = requests.session() + # self.jira_id = get_cfg.get_value(sections='run_jira_id', options='huohua-podenv') + self.jira_id = ReadConfig(env_choose_path).get_value(sections='run_jira_id', options='huohua-podenv') + + # def sparkle_pc_login(self, phone, passwd=123456): + def sparkle_pc_login(self, phone, passwd='Mima@123'): + if token_dict.get(str(phone)): + return token_dict.get(str(phone)) + url = self.sparkle_pc_token_url + data = {"username": phone, "userType": 2, "password": str(passwd)} + self.session.headers.update({"Authorization": self.spark_pc_auth}) + resp = json.loads(self.session.post(url, params=data).content) + header = {"account-token": resp.get("data").get("accessToken")} + if self.jira_id: + podenv = {"huohua-podenv": self.jira_id} + self.session.headers.update(podenv) + self.session.headers.update(header) + token_dict[str(phone)] = self.session + return self.session + + +class AgentApiLogin(InitConfig): + def __init__(self, host=None, curt_user=None, current_evn=None): + # super().__init__(run_user_name=None, current_evn=current_evn) + InitConfig.__init__(self, run_user_name=None, current_evn=current_evn) + self.agent_api_token = None + self.session = requests.session() + # self.jira_id = get_cfg.get_value(sections='run_jira_id', options='huohua-podenv') + self.jira_id = ReadConfig(env_choose_path).get_value(sections='run_jira_id', options='huohua-podenv') + + def agent_api_login(self, phone, authCode, countryCode=86): + if token_dict.get(str(phone)): + return token_dict.get(str(phone)) + url = '{}/login/loginByAuthCode'.format(self.hhr_api_host) + data = {"phone": str(phone), "authCode": str(authCode), "countryCode": str(countryCode)} + resp = json.loads(self.session.post(url, json=data).content) + header = {"user-token": resp.get("data").get("token")} + if self.jira_id: + podenv = {"huohua-podenv": self.jira_id} + self.session.headers.update(podenv) + self.session.headers.update(header) + token_dict[str(phone)] = self.session + return self.session + +class MarketApiLogin(InitConfig): + def __init__(self, host=None, curt_user=None, current_evn=None): + InitConfig.__init__(self, run_user_name=None, current_evn=current_evn) + self.session = requests.session() + self.jira_id = ReadConfig(env_choose_path).get_value(sections='run_jira_id', options='huohua-podenv') + + def market_api_login(self, user=None): + login_url = self.market_url + result = parse.urlparse(url=login_url ,allow_fragments=True) + host = result.hostname + choose_user = ReadConfig(env_choose_path).get_options('run_user_name') + if host in choose_user: + default_user = ReadConfig(env_choose_path).get_value( + sections='run_user_name' ,options=host) + self.current_user = default_user + if user: + self.current_user = user + try: + login_user = ReadConfig().get_value(sections=self.current_user ,options='username') + login_password = ReadConfig().get_value(sections=self.current_user ,options='password') + except Exception as e: + login_user = ReadConfig(astwb_config).get_value(sections=self.current_user ,options='username') + login_password = ReadConfig(astwb_config).get_value(sections=self.current_user ,options='password') + + post_data = {'phone': login_user ,'password': login_password} + resp = self.session.post(url=login_url ,json=post_data , verify=False, + headers={'huohua-podenv': self.jira_id}) + + if resp.status_code != 200: + raise KeyError('获取 市场管理投放系统 token失败!') + set_cookie = resp.headers['set-cookie'].split(";")[-1].split(",")[1] + headers = {"Cookie":"gray_id=3077c7810744b47fc06ec513cf07801e; gray_tag=stable; "+set_cookie} + self.session.headers.update(headers) + return self.session + + + +class AllSchool(InitConfig): + def __init__(self, curt_user=None, current_evn=None): + # super().__init__(run_user_name=curt_user, current_evn=current_evn) + InitConfig.__init__(self, run_user_name=curt_user, current_evn=current_evn) + self.jira_id = ReadConfig(env_choose_path).get_value(sections='run_jira_id', options='huohua-podenv') + + def all_school_token(self, user=None, login_type=1): + """ + :param user: 登录用户 + :param login_type: 登录系统 1:教师工作台 + 2:机构工作台 + 3:选课平台 + :return: token + """ + if login_type == 1: + login_url = self.all_school_url + elif login_type == 2: + login_url = self.all_school_org_url + elif login_type == 3: + login_url = self.find_classes_url + result = parse.urlparse(url=login_url, allow_fragments=True) + host = result.hostname + choose_user = ReadConfig(env_choose_path).get_options('run_user_name') + if host in choose_user: + if login_type == 2: + host = 'org-' + host + elif login_type == 3: + host = 'find-' + host + default_user = ReadConfig(env_choose_path).get_value( + sections='run_user_name', options=host) + self.current_user = default_user + if user: + self.current_user = user + try: + login_user = ReadConfig().get_value(sections=self.current_user, options='username') + login_password = ReadConfig().get_value(sections=self.current_user, options='password') + except Exception as e: + login_user = ReadConfig(astwb_config).get_value(sections=self.current_user, options='username') + login_password = ReadConfig(astwb_config).get_value(sections=self.current_user, options='password') + if int(login_type) == 3: + post_data = {"data": {"email": login_user, "password": login_password, "countryCode": "86", + "loginType": "EMAIL_PSW"}} + else: + post_data = {'email': login_user, 'password': login_password} + as_token = requests.post(url=login_url, json=post_data, verify=False, + headers={'huohua-podenv': self.jira_id}) + rtn_temp = as_token.json() + if as_token.status_code != 200: + raise KeyError('获取 allSchool token失败!') + user_token = rtn_temp['data']['token'] + return user_token + + +class ParentLogin(InitConfig): + def __init__(self,host=None, curt_user=None, current_evn=None): + # super().__init__(run_user_name=curt_user, current_evn=current_evn) + InitConfig.__init__(self, run_user_name=curt_user, current_evn=current_evn) + self.jira_id = ReadConfig(env_choose_path).get_value(sections='run_jira_id', options='huohua-podenv') + + @retry(stop_max_attempt_number=5, wait_fixed=3000) + def parent_send_msg(self, phone, authType=2, countryCode=None, + sessionId=None, **kwargs): + """ + | 功能说明: | M站(学生端)发送短信 | + | 输入参数: | phone | 手机号 | + | 返回参数: | XXX | + | 作者信息: | 作者 | 修改时间 | + 举例说明: + | m_send_msg | | + """ + url = self.m_host + '/passport/auth_code/send' + post_data = { + "phone": phone, + "authType": authType, + "countryCode": countryCode, + "isLogin": False, + "sessionId": sessionId} + resp = requests.post( + url=url, + json=post_data, + verify=False) + time.sleep(2) + return resp + + def get_parent_sms_code(self, phone): + """ + M站获取手机验证码 + :param phone:电话号码 + :return: token + """ + auth_url = self.curt_sso_url + get_code_url = self.get_code_url + token_url = self.get_token_url + sms_url = "https://smm.qa.huohua.cn/sms/log/page/all?WHERE.%5BEQ%5Dphone={}&WHERE.%5BEQ%5DtypeName=&WHERE.%5BEQ%5Dchannel=&WHERE.%5BEQ%5DgatewayType=&WHERE.%5BGT%5DcreateTime=&WHERE.%5BLT%5DcreateTime=&WHERE.%5BGT%5DsubmitTime=&WHERE.%5BLT%5DsubmitTime=&pageNum=1&pageSize=20&orderBy=id%20desc".format( + phone) + session = requests.session() + session.post(url=auth_url, verify=False, + data={'showUsername': 'liuruiquan', 'username': 'liuruiquan@huohua.cn', 'password': 'lrq5823LRQ'}) + get_code = session.get(url=get_code_url, verify=False, + params={'client_id': 'crmnew', 'response_type': 'code', + 'redirect_uri': 'https://crmv2.qa.huohua.cn'}, allow_redirects=False) + code_temp = get_code.headers + code = code_temp['location'] + code = code.split("=")[-1] + token_get = session.post(url=token_url, verify=False, data={'code': code, 'redirect_uri': 'https://crmv2.qa.huohua.cn', + 'grant_type': 'authorization_code'}, + headers={'authorization': 'Basic Y3JtbmV3OmNybW5ldw=='}) + token_temp = token_get.json() + token = {"accesstoken": token_temp['access_token']} + sms_temp = session.post(url=sms_url, verify=False, headers=token).json() + smm_code = sms_temp['list'][0]['msg'] + reg = re.compile(r"(?<=手机短信验证码:)\d+") + match = reg.search(smm_code) + smm_code = match.group(0) + return smm_code + + def get_parent_token(self, phone=None): + """ + M站token获取 + :param phone:电话号码 + :return: token + """ + login_url = "http://m.qa.huohua.cn/passport/login" + result = parse.urlparse(url=login_url, allow_fragments=True) + host = 'parent-' + result.hostname + choose_user = ReadConfig(env_choose_path).get_options('run_user_name') + if host in choose_user: + default_user = ReadConfig(env_choose_path).get_value( + sections='run_user_name', options=host) + if phone: + user = ReadConfig().get_value(sections=phone, options='username') + self.current_user = user + else: + phone = ReadConfig().get_value( + sections=default_user, options='username') + self.current_user = phone + if student_token_cache.get(self.current_user): + return student_token_cache[self.current_user] + send_code = self.parent_send_msg(phone=self.current_user).json() + # if not send_code['success']: + # raise ValueError(send_code) + verify_code = self.get_parent_sms_code(phone=self.current_user) + post_data = {"phone": self.current_user, "authCode": verify_code, "countryCode": None, "loginType": 1, + "userType": 1, + "subjectType": 0} + m_token = requests.post(url=login_url, json=post_data, verify=False, headers={'huohua-podenv': self.jira_id}) + if m_token.status_code == 200: + token = m_token.json() + token = token['data']['token'] + student_token_cache[self.current_user] = token + return token + else: + raise KeyError('获取 parent token失败!') + + def get_parent_token_by_pwd(self, phone=None): + env_name = get_current_config(section='run_evn_name', key='current_business') + obj_log.info("env_name:{}".format(env_name)) + if env_name and env_name=='hhi': + login_url = "https://hhi-user-auth-api.qa.visparklearning.com/login" + elif 'hhi' in phone.split('-'): + login_url = "https://pst-gw.qa.huohua.cn/passport/login" + else: + login_url = "http://m.qa.huohua.cn/passport/login" + result = parse.urlparse(url=login_url, allow_fragments=True) + host = 'parent-' + result.hostname + choose_user = ReadConfig(env_choose_path).get_options('run_user_name') + obj_log.info("host:{},choose_user:{}".format(host, choose_user)) + if host in choose_user: + default_user = ReadConfig(env_choose_path).get_value( + sections='run_user_name', options=host) + if phone.split('-')[1]: + self.current_user = phone.split('-')[1] + else: + phone = ReadConfig(config_file_path).get_value( + sections=default_user, options='username') + self.current_user = phone + else: + phone = ReadConfig(config_file_path).get_value( + sections='lzp', options='username') + self.current_user = phone + if student_token_cache.get(self.current_user): + return student_token_cache[self.current_user] + # 支持传入海外区号用户 + if '+' in self.current_user : + phone_list =self.current_user.split("+") + countryCode = phone_list[1] + self.current_user = phone_list[0] + else: + countryCode = 86 + post_data = {"phone": self.current_user, "password": "A123456", "countryCode": countryCode, "loginType": 2, + "userType": 1, + "subjectType": 0} + + try: + m_token = requests.post(url=login_url, json=post_data, verify=False, + headers={'huohua-podenv': self.jira_id}) + if m_token.status_code != 200: + raise KeyError('获取 parent token失败!') + token = m_token.json() + token = token['data']['token'] + student_token_cache[self.current_user] = token + return token + except : + raise TypeError('获取 parent token失败!') + + def get_xdu_parent_token_by_pwd(self, phone=None, host=None): + env_name = get_current_config(section='run_evn_name', key='current_business') + obj_log.info("env_name:{}".format(env_name)) + login_url = 'http://'+host+"/app/sign/phone" + result = parse.urlparse(url=login_url, allow_fragments=True) + host = result.hostname + choose_user = ReadConfig(env_choose_path).get_options('run_user_name') + obj_log.info("host:{},choose_user:{}".format(host, choose_user)) + if host in choose_user: + default_user = ReadConfig(env_choose_path).get_value( + sections='run_user_name', options=host) + post_data = {"phone": phone, "verifyCode": 1111, "countryCode": 86, "type": 1} + try: + m_token = requests.post(url=login_url, json=post_data, verify=False, + headers={'huohua-podenv': self.jira_id}) + if m_token.status_code != 200: + raise KeyError('获取 parent token失败!') + token = m_token.json() + token = token['data']['token'] + student_token_cache[self.current_user] = token + return token + except : + raise TypeError('获取 parent token失败!') + + +class StudentLogin(InitConfig): + def __init__(self, host=None, curt_user=None, current_evn=None): + # super().__init__(run_user_name=curt_user, current_evn=current_evn) + InitConfig.__init__(self, run_user_name=curt_user, current_evn=current_evn) + self.jira_id = ReadConfig(env_choose_path).get_value(sections='run_jira_id', options='huohua-podenv') + + def get_student_token_by_pwd(self, phone=None): + env_name = get_current_config(section='run_evn_name', key='current_business') + if env_name and env_name=='hhi': + login_url = "https://hhi-core-api.qa.visparklearning.com/token" + else: + login_url = "https://core-api.qa.huohua.cn/token" + result = parse.urlparse(url=login_url, allow_fragments=True) + host = result.hostname + choose_user = ReadConfig(env_choose_path).get_options('run_user_name') + if host in choose_user: + default_user = ReadConfig(env_choose_path).get_value( + sections='run_user_name', options=host) + if phone: + self.current_user = phone + else: + phone = ReadConfig(config_file_path).get_value( + sections=default_user, options='username') + self.current_user = phone + else: + phone = ReadConfig(config_file_path).get_value( + sections='lzp', options='username') + self.current_user = phone + if student_token_cache.get(self.current_user): + return student_token_cache[self.current_user] + # 支持传入海外区号用户 + if '+' in self.current_user : + phone_list =self.current_user.split("+") + countryCode = phone_list[1] + self.current_user = phone_list[0] + else: + countryCode = 86 + post_data = {"phone": self.current_user, "password": "A123456", "countryCode": countryCode, "authCodeType":2,"loginType": 2} + try: + m_token = requests.post(url=login_url, json=post_data, verify=False, headers={'huohua-podenv': self.jira_id}) + + if m_token.status_code != 200: + raise KeyError('获取 student token失败!') + token = m_token.json() + token = token['data']['token'] + student_token_cache[self.current_user] = token + return token + except : + raise TypeError('获取 student token失败!') + + +class SparkEduLogin(InitConfig): + def __init__(self, host=None, curt_user=None, current_evn=None): + # super().__init__(run_user_name=curt_user, current_evn=current_evn) + InitConfig.__init__(self, run_user_name=curt_user, current_evn=current_evn) + self.jira_id = ReadConfig(env_choose_path).get_value(sections='run_jira_id', options='huohua-podenv') + team_name = ReadConfig(env_choose_path).get_value(sections='run_evn_name', options="current_team") + + def get_spark_edu_graphic_code(self): + """ + 功能:获取图形验证码 + """ + code_url = "https://api.qa.sparkedu.com/third/passport/captcha" + post_data = {"width":240,"height":90} + resp = requests.post(url=code_url, json=post_data, verify=False, headers={'huohua-podenv': self.jira_id}) + resp = resp.json() + return resp['data']['captchaId'] + + def check_spark_edu_graphic_code(self, captcha_id, captcha='1234',): + """ + 功能:校验图形验证码 + captcha_id:图形验证码id,由get_spark_edu_graphic_code返回 + captcha:图形验证码,默认1234,需事先配置好 + """ + check_url = "https://api.qa.sparkedu.com/third/passport/captcha/check" + post_data = {"captchaId":captcha_id,"captcha":"1234"} + resp = requests.post(url=check_url, json=post_data, verify=False, headers={'huohua-podenv': self.jira_id}) + resp = resp.json() + print(resp) + + def spark_edu_login_by_code(self, phone=None, auth_code=None, + auth_code_type=17, country_code=86, language="zh-HK", platform="1", + login_type=1, user_type=1): + """ + 功能:验证码登录 + phone: 登录用户手机号 + auth_code:验证码,这里默认验证码为1234,需提前配置 + 其余参数:默认 + """ + login_url = "https://api.qa.sparkedu.com/third/passport/login" + if not phone: + # 如果没有传入phone,则从小组配置文件中读取 + team_config = os.path.abspath(os.path.join(ROOT_PATH, 'UBJ/library/Config/team_config.ini')) + phone = ReadConfig(team_config).get_value(sections='default_spark_edu_user', options='phone') + if not auth_code: + # 如果没有传入验证码,则从小组配置文件中读取 + team_config = os.path.abspath(os.path.join(ROOT_PATH, 'UBJ/library/Config/team_config.ini')) + auth_code = ReadConfig(team_config).get_value(sections='default_spark_edu_user', options='auth_code') + post_data = {"phone":phone, + "authCode":auth_code, + "countryCode":country_code, + "authCodeType":auth_code_type, + "language":language, + "platform":platform, + "loginType":login_type, + "userType":user_type} + m_token = requests.post(url=login_url, json=post_data, verify=False, headers={'huohua-podenv': self.jira_id}) + if m_token.status_code == 200: + token = m_token.json() + return token['data']['token'] + else: + print(m_token.content) + raise KeyError('获取 student token失败!') + + +class SparkSaasLogin(InitConfig): + def __init__(self, host=None, curt_user=None, current_evn=None): + # super().__init__(run_user_name=curt_user, current_evn=current_evn) + InitConfig.__init__(self, run_user_name=curt_user, current_evn=current_evn) + self.team_config = os.path.abspath(os.path.join(ROOT_PATH, 'BIZ/library/Config/team_config.ini')) + self.jira_id = ReadConfig(env_choose_path).get_value(sections='run_jira_id', options='huohua-podenv') + + def login_biz_saas(self, phone="", email="", password="", country_code="", auth_code="9999"): + """ + Args: + phone: + email: + password: 密码, + country_code: 86, config.country_code - 目前有问题,暂时写到86 + auth_code: 验证码 + Returns: + """ + url = "https://sc-saas-api.qa.huohua.cn/api/session/integration/login" + team_config = os.path.abspath(os.path.join(ROOT_PATH, 'BIZ/library/Config/team_config.ini')) + a = ReadConfig(team_config).get_value(sections='saas_org_info', options='SAAS_BELONG_SCHOOL') + country_code = ReadConfig(team_config).get_value(sections='saas_org_info', options='COUNTRY_CODE') + data = { + } + if phone and password == "": + data["authCode"] = auth_code + data["loginType"] = 2 # 登录类型 + data["phone"] = str(phone) + data["countryCode"] = country_code + elif phone and password: + data["password"] = password + data["loginType"] = 1 # 登录类型 + data["phone"] = str(phone) + data["countryCode"] = country_code + elif email and password: + data["password"] = password + data["loginType"] = 3 # 登录类型 + data["email"] = email + elif phone == "" and password == "": + phone = ReadConfig(team_config).get_value(sections='saas_org_info', options='SAAS_NAME') + data["authCode"] = auth_code + data["loginType"] = 2 # 登录类型 + data["phone"] = str(phone) + data["countryCode"] = country_code + print(str(country_code),str(phone),str(auth_code)) + else: + print("登录方式错误...") + return False + + print("开始登录 saas 系统..., 打印登录信息: {}".format(phone)) + response = requests.post(url=url, json=data, verify=False, headers={'huohua-podenv': self.jira_id, "Content-Type": "application/json;charset=UTF-8"}) + if response: + pass + else: + print("登录 SaaS 系统失败...{}".format(response)) + return False + print(response.text) + + token = response.json()['data']['token'] + status = response.status_code + if status: + if status == 200 and token: + print("登录 SaaS 系统成功..., Token: {}".format(token[0])) + print("写入到config配置文件...") + return token + else: + print("登录 SaaS 系统失败...{}".format(response)) + return False + else: + print("登录 SaaS 系统失败...{}".format(response)) + return False + +class SparkSaasTeacherLogin(InitConfig): + def __init__(self, host=None, curt_user=None, current_evn=None): + # super().__init__(run_user_name=curt_user, current_evn=current_evn) + InitConfig.__init__(self, run_user_name=curt_user, current_evn=current_evn) + self.team_config = os.path.abspath(os.path.join(ROOT_PATH, 'BIZ/library/Config/team_config.ini')) + self.jira_id = ReadConfig(env_choose_path).get_value(sections='run_jira_id', options='huohua-podenv') + + def rsa_encrypt(self, text): + """ + Args: + text: + Returns: + """ + public_key = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDGsNh8Pu1zY9SrblsJDuITHQ+ObAyjGbQARgh3TQtaz5vYQ+avTZJJsJLxiZ5hpf1/5OCb3QZRzbWebrk9cgU892GGc8MDBtH7l/I1GrZq2mY9LO4wWjA0gC7qK0o3yqAwwNyh3uXkSsN6KlpXnac1G1o+Sjlr1P1IXoB4I5MlAQIDAQAB" + public_key_str = "-----BEGIN PUBLIC KEY-----\n{}\n-----END PUBLIC KEY-----".format(public_key) + + # 字符串指定编码(转为bytes) + text = text.encode('utf-8') + # 构建公钥对象 + cipher_public = PKCS1_v1_5.new(RSA.importKey(public_key_str)) + # 加密(bytes) + text_encrypted = cipher_public.encrypt(text) + # base64编码,并转为字符串 + text_encrypted_base64 = base64.b64encode(text_encrypted).decode() + return text_encrypted_base64 + + def spark_login_teacher(self, phone, auth_code='9999', auth_code_type=2, country_code="", pass_word="abc123", login_type="验证码"): + """ + Args: + phone: + auth_code: + auth_code_type: + country_code: + login_type: 验证码,密码 + pass_word: 密码 + Returns: token + """ + team_config = os.path.abspath(os.path.join(ROOT_PATH, 'BIZ/library/Config/team_config.ini')) + country_code = ReadConfig(team_config).get_value(sections='saas_org_info', options='TEACHER_COUNTRY_CODE') + data = { + "authCodeType": auth_code_type, + "countryCode": country_code + } + + phone_encrypt = SparkSaasTeacherLogin().rsa_encrypt(str(phone)) + data["phone"] = phone_encrypt + + if login_type == "验证码": + print("教师端验证码登录") + url = "https://sc-mce-teacher-api.qa.huohua.cn/api/session" + data["authCode"] = auth_code + print(data) + + else: + print("教师端密码登录") + url = "https://sc-mce-teacher-api.qa.huohua.cn/api/session/phone_pass" + pass_word_encrypt = SparkSaasTeacherLogin().rsa_encrypt(pass_word) + data["password"] = pass_word + + print("开始登录 教师端 系统..., 打印登录手机号码, {}".format(phone)) + # response = self.request.http_request(url, method='post', json=data) + response = requests.post(url=url, json=data, verify=False, headers={'huohua-podenv': self.jira_id, "Content-Type": "application/json;charset=UTF-8"}) + if response: + pass + else: + print("登录 教师端 系统失败...{}".format(response)) + return False + token = response.json()['data']['token'] + status = response.status_code + if status: + if status and token: + print("登录 教师端 系统成功..., Token: {}".format(token)) + print("写入到config配置文件...") + return token + else: + print("登录 教师端 系统失败...{}".format(response)) + return False + else: + print("登录 教师端 系统失败...{}".format(response)) + return False + +if __name__ == '__main__': + test = LoginSys() + id = test.ug_sso_login_by_starlight() + print(id) + headers = {"Content-Type": "application/json;charset=UTF-8"} + kwargs = {"lessonQueryModel":{},"pageModel":{"pageNum":1,"pageSize":10}} + url = "https://starlight.qa.huohua.cn/api/lesson/queryList" + resp = id.post(url=url, json=kwargs, headers=headers, allow_redirects=False, verify=False) + print(resp) diff --git a/base_framework/public_tools/huohua_dbs.py b/base_framework/public_tools/huohua_dbs.py new file mode 100644 index 0000000..e4be1a0 --- /dev/null +++ b/base_framework/public_tools/huohua_dbs.py @@ -0,0 +1,405 @@ +# encoding: utf-8 +# 维护人员:qiaoxinjiu +import re +import time +import requests +import json +import urllib3 +from base_framework.public_tools.read_config import get_current_env, ReadConfig + +urllib3.disable_warnings() + + +class HuoHuaDBS: + def __init__(self, user_name=None, pwd=None): + self.conf = ReadConfig() + # if not user_name: + # user_name = self.conf.get_value(sections="lrq", options="show_username") + # pwd = self.conf.get_value(sections="lrq", options="password") + self.req = requests.session() + self.user_name = user_name + self.user_pwd = pwd + self.headers = {} + self.session_id = None + self.authenticate_csrf_token = None + self.query_url = "https://dbs.qc.huohua.cn/query/" + self.check_url = "https://dbs.qc.huohua.cn/simplecheck/" + self.commit_url = "https://dbs.qc.huohua.cn/autoreview/" + self.execute_url = "https://dbs.qc.huohua.cn/execute/" + self.get_status_url = "https://dbs.qc.huohua.cn/sqlworkflow/detail_content/?workflow_id=" + self.env = get_current_env().lower() + if self.env == "sim": # sim环境读取一次db配置, 方便后续场景中直接使用,避免重复查询 + self.sim_db = {} # 最终格式: {"teach_teacher": "sim126", "teach_classes": "sim128"} + instance_list = self.conf.get_options(section="DB_LIST") + for instance in instance_list: + db_list = self.conf.get_value(sections="DB_LIST", options=instance) + dbs = db_list.split(',') + for db in dbs: + self.sim_db[db] = instance + + def __get_instance_name(self, sql_content): + if self.env.lower() == "qa" or "-" in self.env: # 如果是qa或独立环境,则默认查询qadb-slave + if sql_content.split(' ')[0].lower() == "select": + instance_name = "qadb-slave" # sql查询时,使用此名 + else: + instance_name = "qadb-master" # sql执行时,使用此名 + else: + # db_name = sql_content.split(".")[0].split(" ")[-1].replace("`", "").replace(" ", "") + db_names = self.__get_db_names_from_sql(sql_statements=sql_content) + instance_list = [] + no_config_list = [] + for db_name in db_names: + if db_name not in self.sim_db: + no_config_list.append(db_name) + continue + if self.sim_db[db_name] not in instance_list: + instance_list.append(self.sim_db[db_name]) + if len(no_config_list) > 0: + raise Exception("你本次查询的数据库:{} 未做sim配置,请添加....".format(no_config_list)) + if len(set(instance_list)) > 1: + raise Exception("你本次查询的数据库:{}\n位于不同的实例:{}\n请修改sql....".format(db_names,instance_list)) + instance_name = instance_list[0] + return instance_name + + @staticmethod + def __check_sql_content(sql_content): + """检查sql语句是否符合规范""" + sql_list = sql_content.split(" ") + if sql_list[0].lower() in ["insert", "update", "delete", "select"]: + if "." not in sql_content: + raise Exception("sql语句必现是db_name.table_name格式,请修正:{}".format(sql_content)) + + @staticmethod + def __get_db_names_from_sql(sql_statements): + """ + 提取sql语句中的数据库名称 + :param sql_statements: sql语句 + :return: 数据库名称列表,去重 + """ + sql_statements = sql_statements.replace("`", "") + pattern = re.compile(r'\b(?:FROM|INTO|UPDATE|JOIN)\s+([a-zA-Z0-9_]+)\.', re.IGNORECASE) + + databases = set() + # 移除注释和多余空格 + sql_clean = re.sub(r'--.*', '', sql_statements) # 移除行注释 + sql_clean = ' '.join(sql_clean.split()) # 合并多余空格 + # 查找所有匹配项 + matches = pattern.findall(sql_clean) + if matches: + databases.update(matches) + return list(databases) + + def dbs_login(self): + login_index = 'http://dbs.qc.huohua.cn/login/' + res_login = self.req.get(login_index) + self.authenticate_csrf_token = res_login.cookies.get('csrftoken') + authenticate_url = 'https://dbs.qc.huohua.cn/authenticate/' + params_json_str = {"username": self.user_name, "password": self.user_pwd} + self.headers.update({"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8", + "Cookie": "experimentation_subject_id=IjdmMmE5MmYxLTk4N2QtNDU1YS1hMGRjLTZhZWIwZWY3YWEyMiI%3D--fc2a7fdd42c874af494e99f3f5fdf0c372d3b4d2; __UUID=93b21b91-675f-4019-fe4f-4442ec9f6e65-x; __DEVICE_ID=93b21b91-675f-4019-fe4f-4442ec9f6e65-x; csrftoken={}".format( + self.authenticate_csrf_token)}) + self.headers.update({"X-CSRFToken": "{}".format(self.authenticate_csrf_token), "X-Requested-With": "XMLHttpRequest"}) + resp = self.req.post(authenticate_url, params_json_str, headers=self.headers) + # print(json.loads(resp.text).get("msg")) + self.session_id = resp.cookies.get('sessionid') + self.headers.update({ + "Cookie": "experimentation_subject_id=IjdmMmE5MmYxLTk4N2QtNDU1YS1hMGRjLTZhZWIwZWY3YWEyMiI%3D--fc2a7fdd42c874af494e99f3f5fdf0c372d3b4d2; __UUID=93b21b91-675f-4019-fe4f-4442ec9f6e65-x; __DEVICE_ID=93b21b91-675f-4019-fe4f-4442ec9f6e65-x; csrftoken={};sessionid={}".format( + self.authenticate_csrf_token, self.session_id)}) + # print(self.session_id) + + def dbs_query(self, instance_name, sql_content, limit_num="100"): + """ + dbs查询 + | 请求参数名 | 说明 | 类型 | 是否必填 | + | instance_name | 数据库实例: 例如sim-my-allschool | string | True | + | db_name | 数据库名称: hulk_class_help | string | True | + | schema_name | 数据库模式: 默认空不填 | string | True | + | tb_name | 表名 | string | True | + | sql_content | 查询sql | string | True | + | limit_num | 查询行数 | string | True | + """ + if not self.session_id: + self.dbs_login() + db_name = sql_content.split(".")[0].split(" ")[-1].replace("`", "").replace(" ", "") + data = {"instance_name": instance_name, "db_name": db_name, "schema_name": "", "tb_name": "", + "sql_content": sql_content, "limit_num": limit_num} + self.headers.update({"Referer": "https://dbs.qc.huohua.cn/sqlquery/"}) + resp = self.req.post(self.query_url, data, headers=self.headers) + result = json.loads(resp.text) + return result.get('data').get('rows') + + def dbs_query_as_json(self, instance_name, sql_content, limit_num="1000"): + """ + 通过DBS查询数据,并返回json格式 + Args: + instance_name: 数据库实例: 例如sim-my-allschool + sql_content: 查询语句 + limit_num: 查询行数 + Returns: + [{key:value}] + """ + if not self.session_id: + self.dbs_login() + self.headers.update({"Referer": "https://dbs.qc.huohua.cn/sqlquery/"}) + # db_name = sql_content.lower().split("from ")[1].split(".")[0].replace("`", "").replace(" ", "") + db_name = self.__get_db_names_from_sql(sql_statements=sql_content)[0] + data = {"instance_name": instance_name, "db_name": db_name, "schema_name": "", "tb_name": "", + "sql_content": sql_content, "limit_num": limit_num, "workflow_name": "ODS线上数据监控"} + resp = self.req.post(self.query_url, data, headers=self.headers) + result = json.loads(resp.text) + if result.get('status') != 0: + raise Exception(result) + else: + r_data = self.__make_format_data(key_list=result.get('data').get('column_list'), + column_list=result.get('data').get('rows')) + return r_data + + def dbs_query_as_json_auto_identify_env(self, sql_content, instance_name=None, limit_num="1000"): + """ + 通过DBS查询数据,并返回json格式【建议别链表查询,因为sim环境不支持,除非你的函数不在sim使用】 + Args: + sql_content: 查询语句 + instance_name: 数据库实例: env_choose中是qa或独立环境时,此参数失效,否则已输入为准,不传则默认为sim126 + limit_num: 查询行数 + Returns: + Note:可以根据你的查询表,直接指定sim环境的instance_name,因为qa环境时不使用此参数 + """ + self.__check_sql_content(sql_content) # sql格式检查 + if not instance_name: + instance_name = self.__get_instance_name(sql_content=sql_content) # 自动识别环境 + + if not self.session_id: + self.dbs_login() + self.headers.update({"Referer": "https://dbs.qc.huohua.cn/sqlquery/"}) + # db_name = sql_content.split(".")[0].split(" ")[-1].replace("`", "").replace(" ", "") + db_name = sql_content.lower().split("from ")[1].split(".")[0].replace("`", "").replace(" ", "") + data = {"instance_name": instance_name, "db_name": db_name, "schema_name": "", "tb_name": "", + "sql_content": sql_content, "limit_num": limit_num, "workflow_name": "Test_online_data_check"} + resp = self.req.post(self.query_url, data, headers=self.headers) + result = json.loads(resp.text) + if result.get('status') != 0: + raise Exception(result) + else: + r_data = self.__make_format_data(key_list=result.get('data').get('column_list'), + column_list=result.get('data').get('rows')) + return r_data + + def dbs_query_all_data_as_json(self, sql_content, instance_name=None, sort_key='id', sort_type='asc'): + """ + 通过DBS查询db中的所有数据(超过1000条也行),并返回json格式,别带limit关键字,带了就走普通查询 + Args: + sql_content: 查询语句 + instance_name: 数据库实例: env_choose中是qa或独立环境时,此参数失效,否则已输入为准,不传则默认为sim126 + sort_key: 排序字段,默认为id + sort_type: 排序类型,默认为asc-顺排,desc-倒排 + """ + # 如果查询语句带有limit限制,则直接走普通查询 + if " limit " in sql_content.lower(): + return self.dbs_query_as_json_auto_identify_env(sql_content=sql_content, instance_name=instance_name) + # 否则走全量查询 + if " join " in sql_content.lower() or " union " in sql_content.lower() or sql_content.lower().count("from") > 1: + raise Exception("链表查询不支持全量查询,请修改查询语句") + if "where" not in sql_content.lower(): + raise Exception("全量查询必须带有where条件,防止数量过多,请修改查询语句") + # 全量查询 + all_data = [] + off_set = 0 + str_list = sql_content.lower().split("where") + while True: + if sort_type.lower() == 'asc': + sql_str = ("{} where {}>{} and {}" + .format(str_list[0], sort_key, off_set, str_list[1])) + elif sort_type.lower() == 'desc': + sql_str = ("{} where {}<{} and {}" + .format(str_list[0], sort_key, off_set, str_list[1])) + db_data = self.dbs_query_as_json_auto_identify_env(instance_name='prod-my-teach_classes-slave01', + sql_content=sql_str) + all_data += db_data + if len(db_data) < 1000: + break + else: + off_set = db_data[-1][sort_key] + return all_data + + def dbs_query_as_list(self, instance_name, sql_content, limit_num="100"): + """ + 通过DBS查询数据,并返回list格式 + Args: + instance_name: 数据库实例: 例如sim-my-allschool + sql_content: 查询语句 + limit_num: 查询行数 + Returns: + [{key:value}] + """ + res = self.dbs_query_as_json(instance_name=instance_name, sql_content=sql_content, limit_num=limit_num) + out_data = [] + if len(res) > 0: + for item in res: + tmp_data = [] + for key in item: + if len(item) > 1: # 查询结课是多列,则返回二维数组 + tmp_data.append(item[key]) + else: # 查询结课是单列,则返回一维数组 + tmp_data = item[key] + out_data.append(tmp_data) + + return out_data + + @staticmethod + def __make_format_data(key_list, column_list): + """ + 组装json格式的数据类型 + Args: + key_list: 列名,如['a','b'] + column_list: 数据,如[[1,2],[3,4]] + Returns: + [{key:value}],如[['a':1,'b':2],['a':3,'b':4]] + """ + obj_data = [] + for column in column_list: + col_data = {} + for index in range(len(key_list)): + col_data[key_list[index]] = column[index] + obj_data.append(col_data) + return obj_data + + def dbs_execute(self, instance_name, sql_content, time_out=60): + """ + SIM环境执行insert、update、del数据库操作 + Args: + instance_name: 选择实例,例如sim126 + sql_content: sql语句 + time_out: 超时时间,默认60秒 + """ + self.__check_sql_content(sql_content) # sql格式检查 + sql_info = sql_content.split(".")[0].split(" ") + db_name = sql_info[-1].replace("`", "").replace(" ", "") + if not self.session_id: + self.dbs_login() + try: + # 检查SQL + req_params = {"sql_content": sql_content, "instance_name": instance_name, "db_name": db_name, + "sql_type": "online"} + resp_check = self.req.post(self.check_url, req_params, headers=self.headers) + if resp_check.status_code == 200: + result_check = json.loads(resp_check.text) + if result_check.get("data").get("CheckErrorCount") != 0: + raise Exception("sql检查未通过,请检查调整后,再提交,msg: {}".format(result_check)) + else: + raise Exception("dbs执行检查sql失败,错误状态码: {}, res:{}".format(resp_check.status_code, resp_check.text)) + + # 提交SQL + req_params = {"csrfmiddlewaretoken": self.authenticate_csrf_token, "sql_content": sql_content, + "instance_name": instance_name, "db_name": db_name, + "sql_type": "online", "sql-upload": "", "workflow_id": "", "workflow_name": "test", + "demand_url": "", + "group_name": "测试组", "run_date_start": "", "run_date_end": "", "workflow_auditors": 1} + resp_commit = self.req.post(self.commit_url, req_params, headers=self.headers) + if resp_commit.status_code == 200: + workflow_id = resp_commit.history[0].headers.get("Location").split("/")[2] + else: + raise Exception("dbs执行提交sql失败,错误状态码: {}, res:{}".format(resp_commit.status_code, resp_commit.text)) + + # 执行sql + req_params = {"csrfmiddlewaretoken": self.authenticate_csrf_token, "workflow_id": workflow_id, + "mode": "auto"} + resp_execute = self.req.post(self.execute_url, req_params, headers=self.headers) + if resp_execute.status_code == 200: + if not resp_execute.ok: + raise Exception("sql执行未通过,请检查调整后,再提交,msg: {}".format(resp_execute.reason)) + else: + raise Exception("dbs执行sql失败,错误状态码: {}, res:{}".format(resp_execute.status_code, resp_execute.text)) + + # 查询执行状态 + time_out = int(time_out) + while time_out: + time_out -= 1 + resp_get_status = self.req.get(self.get_status_url + str(workflow_id), headers=self.headers) + if resp_get_status.status_code == 200: + result_status = json.loads(resp_get_status.text) + if "Execute Successfully" in result_status.get("rows")[1].get("stagestatus"): + return True + else: + time.sleep(1) + else: + time.sleep(1) + raise Exception("超时未查询到结果,请确认sql是不是需要执行很久,若有必要请调整超时参数") + except Exception as e: + raise Exception(e) + + def dbs_query_by_db_name(self, instance_name, db_name, sql_content, limit_num="100"): + """ + dbs查询 + | 请求参数名 | 说明 | 类型 | 是否必填 | + | instance_name | 数据库实例: 例如sim-my-allschool | string | True | + | db_name | 数据库名称: hulk_class_help | string | True | + | schema_name | 数据库模式: 默认空不填 | string | True | + | tb_name | 表名 | string | True | + | sql_content | 查询sql | string | True | + | limit_num | 查询行数 | string | True | + """ + if not self.session_id: + self.dbs_login() + data = {"instance_name": instance_name, "db_name": db_name, "schema_name": "", "tb_name": "", + "sql_content": sql_content, "limit_num": limit_num} + self.headers.update({"Referer": "https://dbs.qc.huohua.cn/sqlquery/"}) + resp = self.req.post(self.query_url, data, headers=self.headers) + result = json.loads(resp.text) + return result.get('data').get('rows') + + def dbs_select(self, sql_content, r_type='json', instance_name=None): + """ + 功能:通过dbs查询并返回全部数据 + Arg: + sql: 查询语句 + r_type: 返回类型,json或list + instance_name: 数据库实例名,不传则根据env_choose文件中的环境配置来获取 + Note: + 1. 若想要返回一条数据,自行在sql语句中添加limit 1限制 + 2. 用于线上环境查询时,指定instance_name参数即可 + """ + self.__check_sql_content(sql_content) # sql格式检查 + if not instance_name: + instance_name = self.__get_instance_name(sql_content=sql_content) # 获取数据库实例名 + if r_type == 'json': + db_data = self.dbs_query_as_json(sql_content=sql_content, instance_name=instance_name) + elif r_type == 'list': + db_data = self.dbs_query_as_list(instance_name=instance_name, sql_content=sql_content) + else: + raise Exception("返回类型只能是json或list") + # if 'limit 1;' in sql_content: + # if len(db_data) == 0: + # return [] + # return db_data[0] + # else: + # return db_data + return db_data + + def dbs_execute_sql(self, sql_content, instance_name=None, time_out=60): + """ + 通过DBS执行insert、update、del操作,支持concat拼接sql + Args: + sql_content: 待执行语句 + instance_name: 数据库实例: env_choose中是qa或独立环境时,此参数失效,否则已输入为准,不传则默认为sim126 + time_out: 超时时间,默认60秒 + Returns: + Note:可以根据你的查询表,直接指定sim环境的instance_name,因为qa环境时不使用此参数 + """ + if not instance_name: + instance_name = self.__get_instance_name(sql_content=sql_content) # 自动识别环境 + if "concat" in sql_content.lower(): # 如果是拼接sql,则拆分后执行 + sql_list = self.dbs_query_as_json_auto_identify_env(sql_content=sql_content) + for sql in sql_list: + for key in sql: + self.dbs_execute_sql(sql_content=sql[key]) + else: + self.dbs_execute(instance_name=instance_name, sql_content=sql_content, time_out=time_out) + + + +if __name__ == '__main__': + hh_dbs = HuoHuaDBS() + # hh_dbs.dbs_query_as_json("") + sql = "SELECT ui.user_id,sp.id AS student_id,ui.user_name,ui.logo,ui.phone,ui.phone_code,ui.nick_name,ui.english_nickname,ui.share_code,ui.invite_code,ui.customer_id,ui.channel_id,ui.from_user_id,ui.email,ss.business_line FROM (SELECT up.id as user_id,up.user_name,up.logo,up.phone,up.phone_code,up.nick_name,up.english_nickname,up.share_code,up.invite_code,up.customer_id,up.channel_id,up.from_user_id,uc.contact_info as email FROM ucenter.user_profile up LEFT JOIN ucenter.user_contact uc ON up.id=uc.user_id WHERE up.phone_code='653666709542760459') ui LEFT JOIN peppa_channel.statistics_setting ss ON ui.channel_id = ss.channel_id LEFT JOIN ucenter.student_profile sp ON sp.user_id=ui.user_id;" + + print(hh_dbs.get_instance_name(sql_content=sql)) \ No newline at end of file diff --git a/base_framework/public_tools/log.py b/base_framework/public_tools/log.py new file mode 100644 index 0000000..90ab746 --- /dev/null +++ b/base_framework/public_tools/log.py @@ -0,0 +1,91 @@ +import logging +import time +from os import path +from base_framework.base_config.current_pth import * +from concurrent_log_handler import ConcurrentRotatingFileHandler +import platform + + +time_flag = time.time() +d = path.dirname(__file__) +parent_path = os.path.dirname(d) +parent_path = os.path.dirname(parent_path) +# obj_tool = Tools() + +if platform.system() == 'Darwin': + LOG_FILENAME = log_path + '/' + 'run.log' +else: +# Windows路径 + LOG_FILENAME = log_path + '\\' + 'run.log' +if not os.path.exists(log_path): + try: + os.mkdir(log_path) + except Exception as e: + print('日志文件夹创建失败:{}'.format(e)) + +# MAC 路径 +# LOG_FILENAME = log_path + '/' + 'run.log' +# LOG_FILENAME = (os.path.split(os.path.realpath(__file__)))[0] + '\\' + 'log\\run.log' + + +# Color escape string +COLOR_RED = '\033[1;31m' +COLOR_GREEN = '\033[1;32m' +COLOR_YELLOW = '\033[1;33m' +COLOR_BLUE = '\033[1;34m' +COLOR_PURPLE = '\033[1;35m' +COLOR_CYAN = '\033[1;36m' +COLOR_GRAY = '\033[1;37m' +COLOR_WHITE = '\033[1;38m' +COLOR_RESET = '\033[1;0m' + +# Define log color +LOG_COLORS = { + 'DEBUG': COLOR_BLUE + '%s' + COLOR_RESET, + 'INFO': COLOR_GREEN + '%s' + COLOR_RESET, + 'WARNING': COLOR_YELLOW + '%s' + COLOR_RESET, + 'ERROR': COLOR_RED + '%s' + COLOR_RESET, + 'CRITICAL': COLOR_RED + '%s' + COLOR_RESET, + 'EXCEPTION': COLOR_RED + '%s' + COLOR_RESET, +} + + +class log_colour(logging.Formatter): + + def __init__(self, fmt=None, datefmt=None): + logging.Formatter.__init__(self, fmt, datefmt) + + def format(self, record): + level_name = record.levelname + msg = logging.Formatter.format(self, record) + return LOG_COLORS.get(level_name, '%s') % msg + + +def get_logger(): + logger = logging.Logger('Automation') + logger.setLevel(0) + terminal = logging.StreamHandler() + terminal.setLevel(logging.DEBUG) + # log_file = logging.handlers.TimedRotatingFileHandler(filename=LOG_FILENAME, when="S", backupCount=3, + # encoding='utf-8', interval=5) + log_file = ConcurrentRotatingFileHandler( + LOG_FILENAME, maxBytes=5 * 1024 * 1024, backupCount=4, encoding="utf-8") + log_file.setLevel(logging.INFO) + + formatter_ter = log_colour( + '%(asctime)s [tid:%(thread)d] %(filename)s[line:%(lineno)d] %(levelname)s %(message)s') + formatter_log = logging.Formatter( + '%(asctime)s [tid:%(thread)d pid:%(process)d] %(filename)s[line:%(lineno)d] %(levelname)s %(message)s') + + terminal.setFormatter(formatter_ter) + log_file.setFormatter(formatter_log) + + logger.addHandler(terminal) + logger.addHandler(log_file) + + return logger + + +def set_log_file(logfilename): + global LOG_FILENAME + LOG_FILENAME = logfilename diff --git a/base_framework/public_tools/mail_help.py b/base_framework/public_tools/mail_help.py new file mode 100644 index 0000000..c5f0f36 --- /dev/null +++ b/base_framework/public_tools/mail_help.py @@ -0,0 +1,98 @@ +# -*- coding:utf-8 -*- + +""" +Author: qiaoxinjiu +Create Data: 2021/11/22 10:41 +""" +import time +from base_framework.public_tools.my_faker import MyFaker +from base_framework.public_tools.pymailtm.pymailtm import MailTm +from retrying import retry +from base_framework.public_tools import log +from base_framework.public_tools.runner import Runner + +magic_faker = MyFaker() + + +class MailHelper: + """ + 邮件相关操作 + """ + new_mt_mail_obj = None + + def __init__(self): + self.mail_account = MailTm() + self.obj_runner = Runner() + self.obj_log = log.get_logger() + + @retry(stop_max_attempt_number=5, wait_fixed=5000) + def get_ci_new_mail(self): + """ + | 功能说明: | 生成随机邮件地址 | + | 返回参数: | dic | 邮件地址 | + | 作者信息: | 作者 林于棚 | 修改时间 | + 举例说明: + | get_ci_new_mail() | + """ + faker_mail = magic_faker.gen_str(min_chars=8) + faker_num = magic_faker.create_num(start=1, end=10000) + return faker_mail.lower()+str(faker_num)+'@citest.com' + + @retry(stop_max_attempt_number=5, wait_fixed=5000) + def get_new_mail_mt(self): + """ + | 功能说明: | https://mail.tm/zh/ 生成随机邮件地址 | + | 返回参数: | dic | 邮件地址 | + | 作者信息: | 作者 林于棚 | 修改时间 | + 举例说明: + | get_new_email() | + """ + self.new_mt_mail_obj = self.mail_account.get_account(password='666666') + return self.new_mt_mail_obj.address + # obj_log.info('生成的邮件地址:{}'.format(self.new_mt_mail_obj.address)) + + # @retry(stop_max_attempt_number=5, wait_fixed=5000) + def get_mt_mail_message(self, address=None, password='666666'): + """ + | 功能说明: | 获取 https://mail.tm/zh/ 生成邮件地址的信息 | + | 输入参数: | mail_obj | self.new_mt_mail_obj | + | 返回参数: | dic | 邮件地址 | + | 作者信息: | 作者 林于棚 | 修改时间 | + 举例说明: + | get_mt_mail_message() | + """ + mail_message = [] + first_flag = 0 + self.get_new_mail_mt() + account = self.new_mt_mail_obj + if account.address != address and address: + account.address = address + account.password = password + account.jwt = MailTm._make_account_request("token", address, password) + account.auth_headers = { + "accept": "application/ld+json", + "Content-Type": "application/json", + "Authorization": "Bearer {}".format(account.jwt["token"]) + } + self.obj_log.info('生成的邮件地址:{}'.format(account.address)) + self.obj_log.info('收取邮件中.............') + for i in range(2): + first_len = len(mail_message) + mail_message = account.get_messages() + last_len = len(mail_message) + if first_len <= last_len != 0: + continue + elif last_len == 0 and first_flag < 1: + first_flag += 1 + self.obj_log.info('没有收到邮件,重试中..............') + time.sleep(4) + else: + self.obj_log.info('没有收任何到邮件!') + break + return mail_message + + +if __name__ == '__main__': + obj_mail = MailHelper() + print(obj_mail.get_ci_new_mail()) + # print(obj_mail.get_mt_mail_message()) \ No newline at end of file diff --git a/base_framework/public_tools/mg_keyword.py b/base_framework/public_tools/mg_keyword.py new file mode 100644 index 0000000..be06f8b --- /dev/null +++ b/base_framework/public_tools/mg_keyword.py @@ -0,0 +1,231 @@ +# -*- coding:utf-8 -*- + +""" +Author: qiaoxinjiu +Create Data: 2020/10/15 17:35 +""" +import time + +from base_framework.public_tools.log import get_logger +from base_framework.public_tools.my_faker import MyFaker +from base_framework.public_tools.runner import Runner +import re +obj_log = get_logger() + +obj_faker = MyFaker() +obj_runner = Runner() + + +class ManageKeyWord: + def __init__(self): + pass + + @staticmethod + def kw_get_my_permissions(app_type, employee_id, **kwargs): + """ + | 功能说明: | 获取权限 | + | 输入参数: | phone | 新增线索的手机号 | + | 返回参数: | XXX | + | 作者信息: | 作者 | 修改时间 | + | 存放位置: | | | + 举例说明: + | kw_get_my_permissions | | + """ + options_dict = dict(**kwargs) + change_user = options_dict.get('user', None) + api_url = obj_runner.manage_host + '/permission/getMyPermissions' + req_type = 'POST' + post_data = { + "appType": app_type, + "employeeId": employee_id} + resp = obj_runner.call_rest_api( + user=change_user, + API_URL=api_url, + req_type=req_type, + params=post_data) + return resp + + @staticmethod + def kw_get_sms_code(phone=None, **kwargs): + """ + | 功能说明: | 获取短信验证码 | + | 输入参数: | phone | 手机号码 | + | 返回参数: | XXX | + | 作者信息: | 作者 | 修改时间 | + | 存放位置: | | | + 举例说明: + | kw_get_sms_code | | + """ + options_dict = dict(**kwargs) + change_user = options_dict.get('user', None) + current_evn = options_dict.get('current_evn', None) + url = "https://smm.qa.huohua.cn/sms/log/page/all?WHERE.%5BEQ%5Dphone={}&WHERE.%5BEQ%5DtypeName=&WHERE.%5BEQ%5Dchannel=&WHERE.%5BEQ%5DgatewayType=&WHERE.%5BGT%5DcreateTime=&WHERE.%5BLT%5DcreateTime=&WHERE.%5BGT%5DsubmitTime=&WHERE.%5BLT%5DsubmitTime=&pageNum=1&pageSize=20&orderBy=id%20desc".format( + phone) + req_type = 'POST' + resp = obj_runner.call_rest_api( + user=change_user, + API_URL=url, + req_type=req_type, + current_evn=current_evn) + smm_code = resp['list'][0]['msg'] + reg = re.compile(r"(?<=验证码:)\d+") + match = reg.search(smm_code) + smm_code = match.group(0) + return smm_code + + @staticmethod + def kw_get_authcode_without_message(phone,type=2,**kwargs): + """ + | 功能说明: | 直接获取短信验证码,不会发送短信 | + | 输入参数: | phone | 手机号码 | + | | type | 获取类型(2、磐石) | + | 返回参数: | XXX | + | 作者信息: | 作者 | 修改时间 | + | 存放位置: | | | + 举例说明: + | kw_get_authcode_without_message | phone=XXX type=XXX | + """ + if "country_code" in kwargs.keys(): + if "-" not in phone and not str(phone).startswith("0") and int(kwargs.get("country_code")) != 86: + phone="{}-{}".format(kwargs.get("country_code"),phone) + url = "{}/user_profile/sendLoginAuthCodeWithoutMessage?phone={}&type={}".format(obj_runner.manage_host, phone, type) + obj_log.info("your input:{0}".format(phone)) + resp = obj_runner.call_rest_api(API_URL=url, req_type="POST") + try: + auth_code = resp['data'][-4::1] + obj_log.info(auth_code) + except: + raise Exception("未获取到验证码,获取接口返回:%s"%resp) + return auth_code + + @staticmethod + def kw_execute_xxl_job(job_id, para="", **kwargs): + """ + | 功能说明: | 执行xxljob定时任务 | + | 输入参数: | job_id | job的任务id | + | | para=none | job运行时的参数 | + | 返回参数: | 无 | + | 作者信息: | 林于棚 | 2020.12.14 | + | 函数位置: | Public/Common/mg_keyword.py | | + 举例说明: + | execute_xxl_job | 2109 | + """ + startTime_stamp = time.localtime() + # startTime = time.strftime("%Y-%m-%d %H:%M:%S", startTime_stamp) + endTime = time.strftime("%Y-%m-%d 23:59:59", startTime_stamp) + jobGroup = None + if "jobGroup" in kwargs: + jobGroup = kwargs.pop("jobGroup") + post_data = { + "id": job_id, + 'executorParam': para} + api_url = obj_runner.xxl_job_host + "/jobinfo/trigger" + # obj_runner.call_rest_api(API_URL=api_url, req_type="POST", data=post_data, user="ci009") + startTime = time.strftime("%Y-%m-%d %H:%M:%S", startTime_stamp) + time.sleep(1) + tragger_resp = obj_runner.call_rest_api(API_URL=api_url, req_type="POST", data=post_data) + if tragger_resp.get("code") != 200: + raise EnvironmentError("%s job 触发失败!" % job_id) + time.sleep(3) + if jobGroup: + data_query = {"jobGroup": jobGroup, "jobId": job_id, "logStatus": -1, + "filterTime": "%s - %s" % (startTime, endTime), "start": 0, "length": 10} + url = obj_runner.xxl_job_host + "/joblog/pageList" + i = 0 + while i < 300: + resp = obj_runner.call_rest_api(API_URL=url, params=data_query, req_type="POST") + if 'data' in resp and len(resp['data']) > 0: + if resp['data'][0].get("handleCode") == 200: + obj_log.info(resp['data'][0]) + obj_log.info("job:%s执行成功" % job_id) + break + time.sleep(5) + i += 1 + else: + raise EnvironmentError("%s job 执行失败!" % job_id) + + + @staticmethod + def kw_modify_apollo_configuration(): + """ + | 功能说明: | 修改apollo配置 | + | 输入参数: | phone | 手机号码 | + | 返回参数: | XXX | + | 作者信息: | 作者 | 修改时间 | + | 存放位置: | | | + 举例说明: + | execute_xxl_job | | + """ + post_data = { + "id": 28959, + "key": "sms.start.time", + "value": "9:50", + "comment": "", + "dataChangeCreatedBy": "apollo", + "tableViewOperType": "update"} + api_url = "http://apollo.qa.huohua.cn/apps/peppa-cc-manage/envs/QA/clusters/default/namespaces/application/item" + obj_runner.call_rest_api(API_URL=api_url, req_type="PUT", json=post_data) + + @staticmethod + def course_upload(**kwargs): + """ + | 功能说明: | 上传图片 | + | 输入参数: | phone | 手机号码 | + | 返回参数: | XXX | + | 作者信息: | 作者 | 修改时间 | + | 存放位置: | | | + 举例说明: + | execute_xxl_job | | + """ + options_dict = dict(**kwargs) + change_user = options_dict.get('user', None) + file = r"C:\Users\HuoHua\Pictures\桌面图片\download.jpg" + api_url = obj_runner.teach_host + "/peppa-teach-api/common/upload" + files = { + "type": (None, "lesson"), + "file": ("file", open(file, "rb"), "image/jpeg"), + } + # headers = { + # "Content-Type": "multipart/form-data" # 上传文件不要设置type,会报错 + # } + response = obj_runner.call_rest_api( + user=change_user, + req_type="POST", + API_URL=api_url, + files=files + ) + return response + + @staticmethod + def sku_classfiy_get(**kwargs): + """ + | 功能说明: | 获取SKU分类枚举值 | + | 输入参数: | 无 | 无 | + | 返回参数: | XXX | + | 作者信息: | 作者 | 修改时间 | + 举例说明: + | sku_classfiy_get | | + """ + options_dict = dict(**kwargs) + change_user = options_dict.get('user', None) + api_url = obj_runner.scm_host + '/smart/expressSku/skuClassifyEnum' + req_type = 'GET' + resp = obj_runner.call_rest_api( + user=change_user, + API_URL=api_url, + req_type=req_type) + return resp + + +if __name__ == '__main__': + url = "https://teach-opt-api.qa.huohua.cn/api/quality-manage/template/items/385/1/" + res = obj_runner.call_rest_api(url, "get") + + a = ManageKeyWord() + # [a.kw_add_case(phone) for phone in ['1878201' + + # str(random.randrange(1111, 9999, 2)) for _ in range(2)]] + # a.kw_get_my_permissions('crmnew', 586470) + # a.kw_get_sms_code('18782019436') + # a.course_upload(user='xl') + a.execute_xxl_job(2865, "[262]") + # a.sku_classfiy_get() diff --git a/base_framework/public_tools/mongohelper.py b/base_framework/public_tools/mongohelper.py new file mode 100644 index 0000000..e5a0cca --- /dev/null +++ b/base_framework/public_tools/mongohelper.py @@ -0,0 +1,175 @@ +# -*- coding:utf-8 -*- + +import pymongo +from base_framework.public_tools.db_dbutils_init import get_my_mongo_connection +from base_framework.public_tools import log +obj_log = log.get_logger() + + +class MongoDbHelper: + def __init__(self): + # """ + # 初始化mongodb、并且链接指定数据库 + # :param collection: + # :param collect_db_name: + # """ + # init = InitConfig() + # self.host = init.MONGO_HOST + # self.port = init.MONGO_PORT + # self.user = init.MONGO_USER + # self.pwd = init.MONGO_PASSWORD + # + # try: + # self.connect_ = pymongo.MongoClient(host=self.host, + # port=int(self.port), + # username=self.user, + # password=self.pwd, + # authSource="hulk_teach_marketing" + # ) + # except Exception as e: + # obj_log.error("mongdb连接失败:{}".format(e)) + self.conn = get_my_mongo_connection().mongo_connect() + + def mongo_find(self, db_name, collection_name, query={}, select_value=None): + db = self.conn[db_name] + data = db.get_collection(collection_name).find(query, select_value) + obj_log.info("mongo查询语句:db.getCollection({}).find({},{}).limit(501)".format(collection_name, query, select_value)) + return data + + def mongo_select_all(self, db_name, collection_name, query={}, select_value=None): + """ + | 功能说明: | 查询mongo数据 | + | 输入参数: | db_name | 数据库 | + | | collection_name | 表(集合) | + | | query={} | 查询条件 | + | | column={} | 展示列:默认会展示 "_id" | + | 返回参数: | 查询结果(tuple) | + | 作者信息: | xl | 2020/11/26 21:11 | + | 函数位置: | public_tool/mongohelper.py || + """ + data = self.mongo_find(db_name, collection_name, query, select_value) + d = [] + for i in data: + d.append(i) + return tuple(d) + + def mongo_insert_many(self, db_name, collection_name, insert_value): + """ + | 功能说明: | 插入mongo数据 | + | 输入参数: | db_name | 数据库 | + | | collection_name | 表(集合) | + | | insert_value | 插入值 | + | 返回参数: | TRUE/FALSE | + | 作者信息: | xl | 2020/11/26 21:11 | + | 函数位置: | public_tool/mongohelper.py || + """ + try: + db = self.conn[db_name] + db.get_collection(collection_name).insert_many(insert_value) + obj_log.info("mongo插入语句:db.getCollection({}).insertMany({})".format(collection_name, insert_value)) + except Exception as e: + obj_log.error("插入数据失败:{}".format(e)) + return False + return True + + def mongo_update_many(self, db_name, collection_name, query, update_value): + """ + | 功能说明: | 修改mongo数据 | + | 输入参数: | db_name | 数据库 | + | | collection_name | 表(集合) | + | | query | 修改条件 | + | | update_value | 修改值 | + | 返回参数: | TRUE/FALSE | + | 作者信息: | xl | 2020/11/26 21:11 | + | 函数位置: | public_tool/mongohelper.py || + """ + try: + db = self.conn[db_name] + result = db.get_collection(collection_name).update_many(query, update_value) + obj_log.info("mongo修改语句:db.getCollection({}).updateMany({})".format(collection_name, update_value)) + obj_log.info("修改数量:{}".format(result.modified_count)) + except Exception as e: + obj_log.error("修改失败:{}".format(e)) + return False + return True + + def mongo_delete_many(self, db_name, collection_name, delete_value): + """ + | 功能说明: | 删除mongo数据 | + | 输入参数: | db_name | 数据库 | + | | collection_name | 表(集合) | + | | delete_value | 删除值 | + | 返回参数: | TRUE/FALSE | + | 作者信息: | xl | 2020/11/26 21:11 | + | 函数位置: | public_tool/mongohelper.py || + """ + try: + db = self.conn[db_name] + result = db.get_collection(collection_name).delete_many(delete_value) + obj_log.info("mongo删除语句:db.getCollection({}).deleteMany({})".format(collection_name, delete_value)) + obj_log.info("删除数量:{}".format(result.deleted_count)) + except Exception as e: + obj_log.error("删除失败:{}".format(e)) + return False + return True + + def mongo_count(self, db_name, collection_name, filter, session=None, **kwargs): + """ + | 功能说明: | 查询mongo数据 | + | 输入参数: | db_name | 数据库 | + | | collection_name | 表(集合) | + | | filter | 查询条件 | + | 返回参数: | 统计结果 | + | 作者信息: | xl | 2020/11/26 21:11 | + | 函数位置: | public_tool/mongohelper.py || + """ + db = self.conn[db_name] + db_collection = db[collection_name] + db_count = db_collection.count_documents(filter, session, **kwargs) + return db_count + + def mongo_aggregate(self, db_name, collection_name, pipline, session=None, **kwargs): + """ + | 功能说明: | (aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果 | + | 输入参数: | db_name | 数据库 | + | | collection_name | 表(集合) | + | | pipline | a list of aggregation pipeline stages | + | 返回参数: | 返回聚合结果 | + | 作者信息: | xl | 2020/11/26 21:11 | + | 函数位置: | public_tool/mongohelper.py || + """ + db = self.conn[db_name] + db_collection = db[collection_name] + data = db_collection.aggregate(pipline, session, **kwargs) + s = [] + for i in data: + s.append(i) + return s + + +if __name__ == '__main__': + s = MongoDbHelper() + + # 查询 + select_value = {"name": {"$regex": "^测试"}, "status":0} + ss = s.mongo_count(db_name='hulk_teach_marketing', collection_name='activity', filter=select_value) + print(ss) + # print("{}\n".format(ss)) + # for i in ss: + # print(i) + + # 插入数据 + # dd = [{"code": "ACT2079479569078477", "created_time":1637897233410, "creator_id":586470, "creator_name":"xiaoliang02", "creator_type":1, "delete_flag":0, "deleted_time":0,"language":0,"name":"自动插入活动1-page-1001","region":"1","remark":"自动添加活动备注信息","status":1,"type":0,"user_type":0}, + # {"code": "ACT2079479569078476", "created_time":1637897233410, "creator_id":586470, "creator_name":"xiaoliang02", "creator_type":1, "delete_flag":0, "deleted_time":0,"language":0,"name":"自动插入活动2-page-1001","region":"1","remark":"自动添加活动备注信息","status":2,"type":0,"user_type":0}] + # s.mongo_insert_many(db_name='hulk_teach_marketing', collection_name='activity', insert_value=dd) + + # # 修改数据 + # q = {"code": "ACT210477219578032135"} + # y = {"$set": {"name": "切图信息-右下角B区"}} + # dd = s.mongo_update_many(db_name='hulk_teach_marketing', collection_name='activity', query=q, update_value=y) + + # # 删除数据 + # ddd = {"name": {"$regex": "^自动插入"}} + # s.mongo_delete_many(db_name='hulk_teach_marketing', collection_name='activity', delete_value=ddd) + # pipline = [{"$match": {"region": "5", "delete_flag": 0}}, {"$group": {"_id": "$region", "max_order": {"$max": "$order"}}}] + # p = s.mongo_aggregate('hulk_teach_marketing', 'activity', pipline) diff --git a/base_framework/public_tools/my_faker.py b/base_framework/public_tools/my_faker.py new file mode 100644 index 0000000..7216fbe --- /dev/null +++ b/base_framework/public_tools/my_faker.py @@ -0,0 +1,105 @@ +from faker import Faker +import random + +''' +简体中文:zh_CN +繁体中文:zh_TW +美国英文:en_US +英国英文:en_GB +德文:de_DE +日文:ja_JP +韩文:ko_KR +法文:fr_FR''' + + +class MyFaker: + def __init__(self): + # 选择中文语言 + self.fake = Faker("zh_CN") + + def gen_name(self): + return self.fake.name() + + def gen_address(self): + return self.fake.address() + + def gen_phone_number(self, num=1): + return [self.fake.phone_number() for _ in range(num)] + + # 随机身份证 + def gen_identity_number(self): + return self.fake.ssn() + + # 信用卡号 + def credit_card_number(self): + return self.fake.credit_card_number() + + # MD5 + def gen_md5(self): + return self.fake.md5() + + # sku_name + def gen_word(self): + return self.fake.word() + + # password + def gen_password(self): + return self.fake.password() + + # 随机文本 + + def gen_txt(self, max_nb_chars=15): + return self.fake.text(max_nb_chars=max_nb_chars) + + def gen_str(self, min_chars=1, max_chars=10): + """ + 范围长度内随机字符串 + :param min_chars: + :param max_chars: + :return: + """ + return self.fake.pystr(min_chars=min_chars, max_chars=max_chars) + + def gen_letter(self, length=1): + """ + 生成范围长度内随机字母a-z + :param length: 生成的长度 + :return: str + """ + return_str = '' + for _ in range(length): + return_str = return_str + self.fake.random_letter() + return return_str.lower() + + def create_num(self, start, end): + """ + 随机生成一个数字 + :param:起始范围 + :return:int + """ + return random.randint(start, end) + + def gen_email(self): + """ + 生成虚拟测试邮箱 + """ + host_name_list=['roptaoti.com','prcf.site','curcuplas.me','hotmail.red','wpdork.com','gmailni.com'] + self.create_num(0,len(host_name_list)-1) + host_name=host_name_list[self.create_num(0,len(host_name_list)-1)] + # return self.fake.email() #这种方法可能存在真实邮箱 + return self.gen_str().lower() + '@' + host_name + def gen_word(self): + """ + 随机生成一个汉字 + :return: + """ + head = random.randint(0xb0, 0xf7) + body = random.randint(0xa1, 0xfe) + val = f'{head:x} {body:x}' + word = bytes.fromhex(val).decode('gbk') + return word + +if __name__ == '__main__': + my_faker = MyFaker() + # print(my_faker.gen_name()) + print(my_faker.gen_email()) diff --git a/base_framework/public_tools/pgsqlhelper.py b/base_framework/public_tools/pgsqlhelper.py new file mode 100644 index 0000000..001e093 --- /dev/null +++ b/base_framework/public_tools/pgsqlhelper.py @@ -0,0 +1,555 @@ +# -*- coding:utf-8 -*- +""" +Author: qiaoxinjiu +Create Data: 2020/11/6 17:30 +""" +import time +import re +from retrying import retry +from base_framework.public_tools import log +from base_framework.public_tools.db_dbutils_init import get_pg_connection # 假设有PostgreSQL连接池 +from base_framework.public_tools.read_config import get_current_config, get_current_env +from base_framework.public_tools.huohua_dbs import HuoHuaDBS + +obj_log = log.get_logger() + + +class PgSqlHelper: + """ + PostgreSQL数据库操作 + """ + + def __init__(self): + self.db = get_pg_connection() # PostgreSQL连接池 + self.current_business = get_current_config(section='run_evn_name', key='current_business') + self.current_evn = get_current_env() + self.qa_db_to_sim_instance_name = {} + self.sim_dbs = HuoHuaDBS() + + # 封装执行命令 + def execute(self, sql, param=None, auto_close=False, choose_db=None): + """ + | 功能说明: | 执行具体的sql语句 | + | 输入参数: | sql | 待执行的sql语句 | + | | param=None | sql语句中where后跟的参数,也可直接写在sql语句中 | + | | auto_close=True | 是否自动关闭数据库连接,默认:自动关闭 | + | 返回参数: | conn,cursor,count | 连接,游标,行数 | + """ + if self.current_evn.lower() == "sim": + raise Exception("SIM环境请直接使用huohua_dbs.py中的函数") + + try: + cursor, conn = self.db.getconn(choose_db=choose_db) # 从连接池获取连接 + except Exception as e: + # 打印详细的连接信息 + try: + from base_framework.public_tools.read_config import ReadConfig + from base_framework.base_config.current_pth import config_file_path + rc = ReadConfig(config_file_path) + db_host = rc.get_value(sections='PostgreSQL', options='db_test_host') + db_port = rc.get_value(sections='PostgreSQL', options='db_test_port') + db_name = rc.get_value(sections='PostgreSQL', options='db_test_dbname') + db_user = rc.get_value(sections='PostgreSQL', options='db_test_user') + db_password = rc.get_value(sections='PostgreSQL', options='db_test_password') + + error_info = """ +PostgreSQL连接失败! +连接配置信息: + 主机(Host): {} + 端口(Port): {} + 数据库名(Database): {} + 用户名(User): {} + 密码(Password): {} (已隐藏) + 选择数据库(ChooseDB): {} +错误详情: {} + """.format( + db_host, db_port, db_name, db_user, + '*' * len(db_password) if db_password else 'None', + choose_db or 'default', + str(e) + ) + print(error_info) + obj_log.error(error_info) + except Exception as config_error: + error_info = "PostgreSQL连接失败: {} (无法读取配置信息: {})".format(str(e), str(config_error)) + print(error_info) + obj_log.error(error_info) + raise ValueError("PostgreSQL连接失败: {}".format(str(e))) + + try: + # count : 为改变的数据条数 + if param: + # PostgreSQL使用 %s 作为占位符,与MySQL相同 + count = cursor.execute(sql, param) + else: + count = cursor.execute(sql) + conn.commit() + if auto_close: + self.close(cursor, conn) + except Exception as e: + obj_log.error("PostgreSQL执行SQL失败: {}, SQL: {}".format(str(e), sql)) + raise ValueError("数据库操作失败,SQL语句:{}, 错误: {}".format(sql, str(e))) + return cursor, conn, count + + # 释放连接 + @staticmethod + def close(cursor, conn): + cursor.close() + conn.close() + + # 查询所有 + def select_all(self, sql, param=None, choose_db=None, show_log=True): + """ + | 功能说明: | 查询数据库 | 并返回所有结果 | + """ + if self.current_evn.lower() == "sim": + return self.sim_dbs.dbs_select(sql_content=sql) + + if show_log: + if param is not None: + obj_log.info('SQL语句:{}|{}'.format(sql, param)) + else: + obj_log.info('SQL语句:{}'.format(sql)) + + cursor, conn = None, None + try: + cursor, conn, count = self.execute(sql, param, choose_db=choose_db) + res = cursor.fetchall() + if show_log: + obj_log.info('数据库查询结果:{}'.format(res)) + return res + except Exception as e: + if cursor and conn: + self.close(cursor, conn) + raise RuntimeError(e.args) + finally: + if cursor and conn and not show_log: + self.close(cursor, conn) + + def select_all_as_list(self, sql, choose_db=None): + """ + | 功能说明: | 查询数据库 | 功能同select_all,仅返回结果为list | + """ + if self.current_evn.lower() == "sim": + return self.sim_dbs.dbs_select(sql_content=sql, r_type='list') + + cursor, conn = None, None + try: + cursor, conn, count = self.execute(sql, choose_db=choose_db) + res = cursor.fetchall() + has_data = False + for item in res: + for key in item: + if item[key]: + has_data = True + break + if not has_data: + return [] + res_list = [] + for index in range(len(res)): + if len(res[index]) > 1: + res_row = [] + for key in res[index]: + res_row.append(res[index][key]) + res_list.append(res_row) + else: + for key in res[index]: + res_list.append(res[index][key]) + return res_list + except Exception as e: + if cursor and conn: + self.close(cursor, conn) + raise RuntimeError(e.args) + finally: + if cursor and conn: + self.close(cursor, conn) + + # 查询单条 + def select_one(self, sql, param=None, choose_db=None): + """ + | 功能说明: | 查询数据库,并返第一行 | + """ + if self.current_evn.lower() == "sim": + if 'limit' not in sql.lower(): + sql = sql.split(';')[0] + ' limit 1;' + sim_data = self.sim_dbs.dbs_select(sql_content=sql) + if sim_data: + return sim_data[0] + else: + return {} + + cursor, conn = None, None + try: + cursor, conn, count = self.execute(sql, param, choose_db=choose_db) + res = cursor.fetchone() + return res + except Exception as e: + if cursor and conn: + self.close(cursor, conn) + raise RuntimeError(e.args) + finally: + if cursor and conn: + self.close(cursor, conn) + + # 增加单条数据 + def insert_one(self, sql, param=None, choose_db=None, log_level='info'): + """ + | 功能说明: | insert一行数据 | + """ + if self.current_evn.lower() == "sim": + return self.sim_dbs.dbs_execute_sql(sql_content=sql) + + if log_level.lower() == 'info': + if param is not None: + obj_log.info('SQL语句:{}|{}'.format(sql, param)) + else: + obj_log.info('SQL语句:{}'.format(sql)) + + cursor, conn = None, None + try: + cursor, conn, count = self.execute(sql, param, choose_db=choose_db) + conn.commit() + if log_level.lower() == 'info': + obj_log.info('插入数据库条数:{}'.format(count)) + return count + except Exception as e: + if conn: + conn.rollback() + raise RuntimeError(e.args) + finally: + if cursor and conn: + self.close(cursor, conn) + + # 插入后返回插入ID(PostgreSQL方式) + def insert_one_extension(self, sql, param=None, choose_db=None): + """ + | 功能说明: | insert一行数据,并返回插入行的ID | + """ + return_dict = dict() + if param is not None: + obj_log.info('SQL语句:{}|{}'.format(sql, param)) + else: + obj_log.info('SQL语句:{}'.format(sql)) + + # PostgreSQL需要在INSERT语句后添加RETURNING子句来获取ID + # 如果SQL中已有RETURNING,则直接使用 + if 'RETURNING' not in sql.upper(): + # 尝试提取表名和主键列名(简化处理,实际需根据业务调整) + table_match = re.search(r'INSERT INTO\s+(\w+\.)?(\w+)', sql, re.IGNORECASE) + if table_match: + sql = sql.rstrip(';') + ' RETURNING id;' + + cursor, conn = None, None + try: + cursor, conn = self.db.getconn(choose_db=choose_db) + if param: + cursor.execute(sql, param) + else: + cursor.execute(sql) + + # 获取返回的ID + inserted_id = cursor.fetchone()[0] if 'RETURNING' in sql.upper() else None + count = cursor.rowcount + + conn.commit() + obj_log.info('插入数据库条数:{}'.format(count)) + return_dict['insert_count'] = count + return_dict['insert_id'] = inserted_id + return return_dict + except Exception as e: + if conn: + conn.rollback() + raise RuntimeError(e.args) + finally: + if cursor and conn: + self.close(cursor, conn) + + # 插入多条数据 + def insert_many(self, sql, param=None, choose_db=None): + """ + | 功能说明: | insert多行数据 | + """ + if param is not None: + obj_log.info('SQL语句:{}|{}'.format(sql, param)) + else: + obj_log.info('SQL语句:{}'.format(sql)) + + cursor, conn = None, None + try: + cursor, conn = self.db.getconn(choose_db=choose_db) + count = cursor.executemany(sql, eval(str(param))) + conn.commit() + obj_log.info('插入数据库条数:{}'.format(count)) + return count + except Exception as e: + if conn: + conn.rollback() + raise RuntimeError(e.args) + finally: + if cursor and conn: + self.close(cursor, conn) + + # 插入多条并返回ID + def insert_many_extension(self, sql, param=None, choose_db=None): + """ + | 功能说明: | insert多行数据,并返回ID列表 | + """ + return_dict = dict() + if param is not None: + obj_log.info('SQL语句:{}|{}'.format(sql, param)) + else: + obj_log.info('SQL语句:{}'.format(sql)) + + # PostgreSQL需要在INSERT语句后添加RETURNING子句 + if 'RETURNING' not in sql.upper(): + sql = sql.rstrip(';') + ' RETURNING id;' + + cursor, conn = None, None + try: + cursor, conn = self.db.getconn(choose_db=choose_db) + if param: + cursor.executemany(sql, eval(str(param))) + else: + cursor.execute(sql) + + # 获取所有返回的ID + inserted_ids = [row[0] for row in cursor.fetchall()] if 'RETURNING' in sql.upper() else [] + count = cursor.rowcount + + conn.commit() + obj_log.info('插入数据库条数:{}'.format(count)) + return_dict['insert_count'] = count + return_dict['insert_ids'] = inserted_ids + if inserted_ids: + return_dict['insert_id'] = inserted_ids[0] # 第一条数据的ID + return return_dict + except Exception as e: + if conn: + conn.rollback() + raise RuntimeError(e.args) + finally: + if cursor and conn: + self.close(cursor, conn) + + # 删除 + def delete(self, sql, param=None, choose_db=None): + """ + | 功能说明: | 删除数据库记录 | + """ + if self.current_evn.lower() == "sim": + return self.sim_dbs.dbs_execute_sql(sql_content=sql) + + if param is not None: + obj_log.info('SQL语句:{}|{}'.format(sql, param)) + else: + obj_log.info('SQL语句:{}'.format(sql)) + + cursor, conn = None, None + try: + cursor, conn, count = self.execute(sql, param, choose_db=choose_db) + obj_log.info('删除数据库条数:{}'.format(count)) + return count + except Exception as e: + if cursor and conn: + self.close(cursor, conn) + raise RuntimeError(e.args) + finally: + if cursor and conn: + self.close(cursor, conn) + + # 更新 + def update(self, sql, param=None, choose_db=None): + """ + | 功能说明: | 更新数据库记录 | + """ + if self.current_evn.lower() == "sim": + return self.sim_dbs.dbs_execute_sql(sql_content=sql) + + if param is not None: + obj_log.info('SQL语句:{}|{}'.format(sql, param)) + else: + obj_log.info('SQL语句:{}'.format(sql)) + + cursor, conn = None, None + try: + cursor, conn, count = self.execute(sql, param, choose_db=choose_db) + conn.commit() + obj_log.info('更新数据库条数:{}'.format(count)) + return count + except Exception as e: + if conn: + conn.rollback() + raise RuntimeError(e.args) + finally: + if cursor and conn: + self.close(cursor, conn) + + # 以下方法保持原样,仅需确保内部调用的方法正确 + def check_result_exist(self, *select_statement, retry_count=3): + start = 0 + flag = 0 + while start <= retry_count: + for sql in select_statement: + res = self.select_all(sql=sql) + if not res: + if start == retry_count: + obj_log.info('the result is not exist,but expect at least one') + raise RuntimeError(u'No results, when exec sql: %s' % sql) + else: + obj_log.info('the result is not exist,retry {}'.format(start + 1)) + start += 1 + time.sleep(1) + else: + obj_log.info('find [{0}] results when exec :{1}'.format(len(res), sql)) + flag += 1 + break + if flag > 0: + break + + def check_result_not_exist(self, *select_statement): + for sql in select_statement: + res = self.select_all(sql=sql) + if res: + obj_log.info('find [{0}] results, but expect 0'.format(len(res))) + raise RuntimeError(u'the result exist, when exec sql: %s' % sql) + else: + obj_log.info('the result is not exist, this step pass...') + + def row_count(self, selectStatement, param=None): + cursor, conn = None, None + try: + cursor, conn, count = self.execute(selectStatement, param) + return count + except Exception as e: + print("error_msg:", e.args) + return 0 + finally: + if cursor and conn: + self.close(cursor, conn) + + # 数据库断言方法(适配PostgreSQL) + def kw_check_if_exists_in_database(self, selectStatement, choose_db=None): + obj_log.info('Executing : Check If Exists In Database | %s ' % selectStatement) + if not self.select_one(selectStatement, choose_db=choose_db): + raise AssertionError("Expected to have have at least one row from '%s' " + "but got 0 rows." % selectStatement) + else: + return True + + def kw_check_if_not_exists_in_database(self, selectStatement, choose_db=None): + obj_log.info('Executing : Check If Not Exists In Database | %s ' % selectStatement) + queryResults = self.select_one(selectStatement, choose_db=choose_db) + if queryResults: + raise AssertionError("Expected to have have no rows from '%s' " + "but got some rows : %s." % (selectStatement, queryResults)) + else: + return True + + def kw_row_count_is_0(self, selectStatement): + obj_log.info('Executing : Row Count Is 0 | %s ' % selectStatement) + num_rows = self.row_count(selectStatement) + if num_rows > 0: + raise AssertionError("Expected zero rows to be returned from '%s' " + "but got rows back. Number of rows returned was %s" % (selectStatement, num_rows)) + else: + return True + + def kw_row_count_is_equal_to_x(self, selectStatement, numRows): + obj_log.info('Executing : Row Count Is Equal To X | %s | %s ' % (selectStatement, numRows)) + num_rows = self.row_count(selectStatement) + if num_rows != int(str(numRows)): + raise AssertionError("Expected same number of rows to be returned from '%s' " + "than the returned rows of %s" % (selectStatement, num_rows)) + else: + return True + + def kw_row_count_is_greater_than_x(self, selectStatement, numRows): + obj_log.info('Executing : Row Count Is Greater Than X | %s | %s ' % (selectStatement, numRows)) + num_rows = self.row_count(selectStatement) + if num_rows <= int(numRows): + raise AssertionError("Expected more rows to be returned from '%s' " + "than the returned rows of %s" % (selectStatement, num_rows)) + else: + return True + + def kw_row_count_is_less_than_x(self, selectStatement, numRows): + obj_log.info('Executing : Row Count Is Less Than X | %s | %s ' % (selectStatement, numRows)) + num_rows = self.row_count(selectStatement) + if num_rows >= int(numRows): + raise AssertionError("Expected less rows to be returned from '%s' " + "than the returned rows of %s" % (selectStatement, num_rows)) + else: + return True + + def kw_table_must_exist(self, tableName): + """ + PostgreSQL检查表是否存在 + """ + obj_log.info('Executing : Table Must Exist | %s ' % tableName) + # PostgreSQL检查表是否存在的查询 + selectStatement = """ + SELECT EXISTS ( + SELECT 1 + FROM information_schema.tables + WHERE table_schema = 'public' + AND table_name = %s + ); + """ + try: + result = self.select_one(selectStatement, (tableName,)) + if not result or not result[0]: # 结果为False或None + raise AssertionError("Table '%s' does not exist in the database" % tableName) + return True + except Exception as e: + raise AssertionError("Error checking table existence: %s" % str(e)) + + # 生成器方式查询 + def select_all_as_generator(self, sql, param=None, choose_db=None): + if param is not None: + obj_log.info('SQL语句:{}|{}'.format(sql, param)) + else: + obj_log.info('SQL语句:{}'.format(sql)) + + cursor, conn = None, None + try: + cursor, conn, counts = self.execute(sql, param, choose_db=choose_db) + for count in range(counts): + item = cursor.fetchone() + obj_log.info(item) + yield item + except Exception as e: + raise RuntimeError(e.args) + finally: + if cursor and conn: + self.close(cursor, conn) + + def get_qa_to_sim_dbs(self): + get_sql = "select qa_db,sim_instance from sparkatp.qa_db_mapping_sim_dbs where status=1 and is_delete=0" + res_info = self.sim_dbs.dbs_query_by_db_name("qadb-slave", "sparkatp", get_sql, limit_num=1000) + for item in res_info: + self.qa_db_to_sim_instance_name.update({item[0]: item[1]}) + return self.qa_db_to_sim_instance_name + + def get_instance_name(self, sql_str): + db_name = sql_str.split(".")[0].split(" ")[-1].replace("`", "").replace(" ", "") + return self.qa_db_to_sim_instance_name.get(db_name) + + +if __name__ == '__main__': + # 测试示例 + db = PgSqlHelper() + + # 测试查询 + sql = "SELECT * FROM account.account LIMIT 5;" + res = db.select_one(sql=sql) + print(res) + + # 测试插入(带RETURNING) + insert_sql = """ + INSERT INTO account.account (username, email, created_at) + VALUES (%s, %s, NOW()) + RETURNING id; + """ + insert_params = ('test_user', 'test@example.com') + insert_result = db.insert_one_extension(insert_sql, insert_params) + print(f"插入结果: {insert_result}") \ No newline at end of file diff --git a/base_framework/public_tools/pymailtm/__init__.py b/base_framework/public_tools/pymailtm/__init__.py new file mode 100644 index 0000000..c4e72ef --- /dev/null +++ b/base_framework/public_tools/pymailtm/__init__.py @@ -0,0 +1,3 @@ +from base_framework.public_tools.pymailtm.pymailtm import MailTm, Account, Message + +__version__ = '1.0' diff --git a/base_framework/public_tools/pymailtm/cli.py b/base_framework/public_tools/pymailtm/cli.py new file mode 100644 index 0000000..ad7dbe5 --- /dev/null +++ b/base_framework/public_tools/pymailtm/cli.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +from argparse import ArgumentParser +import signal +import sys +from pymailtm import MailTm + + +def init(): + def signal_handler(sig, frame) -> None: + print('\n\nClosing! Bye!') + sys.exit(0) + + signal.signal(signal.SIGINT, signal_handler) + + parser = ArgumentParser( + description="A python interface to mail.tm web api. The temp mail address " + "will be copied to the clipboard and the utility will then " + "wait for a message to arrive. When it does, it will be " + "opened in a browser. Exit the loop with ctrl+c.") + parser.add_argument('-n', '--new-account', action='store_true', + help="whether to force the creation of a new account") + parser.add_argument('-l', '--login', action='store_true', + help="print the credentials and open the login page, then exit") + args = parser.parse_args() + + if args.login: + MailTm().browser_login(new=args.new_account) + else: + MailTm().monitor_new_account(force_new=args.new_account) + + +if __name__ == "__main__": + init() diff --git a/base_framework/public_tools/pymailtm/pymailtm.py b/base_framework/public_tools/pymailtm/pymailtm.py new file mode 100644 index 0000000..0973563 --- /dev/null +++ b/base_framework/public_tools/pymailtm/pymailtm.py @@ -0,0 +1,226 @@ +import json +import os +import pyperclip +import random +import requests +import string +import webbrowser + +from random_username.generate import generate_username +from dataclasses import dataclass +from pathlib import Path +from tempfile import NamedTemporaryFile +from time import sleep +from typing import Dict + + +class Account: + """Representing a temprary mailbox.""" + + def __init__(self, id, address, password): + self.id_ = id + self.address = address + self.password = password + # Set the JWT + jwt = MailTm._make_account_request("token", + self.address, self.password) + self.auth_headers = { + "accept": "application/ld+json", + "Content-Type": "application/json", + "Authorization": "Bearer {}".format(jwt["token"]) + } + self.api_address = MailTm.api_address + + def get_messages(self, page=1): + """Download a list of messages currently in the account.""" + r = requests.get("{}/messages?page={}".format(self.api_address, page), + headers=self.auth_headers, verify=False) + messages = [] + for message_data in r.json()["hydra:member"]: + # recover full message + r = requests.get( + f"{self.api_address}/messages/{message_data['id']}", headers=self.auth_headers, verify=False) + text = r.json()["text"] + html = r.json()["html"] + # prepare the mssage object + messages.append(Message( + message_data["id"], + message_data["from"], + message_data["to"], + message_data["subject"], + message_data["intro"], + text, + html, + message_data)) + return messages + + def delete_account(self): + """Try to delete the account. Returns True if it succeeds.""" + r = requests.delete("{}/accounts/{}".format(self.api_address, + self.id_), headers=self.auth_headers, verify=False) + return r.status_code == 204 + + def monitor_account(self): + """Keep waiting for new messages and open them in the browser.""" + while True: + print("\nWaiting for new messages...") + start = len(self.get_messages()) + while len(self.get_messages()) == start: + sleep(1) + print("New message arrived!") + self.get_messages()[0].open_web() + + +@dataclass +class Message: + """Simple data class that holds a message information.""" + id_: str + from_: Dict + to: Dict + subject: str + intro: str + text: str + html: str + data: Dict + + def open_web(self): + """Open a temporary html file with the mail inside in the browser.""" + with NamedTemporaryFile(mode="w", delete=False, suffix=".html") as f: + + html = self.html[0].replace("\n", "
").replace("\r", "") + message = """ + + + from: {}
+ to: {}
+ subject: {}

+ {} + """.format(self.from_, self.to, self.subject, html) + + f.write(message) + f.flush() + file_name = f.name + + open_webbrowser("file://{}".format(file_name)) + # Wait a second before deleting the tempfile, so that the + # browser can load it safely + sleep(1) + # os.remove(file_name) + + +def open_webbrowser(link: str) -> None: + """Open a url in the browser ignoring error messages.""" + saverr = os.dup(2) + os.close(2) + os.open(os.devnull, os.O_RDWR) + try: + webbrowser.open(link) + finally: + os.dup2(saverr, 2) + + +class CouldNotGetAccountException(Exception): + """Raised if a POST on /accounts or /authorization_token return a failed status code.""" + + +class InvalidDbAccountException(Exception): + """Raised if an account could not be recovered from the db file.""" + + +class MailTm: + """A python wrapper for mail.tm web api, which is documented here: + https://api.mail.tm/""" + + api_address = "https://api.mail.tm" + db_file = os.path.join(Path.home(), ".pymailtm") + + def _get_domains_list(self): + r = requests.get("{}/domains".format(self.api_address), verify=False) + response = r.json() + domains = list(map(lambda x: x["domain"], response["hydra:member"])) + return domains + + def get_account(self, password=None): + """Create and return a new account.""" + username = (generate_username(1)[0]).lower() + domain = random.choice(self._get_domains_list()) + address = "{}@{}".format(username, domain) + if not password: + password = self._generate_password(6) + response = self._make_account_request("accounts", address, password) + account = Account(response["id"], response["address"], password) + self._save_account(account) + return account + + def _generate_password(self, length): + letters = string.ascii_letters + string.digits + return ''.join(random.choice(letters) for i in range(length)) + + @staticmethod + def _make_account_request(endpoint, address, password): + account = {"address": address, "password": password} + headers = { + "accept": "application/ld+json", + "Content-Type": "application/json" + } + r = requests.post("{}/{}".format(MailTm.api_address, endpoint), + data=json.dumps(account), headers=headers, verify=False) + if r.status_code not in [200, 201]: + raise CouldNotGetAccountException() + return r.json() + + def monitor_new_account(self, force_new=False): + """Create a new account and monitor it for new messages.""" + account = self._open_account(new=force_new) + account.monitor_account() + + def _save_account(self, account: Account): + """Save the account data for later use.""" + data = { + "id": account.id_, + "address": account.address, + "password": account.password + } + with open(self.db_file, "w+") as db: + json.dump(data, db) + + def _load_account(self): + """Return the last used account.""" + with open(self.db_file, "r") as db: + data = json.load(db) + # send a /me request to ensure the account is there + if "address" not in data or "password" not in data or "id" not in data: + # No valid db file was found, raise + raise InvalidDbAccountException() + else: + return Account(data["id"], data["address"], data["password"]) + + def _open_account(self, new=False): + """Recover a saved account data, check if it's still there and return that one; otherwise create a new one and + return it. + + :param new: bool - force the creation of a new account""" + def _new(): + account = self.get_account() + print("New account created and copied to clipboard: {}".format(account.address), flush=True) + return account + if new: + account = _new() + else: + try: + account = self._load_account() + print("Account recovered and copied to clipboard: {}".format(account.address), flush=True) + except Exception: + account = _new() + pyperclip.copy(account.address) + print("") + return account + + def browser_login(self, new=False): + """Print login credentials and open the login page in the browser.""" + account = self._open_account(new=new) + print("\nAccount credentials:") + print("\nEmail: {}".format(account.address)) + print("Password: {}\n".format(account.password)) + open_webbrowser("https://mail.tm/") + sleep(1) # Allow for the output of webbrowser to arrive diff --git a/base_framework/public_tools/read_config.py b/base_framework/public_tools/read_config.py new file mode 100644 index 0000000..ea83f84 --- /dev/null +++ b/base_framework/public_tools/read_config.py @@ -0,0 +1,196 @@ +# coding=utf-8 + +import configparser +import os +import codecs +from base_framework.base_config.current_pth import * +from base_framework.public_tools import log + +obj_log = log.get_logger() + + +class ReadConfig: + """ + 专门读取配置文件的,.ini文件格式 + """ + + def __init__(self, filename=config_file_path): + self.config_path = filename + fd = open(self.config_path, encoding='utf-8') + data = fd.read() + if data[:3] == codecs.BOM_UTF8: + data = data[3:] + files = codecs.open(self.config_path, "w") + files.write(data) + files.close() + fd.close() + + self.cf = configparser.SafeConfigParser(allow_no_value=True) + self.cf.read(self.config_path, encoding='utf-8') + + def get_value(self, sections, options): + """ + 获取config文件数据 + """ + return self.cf.get(sections, options) + + def read_cfg(self): + """ + 读取配置文件路径 + """ + return self.cf.read(filenames=self.config_path, encoding='utf-8') + + def get_sections(self): + """ + 读取配置文件中所有的section(可以理解为组名) + """ + return self.cf.sections() + + def get_options(self, section): + """ + 读取该section下所有的option(可以理解成读取该组下的所有key) + """ + return self.cf.options(section) + + def get_items(self, section): + """ + 读取该section下的所有值,并以键值对形式输出 + """ + return self.cf.items(section) + + def add_section(self, section): + """ + 添加一个section,参数为section的名称 + """ + self.cf.add_section(section) + with open(self.config_path, 'w') as fw: # 循环写入 + self.cf.write(fw) + + def set_section(self, section, option, value): + """ + 在section下面添加一条数据(key=value),需要调用write()将内容写入文件 + """ + self.cf.set(section, option, value) + with open(self.config_path, 'w') as fw: # 循环写入 + self.cf.write(fw) + + def get_all_cfg(self, section=None): + all_config = {} + for key in self.get_sections(): + all_values = {} + for value_key in self.get_options(key): + all_values[value_key] = self.get_value(key, value_key) + all_config[key] = all_values + if section: + if section in all_config: + return all_config[section] + else: + raise Exception("配置节点:{}在文件中不存在,请检查....".format(section)) + else: + return all_config + + +def get_current_config(file_path=env_choose_path, section=None, key=None): + """ + 功能:读取env_choose.ini中的配置并返回 + """ + rd = ReadConfig(file_path) + if key in rd.get_options(section=section): + return rd.get_value(sections=section, options=key) + else: + return "server not exist" + + +def get_zhyy_config(file_path=config_choose_path, section=None, key=None): + """ + 功能:读取env_choose.ini中的配置并返回 + """ + rd = ReadConfig(file_path) + if key in rd.get_options(section=section): + return rd.get_value(sections=section, options=key) + else: + return "server not exist" + + +def get_current_env(): + """ + 功能:读取env_choose.ini中的当前环境配置并返回 + """ + rd = ReadConfig(env_choose_path) + env = rd.get_value(sections="run_jira_id", options="huohua-podenv") + if not env: + env = rd.get_value(sections="run_evn_name", options="current_evn") + return env + + +def get_uat_config(website, page, key=None, section='page_element'): + """ + | 功能说明: | 从uat_config文件夹下的对应文件中读取key | + | 输入参数: | website | 站点名,public_business/uat/uat_config目录下的文件名 | + | | page | web页面名称,website文件夹下的文件名,不用.ini的文件后缀名 | + | | section | 配置文件中的节点名,默认读取页面元素 | + | | key | 配置文件中的key,如果不输入,将以当前构建环境为key,如qa,sim | + | 作者信息: | 吴勇刚 | 2022.06.22 | + """ + config_path = os.path.join(uat_config_path, website, "{}.ini".format(page)) + if not key: + key = get_current_config(section="run_evn_name", key="current_evn").lower() + rd = ReadConfig(config_path) + return rd.get_value(sections=section, options=key) + + +class InitConfig: + + def __init__(self, run_user_name=None, current_evn=None): + self.cfg_rtn = ReadConfig() + self.evn_rtn = ReadConfig(env_choose_path) + self.db_rtn = ReadConfig(db_config_path) + self.config_rtn = ReadConfig(config_choose_path) + self.all_cfg = self.cfg_rtn.get_all_cfg() + self.config_cfg = self.config_rtn.get_all_cfg() + self.evn_cfg = self.evn_rtn.get_all_cfg() + self.db_cfg = self.db_rtn.get_all_cfg() + try: + self.astwb_cfg = ReadConfig(astwb_config) + self.astwb_all_cfg = self.astwb_cfg.get_all_cfg() + # 合并配置文件 + self.all_cfg.update(self.astwb_all_cfg) + except Exception as e: + pass + if current_evn is None: + self.current_evn = self.evn_cfg['run_evn_name']['current_evn'] if self.evn_cfg['run_evn_name'][ + 'current_evn'] != '' else 'QA' + else: + self.current_evn = current_evn + if run_user_name: + self.current_user = run_user_name + else: + self.current_user = self.evn_cfg['run_user_name']['default_user'] + self.zhyy_host = self.get_current_env_info(self.config_cfg, self.current_evn, 'zhyy_login') + + def get_current_env_info(self, cfg_obj, current_env, key): + """ + config.ini中环境处理专用 + :param cfg_obj: self.cfg_rtn = ReadConfig() self.all_cfg = self.cfg_rtn.get_all_cfg() cfg_obj = self.all_cfg + :param current_env: QA SIM PRODUCT + :param key: options + :return: + """ + try: + value = cfg_obj[current_env][key] + except KeyError: + value = cfg_obj['QA'][key] + if current_env == 'SIM': + value = value.replace('.qa.', '.sim.') + return value + elif current_env == 'PRODUCT': + value = value.replace('.qa.', '.') + return value + return value + + +if __name__ == '__main__': + pass + # init_cfg = InitConfig(curt_evn='QA', curt_user='lrq') + print(ReadConfig().add_section(section='temp_teacher')) + print(ReadConfig().set_section(section='temp_teacher', option='show_username', value=11)) diff --git a/base_framework/public_tools/redis_api.py b/base_framework/public_tools/redis_api.py new file mode 100644 index 0000000..7c5af21 --- /dev/null +++ b/base_framework/public_tools/redis_api.py @@ -0,0 +1,247 @@ +# coding: utf-8 +from redis import ConnectionPool, StrictRedis +import time +from base_framework.public_tools.read_config import ReadConfig +from base_framework.base_config.current_pth import env_choose_path + + +class RedisApi: + """ + | 功能说明: | 操作redis | + | 作者信息: | 作者 qiaoxinjiu | + | 修改时间: | 2021-09-03 | + """ + + def __init__(self): + self.redis_con = None + self.pool = None + self.host = None + self.pwd = "" + self.port = 6379 + self.evn_cfg = ReadConfig(env_choose_path) + self.current_business = self.evn_cfg.get_value(sections="run_evn_name", options="current_business") + + def kw_conn_redis_service(self, redis_dbname, is_as=True): + """ + | 功能说明: | 与redis server 建立连接 | + | 输入参数: | + | | redis_dbname | redis数据库名,默认为5 | + | | redis_ip_addr | redis服务器地址 | + | | password | redis密码 | + | | redis_port | redis端口号,默认为6379 | + | | is_as | 是否为ALLSchool业务 | + | 返回参数: | 无 | + | 作者信息: | 作者 huaxuemin | 修改时间 2021-09-03 | + 说明:初始化 pool和redis_con + """ + if not self.host: # 如果没有指定redis域名 + if is_as: + self.host = 'redis-qa2.redis.rds.aliyuncs.com' + self.pwd = 'AcUVeRb8lN' + else: + if self.current_business == "hh": + self.host = 'redis.qa.huohua.cn' + self.pwd = 'AcUVeRb8lN' + elif self.current_business == "hhi": + self.host = 'redis.qa.visparklearning.com' + self.pwd = 'hxTjlWBYdK6UpAGF' + try: + self.pool = ConnectionPool(host=self.host, port=self.port, + db=redis_dbname, password=self.pwd, decode_responses=True) + self.redis_con = StrictRedis(connection_pool=self.pool) + except RuntimeError as e: + raise RuntimeError('Failed to connect redis service. error: %s' % e) + + def kw_get_redis_message(self, redis_dbname, redis_key, timeout=5, is_as=True): + """ + | 功能说明: | 获取redis_key对应的value | + | 输入参数: | redis_key | + | | is_as | 是否为ALLSchool业务 | + | 返回参数: | redis_key对应的value | + | 作者信息: | 作者 huaxuemin | 修改时间 2021-09-03 | + """ + self.kw_conn_redis_service(redis_dbname, is_as=is_as) + try: + while timeout > 0: + if not self.redis_con.exists(redis_key): + time.sleep(1) + timeout -= 1 + continue + else: + resp = self.redis_con.get(redis_key) + return resp + raise RuntimeError('not have redis_key, please check') + except RuntimeError as e: + raise RuntimeError('get redis value. error: %s' % e) + finally: + self.kw_disconnect_redis() + + def kw_verify_redis_have_key(self, redis_dbname, key, timeout=5, is_as=True): + """ + | 功能说明: | 验证缓存中存在KEY | + | 输入参数: | key | key值 | + | | timeout | 超时时间,默认为5s | + | | is_as | 是否为ALLSchool业务 | + | 返回参数: | True/False:存在返回true,否则失败 | + | 作者信息: | 作者 huaxuemin | 修改时间 2021-09-03 | + """ + self.kw_conn_redis_service(redis_dbname, is_as=is_as) + try: + while timeout > 0: + if self.redis_con.exists(key): + return True + else: + time.sleep(1) + timeout -= 1 + continue + raise RuntimeError('not have this key: %s' % key) + except RuntimeError as e: + raise RuntimeError('verify_redis_have_key. error: %s' % e) + finally: + self.kw_disconnect_redis() + + def kw_verify_redis_not_have_key(self, redis_dbname, key, timeout=5, is_as=True): + """ + | 功能说明: | 验证缓存中不存在KEY | + | 输入参数: | key | key值 | + | | timeout | 超时时间,默认为5s | + | | is_as | 是否为ALLSchool业务 | + | 返回参数: | True/False:不存在返回true,否则失败 | + | 作者信息: | 作者 huaxuemin | 修改时间 2021-09-03 | + """ + self.kw_conn_redis_service(redis_dbname, is_as=is_as) + try: + while timeout > 0: + if not self.redis_con.exists(key): + return True + else: + time.sleep(1) + timeout -= 1 + continue + raise RuntimeError('have this key: %s' % key) + except RuntimeError as e: + raise RuntimeError('verify_redis_not_have_key. error: %s' % e) + finally: + self.kw_disconnect_redis() + + def kw_disconnect_redis(self): + """ + | 功能说明: | 断开rebids服务 | + | 输入参数: | 无 | + | 返回参数: | 无 | + | 作者信息: | 作者 huaxuemin | 修改时间 2021-09-03 | + 备注: 断开连接池 + """ + try: + self.redis_con.close() + self.pool.disconnect() + except RuntimeError as e: + raise RuntimeError('disconnect redis service failed. error: %s' % e) + + def kw_del_key_by_key(self, redis_dbname, redis_key, is_as=True, is_check=True, host=None, pwd=None): + """ + | 功能说明: | 删掉redis中的 redis_key | + | 输入参数: | host | redis域名,可以不传,按环境走默认配置 | + | | pwd | redis密码,可以不传,按环境走默认配置 | + | | redis_dbname | redis数据库编号,必传 | + | | redis_key | 关键key,必传 | + | | is_as | 是否为ALLSchool业务 | + | 返回参数: | 无 | + | 作者信息: | 作者 huaxuemin | 修改时间 2021-09-03 | + """ + if host and pwd: # 当指定了域名,则按入参查询 + self.host = host + self.pwd = pwd + self.kw_conn_redis_service(redis_dbname, is_as=is_as) + try: + resp = self.redis_con.delete(redis_key) + if not resp and is_check: + raise RuntimeError('del key %s failed. error: %s' % (redis_key, resp)) + except Exception as e: + print(e) + self.kw_disconnect_redis() + + # def kw_del_key_by_key_pre(self, redis_dbname, key_pre, is_as=True): + # """ + # | 功能说明: | 根据前缀删除| + # | 输入参数: | key_pre | + # | | is_as | 是否为ALLSchool业务 | + # | 返回参数: | 无 | + # | 作者信息: | 作者 huaxuemin | 修改时间 2021-09-03 | + # """ + # self.kw_conn_redis_service(redis_dbname, is_as=is_as) + # try: + # key_pre = key_pre + "*" + # res = self.redis_con.scan(match=key_pre, count=9999999999) + # if len(res[1]) > 0: + # for key in res[1]: + # resp = self.redis_con.delete(key) + # if not resp: + # raise RuntimeError('del key %s failed. error: %s' % (key, resp)) + # except Exception as e: + # print(e) + # self.kw_disconnect_redis() + + def kw_get_key_by_key_pre(self, redis_dbname, key_pre, is_as=True): + """ + | 功能说明: | 根据前缀删除| + | 输入参数: | key_pre | + | | is_as | 是否为ALLSchool业务 | + | 返回参数: | 无 | + | 作者信息: | 作者 huaxuemin | 修改时间 2021-10-07 | + """ + self.kw_conn_redis_service(redis_dbname, is_as=is_as) + key_pre = key_pre + "*" + # for key in self.redis_con.scan_iter(match=key_pre): + # self.redis_con.delete(key) + for key in self.redis_con.keys(key_pre): + return key + self.kw_disconnect_redis() + return "" + + def kw_set_redis_by_db_key_value(self, db_num, key, value, is_as=False): + self.kw_conn_redis_service(db_num, is_as=is_as) + self.redis_con.set(key, value) + if not self.redis_con.exists(key): + raise ValueError("设置redis失败。") + + def kw_get_zset_message(self, redis_dbname, redis_key, timeout=5, is_as=True): + """ + | 功能说明: | 获取redis_key 有序集合对应的value | + | 输入参数: | redis_key | + | | is_as | 是否为ALLSchool业务 | + | 返回参数: | redis_key 有序集合对应的value | + | 作者信息: | 作者 huaxuemin | 修改时间 2022-07-27 | + """ + self.kw_conn_redis_service(redis_dbname, is_as=is_as) + try: + while timeout > 0: + if not self.redis_con.exists(redis_key): + time.sleep(1) + timeout -= 1 + continue + else: + resp = self.redis_con.zrange(redis_key, 0, -1) + return resp + print('not have redis_key: {}, please check'.format(redis_key)) + return [] + except RuntimeError as e: + print('get redis value. error: %s' % e) + return [] + finally: + self.kw_disconnect_redis() + + +if __name__ == '__main__': + test = RedisApi() + # res = test.kw_get_redis_message(9, "peppa:ticket:call:pcsm:13708231975", is_as=False) + res = test.kw_del_key_by_key(host='rediscourse.qa.huohua.cn', + pwd='Bkl6LvqfzFCzYPAh', + redis_dbname='9', + redis_key="public-holiday-teacher-v1:1_82908", + is_as=False) + print(res) + # test.kw_conn_redis_service(0) + # test.kw_del_key_by_key(9, "REVISIT_TASK:REVISITING_IDS", is_as=False) + # print(test.kw_get_key_by_key_pre(0, "HULK-ORG-API:RESET-TOKEN:huaxuemin@huohua.cn#")) + diff --git a/base_framework/public_tools/rocket_mq.py b/base_framework/public_tools/rocket_mq.py new file mode 100644 index 0000000..c867b51 --- /dev/null +++ b/base_framework/public_tools/rocket_mq.py @@ -0,0 +1,138 @@ +# coding=utf-8 +# 用于MQ的各类操作 +import time +import os + +from base_framework.public_business.common.UBRD.kw.promotion_keyword import obj_log +from base_framework.public_tools.read_config import ReadConfig, get_current_env, get_current_config +from base_framework.public_tools.runner import Runner +from base_framework.public_tools.custom_error import BusinessError +obj_runner = Runner() + + +class RocketMQ: + + def __init__(self, env=''): + self.need_login = True # 是否需要登录 + self.team = get_current_config(section="run_evn_name", key="current_team") + if not env: + env = get_current_env().lower() + if env.lower() == 'sim': + if self.team in ['XUEDAU']: + self.mq_host = "" + self.need_login = False + else: + self.mq_host = "https://mq-console.sim.huohua.cn" + else: + if self.team in ['XUEDAU']: + self.mq_host = "http://10.250.200.3:19876" + self.need_login = False + else: + self.mq_host = "https://mq-console.qa.huohua.cn" + + def mq_query_msg_info(self, topic, begin_time, end_time): + """查询mq消息""" + msg_id_list = self.__query_msg_id_list(topic=topic, begin_time=begin_time, end_time=end_time) + msg_info = [] + for msg_id in msg_id_list: + m_info = self.__query_msg_detail_by_id(topic=topic, msg_id=msg_id) + msg_info.append(m_info) + return msg_info + + def mq_query_msg_info_by_sub_str(self, topic, begin_time, end_time, sub_str=None): + """查询mq消息,并返回匹配的消息内容""" + msg_info = self.mq_query_msg_info(topic=topic, begin_time=begin_time, end_time=end_time) + if sub_str: + sub_msg = [] + for msg in msg_info: + if str(sub_str) in str(msg["msg_body"]): + sub_msg.append(msg) + return sub_msg + else: + return msg_info + + def __query_msg_id_list(self, topic, begin_time, end_time): + """查询mq的id列表""" + mq_url = "{}/message/queryMessageByTopic.query".format(self.mq_host) + post_data = {"topic": topic, + "begin": str(time.mktime(time.strptime(str(begin_time), '%Y-%m-%d %H:%M'))).split('.')[0] + '000', + "end": str(time.mktime(time.strptime(str(end_time), '%Y-%m-%d %H:%M'))).split('.')[0] + '000'} + if self.need_login: + resp = obj_runner.call_rest_api(user=None, API_URL=mq_url, req_type="GET", params=post_data) + else: + resp = obj_runner.call_rest_api(user=None, API_URL=mq_url, req_type="GET", token=False, params=post_data) + if resp['status'] == 0: + msg_id_list = [] + for item in resp['data']: + msg_id_list.append(item['msgId']) + return msg_id_list + else: + raise Exception("查询MQ消息列表失败:{}".format(resp)) + + def __query_msg_detail_by_id(self, topic, msg_id): + """查询mq消息明细""" + mq_url = "{}/message/viewMessage.query".format(self.mq_host) + post_data = {"topic": topic, "msgId": msg_id} + if self.need_login: + resp = obj_runner.call_rest_api(user=None, API_URL=mq_url, req_type="GET", params=post_data) + else: + resp = obj_runner.call_rest_api(user=None, API_URL=mq_url, req_type="GET", token=False, params=post_data) + if resp['status'] == 0: + msg_tag = resp['data']['messageView']['properties'].get('TAGS', None) + msg_key = resp['data']['messageView']['properties'].get('KEYS', None) + msg_env = resp['data']['messageView']['properties'].get('podenv', None) + msg_body = resp['data']['messageView'].get('messageBody', None) + return {"msg_id": msg_id, "msg_tag": msg_tag, "msg_key": msg_key, "msg_env": msg_env, "msg_body": msg_body} + else: + raise Exception("查询MQ消息明细失败:{}".format(resp)) + + def mq_send_msg(self, topic, message_body, tag=None, key=None, pro=None, **kwargs): + """mq消息发送""" + mq_url = "{}/topic/sendTopicMessage.do".format(self.mq_host) + post_data = {'topic': topic, + 'tag': tag, + 'key': key, + 'messageBody': str(message_body).replace("'", "\"") + } + if not pro: + # 添加启动文件中的独立环境编号 + cfg_path = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) + ini_path = cfg_path + '/base_framework/base_config/env_choose.ini' + jira_id = ReadConfig(ini_path).get_value(sections='run_jira_id', options='huohua-podenv') + if jira_id: + post_data['pro'] = jira_id + else: + post_data['pro'] = 'qa' + else: + post_data['pro'] = pro + + if self.team.lower() == "xuedau": + resp = obj_runner.call_rest_api(user=None, API_URL=mq_url, req_type="POST", json=post_data, **kwargs) + else: + resp = obj_runner.call_rest_api(user=None, API_URL=mq_url, req_type="POST", json=post_data) + + try: + obj_log.info("发送mq消息返回:{}".format(resp)) + if resp and 'errMsg' in resp and 'timeout' in str(resp['errMsg']): + time.sleep(1) # 如果接口返回超时,就再发送一次 + resp = obj_runner.call_rest_api(user=None, API_URL=mq_url, req_type="POST", json=post_data) + elif resp and 'message' in resp and '无效的token' in str(resp['message']): + time.sleep(1) # 如果token过期,就再发送一次 + resp = obj_runner.call_rest_api(user=None, API_URL=mq_url, req_type="POST", json=post_data) + elif resp and resp['status'] == 0 and resp['data']['sendStatus'] == 'SEND_OK': + return True + else: + raise Exception("发送mq消息失败:{}".format(resp)) + except Exception as e: + obj_log.info("发送mq消息体:{}".format(post_data)) + raise Exception("发送mq消息失败:{}|{}".format(e, resp)) + + +if __name__ == '__main__': + + mq = RocketMQ() + mb = {"platformId": 2, "eventType": "user.join", "roleType": 200, "roleId": 48711, "appId": "", "source": "SparkEnglish", "classroomCode": "", "classroomType": 1, "scheduleCode": "ECR666358307917090884", "joinTime": 1747189786903, "classSizeType": 1, "isFirst": "", "subChannelCodeList": "", "deviceId": "20200830-9CFC-E8BE-8D68-9CFCE8BE8D6C", "classroomVersion": "25.5.3-qa.2212381", "appVersion": "25.4.7-stable.2172220"} + rsp = mq.mq_send_msg(topic='CP_CLASSROOM_STATUS_SYNC', message_body=mb, tag='TAG_ROLE_JOIN_TIME', pro='QA', xuedau="") + print(rsp) + + diff --git a/base_framework/public_tools/runner.py b/base_framework/public_tools/runner.py new file mode 100644 index 0000000..5b4b6b7 --- /dev/null +++ b/base_framework/public_tools/runner.py @@ -0,0 +1,365 @@ +# -*- coding: utf-8 -*- +import requests +import json +import time +from urllib import parse +from base_framework.public_tools.get_token import LoginSys, SparkleLogin, AgentApiLogin, MarketApiLogin, token_dict, \ + AllSchool, \ + ParentLogin, student_token_cache, StudentLogin, SparkEduLogin, SparkSaasLogin, SparkSaasTeacherLogin +from base_framework.public_tools import log +from base_framework.public_tools.read_config import ReadConfig, get_current_env +from base_framework.base_config.current_pth import * +from base_framework.public_tools.read_config import get_current_config +from base_framework.public_tools.sqlhelper import MySqLHelper + +obj_log = log.get_logger() +evn_cfg = ReadConfig(env_choose_path) +base_cfg = ReadConfig(config_file_path) +all_school_api_host = ['api.qa.allschool.com', 'api.sim.allschool.com', 'api.allschool.com'] + + +class Runner(LoginSys): + def __init__(self): + super().__init__() + # self.env = get_current_config(section="run_evn_name", key="current_evn") + self.env = get_current_env() + self.is_need_sso = ["teach-api"] + self.team = get_current_config(section="run_evn_name", key="current_team") + self.db_con = MySqLHelper() + self.sql_data = [] # 存放接口响应时间 + self.week = time.strftime("%w", time.localtime()) + + def __new__(cls, *args, **kwargs): + + if not hasattr(cls, 'instance'): + cls.instance = super().__new__(cls) + + return cls.instance + + def __del__(self): + if self.sql_data: # 如果有数据未插入 + # sql_str = ("insert into sparkatp.interface_response_time(team, in_type, rp_time, in_url,in_id) " + # "values (%s,%s,%s,%s,%s);") + # self.db_con.insert_many(sql=sql_str, param=self.sql_data) + # self.sql_data.clear() + pass + + def __call_api(self, session, api_url, req_type, **kwargs): + try: + if req_type == "GET": + req = session.get(api_url, verify=False, **kwargs) + elif req_type == "POST": + req = session.post(api_url, verify=False, **kwargs) + elif req_type == "PUT": + req = session.put(api_url, verify=False, **kwargs) + elif req_type == "DELETE": + req = session.delete(api_url, verify=False, **kwargs) + elif req_type == "PATCH": + req = session.patch(api_url, verify=False, **kwargs) + else: + obj_log.info(f'req_type,输入错误!不支持请求方法{req_type}') + raise Exception(f'req_type,输入错误!不支持请求方法{req_type}') + except Exception as e: + obj_log.info('返回数据:{}'.format(e)) + return e + + if self.env.lower() != "sim": # 非sim环境才记录接口响应时间 + # 每周六,记录一次所有接口的响应时间 + if self.week in ['6']: + current_time = int(time.strftime("%H", time.localtime())) + if current_time >= 5: # 5点之前的404校验脚本不统计,因为都是非正常参数,详见:http://10.250.200.1:8080/jenkins/view/2-%E8%BE%85%E5%8A%A9%E7%A8%8B%E5%BA%8F/job/%E6%8E%A5%E5%8F%A3404%E6%A3%80%E6%9F%A5/ + run_time = int(req.elapsed.total_seconds() * 1000) + if run_time > 200: # 超过200ms的接口记录到数据库 + in_data = (self.team, req_type, run_time, api_url, 0) + self.sql_data.append(in_data) + if len(self.sql_data) >= 10: # 每10条数据插入一次, 最后一次不足100条时在__del__中插入 + sql_str = ( + "insert into sparkatp.interface_response_time(team, in_type, rp_time, in_url,in_id) " + "values (%s,%s,%s,%s,%s);") + self.db_con.insert_many(sql=sql_str, param=self.sql_data) + self.sql_data.clear() + return req + + def call_rest_api(self, API_URL, req_type, user=None, + token=None, current_evn=None, is_file=False, **kwargs): + ''' + 功能:所有接口访问入口 + ''' + if self.env.lower() == "sim": + API_URL = API_URL.replace(".qa.", ".sim.") + return self._call_zhyy_api(API_URL, req_type, user, + token, current_evn, is_file, **kwargs) + + def _call_zhyy_api(self, api_url, req_type, user=None, token=None, current_evn=None, is_file=False, **kwargs): + """ + 功能:模拟走ruoyi登录模式,带token访问接口 + :param API_URL: + :param req_type: + :param user: 使用的自定义用户登录 + :param token: 从SSO获取的token,此字段使用默认值None,不是的情况使用False + :param current_evn: QA or SIM + :param is_file: + :param kwargs: + :return: + """ + req_type = req_type.upper() + retry_num = 4 + cnt = 0 + if 'as_login_type' in kwargs.keys(): + kwargs.pop('as_login_type') + options_dict = dict(**kwargs) + result = parse.urlparse(url=api_url, allow_fragments=True) + host = result.hostname + if not user: + user = self.curt_user + obj_log.info('登录系统为{},用户名为手动输入:{}'.format(host, user)) + obj_log.info('请求地址:{}'.format(api_url)) + obj_log.info('请求数据:{}'.format(options_dict)) + header = {"Authorization": "Basic c3BhcmtsZS13ZWI6c3BhcmtsZS13ZWI=", "tenant-id": "1"} + session = requests.session() + session.headers.update(header) + while cnt < retry_num: + if token is None: + if not user: + user = evn_cfg.get_value(sections='run_user_name', options='default_user') + tenant_name = base_cfg.get_value(sections=user, options='tenant') + usr_name = base_cfg.get_value(sections=user, options='username') + usr_pwd = base_cfg.get_value(sections=user, options='password') + # 检查 kwargs 中是否包含登录信息(tenantName/username 和 password) + # 如果包含,使用传入的参数;否则使用配置文件中的登录信息 + json_data = kwargs.get('json', {}) + is_login_params = isinstance(json_data, dict) and 'password' in json_data and ( + 'tenantName' in json_data or 'username' in json_data) + + if is_login_params: + # 使用传入的登录参数 + obj_log.info('检测到 kwargs 中包含登录信息,使用传入的参数进行登录') + login_req = self.__call_api(session, api_url, req_type='POST', **kwargs) + else: + # 使用配置文件中的登录信息 + obj_log.info('使用配置文件中的登录信息进行登录') + request_body = {'json': {'tenantName': tenant_name, 'username': usr_name, 'password': usr_pwd, + 'rememberMe': 'true'}} + api_login_url = self.zhyy_host + login_req = self.__call_api(session, api_login_url, req_type='POST', **request_body) + try: + req_json = login_req.json() + except Exception: + # 如果 json() 失败,尝试将 text 转成 JSON + try: + if isinstance(login_req, Exception): + obj_log.warning("登录请求失败: {}".format(login_req)) + req_json = {} + else: + req_json = json.loads(login_req.text) + except Exception: + if isinstance(login_req, Exception): + obj_log.warning("登录请求失败: {}".format(login_req)) + else: + obj_log.warning("登录响应无法解析为JSON: {}".format(login_req.text)) + req_json = {} + # 判断登录是否成功:code为0或200表示成功,或者data中包含accessToken + login_code = req_json.get("code") + if login_code not in [0, 200] and not req_json.get("data", {}).get('accessToken'): + raise Exception("SSO登录失败: {}".format(req_json)) + user_token = req_json.get("data", {}).get('accessToken') + # 如果 api_url 本身就是登录接口,登录成功后直接返回登录响应 + if is_login_params: + obj_log.info('api_url 是登录接口,直接返回登录响应') + if not is_file: + return req_json + else: + return login_req.content + else: + user_token = token + if user_token: + session.headers.update({'ssotoken': user_token}) + session.headers.update({'sso-token': user_token}) + session.headers.update({'accesstoken': user_token}) + session.headers.update({'Accesstoken': user_token}) + session.headers.update({'access-token': user_token}) + session.headers.update({'Authorization': user_token}) + session.headers.update({'token': user_token}) + obj_log.info(f'请求头headers:{session.headers}') + req = self.__call_api(session, api_url, req_type, **kwargs) + if isinstance(req, Exception): + obj_log.error('请求失败:{}'.format(req)) + return req + if not is_file: + try: + rtn_temp = req.json() + except Exception as e: + # 如果 json() 失败,尝试将 text 转成 JSON + try: + rtn_temp = json.loads(req.text) + except Exception: + # 如果都失败,保持为 text 字符串 + rtn_temp = req.text + else: + rtn_temp = req.content + # SSO登录过期处理开始 + status_code = str(req.status_code) + obj_log.info('------状态码:{}, 返回信息:{}'.format(status_code, rtn_temp)) + try: + # 如果 rtn_temp 是字典,才能使用 .get() 方法 + if isinstance(rtn_temp, dict): + resp_code = str(rtn_temp.get('code', '200')) + else: + resp_code = '200' + except: + resp_code = '200' + if not is_file: + token_dict.clear() # 缓存session过期清理 + student_token_cache.clear() # 缓存session过期清理 + obj_log.warning("--缓存session过期,清理缓存!") + cnt += 1 + if cnt <= 1: + continue + if status_code == "401" or resp_code == '401': + token_dict.clear() # 缓存session过期清理 + student_token_cache.clear() # 缓存session过期清理 + obj_log.warning("缓存session过期,清理缓存!") + cnt += 1 + if cnt <= 1: + continue + if status_code == "200" and "" in str(rtn_temp): + token_dict.clear() # 缓存session过期清理 + student_token_cache.clear() # 缓存session过期清理 + obj_log.warning("--缓存session过期,清理缓存!") + cnt += 1 + if cnt <= 1: + continue + # SSO登录过期处理完成 + req.close() + if not is_file: + obj_log.info('返回数据:{}'.format(rtn_temp)) + else: + obj_log.info('返回数据:文件字节流') + return rtn_temp + return False + + def _call_sso_api(self, api_url, req_type, user=None, token=None, current_evn=None, is_file=False, **kwargs): + """ + 功能:模拟走sso登录模式,带token访问接口 + :param API_URL: + :param req_type: + :param user: 使用的自定义用户登录 + :param token: 从SSO获取的token,此字段使用默认值None,不是的情况使用False + :param current_evn: QA or SIM + :param is_file: + :param kwargs: + :return: + """ + req_type = req_type.upper() + retry_num = 4 + cnt = 0 + if 'as_login_type' in kwargs.keys(): + kwargs.pop('as_login_type') + options_dict = dict(**kwargs) + result = parse.urlparse(url=api_url, allow_fragments=True) + host = result.hostname + if not user: + user = self.curt_user + obj_log.info('登录系统为{},用户名为手动输入:{}'.format(host, user)) + obj_log.info('请求地址:{}'.format(api_url)) + obj_log.info('请求数据:{}'.format(options_dict)) + + header = {"Authorization": "Basic c3BhcmtsZS13ZWI6c3BhcmtsZS13ZWI=", "tenant-id": "1"} + session = requests.session() + session.headers.update(header) + while cnt < retry_num: + if token is None: + if not user: + user = evn_cfg.get_value(sections='run_user_name', options='default_user') + usr_name = base_cfg.get_value(sections=user, options='show_username') + usr_pwd = base_cfg.get_value(sections=user, options='password') + sso_url = "{}?username={}&password={}".format(self.sparkle_pc_token_url, usr_name, usr_pwd) + sso_rsp = session.post(sso_url).json() + if sso_rsp.get("code") != 200: + raise Exception("SSO登录失败: {}".format(sso_rsp)) + user_token = sso_rsp.get("data").get('accessToken') + else: + user_token = token + if user_token: + session.headers.update({'ssotoken': user_token}) + session.headers.update({'sso-token': user_token}) + session.headers.update({'accesstoken': user_token}) + session.headers.update({'Accesstoken': user_token}) + session.headers.update({'access-token': user_token}) + session.headers.update({'token': user_token}) + obj_log.info(f'请求头headers:{session.headers}') + + req = self.__call_api(session, api_url, req_type, **kwargs) + + if not is_file: + try: + rtn_temp = req.json() + except Exception as e: + # 如果 json() 失败,尝试将 text 转成 JSON + try: + rtn_temp = json.loads(req.text) + except Exception: + # 如果都失败,保持为 text 字符串 + rtn_temp = req.text + else: + rtn_temp = req.content + # SSO登录过期处理开始 + status_code = str(req.status_code) + obj_log.info('------状态码:{}, 返回信息:{}'.format(status_code, rtn_temp)) + try: + # 如果 rtn_temp 是字典,才能使用 .get() 方法 + if isinstance(rtn_temp, dict): + resp_code = str(rtn_temp.get('code', '200')) + else: + resp_code = '200' + except: + resp_code = '200' + if not is_file: + token_dict.clear() # 缓存session过期清理 + student_token_cache.clear() # 缓存session过期清理 + obj_log.warning("--缓存session过期,清理缓存!") + cnt += 1 + if cnt <= 1: + continue + + if status_code == "401" or resp_code == '401': + token_dict.clear() # 缓存session过期清理 + student_token_cache.clear() # 缓存session过期清理 + obj_log.warning("缓存session过期,清理缓存!") + cnt += 1 + if cnt <= 1: + continue + if status_code == "200" and "" in str(rtn_temp) and "https://sso.qa.huohua.cn" in str( + rtn_temp): + token_dict.clear() # 缓存session过期清理 + student_token_cache.clear() # 缓存session过期清理 + obj_log.warning("--缓存session过期,清理缓存!") + cnt += 1 + if cnt <= 1: + continue + # SSO登录过期处理完成 + req.close() + if not is_file: + obj_log.info('返回数据:{}'.format(rtn_temp)) + else: + obj_log.info('返回数据:文件字节流') + return rtn_temp + + return False + + +if __name__ == '__main__': + obj_runner = Runner() + # url = "https://api.qa.sparkedu.com/user_language/" + # url = "https://api.qa.sparkedu.com/third/student" + url = "https://api.qa.sparkedu.com/user/appoint/add" + phone = '13716640630' + # post_data = {"avatar":"https://stalegacy.huohua.cn/image/huohua/avatar/default/default_avatar1.png", + # "birthday":"2021-01-03 00:00:00", + # "id":21434807, + # "nickname":"hute222", + # "sex":0} + post_data = {"subjectType": 1} + # resp = obj_runner.call_rest_api(API_URL=url, phone=phone, req_type="POST", json=post_data) + resp = obj_runner.week + print(resp) diff --git a/base_framework/public_tools/selenium_api.py b/base_framework/public_tools/selenium_api.py new file mode 100644 index 0000000..cf17360 --- /dev/null +++ b/base_framework/public_tools/selenium_api.py @@ -0,0 +1,504 @@ +# -*- coding: UTF-8 -*- +import os +import time +import platform +from selenium import webdriver +from selenium.webdriver.support.wait import WebDriverWait +from selenium.webdriver.support import expected_conditions as ec +from selenium.webdriver.common.keys import Keys +from selenium.webdriver.common.by import By +from selenium.webdriver import ActionChains +from selenium.webdriver.support.select import Select +from base_framework.public_tools.read_config import get_current_config + +cull_path = os.path.dirname(os.path.abspath(__file__)) +plugs = os.path.abspath(os.path.join(cull_path, '../{}'.format('/platform_tools/plugins/headerenv.crx'))) +DEFAULT_TIMEOUT = 5 + + +class SeleniumWebUI: + def __init__(self): + self.driver = None + self.action = None # 用于鼠标类事件 + self.business = get_current_config(section="run_evn_name", key="current_business") + self.jira_id = get_current_config(section="run_jira_id", key="huohua-podenv") + self.feature = get_current_config(section="run_jira_id", key="huohua-feature") + # print(self.jira_id) + + def web_open_browser(self, url="", browser_type="chrome", enable_image=True, station='web'): + """ + | 功能说明: | 打开浏览器,默认chrome | + | 输入参数: | url | 网址,默认为空:仅打开浏览器 | + | | browser_type | 浏览器类型:chrome,firefox,edge | + | | enable_image | 浏览器是否加载图片 | + | | station | 浏览器模式:web:web模式,m:手机模式 | + | 作者信息: | 谢祥益 | 2022.06.01 | + """ + if browser_type == "chrome": + options = webdriver.ChromeOptions() + if platform.system() == 'Linux': + default_driver_path = r'/home/chrome_home/chromedriver' + options.add_argument("--headless") + options.add_argument("--no-sandbox") + options.add_argument('--disable-gpu') + options.add_argument('--disable-dev-shm-usage') + self.driver = webdriver.Chrome(executable_path=default_driver_path, options=options) + else: + # 将正确的chrome driver版本放在python根目录下 + options.add_extension(plugs) + if not enable_image: + options.add_argument('blink-settings=imagesEnabled=false') + print('当前为无图片模式') + if station == 'm': + options.add_experimental_option('mobileEmulation', {'deviceName': 'iPhone 12 Pro'}) + self.driver = webdriver.Chrome(options=options) + elif browser_type == "firefox": + self.driver = webdriver.Firefox() + elif browser_type == "edge": + self.driver = webdriver.Edge() + else: + raise Exception("传入的浏览器类型不正确,正确的浏览器类型(chrome, firefox, edge)") + if len(url) > 0: + self.web_open_url(url=url) + self.action = ActionChains(self.driver) + + def web_open_url(self, url): + """ + | 功能说明: | 打开链接并最大化浏览器 | + | 输入参数: | url | 访问地址 | + | 作者信息: | 谢祥益 | 2022.06.01 | + """ + if not self.driver: + self.web_open_browser() + if self.jira_id.lower() not in ('', 'qa'): + # 判断并添加独立环境 + self.web_set_independent_environment() + self.driver.maximize_window() + self.driver.get(url) + + def web_close_browser(self): + """ + | 功能说明: | 关闭浏览器,但注意并没有销毁driver对象 | + | 输入参数: | 无 | + | 作者信息: | 谢祥益 at 2022.06.01 | + 举例说明: + """ + self.driver.quit() + + def web_refresh_page(self): + """ + | 功能说明: | 刷新浏览器当前页面 | + | 输入参数: | | + | 作者信息: | 谢祥益 at 2022.06.01 | + """ + self.driver.refresh() + + def web_switch_handle(self, index): + """ + | 功能说明: | 切换浏览器页面标签,适用于新开页面,或多页面操作 | + | 输入参数: | index | 标签索引 | + | 作者信息: | 谢祥益 | 2022.06.01 | + """ + handles = self.driver.window_handles + self.driver.switch_to_window(handles[index]) + + def web_get_cookie(self): + """ + | 功能说明: | 返回浏览器当前application的cookie | + | 输入参数: | | + | 作者信息: | 谢祥益 at 2022.06.01 | + """ + return self.driver.get_cookies() + + def web_find_element(self, locator, timeout=DEFAULT_TIMEOUT, raise_exception=True): + """ + | 功能说明: | 传入元素定位器,定位到该元素,返回第一个元素 | + | 输入参数: | locator | 元素路径 | + | | timeout | 超时时间,默认10秒 | + | 作者信息: | 谢祥益 | 2022.06.01 | + """ + if not isinstance(locator, tuple): + locator = (By.XPATH, locator) + element = WebDriverWait(self.driver, timeout).until(ec.presence_of_element_located(locator), + message=['The element is not exist,please check....\n' + 'element xpath:{}'.format(locator) + if raise_exception else None]) + return element + + def web_find_elements(self, locator, timeout=DEFAULT_TIMEOUT, raise_exception=True): + """ + | 功能说明: | 传入元素定位器,定位到该元素,返回所有元素 | + | 输入参数: | locator | 元素路径 | + | | timeout | 超时时间,默认10秒 | + | | raise_exception | 当找不到元素时:True-阻断报错(默认),False-返回空list | + | 作者信息: | 谢祥益 | 2022.06.01 | + """ + if not isinstance(locator, tuple): + locator = (By.XPATH, locator) + element = [] + try: + element = WebDriverWait(self.driver, timeout).until(ec.presence_of_all_elements_located(locator)) + except: + if raise_exception: + raise Exception("The element is not exist,please check....\n element xpath:{}".format(locator)) + return element + + def web_click_element(self, locator, timeout=DEFAULT_TIMEOUT): + """ + | 功能说明: | 传入元素定位器,定位到该元素,普通方法点击 | + | 输入参数: | locator | 元素路径 | + | | timeout | 超时时间,默认10秒 | + | 作者信息: | 谢祥益 | 2022.06.01 | + """ + element = self.web_find_element(locator, timeout) + element.click() + + def web_click_elements(self, locator, timeout=DEFAULT_TIMEOUT): + """ + | 功能说明: | 点击所有元素 | + | 输入参数: | locator | 元素路径 | + | | timeout | 超时时间,默认10秒 | + | 作者信息: | 谢祥益 | 2022.06.01 | + """ + elements = self.web_find_elements(locator, timeout) + for element in elements: + element.click() + + def web_click_element_by_js(self, locator=None, element=None, timeout=DEFAULT_TIMEOUT): + """ + | 功能说明: | 传入元素定位器,定位到该元素,以JS的方式点击 | + | 输入参数: | locator | 元素路径,当没有element时输入 | + | | element | 元素对象,当没有locator时输入 | + | | timeout | 超时时间,默认10秒 | + | 作者信息: | 谢祥益 | 2022.06.01 | + """ + if not element and locator: + obj = self.web_find_element(locator, timeout) + self.driver.execute_script("arguments[0].click();", obj) + elif element and not locator: + self.driver.execute_script("arguments[0].click();", element) + else: + raise Exception('不支持的传参方式,locator和element必须且只能传一个') + + def web_move_element_to_top(self, locator, timeout=DEFAULT_TIMEOUT): + """ + | 功能说明: | 将指定元素滑动到当前页面顶部 | + | 输入参数: | locator | 元素路径 | + | | timeout | 超时时间,默认10秒 | + | 作者信息: | 谢祥益 | 2022.08.12 | + """ + element = self.web_find_element(locator, timeout) + self.driver.execute_script("arguments[0].scrollIntoView();", element) + + def web_move_element_to_bottom(self, locator, timeout=DEFAULT_TIMEOUT): + """ + | 功能说明: | 将指定元素滑动到当前页面底部 | + | 输入参数: | locator | 元素路径 | + | | timeout | 超时时间,默认10秒 | + | 作者信息: | 谢祥益 | 2022.08.12 | + """ + element = self.web_find_element(locator, timeout) + self.driver.execute_script("arguments[0].scrollIntoView(false);", element) + + def web_move_to_top(self): + """ + | 功能说明: | 滑动到当前页面顶部 | + | 作者信息: | 谢祥益 | 2022.08.12 | + """ + self.driver.execute_script("window.scrollTo(document.body.scrollHeight,0)") + + def web_move_to_bottom(self): + """ + | 功能说明: | 滑动到当前页面底部 | + | 作者信息: | 谢祥益 | 2022.08.12 | + """ + self.driver.execute_script("window.scrollTo(0,document.body.scrollHeight)") + + def web_send_keys(self, locator, text, timeout=DEFAULT_TIMEOUT): + """ + | 功能说明: | 传入元素定位器,定位到该元素,清空输入框,写入text | + | 输入参数: | locator | 元素路径 | + | | text | 输入内容 | + | | timeout | 超时时间,默认10秒 | + | 作者信息: | 谢祥益 | 2022.06.01 | + """ + element = self.web_find_element(locator, timeout) + element.send_keys(Keys.CONTROL, 'a') + element.send_keys(text) + + def web_get_element_text(self, locator, timeout=DEFAULT_TIMEOUT): + """ + | 功能说明: | 传入元素定位器,定位到该元素,返回该元素的文本值 | + | 输入参数: | locator | 元素路径 | + | | timeout | 超时时间,默认10秒 | + | 作者信息: | 谢祥益 | 2022.06.01 | + """ + element = self.web_find_element(locator, timeout) + return element.text + + def web_click_single_box(self, locator, timeout=DEFAULT_TIMEOUT): + """ + | 功能说明: | 传入单选框元素定位器,定位到该元素,依次点击单选框 | + | 输入参数: | locator | 元素路径 | + | | timeout | 超时时间,默认10秒 | + | 作者信息: | 谢祥益 | 2022.06.01 | + """ + elements = self.web_find_elements(locator, timeout) + for element in elements: + self.driver.execute_script("arguments[0].click();", element) + time.sleep(1.5) + + def web_select_drop_down_box(self, locator, index=0, timeout=DEFAULT_TIMEOUT): + """ + | 功能说明: | 传入下拉框定位器,定位到该元素,选择下拉框的第index个选项 | + | 输入参数: | locator | 元素路径 | + | | index | 第几个元素的下标 | + | | timeout | 超时时间,默认10秒 | + | 作者信息: | 谢祥益 | 2022.06.01 | + """ + select = Select(self.web_find_element(locator, timeout)) + select.select_by_index(index) + + def web_click_checkbox(self, locator, timeout=DEFAULT_TIMEOUT): + """ + | 功能说明: | 传入多选框元素定位器,定位到该元素,全选 | + | 输入参数: | locator | 元素路径 | + | | timeout | 超时时间,默认10秒 | + | 作者信息: | 谢祥益 | 2022.06.01 | + """ + checkbox = self.web_find_elements(locator, timeout) + for i in checkbox: + if not i.is_selected(): + i.click() + + def web_upload_file(self, locator, path, timeout=DEFAULT_TIMEOUT): + """ + | 功能说明: | 传入元素定位器,定位到该元素,传入文件路径,只适用于input标签 | + | 输入参数: | locator | 元素路径 | + | | path | 文件完整路径 | + | | timeout | 超时时间,默认10秒 | + | 作者信息: | 谢祥益 | 2022.06.01 | + """ + element = self.web_find_element(locator, timeout) + element.send_keys(path) + + def web_move_windows(self, x=0, y=0): + """ + | 功能说明: | 传滑动窗口 | + | 输入参数: | x | x轴距离 | + | | y | y轴距离 | + | 作者信息: | 谢祥益 | 2022.06.01 | + """ + self.driver.execute_script("window.scrollBy({},{})".format(x, y)) + + def web_go_to_previous_page(self): + """ + | 功能说明: | 返回上一页(浏览器工具栏向左箭头) | + | 输入参数: | 无 | + | 作者信息: | 谢祥益 at 2022.06.01 | + """ + self.driver.back() + + def web_go_to_next_page(self): + """ + | 功能说明: | 前进一页(浏览器工具栏向右箭头) | + | 输入参数: | 无 | + | 作者信息: | 谢祥益 at 2022.06.01 | + """ + self.driver.forward() + + def web_close_current_page(self): + """ + | 功能说明: | 关闭当前窗口页面 | + | 输入参数: | 无 | + | 作者信息: | 谢祥益 at 2022.06.01 | + """ + self.driver.close() + + def web_get_window_title(self): + """ + | 功能说明: | 获取当前浏览器标题 | + | 输入参数: | 无 | + | 作者信息: | 谢祥益 at 2022.06.01 | + """ + return self.driver.title + + def web_get_element_attribute_value(self, locator, attr: str, timeout=DEFAULT_TIMEOUT): + """ + | 功能说明: | 获取元素属性值 | + | 输入参数: | locator | 元素路径 | + | | attr | 属性名 | + | | timeout | 超时时间,默认10秒 | + | 作者信息: | 谢祥益 | 2022.06.01 | + """ + obj = self.web_find_element(locator, timeout=timeout) + attr_value = obj.get_attribute(attr) + return attr_value + + def web_modify_tag_attribution(self, locator, attr, value, timeout=DEFAULT_TIMEOUT): + """ + | 功能说明: | 修改页签属性 | + | 输入参数: | locator | 元素路径 | + | | attr | 修改属性名 | + | | value | 修改后的值 | + | | timeout | 超时时间,默认10秒 | + | 作者信息: | 谢祥益 | 2022.06.01 | + """ + obj = self.web_find_element(locator, timeout) + self.driver.execute_script("arguments[0].{attr}={value};".format(attr=attr, value=value), obj) + + def web_input_to_readonly_tag(self, locator, text, timeout=DEFAULT_TIMEOUT): + """ + | 功能说明: | 带只读属性的标签输入 | + | 输入参数: | locator | 元素路径 | + | | text | 输入内容 | + | | timeout | 超时时间,默认10秒 | + | 作者信息: | 谢祥益 | 2022.06.01 | + """ + obj = self.web_find_element(locator, timeout) + self.driver.execute_script("arguments[0].removeAttribute('readonly');", obj) + self.web_send_keys(locator=locator, text=text) + + def web_get_css_property_value(self, locator, css_property, timeout=DEFAULT_TIMEOUT): + """ + | 功能说明: | 获取css属性值 | + | 输入参数: | locator | 元素路径 | + | | text | 输入内容 | + | | timeout | 超时时间,默认10秒 | + | 作者信息: | 谢祥益 | 2022.07.08 | + """ + obj = self.web_find_element(locator, timeout) + value = obj.value_of_css_property(css_property) + return value + + def web_take_screenshot(self, save_path, picture_name): + """ + | 功能说明: | 截屏浏览器当前页面 | + | 输入参数: | save_path | 保存地址 | + | | picture_name | 图片名称 | + | 作者信息: | 谢祥益 | 2022.06.01 | + """ + picture_path = os.path.join(save_path, picture_name + '.png') + self.driver.get_screenshot_as_file(picture_path) + + def web_set_independent_environment(self): + """ + | 功能说明: | 设置独立环境 | + | 输入参数: | 无 | + | 作者信息: | 谢祥益 at 2022.06.01 | + """ + # 新增独立环境 + new_loc = ('xpath', '/html/body/div/div[3]/button/div/div/i') + # 独立环境名称 + rule_name_loc = ('id', 'rule-name') + # 规则类型 + rule_type_loc = ('xpath', '//*[@id="edit-page"]/div[2]/div[1]/div/div[2]/div[2]/div[2]/div[3]/div') + # 头名称 + rule_header_name_loc = ('id', 'rule-headerName') + # 头内容 + rule_header_value_loc = ('id', 'rule-headerValue') + # 保存 + create_loc = ('xpath', '//*[@id="edit-page"]/div[2]/div[2]/div[2]/div[2]/button/div/div') + + self.driver.get("chrome-extension://eningockdidmgiojffjmkdblpjocbhgh/options/options.html") + + if self.jira_id and self.jira_id.lower()!='qa': + rule_header_name = 'huohua-podenv' + rule_header_value = self.jira_id + self.web_click_element(new_loc) + self.web_send_keys(rule_name_loc, self.jira_id) + self.web_click_element(rule_type_loc) + self.web_send_keys(rule_header_name_loc, rule_header_name) + self.web_send_keys(rule_header_value_loc, rule_header_value) + self.web_click_element(create_loc) + rule_header_name = 'huohua-feature' + rule_header_value = self.feature + self.web_click_element(new_loc) + self.web_send_keys(rule_name_loc, "front") + self.web_click_element(rule_type_loc) + self.web_send_keys(rule_header_name_loc, rule_header_name) + self.web_send_keys(rule_header_value_loc, rule_header_value) + self.web_click_element(create_loc) + else: + print("无独立环境!") + + def web_mouse_move_to_element(self, locator): + """ + | 功能 | 移动鼠标到元素位置 | + | 入参 | locator | 元素xpath路径 | + | 说明 | 多次刷新页面 | 容易导致此方法失效,慎用... | + | | 必要时 | 可以尝试直接点击对应控件:web_click_element | + """ + self.action.reset_actions() + obj = self.web_find_element(locator=locator) + self.action.move_to_element(obj) + self.action.perform() + + def web_mouse_click(self): + """功能:模拟鼠标点击""" + self.action.click().perform() + # self.action.perform() + + def web_check_element_exist(self, locator, quantity=1): + """ + | 功能说明: | 检查页面元素是否存在:0-不存在,1-存在,>1-存在多少个,-1-仅判断存在不判断数量 | + | 输入参数: | locator | 元素路径 | + | | quantity | 对应locator的元素有多少个, 传入-1则仅检查存在,不判断数量 | + | 作者信息: | 吴勇刚 | 2022.08.01 | + """ + if int(quantity) == 0: # 当检查元素不存在时,设计设置为2秒 + time_out = 2 + else: + time_out = DEFAULT_TIMEOUT + elements = self.web_find_elements(locator=locator, raise_exception=False, timeout=time_out) + if quantity != -1: + if len(elements) != int(quantity): + raise Exception("the element [{}] expect exist {},but current is {}..." + .format(locator, quantity, len(elements))) + else: + if len(elements) == 0: + raise Exception("the element is not exist:{}...".format(locator)) + + def web_check_element_text(self, locator, exp_text): + """ + | 功能说明: | 检查源文本是否正确 | 转为字符串比较 | + | 输入参数: | locator | 元素路径 | + | | exp_text | 元素预期文本 | + | 作者信息: | 吴勇刚 | 2022.08.01 | + """ + act_text = self.web_get_element_text(locator=locator) + if act_text != str(exp_text): + raise Exception("The element [{}]'s text expect [{}],bug now it's [{}] " + .format(locator, exp_text, act_text)) + + def web_check_element_attribute(self, locator, attr_name, exp_value): + """ + | 功能说明: | 检查元素属性值是否正确 | + | 输入参数: | locator | 元素路径 | + | | attr_name | 元素属性名称 | + | | exp_value | 元素预期属性值 | + | 作者信息: | 吴勇刚 | 2022.08.01 | + """ + act_value = self.web_get_element_attribute_value(locator=locator, attr=attr_name) + if act_value != exp_value: + raise Exception("The element [{}] attribute [{}]'s value expect [{}],bug now it's [{}] " + .format(locator, attr_name, exp_value, act_value)) + + def web_get_current_url(self): + """ + | 功能说明: | 检查源文本是否正确 | 转为字符串比较 | + | 输入参数: | locator | 元素路径 | + | | exp_text | 元素预期文本 | + | 作者信息: | 吴勇刚 | 2022.08.01 | + """ + current_url = self.driver.current_url + return current_url + + +if __name__ == '__main__': + web_driver = SeleniumWebUI() + vispark_url = "https://sparkle.qa.huohua.cn/intl/schedule" + web_driver.web_open_url(url=vispark_url) + url = web_driver.web_get_current_url() + print(url) + web_driver.web_close_browser() \ No newline at end of file diff --git a/base_framework/public_tools/sqlhelper.py b/base_framework/public_tools/sqlhelper.py new file mode 100644 index 0000000..1b44a29 --- /dev/null +++ b/base_framework/public_tools/sqlhelper.py @@ -0,0 +1,727 @@ +# -*- coding:utf-8 -*- + +""" +Author: qiaoxinjiu +Create Data: 2020/11/6 17:30 +""" +import time +import re +from retrying import retry +from base_framework.public_tools import log +from base_framework.public_tools.db_dbutils_init import get_my_connection,get_pg_connection +from base_framework.public_tools.read_config import get_current_config, get_current_env +from base_framework.public_tools.huohua_dbs import HuoHuaDBS +obj_log = log.get_logger() + + +"""执行语句查询有结果返回结果没有返回0;增/删/改返回变更数据条数,没有返回0""" +# retry 参数说明 +""" +stop_max_attempt_number 指定重试的次数,默认是5 +stop_max_delay 指定重试超时时间,默认是100,单位是ms +wait_fixed 指定每次重试的时间间隔,默认是1000,单位是ms +wait_random_min=None 指定每次重试最小时间,默认为0 +wait_random_max=None, 指定每次重试最大时间,默认为1000 +wait_incrementing_start 指定每次重试递增的开始时间 +wait_incrementing_increment 指定每次重试时间的递增幅度 +wait_exponential_multiplier 指定每次重试时间的递增幅度,跟上面的参数算法差异 +wait_exponential_max 指定每次重试时间的递增幅度的最大值 +retry_on_exception 指定每次出现异常执行的函数 +retry_on_result 指定程序运行正常执行的函数 +wrap_exception 出现异常后返回的异常类型,True是返回原异常,False返回RetryError +stop_func 自定义停止重试的条件,传入参数是一个函数 +wait_func 自定义每次重试的时间间隔,传入参数是一个函数 +wait_jitter_max 指定每次重试时间抖动值 +stop 指定重试停止后,执行Retrying对象的成员函数 +wait 指定重试间隔,执行Retrying对象的成员函数 +""" + + +class MySqLHelper: + """ + mysql数据库操作 + """ + def __init__(self): + self.db = get_my_connection() # 从数据池中获取连接 + self.current_business = get_current_config(section='run_evn_name', key='current_business') + self.current_evn = get_current_env() + self.qa_db_to_sim_instance_name = {} + self.sim_dbs = HuoHuaDBS() + + # def __new__(cls, *args, **kwargs): + # if not hasattr(cls, 'inst'): # 单例 + # cls.inst = super(MySqLHelper, cls).__new__(cls, *args, **kwargs) + # return cls.inst + + + # 封装执行命令 + def execute(self, sql, param=None, auto_close=False, choose_db=None): + """ + | 功能说明: | 执行具体的sql语句 | + | 输入参数: | sql | 待执行的sql语句 | + | | param=None | sql语句中where后跟的参数,也可直接写在sql语句中 | + | | auto_close=True | 是否自动关闭数据库连接,默认:自动关闭 | + | 返回参数: | conn,cursor,count | 连接,游标,行数 | + | 作者信息: | 林于棚 | 2020/11/26 21:11 | + | 函数位置: | Public/Common/mysql_api.py || + """ + if self.current_evn.lower() == "sim": + raise Exception("SIM环境请直接使用huohua_dbs.py中的函数") + + # if 'sparkatp' in sql or 'push_service' in sql : # 自动化和信息化的数据都走huohua + # choose_db = 'hh.qa' + # if "account." in sql or "emp." in sql or "sso." in sql: + # choose_db = 'hh.qa' + + cursor, conn = self.db.getconn(choose_db=choose_db) # 从连接池获取连接 + try: + # count : 为改变的数据条数 + if param: + count = cursor.execute(sql, param) + else: + count = cursor.execute(sql) + conn.commit() + if auto_close: + self.close(cursor, conn) + except Exception as e: + obj_log.error(e) + raise ValueError("数据库操作失败,SQL语句:{}".format(sql)) + return cursor, conn, count + + # 释放连接 + @staticmethod + def close(cursor, conn): + """ + | 功能说明: | 关闭数据库连接 | + | 输入参数: | cursor | 游标 | + | | conn | 连接 | + | 返回参数: | 无 | | + | 作者信息: | 林于棚 | 2020/11/26 21:11 | + 函数位置:Public/Common/mysql_api.py + """ + cursor.close() + conn.close() + + # 查询所有 + # @retry(stop_max_attempt_number=5, wait_fixed=3000) + def select_all(self, sql, param=None, choose_db=None, show_log=True): + """ + | 功能说明: | 查询数据库 | 并返回所有结果 | + | 输入参数: | sql | 待执行的查询语句 | + | | param=None | 查询语句中where条件后的参数,也可直接写入sql语句中 | + | 返回参数: | 所有查询结果 | | + | 作者信息: | 林于棚 | 2020/11/26 21:11 | + 函数位置:Public/Common/mysql_api.py + """ + if self.current_evn.lower() == "sim": + return self.sim_dbs.dbs_select(sql_content=sql) + + if param is not None: + obj_log.info('SQL语句:{}|{}'.format(sql, param)) + else: + obj_log.info('SQL语句:{}'.format(sql)) + if show_log: + if param is not None: + obj_log.info('SQL语句:{}|{}'.format(sql, param)) + else: + obj_log.info('SQL语句:{}'.format(sql)) + + try: + cursor, conn, count = self.execute(sql, param, choose_db=choose_db) + res = cursor.fetchall() + if show_log: + obj_log.info('数据库查询结果:{}'.format(res)) + return res + except Exception as e: + self.close(cursor, conn) + raise RuntimeError(e.args) + + def select_all_as_list(self, sql, choose_db=None): + """ + | 功能说明: | 查询数据库 | 功能同select_all,仅返回结果为list | + | 输入参数: | sql | 待执行的查询语句 | + | 返回参数: | 所有查询结果,类型:list | | + | 作者信息: | 吴勇刚 | 2021/11/22 | + 函数位置:Public/Common/mysql_api.py + """ + if self.current_evn.lower() == "sim": + return self.sim_dbs.dbs_select(sql_content=sql, r_type='list') + + try: + cursor, conn, count = self.execute(sql, choose_db=choose_db) + res = cursor.fetchall() + has_data = False # 以下代码是防止返回空值列表添加的 + for item in res: + for key in item: + if item[key]: + has_data = True # 有一个值非空即可 + break + if not has_data: + return [] # 以上代码是防止返回空值列表添加的 + res_list = [] + for index in range(len(res)): + if len(res[index]) > 1: + res_row = [] + for key in res[index]: + res_row.append(res[index][key]) + res_list.append(res_row) + else: + for key in res[index]: + res_list.append(res[index][key]) + return res_list + except Exception as e: + self.close(cursor, conn) + raise RuntimeError(e.args) + + # 查询单条 + # @retry(stop_max_attempt_number=5, wait_fixed=3000) + def select_one(self, sql, param=None, choose_db=None): + """ + | 功能说明: | 查询数据库,并返第一行 | + | 输入参数: | sql | 待执行的查询语句 | + | | param=None | 查询语句中where条件后的参数,也可直接写入sql语句中 | + | 返回参数: | 返回第一条查询结果 | | + | 作者信息: | 林于棚 | 2020/11/26 21:11 | + 函数位置:Public/Common/mysql_api.py + """ + if self.current_evn.lower() == "sim": + if 'limit' not in sql.lower(): + sql = sql.split(';')[0] + ' limit 1;' + sim_data = self.sim_dbs.dbs_select(sql_content=sql) + if sim_data: + return sim_data[0] + else: + return {} + try: + cursor, conn, count = self.execute(sql, param, choose_db=choose_db) + res = cursor.fetchone() + return res + except Exception as e: + self.close(cursor, conn) + raise RuntimeError(e.args) + + # 增加 + # @retry(stop_max_attempt_number=5, wait_fixed=3000) + def insert_one(self, sql, param=None, choose_db=None, log_level='info'): + """ + | 功能说明: | insert一行数据 | + | 输入参数: | sql | 待执行的查询语句 | + | | param=None | sql语句中where条件后的参数,也可直接写入sql语句中 | + | | log_level='info' | 日志级别:info-全量日志,否则,仅输入报错日志 | + | 返回参数: | 无 | | + | 作者信息: | 林于棚 | 2020/11/26 21:11 | + | 函数位置: | Public/Common/mysql_api.py || + """ + if self.current_evn.lower() == "sim": + return self.sim_dbs.dbs_execute_sql(sql_content=sql) + + if log_level.lower() == 'info': + if param is not None: + obj_log.info('SQL语句:{}|{}'.format(sql, param)) + else: + obj_log.info('SQL语句:{}'.format(sql)) + try: + cursor, conn, count = self.execute(sql, param, choose_db=choose_db) + # _id = cursor.lastrowid() # 获取当前插入数据的主键id,该id应该为自动生成为好 + conn.commit() + self.close(cursor, conn) + if log_level.lower() == 'info': + obj_log.info('插入数据库条数:{}'.format(count)) + return count + # 防止表中没有id返回0 + # if _id == 0: + # return True + # return _id + except Exception as e: + conn.rollback() + self.close(cursor, conn) + raise RuntimeError(e.args) + + # 插入后返回插入ID + # @retry(stop_max_attempt_number=5, wait_fixed=3000) + def insert_many_extension(self, sql, param=None, choose_db=None): + """ + | 功能说明: | insert一行数据,并返回插入行的主键ID | + | 输入参数: | sql | 待执行的查询语句 | + | | param=None | sql语句中where条件后的参数,也可直接写入sql语句中 | + | 返回参数: | insert_count | 插入的条数 | + | last_id | 插入最近一条的ID | + | insert_id | 插入的第一条数据的主键ID(参数param的的第一条) | + | 作者信息: | 林于棚 | 2020/11/26 21:11 | + | 函数位置: | Public/Common/mysql_api.py || + """ + return_dict = dict() + if param is not None: + obj_log.info('SQL语句:{}|{}'.format(sql, param)) + else: + obj_log.info('SQL语句:{}'.format(sql)) + cursor, conn = self.db.getconn(choose_db=choose_db) + try: + count = cursor.executemany(sql, eval(str(param))) + # last_id = cursor.lastrowid # 获取最新插入数据的主键id + insert_id = cursor._result.insert_id # 获取本次插入数据的主键id + # insert_id_list = list(range(insert_id, insert_id+count)) # 获取最新插入数据的主键id_list + conn.commit() + self.close(cursor, conn) + obj_log.info('插入数据库条数:{}'.format(count)) + return_dict['insert_count'] = count + # return_dict['last_id'] = last_id + return_dict['insert_id'] = insert_id + return return_dict + except Exception as e: + conn.rollback() + self.close(cursor, conn) + raise RuntimeError(e.args) + + # 插入后返回插入ID + # @retry(stop_max_attempt_number=5, wait_fixed=3000) + def insert_one_extension(self, sql, param=None, choose_db=None): + """ + | 功能说明: | insert一行数据,并返回插入行的ID | + | 输入参数: | sql | 待执行的查询语句 | + | | param=None | sql语句中where条件后的参数,也可直接写入sql语句中 | + | 返回参数: | insert_count | 插入的条数 | + | last_id | 插入最近一条的ID | + | insert_id | 插入数据的ID | + | 作者信息: | 林于棚 | 2020/11/26 21:11 | + | 函数位置: | Public/Common/mysql_api.py || + """ + return_dict = dict() + if param is not None: + obj_log.info('SQL语句:{}|{}'.format(sql, param)) + else: + obj_log.info('SQL语句:{}'.format(sql)) + try: + cursor, conn, count = self.execute(sql, param, choose_db=choose_db) + # last_id = cursor.lastrowid # 获取最新插入数据的主键id + insert_id = cursor._result.insert_id # 获取本次插入数据的主键id + # insert_id_list = list(range(insert_id, insert_id+count)) # 获取最新插入数据的主键id_list + conn.commit() + self.close(cursor, conn) + obj_log.info('插入数据库条数:{}'.format(count)) + return_dict['insert_count'] = count + # return_dict['last_id'] = last_id + return_dict['insert_id'] = insert_id + return return_dict + except Exception as e: + conn.rollback() + self.close(cursor, conn) + raise RuntimeError(e.args) + + # 增加多行 + # @retry(stop_max_attempt_number=5, wait_fixed=3000) + def insert_many(self, sql, param=None, choose_db=None): + """ + | 功能说明: | insert多行数据 | + | 输入参数: | sql | 待执行的查询语句 | + | | param=None | sql语句中where条件后的参数,必须是元组或列表[(),()]或((),()) | + | 返回参数: | 无 | | + | 作者信息: | 林于棚 | 2020/11/26 21:11 | + | 函数位置: | Public/Common/mysql_api.py || + """ + if param is not None: + obj_log.info('SQL语句:{}|{}'.format(sql, param)) + else: + obj_log.info('SQL语句:{}'.format(sql)) + cursor, conn = self.db.getconn(choose_db=choose_db) + try: + count = cursor.executemany(sql, eval(str(param))) + conn.commit() + self.close(cursor, conn) + obj_log.info('插入数据库条数:{}'.format(count)) + return count + except Exception as e: + conn.rollback() + self.close(cursor, conn) + raise RuntimeError(e.args) + + # 删除 + # @retry(stop_max_attempt_number=5, wait_fixed=3000) + def delete(self, sql, param=None, choose_db=None): + """ + | 功能说明: | 删除数据库记录 | + | 输入参数: | sql | 待执行的查询语句 | + | | param=None | sql语句中where条件后的参数,也可直接写入sql语句中 | + | 返回参数: | 无 | | + | 作者信息: | 林于棚 | 2020/11/26 21:11 | + | 函数位置: | Public/Common/mysql_api.py || + """ + if self.current_evn.lower() == "sim": + return self.sim_dbs.dbs_execute_sql(sql_content=sql) + + if param is not None: + obj_log.info('SQL语句:{}|{}'.format(sql, param)) + else: + obj_log.info('SQL语句:{}'.format(sql)) + try: + cursor, conn, count = self.execute(sql, param, choose_db=choose_db) + self.close(cursor, conn) + obj_log.info('删除数据库条数:{}'.format(count)) + return count + except Exception as e: + conn.rollback() + self.close(cursor, conn) + raise RuntimeError(e.args) + + # 更新 + # @retry(stop_max_attempt_number=5, wait_fixed=3000) + def update(self, sql, param=None, choose_db=None): + """ + | 功能说明: | 更新数据库记录 | + | 输入参数: | sql | 待执行的查询语句 | + | | param=None | sql语句中where条件后的参数,也可直接写入sql语句中 | + | 返回参数: | 无 | | + | 作者信息: | 林于棚 | 2020/11/26 21:11 | + | 函数位置: | Public/Common/mysql_api.py || + """ + if self.current_evn.lower() == "sim": + return self.sim_dbs.dbs_execute_sql(sql_content=sql) + + if param is not None: + obj_log.info('SQL语句:{}|{}'.format(sql, param)) + else: + obj_log.info('SQL语句:{}'.format(sql)) + try: + cursor, conn, count = self.execute(sql, param, choose_db=choose_db) + conn.commit() + self.close(cursor, conn) + obj_log.info('更新数据库条数:{}'.format(count)) + return count + except Exception as e: + conn.rollback() + self.close(cursor, conn) + raise RuntimeError(e.args) + + def check_result_exist(self, *select_statement, retry_count=3): + """ + | 功能说明: | 验证select语句查询的结果存在 | + | 输入参数: | select_statement | select查询语句,可以多个 | + | | retry_count | 重试次数,默认3次 | + | 返回参数: | 无 | 结果存在则pass,否则failed | + | 作者信息: | 吴勇刚 | 2020.12.16 | + 函数位置:Public/Common/mysql_api.py + 举例说明: + | check_result_exist | [sql1,sql2] | + """ + # retry_count = 3 + start = 0 + flag = 0 + while start <= retry_count: + for sql in select_statement: + res = self.select_all(sql=sql) + if not res: + if start == retry_count: + obj_log.info('the result is not exist,but expect at least one') + raise RuntimeError(u'No results, when exec sql: %s' % sql) + else: + obj_log.info('the result is not exist,retry {}'.format(start+1)) + start += 1 + time.sleep(1) + else: + obj_log.info('find [{0}] results when exec :{1}'.format(len(res), sql)) + flag += 1 + break + if flag > 0: + break + + def check_result_not_exist(self, *select_statement): + """ + | 功能说明: | 验证select语句查询的结果不存在 | + | 输入参数: | select_statement | select查询语句列表 | + | 返回参数: | 无 | 结果不存在则pass,否则failed | + | 作者信息: | 吴勇刚 | 2020.12.16 | + 函数位置:Public/Common/mysql_api.py + 举例说明: + | check_result_not_exist | [sql1,sql2] | + """ + for sql in select_statement: + res = self.select_all(sql=sql) + if res: + obj_log.info('find [{0}] results, but expect 0'.format(len(res))) + raise RuntimeError(u'the result exist, when exec sql: %s' % sql) + else: + obj_log.info('the result is not exist, this step pass...') + + def row_count(self, selectStatement, param=None): + """ + Uses the input `selectStatement` to query the database and returns the number of rows from the query. + + For example, given we have a table `person` with the following data: + | id | first_name | last_name | + | 1 | Franz Allan | See | + | 2 | Jerry | Schneider | + + When you do the following: + | ${rowCount} | Row Count | SELECT * FROM person | + | Log | ${rowCount} | + + You will get the following: + 2 + + Also, you can do something like this: + | ${rowCount} | Row Count | SELECT * FROM person WHERE id = 2 | + | Log | ${rowCount} | + + And get the following + 1 + """ + try: + cursor, conn, count = self.execute(selectStatement, param) + self.close(cursor, conn) + return count + except Exception as e: + print("error_msg:", e.args) + self.close(cursor, conn) + + # 数据库断言 + def kw_check_if_exists_in_database(self, selectStatement, choose_db=None): + """ + Check if any row would be returned by given the input `selectStatement`. If there are no results, then this will + throw an AssertionError. Set optional input `sansTran` to True to run command without an explicit transaction + commit or rollback. + + For example, given we have a table `person` with the following data: + | id | first_name | last_name | + | 1 | Franz Allan | See | + + When you have the following assertions in your robot + | Check If Exists In Database | SELECT id FROM person WHERE first_name = 'Franz Allan' | + | Check If Exists In Database | SELECT id FROM person WHERE first_name = 'John' | + + Then you will get the following: + | Check If Exists In Database | SELECT id FROM person WHERE first_name = 'Franz Allan' | # PASS | + | Check If Exists In Database | SELECT id FROM person WHERE first_name = 'John' | # FAIL | + + """ + obj_log.info( + 'Executing : Check If Exists In Database | %s ' % + selectStatement) + if not self.select_one(selectStatement, choose_db=choose_db): + raise AssertionError("Expected to have have at least one row from '%s' " + "but got 0 rows." % selectStatement) + else: + return True + + def kw_check_if_not_exists_in_database(self, selectStatement, choose_db=None): + """ + This is the negation of `check_if_exists_in_database`. + + Check if no rows would be returned by given the input `selectStatement`. If there are any results, then this + will throw an AssertionError. Set optional input `sansTran` to True to run command without an explicit + transaction commit or rollback. + + For example, given we have a table `person` with the following data: + | id | first_name | last_name | + | 1 | Franz Allan | See | + + When you have the following assertions in your robot + | Check If Not Exists In Database | SELECT id FROM person WHERE first_name = 'John' | + | Check If Not Exists In Database | SELECT id FROM person WHERE first_name = 'Franz Allan' | + + Then you will get the following: + | Check If Not Exists In Database | SELECT id FROM person WHERE first_name = 'John' | # PASS | + | Check If Not Exists In Database | SELECT id FROM person WHERE first_name = 'Franz Allan' | # FAIL | + + """ + obj_log.info( + 'Executing : Check If Not Exists In Database | %s ' % + selectStatement) + queryResults = self.select_one(selectStatement, choose_db=choose_db) + if queryResults: + raise AssertionError("Expected to have have no rows from '%s' " + "but got some rows : %s." % (selectStatement, queryResults)) + else: + return True + + def kw_row_count_is_0(self, selectStatement): + """ + Check if any rows are returned from the submitted `selectStatement`. If there are, then this will throw an + AssertionError. Set optional input `sansTran` to True to run command without an explicit transaction commit or + rollback. + + For example, given we have a table `person` with the following data: + | id | first_name | last_name | + | 1 | Franz Allan | See | + + When you have the following assertions in your robot + | Row Count is 0 | SELECT id FROM person WHERE first_name = 'Franz Allan' | + | Row Count is 0 | SELECT id FROM person WHERE first_name = 'John' | + + Then you will get the following: + | Row Count is 0 | SELECT id FROM person WHERE first_name = 'Franz Allan' | # FAIL | + | Row Count is 0 | SELECT id FROM person WHERE first_name = 'John' | # PASS | + """ + obj_log.info('Executing : Row Count Is 0 | %s ' % selectStatement) + num_rows = self.row_count(selectStatement) + if num_rows > 0: + raise AssertionError("Expected zero rows to be returned from '%s' " + "but got rows back. Number of rows returned was %s" % (selectStatement, num_rows)) + else: + return True + + def kw_row_count_is_equal_to_x(self, selectStatement, numRows): + """ + Check if the number of rows returned from `selectStatement` is equal to the value submitted. If not, then this + will throw an AssertionError. Set optional input `sansTran` to True to run command without an explicit + transaction commit or rollback. + + For example, given we have a table `person` with the following data: + | id | first_name | last_name | + | 1 | Franz Allan | See | + | 2 | Jerry | Schneider | + + When you have the following assertions in your robot + | Row Count Is Equal To X | SELECT id FROM person | 1 | + | Row Count Is Equal To X | SELECT id FROM person WHERE first_name = 'John' | 0 | + + Then you will get the following: + | Row Count Is Equal To X | SELECT id FROM person | 1 | # FAIL | + | Row Count Is Equal To X | SELECT id FROM person WHERE first_name = 'John' | 0 | # PASS | + """ + obj_log.info( + 'Executing : Row Count Is Equal To X | %s | %s ' % + (selectStatement, numRows)) + num_rows = self.row_count(selectStatement) + if num_rows != int(str(numRows).encode('ascii')): + raise AssertionError("Expected same number of rows to be returned from '%s' " + "than the returned rows of %s" % (selectStatement, num_rows)) + else: + return True + + def kw_row_count_is_greater_than_x(self, selectStatement, numRows): + """ + Check if the number of rows returned from `selectStatement` is greater than the value submitted. If not, then + this will throw an AssertionError. Set optional input `sansTran` to True to run command without an explicit + transaction commit or rollback. + + For example, given we have a table `person` with the following data: + | id | first_name | last_name | + | 1 | Franz Allan | See | + | 2 | Jerry | Schneider | + + When you have the following assertions in your robot + | Row Count Is Greater Than X | SELECT id FROM person | 1 | + | Row Count Is Greater Than X | SELECT id FROM person WHERE first_name = 'John' | 0 | + + Then you will get the following: + | Row Count Is Greater Than X | SELECT id FROM person | 1 | # PASS | + | Row Count Is Greater Than X | SELECT id FROM person WHERE first_name = 'John' | 0 | # FAIL | + """ + obj_log.info( + 'Executing : Row Count Is Greater Than X | %s | %s ' % + (selectStatement, numRows)) + num_rows = self.row_count(selectStatement) + if num_rows <= int(numRows.encode('ascii')): + raise AssertionError("Expected more rows to be returned from '%s' " + "than the returned rows of %s" % (selectStatement, num_rows)) + else: + return True + + def kw_row_count_is_less_than_x(self, selectStatement, numRows): + """ + Check if the number of rows returned from `selectStatement` is less than the value submitted. If not, then this + will throw an AssertionError. Set optional input `sansTran` to True to run command without an explicit + transaction commit or rollback. + + For example, given we have a table `person` with the following data: + | id | first_name | last_name | + | 1 | Franz Allan | See | + | 2 | Jerry | Schneider | + + When you have the following assertions in your robot + | Row Count Is Less Than X | SELECT id FROM person | 3 | + | Row Count Is Less Than X | SELECT id FROM person WHERE first_name = 'John' | 1 | + + Then you will get the following: + | Row Count Is Less Than X | SELECT id FROM person | 3 | # PASS | + | Row Count Is Less Than X | SELECT id FROM person WHERE first_name = 'John' | 1 | # FAIL | + """ + obj_log.info( + 'Executing : Row Count Is Less Than X | %s | %s ' % + (selectStatement, numRows)) + num_rows = self.row_count(selectStatement) + if num_rows >= int(numRows.encode('ascii')): + raise AssertionError("Expected less rows to be returned from '%s' " + "than the returned rows of %s" % (selectStatement, num_rows)) + else: + return True + + def kw_table_must_exist(self, tableName): + """ + Check if the table given exists in the database. Set optional input `sansTran` to True to run command without an + explicit transaction commit or rollback. + + For example, given we have a table `person` in a database + + When you do the following: + | Table Must Exist | person | + + Then you will get the following: + | Table Must Exist | person | # PASS | + | Table Must Exist | first_name | # FAIL | + """ + obj_log.info('Executing : Table Must Exist | %s ' % tableName) + if self.db_api_module_name in ["cx_Oracle"]: + selectStatement = ( + "SELECT * FROM all_objects WHERE object_type IN ('TABLE','VIEW') AND owner = SYS_CONTEXT('USERENV', 'SESSION_USER') AND object_name = UPPER('%s')" % + tableName) + elif self.db_api_module_name in ["sqlite3"]: + selectStatement = ( + "SELECT name FROM sqlite_master WHERE type='table' AND name='%s' COLLATE NOCASE" % + tableName) + elif self.db_api_module_name in ["ibm_db", "ibm_db_dbi"]: + selectStatement = ( + "SELECT name FROM SYSIBM.SYSTABLES WHERE type='T' AND name=UPPER('%s')" % + tableName) + else: + selectStatement = ( + "SELECT * FROM information_schema.tables WHERE table_name='%s'" % + tableName) + num_rows = self.row_count(selectStatement) + if num_rows == 0: + raise AssertionError( + "Table '%s' does not exist in the db" % + tableName) + + # @retry(stop_max_attempt_number=5, wait_fixed=3000) + def select_all_as_generator(self, sql, param=None, choose_db=None): + if param is not None: + obj_log.info('SQL语句:{}|{}'.format(sql, param)) + else: + obj_log.info('SQL语句:{}'.format(sql)) + + try: + cursor, conn, counts = self.execute(sql, param, choose_db=choose_db) + for count in range(counts): + item = cursor.fetchone() + obj_log.info(item) + yield item + except Exception as e: + self.close(cursor, conn) + raise RuntimeError(e.args) + + def get_qa_to_sim_dbs(self): + get_sql = "select qa_db,sim_instance from sparkatp.qa_db_mapping_sim_dbs where status=1 and is_delete=0" + res_info = self.sim_dbs.dbs_query_by_db_name("qadb-slave", "sparkatp", get_sql, limit_num=1000) + print(res_info) + for item in res_info: + self.qa_db_to_sim_instance_name.update({item[0]: item[1]}) + return self.qa_db_to_sim_instance_name + + def get_instance_name(self, sql_str): + db_name = sql_str.split(".")[0].split(" ")[-1].replace("`", "").replace(" ", "") + return self.qa_db_to_sim_instance_name.get(db_name) + + +if __name__ == '__main__': + db = MySqLHelper() + # sql = "select id,name,code from peppa.grade_group where course_level in (0, 1, 2, 3) order by id asc;" + # sql = " DELETE FROM classes.timetable_plan_teacher WHERE teacher_id in (SELECT id FROM `teach_teacher`.`teacher_profile` WHERE name='666000排课' AND nickname='666000排课');" + # print("【原始sql】:{}".format(sql)) + # res = db.update_db_name(sql=sql) + # print(res) + sql = "SELECT * FROM account.account;" + res = db.select_one(sql=sql) + print(res) + + diff --git a/base_framework/public_tools/utils.py b/base_framework/public_tools/utils.py new file mode 100644 index 0000000..c0060fa --- /dev/null +++ b/base_framework/public_tools/utils.py @@ -0,0 +1,1545 @@ +# -*- coding:utf-8 -*- + +""" +Author: qiaoxinjiu +Create Data: 2020/9/24 11:39 +""" +import base64 +import datetime +import hashlib +import calendar +import math +import re +import time +import pytz +from os import listdir +from dateutil.relativedelta import relativedelta +from chinese_calendar import is_workday, is_holiday + +import jsonpath +import requests +from bs4 import BeautifulSoup + +from base_framework.base_config.current_pth import * +from base_framework.public_tools import log +from base_framework.public_tools.read_config import InitConfig +from base_framework.public_tools.read_config import ReadConfig +from base_framework.public_tools.runner import Runner +from base_framework.public_tools.sqlhelper import MySqLHelper +from base_framework.public_tools.custom_error import BusinessError + +obj_runner = Runner() +obj_log = log.get_logger() +obj_evn_cfg = ReadConfig(env_choose_path) + + +class Tools(InitConfig): + + def __init__(self): + # super().__init__() + InitConfig.__init__(self, run_user_name=None, current_evn=None) + # self.tool_jira_id = tool_cfg.get_value(sections='run_jira_id', options='huohua-podenv') + self.db_con = MySqLHelper() + + def get_jira_id_from_ini(self): + # cfg_path = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) + # ini_path = cfg_path + '/ConfigFiles/env_choose.ini' + tool_jira_id = None + try: + tool_jira_id = obj_evn_cfg.get_value(sections='run_jira_id', options='huohua-podenv') + except Exception as e: + print(e) + return tool_jira_id + + def get_md5(self, str): + m = hashlib.md5() + m.update(str.encode("utf8")) + md5 = m.hexdigest() + return md5 + + def get_format_date(self, r_week=None, r_time=None, t_time=None, r_schedule=None, + r_type=1, + add_months=0, add_weeks=0, add_days=0, add_hours=0, add_minutes=0, add_seconds=0, minutes=60): + """ + | 功能说明: | 返回指定格式的日期类型 | | + | 输入参数: | | | + | | r_schedule | 传入1的时候,直接返回('2023-08-18 15:44:57', '2023-08-18', '15:44:57', 6, '15:45')格式数据,用于教务的档期| + | | r_week | 传入周几,返回下个周几的日期 | + | | r_time | 需要转换的日期(格式):%Y-%m-%d %H:%M:%S, 如果不传则默认取系统当前日期, 时间戳格式不支持此参数 | + | | t_time | 需要转换的日期(格式):时间戳,秒级,默认取前十位数,当 r_time=None 时生效 | + | | r_type | 返回格式:详见下面的明细 | + | | add_month | 月 偏移量:0-当前,1-下月,-1上月,时间戳格式不支持此参数 | + | | add_weeks | 周 偏移量:0-当前,1-下周,-1上周 | + | | add_days | 日 偏移量:0-当前,1-明天,-1昨天 | + | | add_hours | 小时偏移量:0-当前,1-后一小时,-1前一小时 | + | | add_minutes | 分钟偏移量:0-当前,1-后一分钟,-1前一分钟 | + | | add_seconds | 秒级偏移量:0-当前,1-下一秒,-1前一秒 | + | | minutes | 分钟数,仅用于r_schedule不为空的时候 | + | 返回参数: | 按r_type指定的类型返回 | + | 作者信息: | 吴勇刚 | 2019.05.24 | + r_type类型列表: + | 类型编号 | 类型样式 | 实例 | + | 1 | %Y-%m-%d | 2019-05-23 | + | 2 | %Y年%m月%d日 | 2019年5月23日 | + | 3 | %Y年%m月%d日周X | 2019年5月23日星期四 | + | 4 | %Y-%m-%d %H:%M:%S | 2021-06-28 21:54:46 | + | 5 | %Y-%m-%d %H:%M:%S.%f | 2021-06-28 21:54:46.205213 | + | 6 | %Y-%m | 2019-05 | + | 7 | %Y%m%d | 20190505 | + | 8 | %Y%m%d%H%M%S | 20190505102030 | + | 11 | 原始时间戳 | 1499825149.2578925 | + | 12 | 秒级时间戳 | 1499825149 | + | 13 | 毫秒级时间戳 | 1499825149257 | + | 14 | 微秒级时间戳 | 1499825149257892 | + | 15 | %Y-%m-%d 00:00:00 | 2021-06-28 00:00:00 | + | 16 | %Y-%m-%d 23:59:59 | 2021-06-28 23:59:59 | + | 17 | %Y_%m_%d | 2021_06_28 | + | 18 | 返回档期所需的相关格式 | 0:%Y-%m-%d %H:%M:%S; 1:"%H:%M"; 2:档期格式专用week(原week+1); 3:"%Y-%m-%d %H:%M"; 4:原week | + | 19 | 类型15对应的时间戳 | 1640620800000 | + | 20 | 毫秒级时间戳,分秒均为0 | 1499825000000 | + | 21 | 毫秒级时间戳,秒毫秒均为0 | 1499825000000 | + | 22 | 时间拆分为年 月 日 时间 24H制 | {'year':'','month':'','day':'','time_24':{'short_time':'','full_time':''},'time_12h':{'short_time':'','full_time':''}} | + | 23 | %Y%m | 202303 | + | 24 | 下周一的当前时间 | 2021-07-05 21:54:00 | + | 25 | 本月的最后一天 | 2021-07-31 23:59:59 | + | 26 | 本月的第一天 | 2021-07-01 00:00:00 | + | 28 | 当前时间所在周周一 | 2021-07-01 00:00:00 | + | 29 | 当前时间所在周周日 | 2021-07-01 23:59:59 | + | 91 | 对应年份的第几周 | (2022,35,1):2022年第35周第1天 | + | 92 | 对应年份的第几周 | 202235:2022年第35周 | + 函数路径:src/Public/Common/utils.py + """ + r_type = int(r_type) + add_months = int(add_months) + add_weeks = int(add_weeks) + add_days = int(add_days) + add_hours = int(add_hours) + add_minutes = int(add_minutes) + add_seconds = int(add_seconds) + if r_schedule is not None: + if r_schedule == 1: + weeks = {"1": 2, "2": 3, "3": 4, "4": 5, "5": 6, "6": 7, "7": 1} + update_time = (datetime.datetime.now() + datetime.timedelta(minutes=minutes)) + weeks = weeks[str(update_time.isoweekday())] # 获取时间周几 + return update_time.strftime("%Y-%m-%d %H:%M:%S"), update_time.strftime( + "%Y-%m-%d"), update_time.strftime( + "%H:%M:%S"), weeks, update_time.strftime("%H:%M")[0:4] + "5" + else: + raise Exception("r_schedule参数输入错误,请重新输入") + if r_time is None and t_time is None and r_week is None: + r_date = datetime.datetime.now() + datetime.timedelta(weeks=add_weeks, + days=add_days, + hours=add_hours, + minutes=add_minutes, + seconds=add_seconds) + t_date = time.time() + ((add_weeks * 7 + add_days) * 24 + add_hours) * 3600 + add_minutes * 60 + add_seconds + else: + if r_week: + today = datetime.datetime.now() + days_ahead = r_week - 1 - today.weekday() # 4 corresponds to Friday (0 is Monday, 6 is Sunday) + if days_ahead < 0: + days_ahead += 7 + date_time = today + datetime.timedelta(days=days_ahead) + elif r_time: + try: + date_time = datetime.datetime.strptime(r_time, "%Y-%m-%d %H:%M:%S") + except ValueError: + raise ValueError("输入时间格式错误,请输入格式为:%Y-%m-%d %H:%M:%S 的时间字符串") + elif t_time: + if len(str(t_time)) > 10: + t_time = int(str(t_time)[0:10]) + try: + tmp_time = datetime.datetime.fromtimestamp(t_time) + # date_time = tmp_time.strftime("%Y-%m-%d %H:%M:%S") + date_time = datetime.datetime.fromtimestamp(t_time) + except ValueError as e: + raise ValueError("输入时间格式错误,错误原因:{}".format(e)) + r_date = date_time + datetime.timedelta(weeks=add_weeks, + days=add_days, + hours=add_hours, + minutes=add_minutes, + seconds=add_seconds) + t_date = time.mktime(date_time.timetuple()) + ( + (add_weeks * 7 + add_days) * 24 + add_hours) * 3600 + add_minutes * 60 + add_seconds + + if add_months >= 0: + r_date = r_date + relativedelta(months=add_months) + else: + r_date = r_date - relativedelta(months=abs(add_months)) + + if r_type == 1: + return r_date.strftime("%Y-%m-%d") + elif r_type == 2: + return str(r_date.year) + "年" + str(r_date.month) + "月" + str(r_date.day) + "日" + elif r_type == 3: + f_date = str(r_date.year) + "年" + str(r_date.month) + "月" + str(r_date.day) + "日" + return f_date + self.get_week_by_date(format_date=r_date.strftime("%Y-%m-%d"), week_type='Chinese') + elif r_type == 4: + return r_date.strftime("%Y-%m-%d %H:%M:%S") + elif r_type == 5: + return r_date.strftime("%Y-%m-%d %H:%M:%S.%f") + elif r_type == 6: + return r_date.strftime("%Y-%m") + elif r_type == 7: + return r_date.strftime("%Y%m%d") + elif r_type == 8: + return r_date.strftime("%Y%m%d%H%M%S") + elif r_type == 11: + return t_date + elif r_type == 12: + return int(t_date) + elif r_type == 13: + return int(round(t_date * 1000)) + elif r_type == 14: + return int(round(t_date * 1000000)) + elif r_type == 15: + return "{} 00:00:00".format(r_date.strftime("%Y-%m-%d")) + elif r_type == 16: + return "{} 23:59:59".format(r_date.strftime("%Y-%m-%d")) + elif r_type == 17: + return r_date.strftime("%Y_%m_%d") + elif r_type == 18: + weeks = {"1": 2, "2": 3, "3": 4, "4": 5, "5": 6, "6": 7, "7": 1} + weeks = weeks[str(r_date.isoweekday())] # 获取时间周几 + origin_week = int(str(r_date.isoweekday())) + return r_date.strftime("%Y-%m-%d %H:%M:%S"), r_date.strftime("%H:%M")[0:4] + "5", weeks, r_date.strftime( + "%Y-%m-%d %H:%M"), origin_week + elif r_type == 19: + today = "{} 00:00:00".format(r_date.strftime("%Y-%m-%d")) + time_array = time.strptime(today, "%Y-%m-%d %H:%M:%S") + timestamp = int(time.mktime(time_array)) + return str(timestamp * 1000) + elif r_type == 20: + return int(time.mktime(time.strptime(r_date.strftime("%Y-%m-%d %H:00:00"), '%Y-%m-%d %H:%M:%S'))) * 1000 + elif r_type == 21: + return int(time.mktime(time.strptime(r_date.strftime("%Y-%m-%d %H:%M:00"), '%Y-%m-%d %H:%M:%S'))) * 1000 + elif r_type == 23: + return r_date.strftime("%Y%m") + elif r_type == 24: # 返回下周一的当前时间 + today = datetime.datetime.today() + return (today + datetime.timedelta(days=7 - today.weekday())).strftime("%Y-%m-%d %H:%M:%S") + elif r_type == 25: + last_date = calendar.monthrange(year=r_date.year, month=r_date.month) + return "{}-{} 23:59:59".format(r_date.strftime("%Y-%m"), last_date[1]) + elif r_type == 26: + return "{}-{}-01 00:00:00".format(r_date.year, r_date.month) + elif r_type == 27: + # 时区转换 + # 将日期和时间转换为UTC时区 + now_utc_time = r_date.astimezone(pytz.utc) + obj_time = now_utc_time.isoformat()[:19]+"Z" + return obj_time, r_date.strftime("%Y-%m-%d %H:%M:%S") + elif r_type == 28: + now = datetime.datetime.now() + last_monday = now - datetime.timedelta(days=now.weekday()) + return f"{last_monday.strftime('%Y-%m-%d')} 00:00:00" + elif r_type == 29: + now = datetime.datetime.now() + last_sunday = now + datetime.timedelta(days=6 - now.weekday()) + return f"{last_sunday.strftime('%Y-%m-%d')} 23:59:59" + elif r_type == 91: + return r_date.isocalendar() + elif r_type == 92: + t_week = r_date.isocalendar() + return "{}{}".format(t_week[0], t_week[1]) + elif r_type == 22: + date_time_str = r_date.strftime("%Y-%m-%d %H:%M:%S") + date_obj = datetime.datetime.strptime(date_time_str, '%Y-%m-%d %H:%M:%S') + + year = date_obj.year + month = date_obj.month + day = date_obj.day + short_time_12h = date_obj.strftime('%I:%M %p').lower() + full_time_12h = date_obj.strftime('%I:%M:%S %p').lower() + short_time_24h = date_obj.strftime('%I:%M') + full_time_24h = date_obj.strftime('%I:%M:%S') + return {'year': year, 'month': month, 'day': day, + 'time_24': {'short_time': f'{short_time_24h}', 'full_time': f'{full_time_24h}'}, + 'time_12h': {'short_time': f'{short_time_12h}', 'full_time': f'{full_time_12h}'}} + else: + raise Exception( + "调用get_format_date函数的r_type参数不正确....") + + @staticmethod + def get_week_by_date(format_date, week_type='Arabic'): + """ + | 功能说明: | 返回format_date对应的星期 | + | 输入参数: | format_date | 要计算的日期格式:2021-06-28 | + | | week_type | 返回星期的格式:Arabic-阿拉伯数字,Chinese-中文 | + | 返回参数: | 星期几,0-6, 0-星期日 | + | 作者信息: | 吴勇刚 | 2021-06-08 | + 函数路径:src/Public/Common/utils.py + | format_date | week_type | 返回 | + | 2021-06-27 | Arabic | 0 | + | 2021-06-28 | Arabic | 1 | + | 2021-06-27 | Chinese | 星期日 | + | 2021-06-28 | Chinese | 星期一 | + """ + input_date = format_date.split('-') + if len(input_date) != 3: + raise Exception("输入值:{} 不是2021-06-08格式,请检查".format(format_date)) + year = int(input_date[0]) + month = int(input_date[1]) + day = int(input_date[2]) + week = int(datetime.datetime(year, month, day).strftime("%w")) + if week_type == 'Arabic': + return week + elif week_type == 'Chinese': + weeks = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'] + return weeks[week] + + @staticmethod + def check_the_date_is_a_working_day(format_date=None): + """ + 功能:判断给定日期是否为工作日 + | 输入 | format_date | 日期(格式如:2021-09-01),不传则计算今天| + | 返回 | True,工作日 | False,非工作日,即周末或法定节假日 | + | 作者 | 吴勇刚 | 2021.08.31 | + 函数路径:src/Public/Common/utils.py + """ + if not format_date: # 没有传入参数则默认计算当天是否为工作日 + format_date = datetime.date.today() + else: + format_date = datetime.datetime.strptime(format_date, "%Y-%m-%d").date() + if is_workday(format_date) and not is_holiday(format_date): + return True + else: + return False + + @staticmethod + def get_img_bas64(path): + img = open(path, 'rb') + img_bas64 = base64.b64encode(img.read()) + img.close() + '''b 表示 byte的意思,将byte转换回去''' + return str(img_bas64, 'utf-8') + + @staticmethod + def get_files_path(): + path = (os.path.split(os.path.realpath(__file__)))[0] + '\\' + "img" + return path + + @staticmethod + def listdir(self): # 传入存储的list + list_name = [] + path = self.get_files_path() + for file in os.listdir(path): + file_path = os.path.join(path, file) + if os.path.isdir(file_path): + listdir(file_path, list_name) + else: + list_name.append(file_path) + return list_name + + @staticmethod + def del_file(file_name): + size = os.path.getsize(file_name) + file_size = 44 * 1024 # 100K + if size > file_size: + print('remove', size, file_name) + try: + os.remove(file_name) + except Exception as e: + print(e) + + def modify_db_info(self, dbName=None, dbUsername=None, + dbPassword=None, dbHost=None, dbPort=None): + """ + Example usage: + | # explicitly specifies all db property values | + | modify_db_info | psycopg2 | my_db | postgres | s3cr3t | tiger.foobar.com | 5432 | + """ + if dbName: + self.db_rtn.set_section( + section='Mysql', + option='db_test_dbname', + value=dbName) + if dbUsername: + self.db_rtn.set_section( + section='Mysql', + option='db_test_user', + value=dbUsername) + if dbPassword: + self.db_rtn.set_section( + section='Mysql', + option='db_test_password', + value=dbName) + if dbHost: + self.db_rtn.set_section( + section='Mysql', + option='db_test_host', + value=dbHost) + if dbPort: + self.db_rtn.set_section( + section='Mysql', + option='db_test_port', + value=dbPort) + + def __get_eureka_url_from_db(self, team, server): + """ + | 功能 | 从DB中获取服务对应的eureka_url,供get_container_ip_from_eureka函数使用 | + """ + sql_str = "select eureka_url as hh, eureka_url_hhi as hhi from sparkatp.swagger_info where team='{0}' " \ + "and server_name='{1}';".format(team, server) + res = self.db_con.select_one(sql=sql_str) + current_business = self.evn_cfg['run_evn_name']['current_business'] + return res[current_business] + + def get_container_ip_from_eureka(self, server_name, env=None, need_jira_id=False, jira_id_dev=None, + is_contain_qa=True, team_name=None, eureka_url=None): + """ + | 功能说明: | 获取Eureka指定服务的IP | + | 输入参数: | server_name | 服务名 | + | | env | QA or SIM | + | | need_jira_id | True:从config.ini文件获取, False:取QA环境的IP | + | | jira_id_dev | 传入自定义独立环境编号 | + | | is_contain_qa | 当没有对应的独立环境时,是否返回qa环境IP,默认True,返回 | + | | team_name | 小组名,当eureka_url=None时,用于确认eureka_url | + | | eureka_url | eureka地址,没有传入则读取数据库中的值 | + | 返回参数: | dict | container_ip | + | 作者信息: | 吴勇刚 | 2022.03.27 | + 举例说明: + | get_container_ip_from_eureka(server_name='PEPPA-QI-API') | + """ + # 各项配置获取 + if team_name is None: # 业务组 + team_name = obj_evn_cfg.get_value(sections='run_evn_name', options='current_team') + if env is None: # 构建环境 + env = obj_evn_cfg.get_value(sections='run_evn_name', options='current_evn') + if eureka_url is None: # eureka地址 + eureka_url = self.__get_eureka_url_from_db(team=team_name, server=server_name) + # if env.upper() == "QA" and 'sim' in eureka_url.lower(): + # eureka_url = eureka_url.replace('.sim.', '.qa.') + # elif env.upper() == "SIM" and 'qa' in eureka_url.lower(): + # eureka_url = eureka_url.replace('.qa.', '.sim.') + jira_id = None + if jira_id_dev is None: + # if need_jira_id is False: + # jira_id = 'qa' + # else: + jira_id = ReadConfig(env_choose_path).get_value(sections='run_jira_id', options='huohua-podenv') + # if jira_id == "": + # jira_id = 'qa' + else: + jira_id = jira_id_dev + temp_text = requests.get(url=eureka_url).content + soup = BeautifulSoup(temp_text.decode('utf-8'), "html.parser") + all_container_ip = soup.find_all(href=re.compile("actuator/info")) + tree_dict = dict() + for temp in all_container_ip: + server = list(temp.parent.parent)[1].get_text() + server_ip = list(temp) + if server in tree_dict.keys(): + tree_dict[server].extend(server_ip) + else: + tree_dict[server] = server_ip + server_name = server_name.upper() + return_dict = dict() + if server_name in tree_dict.keys(): + if jira_id: + if jira_id in str(tree_dict[server_name]): + container_ip = [ + temp.split(':')[0] for temp in + filter(lambda x: x.split(':')[2] if len(x.split(':')) > 2 else "" == jira_id, + tree_dict[server_name]) + if jira_id in temp] + for ip_temp in container_ip: + if ip_temp.startswith('10'): + container_ip = ip_temp + else: # 防止研发在本地启动了服务导致有多条数据 ,跳过办公网段的ip + obj_log.warning('跳过获取到的IP:{}'.format(ip_temp)) + continue + return_dict['container_ip'] = container_ip + if 'allschool' in eureka_url: + ReadConfig(pz_all_server_ip_path).set_section(section='PZ', option=server_name, + value=container_ip) + return return_dict + elif env.upper() == "SIM": + if len(tree_dict[server_name]) == 1: # sim环境如果只有一个ip,就直接返回此ip + container_ip = [temp.split(':')[0] for temp in tree_dict[server_name]][0] + return_dict['container_ip'] = container_ip + return return_dict + else: + if not is_contain_qa: + return_dict['container_ip'] = '' + return return_dict + # 如果没有对应的独立环境,则返回qa环境的ip + if 'qa' in str(tree_dict[server_name]): + container_ip = [temp.split(':')[0] for temp in tree_dict[server_name] if 'qa' in temp] + for ip_temp in container_ip: + if ip_temp.startswith('10'): + container_ip = ip_temp + else: + obj_log.error('跳过获取到的IP:{}'.format(ip_temp)) + continue + return_dict['container_ip'] = container_ip + return return_dict + elif 'groot' in str(tree_dict[server_name]): + """解决allschool部分服务极限环境为groot而非qa的场景""" + container_ip = [temp.split(':')[0] for temp in tree_dict[server_name] if 'groot' in temp] + for ip_temp in container_ip: + if ip_temp.startswith('10'): + container_ip = ip_temp + else: + obj_log.error('跳过获取到的IP:{}'.format(ip_temp)) + continue + return_dict['container_ip'] = container_ip + # if 'allschool' in eureka_url: + # ReadConfig(pz_all_server_ip_path).set_section(section='PZ', option=server_name, value=container_ip) + return return_dict + elif '${server.port}' in str(tree_dict[server_name]) or 'sim' in str(tree_dict[server_name]): + container_ip = [temp.split(':')[0] + for temp in tree_dict[server_name] if '${server.port}' in temp or 'sim' in temp][0] + return_dict['container_ip'] = container_ip + return return_dict + else: + return_dict['container_ip'] = '' + return return_dict + else: + # raise Exception('从{}中未获取到{}的IP,请检查服务名是否正确!!!'.format(eureka_url, server_name)) + obj_log.error('从{}中未获取到{}的IP,请检查服务名是否正确!!!'.format(eureka_url, server_name)) + return False + + # def get_url_by_eureka(self, server_name, jira_id=False): + # """ + # | 功能说明: | 获取Eureka指定服务的IP后,组装成需要的url前缀 | + # | 输入参数: | server_name | 服务名 | + # | 输入参数: | jira_id | 独立环境编号 | + # """ + # svr_ip = self.get_container_ip_from_eureka(server_name=server_name, need_jira_id=jira_id) + # if svr_ip: + # return "http://" + svr_ip["container_ip"] + ":8080" + # else: + # return False + + def get_container_ip_from_eureka_old(self, server_name, env=None, need_jira_id=False, + jira_id_dev=None, is_contain_qa=True): + """ + | 功能说明: | 获取Eureka指定服务的IP | + | 输入参数: | server_name | 服务名 | + | | env | QA or SIM | + | | need_jira_id | True:从config.ini文件获取, False:取QA环境的IP | + | | jira_id_dev | 传入自定义独立环境编号 | + | 返回参数: | dict | container_ip | + | 作者信息: | 作者 林于棚 | 修改时间 | + 举例说明: + | get_container_ip_from_eureka(server_name='PEPPA-QI-API') | + """ + return_dict = dict() + tree_dict = dict() + # 针对于pz项目的白名单,与其他项目的eureka地址不一致 + pz_eureka_server = ["hulk-teaching-server", "hulk-teacher-api", "hulk-teacher-server", "hulk-teaching-listener", + "hulk-teaching-scheduler", "hulk-operation-api-server", "hulk-operation-listener", + "hulk-operation-server", "hulk-account-server", + 'hulk-org-server', 'hulk-org-api', 'hulk-teacher-api', 'hulk-class-help-server', + 'hulk-content-audit-server', "hulk-teach-backend-api", "hulk-teach-supply-cli-api", + "hulk-ark-gateway", "hulk-class-help-manager", "hulk-content-manage-gateway", + "hulk-teach-supply-server", "hulk-teach-classes-server", "classpod-platform-api", + "classpod-user-api", + "hulk-teach-content-server", "hulk-teach-content-cli-api", "hulk-teach-course-server" + ] + if env is None: + env = obj_evn_cfg.get_value( + sections='run_evn_name', options='current_evn') + if env == "QA": + if server_name.lower() in pz_eureka_server: + # url = "http://eureka-asc.qa.huohua.cn/" + url = "http://eureka.qa.allschool.com/" + else: + url = "http://eureka.qa.huohua.cn/" + else: + if server_name.lower() in pz_eureka_server: + # url = "http://eureka-asc.sim.huohua.cn/" + url = "http://eureka.sim.allschool.com/" + else: + url = 'http://eureka.sim.huohua.cn/' + temp_text = requests.get(url=url).content + soup = BeautifulSoup(temp_text.decode('utf-8'), "html.parser") + all_container_ip = soup.find_all(href=re.compile("actuator/info")) + for temp in all_container_ip: + server = list(temp.parent.parent)[1].get_text() + server_ip = list(temp) + if server in tree_dict.keys(): + tree_dict[server].extend(server_ip) + else: + tree_dict[server] = server_ip + if need_jira_id: + jira_id = ReadConfig(env_choose_path).get_value(sections='run_jira_id', options='huohua-podenv') + if jira_id == "": + jira_id = None + else: + jira_id = None + if jira_id_dev: + jira_id = jira_id_dev + server_name = server_name.upper() + if server_name in tree_dict.keys(): + if jira_id: + if jira_id in str(tree_dict[server_name]): + container_ip = [ + temp.split(':')[0] for temp in + filter(lambda x: x.split(':')[2] == jira_id, tree_dict[server_name]) + if jira_id in temp] + for ip_temp in container_ip: + if ip_temp.startswith('10'): + container_ip = ip_temp + else: + obj_log.warning('跳过获取到的IP:{}'.format(ip_temp)) + continue + return_dict['container_ip'] = container_ip + if server_name.lower() in pz_eureka_server: + ReadConfig(pz_all_server_ip_path).set_section(section='PZ', option=server_name, + value=container_ip) + return return_dict + else: + if not is_contain_qa: + return_dict['container_ip'] = '' + return return_dict + if 'qa' in str(tree_dict[server_name]): + container_ip = [ + temp.split(':')[0] for temp in tree_dict[server_name] if 'qa' in temp] + for ip_temp in container_ip: + if ip_temp.startswith('10'): + container_ip = ip_temp + else: + obj_log.error('跳过获取到的IP:{}'.format(ip_temp)) + continue + return_dict['container_ip'] = container_ip + if server_name.lower() in pz_eureka_server: + ReadConfig(pz_all_server_ip_path).set_section(section='PZ', option=server_name, value=container_ip) + return return_dict + elif '${server.port}' in str(tree_dict[server_name]) or 'sim' in str(tree_dict[server_name]): + container_ip = [temp.split(':')[ + 0] for temp in tree_dict[server_name] if '${server.port}' in temp or 'sim' in temp][ + 0] + return_dict['container_ip'] = container_ip + return return_dict + else: + return_dict['container_ip'] = '' + return return_dict + else: + obj_log.error('未获取到IP,请检查server_name:{} 是否正确!'.format(server_name)) + return_dict['container_ip'] = '' + return return_dict + + def com_get_value_from_json(self, json_object, json_path): + return jsonpath.jsonpath(json_object, json_path) + + @staticmethod + def should_be_equal_as_list(sub_list, obj_list, ignore_nodes=[], check_len=False): + """ + 功能:判断两个list是否相等,不区分排序 + | 输入参数: | sub_list | 列表1,子列表 | + | | obj_list | 列表2,目标列表 | + | | ignore_nodes | sub_list中要忽略的节点,填需要,从0开始 | + | | check_len | 是否检查长度,默认不检查,传入True时,则检查两个list完全相同 | + | 作者: | 吴勇刚 | 2021.05. 16 | + 函数位置:src/Public/Common/utils.py + | 完全相等 | should_be_equal_as_list([1,'a',2,'aa'], [1,2,'b','bb']) | failed | + | 子集相等 | should_be_equal_as_list([1,'a',2,'aa'], [1,2,'b','bb'], [1,3]) | success | + """ + if isinstance(sub_list, str): + try: + sub_list = eval(sub_list) + except Exception as e: + print("输入参数类型错误:{}".format(e)) + if isinstance(obj_list, str): + try: + obj_list = eval(obj_list) + except Exception as e: + print("输入参数类型错误:{}".format(e)) + if not isinstance(sub_list, list) or not isinstance(obj_list, list): + raise RuntimeError("【ERROR】请求参数必现都是list类型,sub_list:{0}, " + "obj_list:{1}".format(type(sub_list), type(obj_list))) + return + if check_len: + if len(obj_list) != len(sub_list): + raise RuntimeError("【ERROR】两个list的长度不同:{}!={},请检查...".format(len(sub_list), len(obj_list))) + return + equal = True + for index in range(len(sub_list)): + if index not in ignore_nodes: + if sub_list[index] not in obj_list: + print("【ERROR】{0} not in obj_list:{1}".format(sub_list[index], obj_list)) + equal = False + if not equal: + raise RuntimeError("【ERROR】sub_list != obj_list,请检查......") + + @staticmethod + def should_be_equal_as_dict(sub_dict, obj_dict, ignore_keys=[]): + """ + 功能:判断两个list是否相等,不区分排序 + | 输入参数: | sub_dict | 列表1,子列表 | + | | obj_dict | 列表2,目标列表 | + | | ignore_keys | sub_dict中要忽略的keys | + | 作者: | 吴勇刚 | 2021.05. 16 | + 函数位置:src/Public/Common/utils.py + | 完全相等 | should_be_equal_as_dict({'a':1,'b':2,'c':True}, {'a':1,'b':2,'c':False}) | not equal | + | 子集相等 | should_be_equal_as_dict({'a':1,'b':2,'c':True}, {'a':1,'b':2,'c':False}, ['c']) | equal | + """ + if not isinstance(sub_dict, dict) or not isinstance(obj_dict, dict): + raise RuntimeError("【ERROR】请求参数必现都是dict类型,sub_dict:{0}, obj_dict:{1}".format(type(sub_dict), + type(obj_dict))) + return + if len(ignore_keys) == 0 and len(sub_dict) != len(obj_dict): + raise RuntimeError( + "【ERROR】两个dict的长度不相等,sub={0},obj={1}".format( + len(sub_dict), len(obj_dict))) + return + equal = True + for key in sub_dict: + if key not in ignore_keys: + if key not in obj_dict: + print( + "【ERROR】{0} not in obj_list:{1}".format( + sub_dict[key], obj_dict)) + equal = False + continue + if sub_dict[key] != obj_dict[key]: + print("【ERROR】'节点{key}':{sub} != {obj}".format(key=key, + sub=sub_dict[key], + obj=obj_dict[key])) + equal = False + if not equal: + raise RuntimeError("【ERROR】sub_dict != obj_dict,请检查......") + + def check_result(self, my_result, result_one=None, + result_two=None, position=None): + if result_one is None: + result_one = [] + + if result_two is None: + result_two = [] + + if position is None: + position = [] + + for key, value in my_result.items(): + position.append(key) + if isinstance(value, dict): + Tools().check_result(value, result_one, result_two, position) + elif isinstance(value, list): + for index, it in enumerate(value): + position.append(index) + if isinstance(it, dict): + Tools().check_result(it, result_one, result_two, position) + elif isinstance(it, str): + data_in_file = Tools().generate(position) + result_one.append(it) + result_two.append(data_in_file) + elif isinstance(it, int): + data_in_file = Tools().generate(position) + result_one.append(it) + result_two.append(data_in_file) + position.pop() + else: + if isinstance(value, str): + data_in_file = Tools().generate(position) + result_one.append(value) + result_two.append(data_in_file) + elif isinstance(value, int): + data_in_file = Tools().generate(position) + result_one.append(value) + result_two.append(data_in_file) + position.pop() + return result_one, result_two + + def generate(self, list_all): + y = "" + for i in list_all: + if isinstance(i, str): + y += '["{}"]'.format(i) + else: + y += '[{}]'.format(i) + return "obj_json" + y + + def mq_send(self, topic, message_body, tag="tag", + key="key", pro="pro", **kwargs): + """ + 已废弃,请使用base_framework/public_tools/rocket_mq.py下的函数 + """ + post_data = dict() + return_data = dict() + cfg_path = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) + ini_path = cfg_path + '/base_framework/base_config/env_choose.ini' + jira_id = ReadConfig(ini_path).get_value( + sections='run_jira_id', options='huohua-podenv') + post_data['topic'] = topic + post_data['messageBody'] = '{}'.format(message_body) + post_data['tag'] = tag + post_data['key'] = key + if pro == 'pro': + if jira_id: + post_data['pro'] = jira_id + else: + post_data['pro'] = 'qa' + else: + post_data['pro'] = pro + change_user = kwargs.get('user', None) + url = obj_runner.mq_host + '/topic/sendTopicMessage.do' + req_type = 'POST' + resp = obj_runner.call_rest_api( + user=change_user, + API_URL=url, + req_type=req_type, + json=post_data) + if resp['data']['sendStatus'] == 'SEND_OK': + obj_log.info('发送MQ消息成功!{}:{}'.format(topic, message_body)) + return True + else: + obj_log.error('发送MQ消息失败!') + return False + + def mq_send_by_sim(self, topic, message_body, tag="tag", + key="key", **kwargs): + """ + 已废弃,请使用base_framework/public_tools/rocket_mq.py下的函数 + """ + post_data = dict() + post_data['topic'] = topic + post_data['messageBody'] = '{}'.format(message_body) + post_data['tag'] = tag + post_data['key'] = key + url = 'https://mq-console.sim.huohua.cn/topic/sendTopicMessage.do' + req_type = 'POST' + resp = obj_runner.call_rest_api( + user=None, + API_URL=url, + req_type=req_type, + json=post_data, + current_evn='sim') + if resp['data']['sendStatus'] == 'SEND_OK': + obj_log.info('发送MQ消息成功!{}:{}'.format(topic, message_body)) + return True + else: + obj_log.error('发送MQ消息失败!') + return False + + def get_mq_msg(self, topic, begin_time, end_time, **kwargs): + """ + 已废弃,请使用base_framework/public_tools/rocket_mq.py下的函数 + """ + post_data = dict() + return_data = dict() + post_data['topic'] = topic + post_data['begin'] = str(time.mktime(time.strptime(begin_time, '%Y-%m-%d %H:%M'))).split('.')[0] + '000' + post_data['end'] = str(time.mktime(time.strptime(end_time, '%Y-%m-%d %H:%M'))).split('.')[0] + '000' + change_user = kwargs.get('user', None) + url = obj_runner.mq_host + '/message/queryMessageByTopic.query' + req_type = 'GET' + resp = obj_runner.call_rest_api( + user=change_user, + API_URL=url, + req_type=req_type, + params=post_data) + if resp['status'] == 0: + obj_log.info('查询MQ消息成功!') + return resp['data'] + else: + obj_log.error('查询MQ消息失败!') + return False + + @staticmethod + def delete_redis(key, db): + import redis + pool = redis.Redis(host='rediscourse.qa.huohua.cn', port=6379, password='Bkl6LvqfzFCzYPAh', db=db) + pool.delete(key) + + @staticmethod + def should_be_exist(sql, number=1, raise_error=True, retry_time=0): + """ + | 功能说明: | 检查数据库中sql语句对应的结果应该存在number条数据 | + | 输入参数: | sql | 查询数据库对应的sql语句 | + | | number | 数据库应该存在多少条这样的数据 | + | | raise_error | True-结果不存在时抛异常阻塞后续流程,False-结果不存在时返回false但不抛异常可继续流程 | + | | retry_time | 重试次数,每次间隔1秒 | + | 返回参数: | 无 | sql条数匹配则通过,否则抛出异常 | + | 作者信息: | 吴勇刚 | 2021.07.05 | + 函数位置:src/Public/Common/utils.py + """ + res = MySqLHelper().select_all(sql=sql) + if int(number) == 0 and int(retry_time) > 0: + rs_flag = False + for index in range(int(retry_time)): + time.sleep(1) + res = MySqLHelper().select_all(sql=sql) + if len(res) == int(number): + rs_flag = True + else: + rs_flag = False + return rs_flag + elif len(res) != int(number): + for index in range(int(retry_time)): + time.sleep(1) + res = MySqLHelper().select_all(sql=sql) + if len(res) != int(number): + continue + else: + return True + print("exec sql: {}".format(sql)) + if raise_error: + raise Exception("check db failed:expect exist {0}, but act is {1}...".format(number, len(res))) + else: + return False + else: + return True + + def should_be_not_contain_sub_json(self, obj_json, sub_json, sort_json=True): + """ + 功能:判断obj_json未包含sub_json + | 输入参数: | obj_json | 目标串 | + | | sub_json | 子串 | + | | sort_json | 是否对json进行排序,默认-False,不排序 | + | 作者: | 吴勇刚 | 2022.05.10 | + """ + tag_equal = True + # 优先替换不规则的null,true和false + obj_json = eval(str(obj_json).replace('null', '""').replace('true', 'True').replace('false', 'False')) + sub_json = eval(str(sub_json).replace('null', '""').replace('true', 'True').replace('false', 'False')) + # 根据类型走不同的递归函数 + if isinstance(obj_json, dict) and isinstance(sub_json, dict): + tag_equal = self._check_dict(obj_json, sub_json, tag_equal, sort_json) + elif isinstance(obj_json, list) and isinstance(sub_json, list): + tag_equal = self._check_list(obj_json, sub_json, tag_equal, sort_json) + else: + tag_equal = False + print("入参需为json串,请检查输入参数....") + if tag_equal: + print("obj_json:{}".format(obj_json)) + print("sub_json:{}".format(sub_json)) + raise Exception("obj_json中包含了sub_json串,校验不通过.....") + else: + print("obj_json中不包含sub_json,校验通过") + + def should_be_equal_as_json(self, obj_json, sub_json, sort_json=False, sort_key=None, sort_type='str'): + """ + 功能:判断sub_json是否被obj_json完全包含 + | 输入参数: | obj_json | 目标串 | + | | sub_json | 子串 | + | | sort_json | 是否对json进行排序,默认-False,不排序 | + | | sort_key | [{},{}]结构排序时,将sort_key对应value按升序排列 | + | | sort_type | 按sort_key排序时,按int还是str类型对比,默认str,非int时会按str对比 | + | 作者: | 吴勇刚 | 2021.07. 29 | + 函数位置:src/Public/Common/utils.py, 用法举例如下: + | obj_json | sub_json | 调用方式 | 对比结果 | 备注说明 | + | {"a":11} | {"a":11} | should_be_equal_as_json(obj_json,sub_json) | 相等 | | + | {"a":11,"b":12} | {"a":11} | 同上 | 相等 | 对比原理是obj_json完整包含sub_json即可 | + | {"a":{"b":[1,2,3]}} | {"a":{"b":[1,2]} | 同上 | 相等 | 支持任一格式的嵌套 | + | {"a":11,"b":12} | {"a":11,"b":"not_check"} | 同上 | 相等 | value=not_check的节点不做检查 | + | [{"a":11},{"b":12}] | [{"a":11},{"b":12}] | 同上 | 相等 | 支持list格式对比 | + | [{"a":11},{"b":12}] | [{"b":12},{"a":11}] | 同上 | 不相等 | 因为list顺序不一样 | + | [{"a":11},{"b":12}] | [{"b":12},{"a":11}] | should_be_equal_as_json(obj_json,sub_json,sort_json=True) | 相等 | 对比前会自动排期 | + """ + tag_equal = True + # 优先替换不规则的null,true和false + obj_json = eval(str(obj_json).replace('null', '""').replace('true', 'True').replace('false', 'False')) + sub_json = eval(str(sub_json).replace('null', '""').replace('true', 'True').replace('false', 'False')) + # 根据类型走不同的递归函数 + if sort_type.lower() not in ('str', 'int'): + sort_type = 'str' + if isinstance(obj_json, dict) and isinstance(sub_json, dict): + # tag_equal = self._check_dict(obj_json, sub_json, tag_equal, sort_json, sort_key, sort_type) + r_equal = self._check_dict(obj_json, sub_json, sort_json, sort_key, sort_type) + if tag_equal: # 当未检查到不等项时才重新赋值,预防最后一项相等时覆盖前面不等项的结果 + tag_equal = r_equal + elif isinstance(obj_json, list) and isinstance(sub_json, list): + # tag_equal = self._check_list(obj_json, sub_json, tag_equal, sort_json, sort_key, sort_type) + r_equal = self._check_list(obj_json, sub_json, sort_json, sort_key, sort_type) + if tag_equal: # 当未检查到不等项时才重新赋值,预防最后一项相等时覆盖前面不等项的结果 + tag_equal = r_equal + else: + raise BusinessError("入参仅支持json格式,请检查输入参数") + if tag_equal: + print("obj_json中完全包含sub_json....") + else: + raise Exception("预期和实际结果不同,请检查前几步中的逻辑:\nobj_json:{}\nsub_json:{}".format(obj_json, sub_json)) + + # def _check_list(self, obj_list, sub_list, should_equal, sort_json=False, sort_key=None, sort_type='str'): + def _check_list(self, obj_list, sub_list, sort_json=False, sort_key=None, sort_type='str'): + """should_be_equal_as_json的内置递归函数""" + should_equal = True + if len(obj_list) == len(sub_list) == 0: # 都为空,则直接返回 + return True + elif len(obj_list) < len(sub_list): # 目标列表长度小于子列表 + print("obj_json的长度是{}<{}sub_json的长度,请检查...".format(len(obj_list), len(sub_list))) + return False + elif len(sub_list) == 0: # 子列表为空 + return True + if sort_json: + if sort_key and sort_key in obj_list[0] and sort_key in sub_list[0]: + if sort_type.lower() == 'str': + obj_list.sort(key=lambda x: str(x[sort_key])) + sub_list.sort(key=lambda x: str(x[sort_key])) + elif sort_type.lower() == 'int': + obj_list.sort(key=lambda x: int(x[sort_key])) + sub_list.sort(key=lambda x: int(x[sort_key])) + else: + obj_list.sort(key=lambda x: str(x)) + sub_list.sort(key=lambda x: str(x)) + for index in range(len(sub_list)): + if index < len(obj_list): + if isinstance(sub_list[index], list) and isinstance(obj_list[index], list): + # should_equal = self._check_list(obj_list[index], sub_list[index], should_equal, sort_json, sort_key, + # sort_type) + r_equal = self._check_list(obj_list[index], sub_list[index], sort_json, sort_key, sort_type) + if should_equal: + should_equal = r_equal + elif isinstance(sub_list[index], dict) and isinstance(obj_list[index], dict): + # should_equal = self._check_dict(obj_list[index], sub_list[index], should_equal, sort_json, sort_key, + # sort_type) + r_equal = self._check_dict(obj_list[index], sub_list[index], sort_json, sort_key, sort_type) + if should_equal: + should_equal = r_equal + elif not isinstance(sub_list[index], list) and not isinstance(obj_list[index], list) and not isinstance( + sub_list[index], dict) and not isinstance(obj_list[index], dict): + if isinstance(sub_list[index], str) and sub_list[index] == "not_check": + continue + else: + if sub_list[index] in obj_list: + pass + else: + should_equal = False + print("{}中的第{}个元素:{}不存在于{}中,请检查..." + .format(sub_list, index + 1, sub_list[index], obj_list)) + elif isinstance(sub_list[index], str) and sub_list[index] == "not_check": + continue + else: + should_equal = False + print("类型不同:{}的类型是{},但{}的类型是{}".format(sub_list[index], + type(sub_list[index]), + obj_list[index], + type(obj_list[index]))) + else: + should_equal = False + print("sub_list的长度是{}!={}obj_list的长度,请检查...".format(len(sub_list), len(obj_list))) + return should_equal + + # def _check_dict(self, obj_dict, sub_dict, should_equal, sort_json=False, sort_key=None, sort_type='str'): + def _check_dict(self, obj_dict, sub_dict, sort_json=False, sort_key=None, sort_type='str'): + """should_be_equal_as_json的内置递归函数""" + should_equal = True + for key in sub_dict: + if key in obj_dict: + if isinstance(sub_dict[key], list) and isinstance(obj_dict[key], list): + # should_equal = self._check_list(obj_dict[key], sub_dict[key], should_equal, sort_json, sort_key, + # sort_type) + r_equal = self._check_list(obj_dict[key], sub_dict[key], sort_json, sort_key, sort_type) + if should_equal: + should_equal = r_equal + elif isinstance(sub_dict[key], dict) and isinstance(obj_dict[key], dict): + # should_equal = self._check_dict(obj_dict[key], sub_dict[key], should_equal, sort_json, sort_key, + # sort_type) + r_equal = self._check_dict(obj_dict[key], sub_dict[key], sort_json, sort_key, sort_type) + if should_equal: + should_equal = r_equal + elif not isinstance(sub_dict[key], list) and not isinstance(sub_dict[key], dict) and not isinstance( + obj_dict[key], list) and not isinstance(obj_dict[key], dict): + if isinstance(sub_dict[key], str) and sub_dict[key] == "not_check": + continue + else: + if sub_dict[key] == obj_dict[key]: + pass + else: + should_equal = False + print("两个对比值中{0}对应的值不相同:{1} != {2},请确认...".format(key, sub_dict[key], + obj_dict[key])) + elif isinstance(sub_dict[key], str) and sub_dict[key] == "not_check": + continue + else: + should_equal = False + print("类型不同:{}的类型是{},但{}的类型是{}".format(sub_dict[key], + type(sub_dict[key]), + obj_dict[key], + type(obj_dict[key]))) + else: + should_equal = False + print("key值{}不存在于目标串{}中,请检查...".format(key, obj_dict)) + return should_equal + + def remove_duplication(self, old_list): + """ + 字典去重 + """ + new_list = [] + for i in old_list: + if i not in new_list: + new_list.append(i) + return new_list + + def list_calculations(self, list_a, list_b, opt_type, removed_duplication=False): + """ + list运算 + :return: + """ + # 交集 + if removed_duplication: + list_a = self.remove_duplication(list_a) + list_b = self.remove_duplication(list_b) + if opt_type == 1: + # return {'intersection': list(set(list_a).intersection(set(list_b)))} + return {'intersection': [x for x in list_a if x in list_b]} + # 并集 + elif opt_type == 2: + # return {'union': list(set(list_a).union(set(list_b)))} + list_a.extend(list_b) + return {'union': list_a} + # 差集 + elif opt_type == 3: + # return {'a_has': list(set(list_a).difference(set(list_b))), + # 'b_has': list(set(list_b).difference(set(list_a)))} + return {'a_has_only': [x for x in list_a if x not in list_b], + 'b_has_only': [x for x in list_b if x not in list_a]} + + def compute_page_info(self, total_numbers=None, data_list=None, page_size=10, page_num=1): + """ + 功能:根据总数,页码,每页数据条数计算查询分页信息 + | 输入参数: | total_numbers | 总数据行数,与data_list二传一即可 | + | | data_list | 原始数据list,与total_numbers二传一即可 | + | | page_size | 每页数据条数 | + | | page_num | 当前页码 | + | 作者: | 吴勇刚 | 2021.09.06 | + 函数位置:src/Public/Common/utils.py + | 返回参数 | 格式{dict} | + | | total | 总数据量 | + | | pageNum | 当前页码 | + | | pageSize | 每页展示数据条数 | + | | size | 当前页实际展示条数 | + | | startRow | 当前页面展示的开始行数 | + | | endRow | 当前页面展示的结束行数 | + | | pages | 总页数 | + | | prePage | 前一个展示页面页码 | + | | nextPage | 后一个展示页面页码 | + | | isFirstPage | 是否为首页 | + | | isLastPage | 是否为尾页 | + | | hasPreviousPage | 是否有上一页 | + | | hasNextPage | 是否有下一页 | + | | navigatePages | 控件展示页码数,一般固定为8 | + | | navigatepageNums | 控件展示的页码列表 | + | | navigateFirstPage | 控件开始页码 | + | | navigateLastPage | 控件结束页码 | + """ + re_data = {} + # 总数据量 + if total_numbers: + re_data['total'] = int(total_numbers) + elif data_list: + re_data['total'] = len(data_list) + # 当前页码 + re_data['pageNum'] = int(page_num) + # 每页数据条数 + re_data['pageSize'] = int(page_size) + # 每页数据条数 + if re_data['pageNum'] * re_data['pageSize'] <= re_data['total']: + re_data['size'] = re_data['pageSize'] + elif (re_data['pageNum'] - 1) * re_data['pageSize'] < re_data['total'] < re_data['pageNum'] * re_data[ + 'pageSize']: + re_data['size'] = re_data['total'] - (re_data['pageNum'] - 1) * re_data['pageSize'] + else: + re_data['size'] = 0 + # 总页数 + re_data['pages'] = math.ceil(re_data['total'] / re_data['pageSize']) + # 当前页面展示的开始行数,当前页面展示的结束行数 + start_row = (re_data['pageNum'] - 1) * re_data['pageSize'] + 1 + end_row = re_data['pageNum'] * re_data['pageSize'] + if start_row > re_data['total']: + re_data['startRow'] = 0 + re_data['endRow'] = 0 + else: + re_data['startRow'] = start_row + if end_row > re_data['total']: + re_data['endRow'] = re_data['total'] + else: + re_data['endRow'] = end_row + # 前一个展示页面页码 + re_data['prePage'] = re_data['pageNum'] - 1 + # 后一个展示页面页码 + if end_row >= re_data['total']: + re_data['nextPage'] = 0 + else: + re_data['nextPage'] = re_data['pageNum'] + 1 + # 是否为首页,是否为尾页 + if re_data['pageNum'] == 1: + re_data['isFirstPage'] = True + else: + re_data['isFirstPage'] = False + # 是否为尾页 + if start_row > re_data['total'] or end_row < re_data['total']: + re_data['isLastPage'] = False + elif start_row <= re_data['total'] <= end_row: + re_data['isLastPage'] = True + # 是否有上一页 + if not re_data['isFirstPage']: + re_data['hasPreviousPage'] = True + else: + re_data['hasPreviousPage'] = False + # 是否有下一页 + if re_data['pageNum'] >= re_data['pages']: + re_data['hasNextPage'] = False + else: + if not re_data['isLastPage']: + re_data['hasNextPage'] = True + else: + re_data['hasNextPage'] = False + # 控件展示页码数,一般固定为8 + re_data['navigatePages'] = 8 + # 控件结束页码 + if 8 < re_data['pageNum'] + 3 <= re_data['pages']: + re_data['navigateLastPage'] = re_data['pageNum'] + 3 + elif re_data['pageNum'] + 3 <= 8 < re_data['pages']: + re_data['navigateLastPage'] = 8 + else: + re_data['navigateLastPage'] = re_data['pages'] + if re_data['navigateLastPage'] - 8 + 1 > 0: + re_data['navigateFirstPage'] = re_data['navigateLastPage'] - 8 + 1 + else: + re_data['navigateFirstPage'] = 1 + # 控件展示的页码列表 + re_data['navigatepageNums'] = [i for i in range(re_data['navigateFirstPage'], re_data['navigateLastPage'] + 1)] + return re_data + + @staticmethod + def should_be_str_contain_str(src_str, *sub_str, raise_flag=True): + """ + 功能:判断src_str包含任意一个sub_str + | 输入参数: | src_str | 原始数据 | + | | *sub_str | 子数据 | + | | raise_flag | 不包含时是否抛出异常 | + | 作者: | 华学敏 | 2021.10. 08 | + 函数位置:src/Public/Common/utils.py + """ + for s_str in sub_str: + if str(s_str) in str(src_str): + return True + if raise_flag: + raise RuntimeError("{} not contain {}".format(str(src_str), str(sub_str))) + else: + return False + + @staticmethod + def compare_data_equal_as_list(sub_list, obj_value): + """ + 功能:判断list中是否含某个值 + | 输入参数: | sub_list | 列表,返回列表 | + | | obj_value | 目标值 | + | 作者: | qxj | 2021.06. 04 | + 函数位置: + | 包含 | compare_data_equal_as_list([1,'a',2,'aa'], 1) | success | + """ + if not isinstance(sub_list, list): + raise RuntimeError("【ERROR】返回参数data必须是list类型,sub_list:{0}".format(type(sub_list))) + equal = False + for value in sub_list: + try: + sub_str = eval(obj_value) + except Exception as e: + sub_str = obj_value + + if str(value) == str(sub_str): + equal = True + break + if equal: + return equal + else: + raise RuntimeError("【ERROR】返回参数data中没有找到值,sub_list:{0},valueP{1}".format(sub_list, obj_value)) + + def copy_account_to_hhi(self, email): + query_hhi_account = "SELECT id FROM hhi_account.account WHERE account_email='{}' limit 1;".format(email) + resp_hhi_account = self.db_con.select_one(query_hhi_account, choose_db='hhi') + if resp_hhi_account: + print("账号已在hhi存在,请检查!") + else: + query_account = "SELECT * FROM account.account WHERE account_email='{}' limit 1;".format(email) + resp_account = self.db_con.select_one(query_account, choose_db='huohua') + if not resp_account: + print("账号在火花库不存在,请检查!") + else: + # account.account + value_account_str = self._get_sql_value_by_dict(resp_account) + insert_hhi_account = "INSERT INTO `hhi_account`.`account`(`id`, `account_unique`, `account_name`, `account_email`, `area_code`, `phone_code`, `mask_phone`, `pwd`, `account_type`, `account_status`, `apply_resource`, `start_date`, `end_date`, `account_owner_id`, `account_dept_id`, `work_order_no`, `data_level`, `employee_id`, `fei_shu_id`, `dict_item_id`, `account_source`, `created_time`, `creator_id`, `creator_name`, `modified_time`, `modifier_id`, `modifier_name`, `pwd_modified`) SELECT {} FROM hhi_account.account LIMIT 1;".format( + value_account_str) + print(insert_hhi_account) + self.db_con.insert_one(insert_hhi_account, choose_db='hhi') + + update_end_date = "update `hhi_account`.`account` set end_date='2022-04-28',account_status=0 where account_email='{}'".format( + email) + self.db_con.execute(update_end_date, choose_db='hhi') + + # account.account_email + query_account_email = "SELECT * FROM account.account_email WHERE account_email='{}' limit 1;".format( + email) + resp_account_email = self.db_con.select_one(query_account_email, choose_db='huohua') + if resp_account_email: + value_account_email_str = self._get_sql_value_by_dict(resp_account_email) + insert_hhi_account_email = "INSERT INTO `hhi_account`.`account_email`(`id`, `account_id`, `account_unique`, `account_email`, `first_choice`, `email_type_value`, `created_time`, `creator_id`, `creator_name`, `modified_time`, `modifier_id`, `modifier_name`) SELECT {} FROM `hhi_account`.`account_email` LIMIT 1;".format( + value_account_email_str) + print(insert_hhi_account_email) + self.db_con.insert_one(insert_hhi_account_email, choose_db='hhi') + + # emp.employee + query_employee = "SELECT `id`, `name`, `employee_no`, `pwd`, `phone`, `email`, `leader_id`, `leader_name`, `position`, `position_no`, `position_level`, `data_level`, `department_id`, `department_no`, `department_name`, `corp_code`, `status`, `cid`, `cname`, `mid`, `mname`, `ctime`, `mtime`, `deleted`, `wechat_code`, `wechat_qrcode_image`, `wechat_qrcode_string`, `avatar`, `cc_hot_line`, `cc_cno`, `cc_pwd`, `cc_bind_tel`, `cc_bind_tel_tmp`, `work_phone`, `frozen`, `qywx_user_id`, `work_tel`, `work_mobile`, `bind_tel_type`, `city_id`, `city_name`, `city_level`, `nick_name`, `sex`, `birthday`, `birthplace`, `education`, `graduated_school`, `intro`, `nature_of_work`, `position_time`, `creator_id`, `creator_name`, `created_time`, `modifier_id`, `modifier_name`, `modified_time`, `change_sign_time`, `account_status`, `dingding_id`, `department_post_id`, `department_post_name`, `dimission_time`, `cc_work_tel`, `cc_work_mobile`, `mask_phone`, `phone_code`, `contract_company`, `pre_prob_date`, `show_name`, `name_pinyin`, `feishu_zhs`, `feishu_eng`, `business_unit`, `international_code`, `first_name_py`, `last_name_py`, `name_ac`, `name_format`, `first_name`, `middle_name`, `last_name`, `reg_region` FROM `emp`.`employee` WHERE email='{}' limit 1;".format( + email) + resp_employee = self.db_con.select_one(query_employee, choose_db='huohua') + if resp_employee: + value_employee_str = self._get_sql_value_by_dict(resp_employee) + insert_hhi_employee = "INSERT INTO `hhi_emp`.`employee`(`id`, `name`, `employee_no`, `pwd`, `phone`, `email`, `leader_id`, `leader_name`, `position`, `position_no`, `position_level`, `data_level`, `department_id`, `department_no`, `department_name`, `corp_code`, `status`, `cid`, `cname`, `mid`, `mname`, `ctime`, `mtime`, `deleted`, `wechat_code`, `wechat_qrcode_image`, `wechat_qrcode_string`, `avatar`, `cc_hot_line`, `cc_cno`, `cc_pwd`, `cc_bind_tel`, `cc_bind_tel_tmp`, `work_phone`, `frozen`, `qywx_user_id`, `work_tel`, `work_mobile`, `bind_tel_type`, `city_id`, `city_name`, `city_level`, `nick_name`, `sex`, `birthday`, `birthplace`, `education`, `graduated_school`, `intro`, `nature_of_work`, `position_time`, `creator_id`, `creator_name`, `created_time`, `modifier_id`, `modifier_name`, `modified_time`, `change_sign_time`, `account_status`, `dingding_id`, `department_post_id`, `department_post_name`, `dimission_time`, `cc_work_tel`, `cc_work_mobile`, `mask_phone`, `phone_code`, `contract_company`, `pre_prob_date`, `show_name`, `name_pinyin`, `feishu_zhs`, `feishu_eng`, `business_unit`, `international_code`, `first_name_py`, `last_name_py`, `name_ac`, `name_format`, `first_name`, `middle_name`, `last_name`, `reg_region`) SELECT {} FROM `hhi_emp`.`employee` LIMIT 1;".format( + value_employee_str) + print(insert_hhi_employee) + self.db_con.insert_one(insert_hhi_employee, choose_db='hhi') + + update_employee_info = "update `hhi_emp`.`employee` set birthday='2022-04-28 10:00:00',pre_prob_date='2022-05-16',dimission_time='2023-04-28 10:00:00',change_sign_time='2022-04-28 10:00:00' where email='{}'".format( + email) + self.db_con.execute(update_employee_info, choose_db='hhi') + + # emp.employee_email + query_employee_email = "SELECT * FROM emp.employee_email where employee_email_value='{}' limit 1;".format( + email) + resp_employee_email = self.db_con.select_one(query_employee_email, choose_db='huohua') + if resp_employee_email: + value_employee_email_str = self._get_sql_value_by_dict(resp_employee_email) + insert_hhi_employee_email = "INSERT INTO `hhi_emp`.`employee_email`(`id`, `employee_id`, `employee_no`, `employee_email_type`, `employee_email_value`, `primary_flag`, `status`, `creator_id`, `creator_name`, `created_time`, `modifier_id`, `modifier_name`, `modified_time`) SELECT {} FROM `hhi_emp`.`employee_email` LIMIT 1;".format( + value_employee_email_str) + print(insert_hhi_employee_email) + self.db_con.insert_one(insert_hhi_employee_email, choose_db='hhi') + + @staticmethod + def _get_sql_value_by_dict(value_dict): + value_str = "" + for v in value_dict.values(): + value_str = value_str + "'" + str(v) + "'" + "," + value_str = value_str[:-1] + # print(value_str) + return value_str + + def wait_and_query_data(self, sql, wait_time=20): + """ + | 功能说明: | 针对数据入库有延迟的场景,根据sql和循环的时间查询并返回数据库查询结果 | + | 输入参数: | sql | 查询数据库对应的sql语句 | + | | wait_time | 等待的最长时间 | + | 返回参数: | db_info | 数据库查询结果 | + 函数位置:src/Public/Common/utils.py + """ + try_time = 0 + if sql: + while try_time < int(wait_time): + db_info = MySqLHelper().select_one(sql=sql) + if db_info: + obj_log.info('try_time={}'.format(try_time)) + return db_info + else: + obj_log.info('try_time={}'.format(try_time)) + time.sleep(5) + try_time += 5 + else: + raise Exception('数据落库超时{}s,请检查'.format(try_time)) + else: + raise Exception('查询sql不能为空......') + + def to_lower_camel(self, name, ): + """下划线转小驼峰法命名""" + return re.sub('_([a-zA-Z])', lambda m: (m.group(1).upper()), name.lower()) + + def to_snake(self, name): + """驼峰转下划线""" + name = re.sub(r'([a-z])([A-Z])', r'\1_\2', name) + return name.lower() + + def change_dict_key_format(self, obj_dict, c_type=1): + """ + 将dict或[dict]中的所有key,从下划线模式转为驼峰模式,或者反向转换 + Args: + obj_dict: 原始dict或list + c_type: 类型:1-下划线转驼峰,2-驼峰转下划线 + Returns: + 转化后的dict,或list + """ + if isinstance(obj_dict, list): + new_dict = [] + for item in obj_dict: + new_dict.append(self.__change_dict_key(c_dict=item, c_type=c_type)) + return new_dict + elif isinstance(obj_dict, dict): + return self.__change_dict_key(c_dict=obj_dict, c_type=c_type) + + def __change_dict_key(self, c_dict, c_type=1): + """ + 内置函数,供change_dict_key_format使用 + Args: + c_dict: 原始dict + c_type: 类型:1-下划线转驼峰,2-驼峰转下划线 + Returns: + 转化后的dict + """ + new_dict = {} + if c_type == 1: + for k in c_dict.keys(): + new_key = self.to_lower_camel(name=k) + new_dict[new_key] = c_dict[k] + elif c_type == 2: + for k in c_dict.keys(): + new_key = self.to_snake(name=k) + new_dict[new_key] = c_dict[k] + return new_dict + + def dict_to_sql_condition(self, dicts): + """ + | 功能说明: | 字典转sql条件;默认会把驼峰key转为下划线| | + | 输入参数: | {} + | 返回参数: | WHERE key='' AND key='' + | 作者信息: | 陈江 | 2022.12.09 | + """ + try: + dicts = eval(dicts) + except: + dicts = dicts + if not dicts: + raise RuntimeError("参数不能为空") + condition = "WHERE " + count = 0 + for k, v in dicts.items(): + condition_key = self.to_snake(k) + if count == 0: + condition += condition_key + "='{}'".format(v) + count = count + 1 + else: + condition += " AND " + condition_key + "='{}'".format(v) + return condition + + def check_http_return_info(self, rsp, exp_code=200, exp_msg="", exp_success=True): + """ + 功能说明:检查http接口返回字段 + 入参: + | rsp | 要检查的接口返回值 | + | exp_code | 期望返回的code | + | exp_msg | 期望返回的message | + | exp_success | 期望返回的success | + 返回值:无,检验通过则继续,否则抛出对应异常 + """ + exp_info = {"code": exp_code, + "success": exp_success, + "message": exp_msg} + self.should_be_equal_as_json(obj_json=rsp, sub_json=exp_info) + + @staticmethod + def update_default_parameters_by_input(input_parameters, default_parameters): + """ + 功能:用接口输入的参数去更新默认参数:有则更新,没有则在默认参数中新增,要求两个参数都的是dict + Args: + input_parameters: 接口输入参数 + default_parameters: 默认参数 + Returns: 更新后的default_parameters + """ + for p_key in input_parameters: + default_parameters[p_key] = input_parameters[p_key] + return default_parameters + + @staticmethod + def get_value_from_post_data_input(post_data_input, key, default_value=None): + """ + 功能:从用户入参post_data_input中找到对应key的value并返回,没有则赋予default_value值并返回 + Args: + post_data_input: 用户输入的入参 + key: 期望从入参中找的key + default_value: 当入参为空,或没有对应的key时,默认赋予的值 + Returns: + 对应key中的value或default_value + """ + # post_data_input.get(key) == 'default' 是用于兼容GPT的调用 + if post_data_input is None or post_data_input.get(key) is None \ + or post_data_input.get(key) in ["", "0", "default"]: + return default_value + else: + return post_data_input.get(key) + + @staticmethod + def get_value_from_kwargs(kwargs, key, required=0, default_value=None): + """ + 功能:从入参kwargs中获取指定key的参数值 + Args: + kwargs: 参数dict + key: 参数key + required: 是否必填:0-非必现,1-必填,若必须填不在kwargs里,则抛出对应异常 + default_value: 非必填项参数不在kwargs里时,赋予的默认值 + Returns: key在kwargs中的具体值,或缺省时的默认值 + """ + if required == 1 and key not in kwargs: + raise BusinessError("参数:{}是必填项,请出入....".format(key)) + if key in kwargs: + return kwargs[key] + else: + return default_value + + @staticmethod + def get_day_of_month(): + """ + 功能:获取当天的日期号数,比如今天是11月12号,return 12 + Returns: + """ + today = datetime.datetime.now() + return today.day + + @staticmethod + def formate_msg_for_aita(r_msg): + """ + 格式化输出用于aita页面展示的消息,要求return_data + Args: + r_msg: 函数返回值,要求中含以下三个字段 + for_gpt: 给gpt的内容,默认第一个为标题,原数据用\n字符分割,描述与值用:分割 + for_user: 给用户在aita页面展示的内容,原数据用\n字符分割,描述与值用:分割 + for_link: 跳转链接,格式:{"link_url":"链接地址","link_str":“链接名称,没有则默认为:[点击查询详情...]“} + Returns: + 标题(加粗,改色) + 描述1 值1 + **** *** + 描述2 值2 + 跳转链接 + """ + for_gpt = r_msg["for_gpt"] if "for_gpt" in r_msg else None + for_user = r_msg["for_user"] if "for_user" in r_msg else None + for_link = r_msg["for_link"] if "for_link" in r_msg else None + if for_gpt: + del r_msg["for_gpt"] + gpt_list = for_gpt.split('\n') + for_gpt = "{}
".format(gpt_list[0]) + for item in gpt_list[1:]: + value_list = item.replace(':', ':').split(':') + # 参数display:inline-block让两个div标签在一行内显示 + key_value = "
" + " " + value_list[0] + "
" \ + + "
" + value_list[1] + "

" + for_gpt = for_gpt + key_value + r_msg["for_gpt"] = for_gpt[:-4] # 删除最后一个换行标签
,因为aita的前端在for_gpt和for_user的显示上做了换行 + if for_user: + del r_msg["for_user"] + for_user_list = for_user.split('\n') + for_user = "" + for item in for_user_list: + value_list = item.replace(':', ':').split(':') + key_value = "
" + " " + value_list[0] + "
"\ + + "
" + value_list[1] + "

" + for_user = for_user + key_value + r_msg["for_user"] = for_user + if for_link: + del r_msg["for_link"] + if "link_str" not in for_link: + for_link["link_str"] = "点击查询详情..." + link_string = "[{}]".format(for_link["link_url"], for_link["link_str"]) + r_msg["for_user"] = r_msg["for_user"] + "
" + " " + link_string + "
" + return r_msg + + +if __name__ == '__main__': + tl = Tools() + # return_data = {"for_gpt": "班级创建成功!\n班级code:CZ24011200722017\n班级id为:1200120727", + # "for_user": "教师id:168389\n手机号:15564555315\n课程id:841626\n用户列表:[1987505, 1987506]", + # "for_link": {"link_url": "https://htmv2.qa.huohua.cn/classManage/classesDetail?id=1200121194", + # "link_str": "点击查看班级详情..."}} + # aa = tl.formate_msg_for_aita(r_msg=return_data) + # print(aa) + # oj = {'data': {'result': False, 'messages': ['课程id:841618已有待执行超售模式,导表操作失败,请检查数据']}} + # sj = {'data': {'result': True, 'messages': []}} + # # oj = [1, 2, 3] + # # sj = [1, 3, 23] + # print(tl.should_be_equal_as_json(obj_json=oj, sub_json=sj)) + + a = {'success': True, 'message': '', 'code': 200, 'data': [{'id': 6854, 'userId': 976049, 'employeeId': 599677, 'accountId': 0, 'code': '78843427', 'name': '教务自动化0706165031', 'nickname': '新账号599677', 'sex': 0, 'phone': '180****6279', 'maskPhone': '180****6279', 'phoneCode': '290141', 'natureOfWork': 1, 'teacherTrainNums': [0], 'email': 'jiaowuzidonghua0706165031@huohua.cn', 'idNumber': '510704199205064230', 'birthday': '1970-01-01 00:00:00', 'seniority': 0, 'education': 1, 'entryTime': '2021-01-14 00:00:00', 'graduateInstitutions': '测试', 'nationalityCountryId': 0, 'nationality': 1, 'nativePlace': 1, 'avatar': 'https://stalegacy.huohua.cn/image/huohua/avatar/default/default_avatar1.png', 'introduce': '测试', 'status': 1, 'openModeTypes': '0', 'introduceVideoUrl': '', 'teacherCertificationUrl': '', 'probationStartDate': '', 'probationEndDate': '', 'gradeGroupType': 3, 'creatorId': 1, 'creatorName': 'EHR用户同步', 'createdTime': '2021-07-06 16:48:22', 'modifierId': 1, 'modifierName': 'sys', 'modifiedTime': '2021-07-06 16:49:17', 'classLocation': 1, 'jobType': 4, 'liveroomCenter': 1, 'professorRange': '1', 'qualityScore': 0.0, 'classQuota': 16, 'teacherRank': 0, 'lastRankTime': '2021-07-06 16:49:17', 'rankEffectTime': '2021-01-14 00:00:00', 'rankStatus': 1, 'departmentId': 17, 'departmentName': '教学部', 'leaderId': 0, 'leaderName': '‘’', 'changeSignTime': '', 'liveroomCode': '', 'eyeProtection': 0, 'legoTeacher': 0, 'legoProbationStartDate': '', 'legoProbationEndDate': '', 'activationStatus': 1, 'dimissionTime': '', 'dimissionSetTime': '', 'teacherType': 1, 'liveroomAllocationStatus': 2, 'flexible': 0, 'classroomVideo': '', 'classroomImg': '', 'pipStart': '1970-01-01 00:00:00', 'pipEnd': '1970-01-01 00:00:00', 'teacherLabel': 1, 'longLeaveStart': '1970-01-01 00:00:00', 'longLeaveEnd': '1970-01-01 00:00:00', 'forecastResignDate': '1970-01-01 00:00:00', 'teacherLabelSubType': 1, 'schoolExcellentTeacher': 0, 'innovationAPlusTeacher': 0, 'businessLine': 101, 'onlineType': 0, 'timeZoneName': '', 'timeZoneCode': '', 'teacherLabelList': [], 'labelList': ''}]} + b = {'success': True, 'message': '', 'code': 200, 'data': [{'id': 6854, 'userId': 976049, 'employeeId': 599677, 'code': '78843427', 'name': '教务自动化0706165031', 'nickname': '新账号599677', 'sex': 0, 'phone': '180****6279', 'maskPhone': '180****6279', 'phoneCode': '290141', 'natureOfWork': 1, 'teacherTrainNums': [0], 'email': 'jiaowuzidonghua0706165031@huohua.cn', 'idNumber': '510704199205064230', 'birthday': '1970-01-01 00:00:00', 'seniority': 0, 'education': 1, 'entryTime': '2021-01-14 00:00:00', 'graduateInstitutions': '测试', 'nationality': 1, 'nativePlace': 1, 'avatar': 'https://stalegacy.huohua.cn/image/huohua/avatar/default/default_avatar1.png', 'introduce': '测试', 'status': 1, 'openModeTypes': '0', 'introduceVideoUrl': '', 'teacherCertificationUrl': '', 'probationStartDate': '', 'probationEndDate': '', 'gradeGroupType': 3, 'creatorId': 1, 'creatorName': 'EHR用户同步', 'createdTime': '2021-07-06 16:48:22', 'modifierId': 1, 'modifierName': 'sys', 'modifiedTime': '2021-07-06 16:49:17', 'classLocation': 1, 'jobType': 4, 'liveroomCenter': 1, 'professorRange': '1', 'qualityScore': 0.0, 'classQuota': 16, 'teacherRank': 0, 'lastRankTime': '2021-07-06 16:49:17', 'rankEffectTime': '2021-01-14 00:00:00', 'rankStatus': 1, 'departmentId': 17, 'departmentName': '教学部', 'leaderId': 0, 'leaderName': '‘’', 'changeSignTime': '', 'liveroomCode': '', 'eyeProtection': 0, 'legoTeacher': 0, 'legoProbationStartDate': '', 'legoProbationEndDate': '', 'activationStatus': 1, 'dimissionTime': '', 'dimissionSetTime': '', 'teacherType': 1, 'liveroomAllocationStatus': 2, 'flexible': 0, 'classroomVideo': '', 'classroomImg': '', 'pipStart': '1970-01-01 00:00:00', 'pipEnd': '1970-01-01 00:00:00', 'teacherLabel': 1, 'longLeaveStart': '1970-01-01 00:00:00', 'longLeaveEnd': '1970-01-01 00:00:00', 'forecastResignDate': '1970-01-01 00:00:00', 'teacherLabelSubType': 1, 'schoolExcellentTeacher': 0, 'innovationAPlusTeacher': 0, 'businessLine': 101, 'timeZoneName': '', 'timeZoneCode': '', 'teacherLabelList': []}]} + + print(tl.should_be_equal_as_json(obj_json=a, sub_json=b)) diff --git a/base_framework/public_tools/webdriver.py b/base_framework/public_tools/webdriver.py new file mode 100644 index 0000000..5e7d16d --- /dev/null +++ b/base_framework/public_tools/webdriver.py @@ -0,0 +1,283 @@ +# -*- coding: UTF-8 -*- +import os +import time +import platform +from seleniumwire import webdriver +from selenium.webdriver.support.wait import WebDriverWait +from selenium.webdriver.support import expected_conditions as ec +from selenium.webdriver.common.keys import Keys +from selenium.webdriver.support.select import Select +from base_framework.public_tools.read_config import get_current_config, ReadConfig, InitConfig +from base_framework.base_config.current_pth import env_choose_path +from copy import copy, deepcopy +from selenium.webdriver.common.action_chains import ActionChains + +cull_path = os.path.dirname(os.path.abspath(__file__)) +plugs = os.path.abspath(os.path.join(cull_path, '../{}'.format('/platform_tools/plugins/headerenv.crx'))) +cc_driver = os.path.abspath(os.path.join(cull_path, '../{}'.format('/public_business/CC/webdriver/chromedriver.exe'))) +DEFAULT_TIMEOUT = 10 + + +class DriverInit(InitConfig): + def __init__(self, driver_path=None): + + # super(DriverInit, self).__init__() + InitConfig.__init__(self, run_user_name=None, current_evn=None) + self.driver_path = cc_driver if not driver_path else driver_path + self.config = ReadConfig(env_choose_path) + self.jira_id = ReadConfig(env_choose_path).get_value('run_jira_id', 'huohua-podenv') + self.option = webdriver.ChromeOptions() + self.option.add_argument('--no-sandbox') + self.option.add_argument('--disable-gpu') + self.option.add_argument('--disable-dev-shm-usage') + # self.option.headless = True + self.option.add_extension(plugs) + # self._init_driver() + + def _init_driver(self): + self.driver = webdriver.Chrome(executable_path=self.driver_path, options=self.option) + self.driver.scopes = [ + '.*huohua.cn.*', '.*\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+.*', '.*allschool.com', '.*sparkedu.com' + ] + self.driver.implicitly_wait(30) + self.driver.maximize_window() + self.actions = ActionChains(self.driver) + self.wait = WebDriverWait(self.driver, timeout=60) + if self.jira_id: + self._set_independent_environment() + + def _set_independent_environment(self): + backend = self.jira_id + frontend = 'feature/%s' % self.jira_id + new_loc = ('xpath', '/html/body/div/div[3]/button/div/div/i') + # 独立环境名称 + rule_name_loc = ('id', 'rule-name') + # 规则类型 + rule_type_loc = ('xpath', '//*[@id="edit-page"]/div[2]/div[1]/div/div[2]/div[2]/div[2]/div[3]/div') + # 头名称 + rule_headerName_loc = ('id', 'rule-headerName') + # 头内容 + rule_headerValue_loc = ('id', 'rule-headerValue') + # 保存 + create_loc = ('xpath', '//*[@id="edit-page"]/div[2]/div[2]/div[2]/div[2]/button/div/div') + + # js = 'window.open("chrome-extension://eningockdidmgiojffjmkdblpjocbhgh/options/options.html");' + # self.driver.execute_script(js) + self.driver.get("chrome-extension://eningockdidmgiojffjmkdblpjocbhgh/options/options.html") + rule_headerName = 'huohua-podenv' + rule_headerValue = backend + self.clickElement(new_loc) + self.sendKeysElement(rule_name_loc, "back") + self.clickElement(rule_type_loc) + self.sendKeysElement(rule_headerName_loc, rule_headerName) + self.sendKeysElement(rule_headerValue_loc, rule_headerValue) + self.clickElement(create_loc) + rule_headerName = 'huohua-feature' + rule_headerValue = frontend + self.clickElement(new_loc) + self.sendKeysElement(rule_name_loc, "front") + self.clickElement(rule_type_loc) + self.sendKeysElement(rule_headerName_loc, rule_headerName) + self.sendKeysElement(rule_headerValue_loc, rule_headerValue) + self.clickElement(create_loc) + + def waitEleDisappear(self, locator, timeout=10): + count = 0 + while count <= timeout: + try: + self.wait.until(ec.invisibility_of_element_located(locator=locator)) + break + except Exception as error: + time.sleep(1) + count += 1 + + def elementScroolToView(self, locator): + ele = self.findElement(locator=locator, timeout=10) + self.driver.execute_script("arguments[0].focus()", ele) + self.driver.execute_script("arguments[0].scrollIntoView();", ele) + + def findElement(self, locator, timeout=10): + """ + | 功能说明: | 传入元素定位器,定位到该元素,返回第一个元素| + | 传入参数: | locator | + 举例说明: + """ + element = WebDriverWait(self.driver, timeout).until(ec.presence_of_element_located(locator)) + return element + + def findElements(self, locator, timeout=10): + """ + | 功能说明: | 传入元素定位器,定位到该元素,返回所有元素| + | 传入参数: | locator | + 举例说明: + """ + element = WebDriverWait(self.driver, timeout).until(ec.presence_of_all_elements_located(locator)) + return element + + def clickElement(self, locator, timeout=10): + """ + | 功能说明: | 传入元素定位器,定位到该元素,普通方法点击| + | 传入参数: | locator | + 举例说明: + """ + element = self.findElement(locator, timeout) + element.click() + + def clickElement_by_JS(self, locator=None, element=None, timeout=10): + """ + | 功能说明: | 传入元素定位器,定位到该元素,以JS的方式点击| + | 传入参数: | locator | 元素定位器 | + | element | 页面对象 | + 举例说明: + """ + if not element and locator: + obj = self.findElement(locator, timeout) + self.driver.execute_script("arguments[0].click();", obj) + elif element and not locator: + self.driver.execute_script("arguments[0].click();", element) + else: + raise Exception('不支持的传参方式,locator和element必须且只能传一个') + + def sendKeysElement(self, locator, text, timeout=10): + """ + | 功能说明: | 传入元素定位器,定位到该元素,清空输入框,写入text| + | 传入参数: | locator,text | + 举例说明: + """ + element = self.findElement(locator, timeout) + element.send_keys(Keys.CONTROL, 'a') + element.send_keys(text) + + def getElementText(self, locator, timeout=10): + """ + | 功能说明: | 传入元素定位器,定位到该元素,返回该元素的文本值| + | 传入参数: | locator | + 举例说明: + """ + element = self.findElement(locator, timeout) + return element.text + + def clickSingleBox(self, locator, timeout=10): + """ + | 功能说明: | 传入单选框元素定位器,定位到该元素,依次点击单选框| + | 传入参数: | locator | + 举例说明: + """ + elements = self.findElements(locator, timeout) + for element in elements: + self.driver.execute_script("arguments[0].click();", element) + time.sleep(1.5) + + def selectDropDownBox(self, locator, index=0, timeout=10): + """ + | 功能说明: | 传入下拉框定位器,定位到该元素,选择下拉框的第index个选项| + | 传入参数: | locator,index | + 举例说明: + """ + select = Select(self.findElement(locator, timeout)) + select.select_by_index(index) + + # 选择多选框-->全选 + def clickCheckbox(self, locator, timeout=10): + """ + | 功能说明: | 传入多选框元素定位器,定位到该元素,全选| + | 传入参数: | locator | + 举例说明: + """ + checkbox = self.findElements(locator, timeout) + for i in checkbox: + if not i.is_selected(): + i.click() + + def uploadFile(self, locator, path, timeout=10): + """ + | 功能说明: | 传入元素定位器,定位到该元素,传入文件路径,只适用于input标签| + | 传入参数: | locator | + 举例说明: + """ + element = self.findElement(locator, timeout) + element.send_keys(path) + + def roll_windows(self, x=0, y=0): + """ + 滑动窗口 + :param x: x轴距离 + :param y: y轴距离 + :return: 无 + """ + self.driver.execute_script("window.scrollBy({},{})".format(x, y)) + + def back_window(self): + """ + 返回上一页(浏览器工具栏向左箭头) + :return: 无 + """ + self.driver.back() + + def forward_window(self): + """ + 前进一页(浏览器工具栏向右箭头) + :return: 无 + """ + self.driver.forward() + + def close_current_window(self): + """ + 关闭当前窗口 + :return: 无 + """ + self.driver.close() + + def get_window_title(self): + """ + 关闭当前窗口 + :return: 无 + """ + current_title = self.driver.title + return current_title + + def get_element_attribute_value(self, locator, attr: str, timeout=10): + """ + 关闭当前窗口 + :param locator 定位器 + :param attr 属性名 + :param timeout 元素查找超时时间 + :return: 无 + """ + obj = self.findElement(locator, timeout=timeout) + attr_value = obj.get_attribute(attr) + return attr_value + + def modify_tag_attribution(self, locator, attr, value, timeout=10): + """ + 修改页签属性 + :param locator: 需要修改的元素定位器 + :param attr: 修改属性名 + :param value: 修改后的值 + :param timeout: 超时时间 + :return: + """ + obj = self.findElement(locator, timeout) + self.driver.execute_script("arguments[0].{attr}={value};".format(attr=attr, value=value), obj) + + def input_to_readonly_tag(self, locator, text, timeout=10): + """ + 带只读属性的标签输入 + :param locator: 元素定位器 + :param text: 输入内容 + :param timeout: 超时时间 + :return:无 + """ + obj = self.findElement(locator, timeout) + self.driver.execute_script("arguments[0].removeAttribute('readonly');", obj) + self.sendKeysElement(locator=locator, text=text) + + def takeScreenshot(self, savePath, pictureName): + """ + | 功能说明: | 截取浏览器当前页面| + | 传入参数: | savePath:保存地址 | + | | pictureName:图片保存名称 + 举例说明: + """ + picturePath = os.path.join(savePath, pictureName + '.png') + self.driver.get_screenshot_as_file(picturePath) diff --git a/base_framework/public_tools/websocket_api.py b/base_framework/public_tools/websocket_api.py new file mode 100644 index 0000000..3e81de4 --- /dev/null +++ b/base_framework/public_tools/websocket_api.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +# @Author : qiaoxinjiu +# @Time : 2021/07/13 +# @File : websocket_api.py + +import time +import json +from functools import wraps +import requests +from websocket import create_connection +from base_framework.public_tools.log import get_logger +from base_framework.public_tools.sqlhelper import MySqLHelper +from base_framework.public_tools.read_config import InitConfig + + +def check_conn(func): + @wraps(func) + def wrap_check_conn(self, *args, **kwargs): + self.ws = self.get_ws_conn() + self.logger.info('%s connected websocket server' % func.__name__) + return func(self, *args, **kwargs) + + return wrap_check_conn + + +class WebSocketAPI(InitConfig): + def __init__(self, user_name=None, env_name=None, timeout=30): + # super().__init__(run_user_name=user_name, current_evn=env_name) + InitConfig.__init__(self, run_user_name=user_name, current_evn=env_name) + self.sso_url = self.all_cfg[self.current_evn]['sso_url'] + self.code_url = self.all_cfg[self.current_evn]['code_url'] + self.token_url = self.all_cfg[self.current_evn]['token_url'] + self.redirect_url = self.all_cfg[self.current_evn]['teach_opt_url'] + self.auth_code = self.all_cfg['Authorization']['sparkle-manage'] + self.logger = get_logger() + self.db_conn = MySqLHelper() + + def get_session(self): + session = requests.Session() + session.headers.update({'Authorization': self.auth_code}) + if not hasattr(self, 'access_token'): + self.access_token = self._get_access_token() + session.headers.update({'accesstoken': self.access_token}) + return session + + def _get_access_token(self): + token_session = requests.Session() + post_data = {'showUsername': self.show_username, 'username': self.username, 'password': self.password} + resp1 = token_session.post(self.sso_url, data=post_data, allow_redirects=False) + assert resp1.status_code == 302, 'incorrect response code %s' % resp1.status_code + get_data = {'client_id': 'tm-manage', 'response_type': 'code', + 'redirect_uri': self.redirect_url} + resp2 = token_session.get(self.code_url, params=get_data, allow_redirects=False) + assert resp2.status_code == 302, 'incorrect response code %s' % resp2.status_code + tmp = resp2.headers + code = tmp.get('Location').split('=')[1] + post_data2 = {'grant_type': 'authorization_code', 'code': code, 'redirect_uri': self.redirect_url} + token_session.headers.update({'Authorization': self.auth_code}) + resp3 = token_session.post(self.token_url, data=post_data2) + token = json.loads(resp3.text).get('access_token') + return token + + def get_ws_conn(self, timeout=60): + self.logger.info('websocket connecting...') + self.access_token = self._get_access_token() + uri = self.all_cfg[self.current_evn]['websocket_server'] + if 'accesstoken' in uri: + self.uri = uri + else: + self.uri = uri + '/?accesstoken=%s' % self.access_token if uri.endswith( + '/') else uri + '/?accesstoken=%s' % self.access_token + ws_conn = create_connection(self.uri, timeout=timeout) + if ws_conn.getstatus() == 101: + return ws_conn + else: + raise Exception('connect websocket server failed!') + + def _close_ws(self): + if hasattr(self, 'ws'): + self.ws.close() + self.logger.info('connection closed success!') + + @check_conn + def send_msg_and_get_response(self, send_data: [dict, str], timeout=3): + if isinstance(send_data, dict): + data = json.dumps(send_data) + self.ws.send(data) + self.logger.info(">>> heart beat!") + self.logger.info(">>> send message:%s" % data) + return self.get_response(ws_conn=self.ws, tm_stamp=send_data.get("timestamp"), timeout=timeout) + elif isinstance(send_data, str): + data = send_data + self.ws.send(data) + self.logger.info(">>> heart beat!") + self.logger.info(">>> send message:%s" % data) + return self.get_response(ws_conn=self.ws, tm_stamp=json.loads(send_data).get("timestamp"), timeout=timeout) + else: + raise Exception('send_data can only support dict or str type') + + def get_response(self, ws_conn, tm_stamp=None, timeout=3): + t0 = time.time() + while time.time() - t0 <= int(timeout): + time.sleep(0.2) + out_put = ws_conn.recv() + if tm_stamp: + if str(tm_stamp) in out_put: + self.logger.info(">>> received message:%s" % out_put) + return json.loads(out_put) + elif out_put: + return json.loads(out_put) + else: + continue + else: + self._close_ws() + raise Exception('>>> received no response, please check ws connect!') diff --git a/base_framework/public_tools/xml_file_api.py b/base_framework/public_tools/xml_file_api.py new file mode 100644 index 0000000..d473ee1 --- /dev/null +++ b/base_framework/public_tools/xml_file_api.py @@ -0,0 +1,39 @@ +# -*- coding:utf-8 -*- +# 功能:对xml文件的操作函数 + +import os +import sys +import xml.etree.ElementTree as ET + + +class XmlFileApi: + def __init__(self, file_path): + self.file_path = file_path + self.root = ET.parse(file_path).getroot() + + def get_contain_by_tag_names(self, tag_names): + """ + 功能:根据多级标签名查询xml文件中的内容 + Args: + tag_names: 多级标签名,从根目录开始,中间用/分隔,例如:statistics/total/stat + Returns: + 对应标签名的内容,格式:[{"text": xxx, "attrib": yyy}] + """ + # for child in self.root: + # print(child.tag, child.attrib) + elements = self.root.findall('.//' + tag_names) + if len(elements) == 0: + print("根据标签:{},未找到对应的内容,请检查标签名是否正确".format(tag_names)) + return + return_data = [] + for element in elements: + return_data.append({"text": element.text, "attrib": element.attrib}) + return return_data + + +if __name__ == '__main__': + xf = XmlFileApi(file_path="C:/Users/17447/Downloads/output_gue_it.xml") + xf.get_contain_by_tag_names(tag_names='statistics/total/stat') + + # C:/Users/17447/Downloads/output_gue_it.xml + # C:/Users/17447/Downloads/output_gue_it.xml \ No newline at end of file diff --git a/base_framework/startup.py b/base_framework/startup.py new file mode 100644 index 0000000..5f41bc2 --- /dev/null +++ b/base_framework/startup.py @@ -0,0 +1,70 @@ +# coding: utf-8 +import argparse +import os +import sys +import configparser +# 这几项添加路径,必须放前面,否则后续import会报错 +# 获取组名 +input_team_name = sys.argv +try: + team_index = input_team_name.index('-t') +except ValueError: + raise Exception("组名是必填参数,请设置....") +else: + TEAM_NAME = input_team_name[team_index+1] +BASIC_PATH = os.path.dirname(os.path.abspath(__file__)) +TEAM_PATH = os.path.abspath(os.path.join(BASIC_PATH, '../{}/'.format(TEAM_NAME))) +sys.path.append(TEAM_PATH) +PROJECT_PATH = os.path.abspath(os.path.join(BASIC_PATH, '../')) +sys.path.append(PROJECT_PATH) +env_choose_path = os.path.join(BASIC_PATH, 'base_config', 'env_choose.ini') +# 获取业务标示:hh or hhi +try: + current_business_index = input_team_name.index('-b') + current_business = input_team_name[current_business_index + 1] +except ValueError: + current_business = "hh" +# 获取环境 QA or SIM +try: + current_evn_index = input_team_name.index('-e') + current_evn = input_team_name[current_evn_index + 1] +except ValueError: + current_evn = "QA" + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="Choose current environment") + parser.add_argument("-b", dest='business', help="hh or hhi", default='hh') + parser.add_argument("-e", dest='env', help="QA or SIM", default='QA') + parser.add_argument("-u", dest='user', help="user", default='lrq') + parser.add_argument("-j", dest='jira_id', help="jira_id", default='') + parser.add_argument("-t", dest='team_name', help="team_name", default='') + args = parser.parse_args() + # choose_env = args.env + choose_user = args.user + # jira_id = args.jira_id + business = args.business + jira_id = args.jira_id + if jira_id.lower() == 'sim': + args.env = 'SIM' + choose_env = args.env + + # 将命令行参数写入env_choose.ini文件中 + cof = configparser.ConfigParser() + cof.read(env_choose_path, encoding='utf-8') + cof.set(section="run_evn_name", option="current_business", value=args.business.lower()) + cof.set(section="run_evn_name", option="current_evn", value=args.env.upper()) + cof.set(section="run_user_name", option="default_user", value=args.user.lower()) + cof.set(section="run_jira_id", option="huohua-podenv", value=args.jira_id.upper()) + cof.set(section="run_evn_name", option="current_team", value=args.team_name.upper()) + with open(env_choose_path, 'w') as fw: # 循环写入 + cof.write(fw) + + if jira_id: + print("running jira_id is:【{}】【{}】【{}】".format(business, TEAM_NAME, jira_id)) + else: + print("running environment is: 【{}】【{}】【{}】".format(business, TEAM_NAME, choose_env)) + + from base_framework.main import main, kill_pid + kill_pid() + main() diff --git a/zhyy/__init__.py b/zhyy/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/zhyy/library/BusinessKw/SZPurchase/ContractManage.py b/zhyy/library/BusinessKw/SZPurchase/ContractManage.py new file mode 100644 index 0000000..e69de29 diff --git a/zhyy/library/BusinessKw/SZPurchase/PurchaseOrderManage.py b/zhyy/library/BusinessKw/SZPurchase/PurchaseOrderManage.py new file mode 100644 index 0000000..868f3a7 --- /dev/null +++ b/zhyy/library/BusinessKw/SZPurchase/PurchaseOrderManage.py @@ -0,0 +1,97 @@ +# -*- coding:utf-8 -*- +""" +Author: qiaoxinjiu +Email: qiaoxinjiu@sparkedu.com +Create Date: 2026/01/22 5:58 下午 +""" +import logging +import os +import sys + +# 添加项目根目录到 Python 路径,以便导入 base_framework 模块 +current_file_path = os.path.abspath(__file__) +project_root = os.path.abspath(os.path.join(os.path.dirname(current_file_path), '../../../../')) +if project_root not in sys.path: + sys.path.insert(0, project_root) + +from base_framework.public_tools import log +from base_framework.public_tools.my_faker import MyFaker +from base_framework.public_tools.runner import Runner +from base_framework.public_tools.pgsqlhelper import PgSqlHelper +from base_framework.public_tools import read_config +from base_framework.public_tools import utils +from base_framework.public_tools import mg_keyword +from zhyy.library.ZZYY_interface import ZhyyInterface + +obj_get_log = log.get_logger() +obj_my_faker = MyFaker() +obj_runner = Runner() +obj_pgsql_helper = PgSqlHelper() +obj_get_way = utils.Tools() +obj_mg_keyword = mg_keyword.ManageKeyWord() + + +class PurchaseOrder(ZhyyInterface): + def __init__(self): + ''' + 这个是针对于读取配置文件的初始化函数,用于读取默认参数 + ''' + super().__init__() + self.config_index_path = os.path.dirname(os.path.abspath(__file__)) + self.config_index_filePath = os.path.join(self.config_index_path, "purchase.ini") + self.config_index_content = read_config.ReadConfig(filename=self.config_index_filePath) + + def kw_zhyy_get_purchase_page_post(self, note, user, **kwargs): + """ + | 功能说明: | 返回采购工作台采购单列表数据 | + | 输入参数: | note | 注释 | + |user | 用户信息,传入 'purchase' 默认读取配置文件里面 'purchase' 对应的默认账号信息| + |supplier_company_ids | 供应商id | 非必填 + |payment_status | 付款状态 | 非必填 + |status | 采购单状态 | 非必填 + |order_sn | 采购单号 | 非必填 + |page_no | 页码 | 必填 + |page_size | 每页条数 | 必填 + | 返回参数: | {"success":true,"message":"success","code":200,"data": + {'todoTask':['PO260116003','PO260115010'],'inProcessTask':['PO260116003','PO260115010']}} | | + | 作者信息: | 谯新久 | 修改时间 | 2022-8-20 | + """ + logging.info("==========={0}===========".format(note)) + # 获取所有参数 + supplier_company_ids = kwargs.get("supplier_company_ids") + payment_status = kwargs.get("payment_status") + status = kwargs.get("status") + order_sn = kwargs.get("order_sn") + page_no = kwargs.get("page_no") + page_size = kwargs.get("page_size") + + # 检查必填参数 + if not page_no or not page_size: + raise Exception("页码和每页条数不能为空") + + # 组装参数字典,只包含非空字段,参数名使用 pageNo 和 pageSize + request_params = { + "pageNo": page_no, + "pageSize": page_size + } + + # 如果字段不为空,才添加到参数字典中 + if supplier_company_ids is not None and supplier_company_ids != "": + request_params["supplier_company_ids"] = supplier_company_ids + if payment_status is not None and payment_status != "": + request_params["payment_status"] = payment_status + if status is not None and status != "": + request_params["status"] = status + if order_sn is not None and order_sn != "": + request_params["order_sn"] = order_sn + + # 使用 ** 方式解包字典传递参数 + get_todo_info = self.kw_in_zhyy_purchase_order_page_post(user=user, **request_params) + print(get_todo_info if get_todo_info else "查询失败") + return get_todo_info + + +if __name__ == '__main__': + test = PurchaseOrder() + a = test.kw_zhyy_get_purchase_page_post(user='purchase', note="测试", page_no=1, page_size=10) + print(a) diff --git a/zhyy/library/BusinessKw/SZPurchase/PurchasePlanManage.py b/zhyy/library/BusinessKw/SZPurchase/PurchasePlanManage.py new file mode 100644 index 0000000..e69de29 diff --git a/zhyy/library/BusinessKw/SZPurchase/__init__.py b/zhyy/library/BusinessKw/SZPurchase/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/zhyy/library/BusinessKw/SZPurchase/__pycache__/PurchaseOrderManage.cpython-38.pyc b/zhyy/library/BusinessKw/SZPurchase/__pycache__/PurchaseOrderManage.cpython-38.pyc new file mode 100644 index 0000000..e0eafa8 Binary files /dev/null and b/zhyy/library/BusinessKw/SZPurchase/__pycache__/PurchaseOrderManage.cpython-38.pyc differ diff --git a/zhyy/library/BusinessKw/SZPurchase/__pycache__/__init__.cpython-38.pyc b/zhyy/library/BusinessKw/SZPurchase/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000..56ce41d Binary files /dev/null and b/zhyy/library/BusinessKw/SZPurchase/__pycache__/__init__.cpython-38.pyc differ diff --git a/zhyy/library/BusinessKw/SZPurchase/__pycache__/index.cpython-38.pyc b/zhyy/library/BusinessKw/SZPurchase/__pycache__/index.cpython-38.pyc new file mode 100644 index 0000000..33bc8c7 Binary files /dev/null and b/zhyy/library/BusinessKw/SZPurchase/__pycache__/index.cpython-38.pyc differ diff --git a/zhyy/library/BusinessKw/SZPurchase/index.py b/zhyy/library/BusinessKw/SZPurchase/index.py new file mode 100644 index 0000000..830d09b --- /dev/null +++ b/zhyy/library/BusinessKw/SZPurchase/index.py @@ -0,0 +1,88 @@ +# -*- coding:utf-8 -*- +""" +Author: qiaoxinjiu +Email: qiaoxinjiu@sparkedu.com +Create Date: 2022/08/20 5:58 下午 +""" +import logging +import os +import sys + +# 添加项目根目录到 Python 路径,以便导入 base_framework 模块 +current_file_path = os.path.abspath(__file__) +project_root = os.path.abspath(os.path.join(os.path.dirname(current_file_path), '../../../../')) +if project_root not in sys.path: + sys.path.insert(0, project_root) + +from base_framework.public_tools import log +from base_framework.public_tools.my_faker import MyFaker +from base_framework.public_tools.runner import Runner +from base_framework.public_tools.pgsqlhelper import PgSqlHelper +from base_framework.public_tools import read_config +from base_framework.public_tools import utils +from base_framework.public_tools import mg_keyword +from zhyy.library.ZZYY_interface import ZhyyInterface + +obj_get_log = log.get_logger() +obj_my_faker = MyFaker() +obj_runner = Runner() +obj_pgsql_helper = PgSqlHelper() +obj_get_way = utils.Tools() +obj_mg_keyword = mg_keyword.ManageKeyWord() + + +class PurchaseIndex(ZhyyInterface): + def __init__(self): + ''' + 这个是针对于读取配置文件的初始化函数,用于读取默认参数 + ''' + super().__init__() + self.config_index_path = os.path.dirname(os.path.abspath(__file__)) + self.config_index_filePath = os.path.join(self.config_index_path, "purchase.ini") + self.config_index_content = read_config.ReadConfig(filename=self.config_index_filePath) + + def kw_zhyy_get_todo(self, note, user): + """ + | 功能说明: | 返回采购工作台首页待办任务的PO与在办任务PO | + | 输入参数: | note | 注释 | + |user | 用户信息,传入 'purchase' 默认读取配置文件里面 'purchase' 对应的默认账号信息| + | 返回参数: | {"success":true,"message":"success","code":200,"data": + {'todoTask':['PO260116003','PO260115010'],'inProcessTask':['PO260116003','PO260115010']}} | | + | 作者信息: | 谯新久 | 修改时间 | 2022-8-20 | + """ + logging.info("==========={0}===========".format(note)) + get_todo_info = self.kw_in_zhyy_purchase_todo_get(user=user) + if get_todo_info['code'] != 0: + raise Exception("查询采购待办任务失败: {}".format(get_todo_info)) + get_todo_info["message"] = "查询采购待办任务成功" + data = get_todo_info.get("data") + if data is None: + raise Exception("返回数据为空,data字段不存在") + list_get_todo_task = data.get("todoTask") or [] + list_get_process_task = data.get("inProcessTask") or [] + list_todo_task_po = [] + list_process_task_po = [] + for todoTask in list_get_todo_task: + if isinstance(todoTask, dict): + list_todo_task_po.append(todoTask.get("businessSn")) + for processTask in list_get_process_task: + if isinstance(processTask, dict): + list_process_task_po.append(processTask.get("businessSn")) + get_todo_info["data"]["todoTask"] = list_todo_task_po + get_todo_info["data"]["inProcessTask"] = list_process_task_po + if list_todo_task_po: + test_purchase = list_todo_task_po[0] + # 表在public schema中,使用public.erp_purchase_order格式 + sql = "SELECT * FROM public.erp_purchase_order WHERE order_sn = '{}'".format(test_purchase) + try: + obj_pgsql_helper.select_one(sql) + except Exception as e: + # 如果查询失败,记录日志但不影响主流程 + obj_get_log.warning("查询采购订单表失败,订单号:{},错误:{}".format(test_purchase, str(e))) + return get_todo_info + + +if __name__ == '__main__': + test = PurchaseIndex() + a = test.kw_zhyy_get_todo(user='purchase',note="测试") + print(a) diff --git a/zhyy/library/BusinessKw/SZPurchase/purchase.ini b/zhyy/library/BusinessKw/SZPurchase/purchase.ini new file mode 100644 index 0000000..3565730 --- /dev/null +++ b/zhyy/library/BusinessKw/SZPurchase/purchase.ini @@ -0,0 +1,2 @@ +[qa-user] +user_info ={"studentId":21797349,"sex":0,"nickName":"auto st test","birthday":1640966400000,"avatar":"https://stalegacy.huohua.cn/image/huohua/avatar/default/default_avatar1.png"} \ No newline at end of file diff --git a/zhyy/library/BusinessKw/__init__.py b/zhyy/library/BusinessKw/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/zhyy/library/BusinessKw/__pycache__/__init__.cpython-38.pyc b/zhyy/library/BusinessKw/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000..e3f1867 Binary files /dev/null and b/zhyy/library/BusinessKw/__pycache__/__init__.cpython-38.pyc differ diff --git a/zhyy/library/CommonFun/__init__.py b/zhyy/library/CommonFun/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/zhyy/library/CommonFun/__pycache__/__init__.cpython-38.pyc b/zhyy/library/CommonFun/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000..a103240 Binary files /dev/null and b/zhyy/library/CommonFun/__pycache__/__init__.cpython-38.pyc differ diff --git a/zhyy/library/CommonFun/__pycache__/asset_common.cpython-38.pyc b/zhyy/library/CommonFun/__pycache__/asset_common.cpython-38.pyc new file mode 100644 index 0000000..abc741d Binary files /dev/null and b/zhyy/library/CommonFun/__pycache__/asset_common.cpython-38.pyc differ diff --git a/zhyy/library/CommonFun/__pycache__/contract_pair_check.cpython-38.pyc b/zhyy/library/CommonFun/__pycache__/contract_pair_check.cpython-38.pyc new file mode 100644 index 0000000..b28df69 Binary files /dev/null and b/zhyy/library/CommonFun/__pycache__/contract_pair_check.cpython-38.pyc differ diff --git a/zhyy/library/CommonFun/__pycache__/course_package_common.cpython-38.pyc b/zhyy/library/CommonFun/__pycache__/course_package_common.cpython-38.pyc new file mode 100644 index 0000000..8557aad Binary files /dev/null and b/zhyy/library/CommonFun/__pycache__/course_package_common.cpython-38.pyc differ diff --git a/zhyy/library/CommonFun/__pycache__/env_check.cpython-38.pyc b/zhyy/library/CommonFun/__pycache__/env_check.cpython-38.pyc new file mode 100644 index 0000000..90cb7a9 Binary files /dev/null and b/zhyy/library/CommonFun/__pycache__/env_check.cpython-38.pyc differ diff --git a/zhyy/library/CommonFun/__pycache__/handle_aita.cpython-38.pyc b/zhyy/library/CommonFun/__pycache__/handle_aita.cpython-38.pyc new file mode 100644 index 0000000..4839ff1 Binary files /dev/null and b/zhyy/library/CommonFun/__pycache__/handle_aita.cpython-38.pyc differ diff --git a/zhyy/library/CommonFun/__pycache__/handle_harmonyos.cpython-38.pyc b/zhyy/library/CommonFun/__pycache__/handle_harmonyos.cpython-38.pyc new file mode 100644 index 0000000..a58fde1 Binary files /dev/null and b/zhyy/library/CommonFun/__pycache__/handle_harmonyos.cpython-38.pyc differ diff --git a/zhyy/library/CommonFun/__pycache__/handle_jenkins.cpython-38.pyc b/zhyy/library/CommonFun/__pycache__/handle_jenkins.cpython-38.pyc new file mode 100644 index 0000000..fc0dfeb Binary files /dev/null and b/zhyy/library/CommonFun/__pycache__/handle_jenkins.cpython-38.pyc differ diff --git a/zhyy/library/CommonFun/__pycache__/handle_jira.cpython-38.pyc b/zhyy/library/CommonFun/__pycache__/handle_jira.cpython-38.pyc new file mode 100644 index 0000000..d4003f7 Binary files /dev/null and b/zhyy/library/CommonFun/__pycache__/handle_jira.cpython-38.pyc differ diff --git a/zhyy/library/CommonFun/__pycache__/handle_login.cpython-38.pyc b/zhyy/library/CommonFun/__pycache__/handle_login.cpython-38.pyc new file mode 100644 index 0000000..1be6c0c Binary files /dev/null and b/zhyy/library/CommonFun/__pycache__/handle_login.cpython-38.pyc differ diff --git a/zhyy/library/CommonFun/__pycache__/handle_tools.cpython-38.pyc b/zhyy/library/CommonFun/__pycache__/handle_tools.cpython-38.pyc new file mode 100644 index 0000000..759301a Binary files /dev/null and b/zhyy/library/CommonFun/__pycache__/handle_tools.cpython-38.pyc differ diff --git a/zhyy/library/CommonFun/__pycache__/host_update.cpython-38.pyc b/zhyy/library/CommonFun/__pycache__/host_update.cpython-38.pyc new file mode 100644 index 0000000..2ab3bc1 Binary files /dev/null and b/zhyy/library/CommonFun/__pycache__/host_update.cpython-38.pyc differ diff --git a/zhyy/library/CommonFun/__pycache__/statistical_function.cpython-38.pyc b/zhyy/library/CommonFun/__pycache__/statistical_function.cpython-38.pyc new file mode 100644 index 0000000..ae45bf1 Binary files /dev/null and b/zhyy/library/CommonFun/__pycache__/statistical_function.cpython-38.pyc differ diff --git a/zhyy/library/CommonFun/__pycache__/ubrd_now_time.cpython-38.pyc b/zhyy/library/CommonFun/__pycache__/ubrd_now_time.cpython-38.pyc new file mode 100644 index 0000000..068e8be Binary files /dev/null and b/zhyy/library/CommonFun/__pycache__/ubrd_now_time.cpython-38.pyc differ diff --git a/zhyy/library/CommonFun/__pycache__/user_common.cpython-38.pyc b/zhyy/library/CommonFun/__pycache__/user_common.cpython-38.pyc new file mode 100644 index 0000000..113f9e6 Binary files /dev/null and b/zhyy/library/CommonFun/__pycache__/user_common.cpython-38.pyc differ diff --git a/zhyy/library/CommonFun/__pycache__/user_message.cpython-38.pyc b/zhyy/library/CommonFun/__pycache__/user_message.cpython-38.pyc new file mode 100644 index 0000000..89046b8 Binary files /dev/null and b/zhyy/library/CommonFun/__pycache__/user_message.cpython-38.pyc differ diff --git a/zhyy/library/CommonFun/asset_common.py b/zhyy/library/CommonFun/asset_common.py new file mode 100644 index 0000000..995fef0 --- /dev/null +++ b/zhyy/library/CommonFun/asset_common.py @@ -0,0 +1,57 @@ +# -*- coding:utf-8 -*- + +""" +Author: 陈江 +Email: chenjiang@huohua.cn +Create Date: 2022/03/03 11:25 下午 +""" +from base_framework.public_tools import log +from base_framework.public_tools.sqlhelper import MySqLHelper + +obj_log = log.get_logger() +obj_my_sql_helper = MySqLHelper() + +class AssetCommon: + def __init__(self): + pass + + def get_course_package_info_by_name(self, course_package_name): + """ + 功能:根据套餐名称获取套餐基本信息 + | 输入参数: | course_package_name string | 套餐名称 | + """ + sql = "SELECT id,code,name,price FROM `peppa`.`course_package` WHERE `name`='{}';".format(course_package_name) + return obj_my_sql_helper.select_one(sql) + + def get_order_info_V2(self, **where_condition): + # user_id = None, course_package_id = None, course_id = None, business_line_type = None, order_id = None, order_code = None + ocp = ['user_id', 'business_line_type', 'order_id', 'order_code', 'status'] + sprl = ['course_package_id', 'course_id'] + where_str = None + for k in where_condition.keys(): + t = '' + if k in sprl: + t = 'sprl' + else: + t = 'ocp' + + if not where_str: + where_str = '{}.{}=\'{}\''.format(t, k, where_condition.get(k)) + else: + where_str = '{} AND {}.{}=\'{}\''.format(where_str, t, k, where_condition.get(k)) + if not where_str: + obj_log.error('参数必填') + return False + sql = "SELECT ocp.id as order_id,ocp.code,ocp.audit_status,ocp.status as order_status,ocp.user_id,ocp.user_address_id,ocp.business_line_type,sprl.course_package_id,sprl.course_id,sprl.subscribe_id,sprl.valid as subscribe_pre_valid,sprl.id as subscribe_pre_id FROM order_center.order_course_package ocp LEFT JOIN order_center.subscribe_pre_create_log sprl ON ocp.id=sprl.order_id WHERE {} ORDER BY ocp.id DESC LIMIT 1".format( + where_str.replace('order_id', 'id')) + obj_log.info(sql) + return obj_my_sql_helper.select_one(sql) + + +if __name__ == '__main__': + user_common_obj = AssetCommon() + print(user_common_obj.get_order_info(**{'order_id': 1193182, 'business_line_type': '1', 'status': 100})) + + + + diff --git a/zhyy/library/CommonFun/contract_pair_check.py b/zhyy/library/CommonFun/contract_pair_check.py new file mode 100644 index 0000000..5ef4fd8 --- /dev/null +++ b/zhyy/library/CommonFun/contract_pair_check.py @@ -0,0 +1,240 @@ +# -*- coding:utf-8 -*- +# @Time : 2023/3/7 13:28 +# @Author: luozhipeng +# @File : contract_pair_check.py +import os +import sys +input_team_name = sys.argv + +BASIC_PATH = os.path.dirname(os.path.abspath(__file__)) +TEAM_PATH = os.path.abspath(os.path.join(BASIC_PATH, '../../../{}'.format("base_framework"))) +sys.path.append(TEAM_PATH) +PROJECT_PATH = os.path.abspath(os.path.join(BASIC_PATH, '../../..')) +sys.path.append(PROJECT_PATH) +from base_framework.public_tools.sqlhelper import MySqLHelper +from base_framework.public_tools.utils import Tools +import requests +import re +import json +import pymysql + +obj_my_sql_helper = MySqLHelper() + + +class ContractPairCheck(): + def __init__(self): + pass + + def get_unfinished_interface(self): + sql_get_interface = "SELECT distinct (x.con_url) 提供方接口,x.pro_url 消费方接口,x.con_method 提供方请求方式,x.con_server 提供方服务,x.pro_server 消费方服务,x.at_num 自动化接口数 FROM sparkatp.contract_pair x WHERE x.at_num =0 and x.status =1 and x.con_server in (select si.server_name from sparkatp.swagger_info si where si.team in ('ubrd','GUE') and access_type IN (2)) and x.pro_server not in (select si.server_name from sparkatp.swagger_info si where si.team ='ubrd' and access_type IN (2)) and x.roles = 0 order by x.pro_server,x.con_url" + unfinished_interface = obj_my_sql_helper.select_all(sql_get_interface) + return unfinished_interface + + def send_reshult(self): + + headers = {"Content-Type": "application/json;charset=UTF-8"} + web_hook = "https://open.feishu.cn/open-apis/bot/v2/hook/9f3556b7-cb60-44bf-adbf-24b5b2552014" + + contract_pair_intf = self.get_unfinished_interface() + if len(contract_pair_intf) != 0: + message_data = {"msg_type": "text", "content": {'text': '未完成自动化的契约对接口{}'.format(contract_pair_intf)}} + + rsp = requests.post(url=web_hook, json=message_data, headers=headers) + + +class AutoInterfaceCheck(): + def __init__(self): + pass + + def get_unfinished_interface(self): + sql_get_interface = """select id,in_url from sparkatp.interface_info a WHERE( a.swagger_id in (SELECT id FROM sparkatp.swagger_info WHERE team in ("UBRD","GUE") and access_type not IN (0)) ) AND a.created_time > "2023-01-01 00:00:50" AND is_used = 1 and (case_numbers IS NULL or case_numbers='') and offline=0 and jira_id is null order by created_time """ + unfinished_interface_list = obj_my_sql_helper.select_all(sql_get_interface) + return unfinished_interface_list + + def get_interface_jira(self,interface): + sql_req_time = "select max(created_time) created_time from sparkatp.request_parameters rp where rp.interface_id ={}".format( + interface["id"]) + req_max_time = obj_my_sql_helper.select_one(sql_req_time)['created_time'] + sql_rep_time = "select max(created_time) created_time from sparkatp.response_parameters rp where rp.interface_id ={}".format( + interface["id"]) + rep_max_time = obj_my_sql_helper.select_one(sql_rep_time)['created_time'] + if rep_max_time and req_max_time: + if rep_max_time <= req_max_time: + sql_req_jira = "select distinct (jira_id) from sparkatp.request_parameters rp where rp.interface_id ={0} and created_time = '{1}'".format( + interface["id"], req_max_time) + interface_jira = obj_my_sql_helper.select_one(sql_req_jira)["jira_id"] + else: + sql_rep_jira = "select distinct (jira_id) from sparkatp.response_parameters rp where rp.interface_id ={0} and created_time = '{1}'".format( + interface["id"], rep_max_time) + interface_jira = obj_my_sql_helper.select_one(sql_rep_jira)["jira_id"] + elif rep_max_time and not req_max_time: + sql_rep_jira = "select distinct (jira_id) from sparkatp.response_parameters rp where rp.interface_id ={0} and created_time = '{1}'".format( + interface["id"], rep_max_time) + interface_jira = obj_my_sql_helper.select_one(sql_rep_jira)["jira_id"] + elif not rep_max_time and req_max_time: + sql_req_jira = "select distinct (jira_id) from sparkatp.request_parameters rp where rp.interface_id ={0} and created_time = '{1}'".format( + interface["id"], req_max_time) + interface_jira = obj_my_sql_helper.select_one(sql_req_jira)["jira_id"] + return interface_jira + + def send_result(self): + unfinished_interface_list = self.get_unfinished_interface() + for interface in unfinished_interface_list: + interface['jira'] = self.get_interface_jira(interface) + if not interface['jira']: + interface['qa'] = None + else: + interface['qa'] = self.get_jira_qa(interface['jira']) + headers = {"Content-Type": "application/json;charset=UTF-8"} + web_hook = "https://open.feishu.cn/open-apis/bot/v2/hook/9f3556b7-cb60-44bf-adbf-24b5b2552014" + if len(unfinished_interface_list) != 0: + message_data = {"msg_type": "text", "content": {'text': 'QA公共环境未完成自动化的接口{}'.format(unfinished_interface_list)}} + rsp = requests.post(url=web_hook, json=message_data, headers=headers) + + def get_jira_qa(self,jira): + try: + conn = pymysql.connect(host='10.250.200.53',user='root',password='peppa@test',database='tools',charset="utf8",port=3306) + except : + raise pymysql.OperationalError("连接数据库失败") + cn =conn.cursor() + sql = "SELECT tester FROM tools.tm_project where jira_number = '{}'".format(jira) + cn.execute(sql) + qa = cn.fetchall() + cn.close() + conn.close() + if qa: + return qa[0][0] + else: + return None + + +class CoverageCheck(): + + # 连接数据库 + @staticmethod + def get_select(sql): + try: + conn = pymysql.connect(host='10.250.200.53', user='root', password='peppa@test', database='tools', + charset="utf8", port=3306) + except: + raise pymysql.OperationalError("连接数据库失败") + cn = conn.cursor() + cn.execute(sql) + res = cn.fetchall() + cn.close() + conn.close() + return res + + # 获取未搜集覆盖率的项目 + @staticmethod + def get_unfinished_coverage(): + start_time = Tools().get_format_date(r_type=15, add_days=-2) + end_time = Tools().get_format_date(r_type=16, add_days=-1) + + # 查询最近一天上线有后端代码变动,需要搜集覆盖率项目 + need_sql = """SELECT env FROM tools.project_plan WHERE ID IN + (SELECT project_id FROM tools.project_tester WHERE tester IN ("陈洁","陈江","罗志鹏","谯新久","刘涛婷")) + AND status IN (14) AND rd_code_add_line>0 AND (it_start_date IS NOT NULL OR qa_start_date IS NOT null) + AND online_date BETWEEN '{}' AND '{}'""".format(start_time, end_time) + need_coverage_list = CoverageCheck().get_select(need_sql) + + # 查询已搜集覆盖率项目 + sql_implemented_sql = '''SELECT `env_name` FROM `sparkatp`.`build_jacoco` WHERE `team` = 'UBRD' AND `status` = '1' + AND `is_delete` = '0' AND `is_pass` = '1' ''' + completed_coverage_list = obj_my_sql_helper.select_all(sql_implemented_sql) + + unfinished_coverage_list = [] + for item1 in range(0, len(need_coverage_list)): + unfinished_coverage_list.append(need_coverage_list[item1][0]) + + # 返回未搜集覆盖率的项目 + for item in completed_coverage_list: + completed_jira = item["env_name"] + if completed_jira in unfinished_coverage_list: + unfinished_coverage_list.remove(completed_jira) + # 根据unfinished_coverage_list 查jira_name + jira_name_list = [] + for item in unfinished_coverage_list: + env_name = item + sql = """SELECT jira_number FROM tools.project_plan WHERE env='{}'""".format(env_name) + jira_name = CoverageCheck().get_select(sql) + jira_name_list.append(jira_name[0][0]) + return jira_name_list + + # 获取未构建基线用例项目 + @staticmethod + def get_not_bulid_jira(): + start_time = Tools().get_format_date(r_type=15, add_days=-2) + end_time = Tools().get_format_date(r_type=16, add_days=-1) + + # 返回有后端变动的jira (有后端代码变更&上线时间在3天内 + jira存在服务变更,项目状态为sim测试) + need_sql = """SELECT env FROM tools.project_plan WHERE + ID IN (SELECT project_id FROM tools.project_tester WHERE tester IN ("陈洁","陈江","罗志鹏","谯新久","刘涛婷")) + AND status IN (14) AND rd_code_add_line>0 AND online_date BETWEEN '{}' AND '{}' + AND qa_start_date IS NOT NULL UNION SELECT env FROM tools.project_plan WHERE ID IN (SELECT project_plan_id + FROM tools.project_plan_server) AND jira_number IN (SELECT jira_number FROM tools.tm_project WHERE status + IN (12) AND `tester` IN ("陈洁","陈江","罗志鹏","谯新久","刘涛婷") AND test_qa_time_consume>0)""".format(start_time, + end_time) + + need_build_list = CoverageCheck().get_select(need_sql) + + # 返回有构建基线用例jira + built_sql = """SELECT DISTINCT special_env FROM sparkatp.build_info WHERE run_type=2 AND team='UBRD' AND STATUS=2""" + built_list = obj_my_sql_helper.select_all(built_sql) + + not_bulit_list = [] + # 取出有后端改动得jira + for item1 in range(0, len(need_build_list)): + not_bulit_list.append(need_build_list[item1][0]) + + # 返回未构建基线用例jira + for item in built_list: + completed_jira = item["special_env"] + if completed_jira in not_bulit_list: + not_bulit_list.remove(completed_jira) + jira_name_list = [] + for item in not_bulit_list: + env_name = item + sql = """SELECT jira_number FROM tools.project_plan WHERE env='{}'""".format(env_name) + jira_name = CoverageCheck().get_select(sql) + jira_name_list.append(jira_name[0][0]) + return jira_name_list + + # 发送消息 + def send_result(self, type=1): + if type == 1: + jira_list = self.get_unfinished_coverage() + elif type == 2: + jira_list = self.get_not_bulid_jira() + + send_list = [] + for item in jira_list: + send_dict = {} + send_dict["jira"] = item + qa_list = AutoInterfaceCheck().get_jira_qa(item) + send_dict["qa"] = qa_list + send_list.append(send_dict) + + headers = {"Content-Type": "application/json;charset=UTF-8"} + web_hook = "https://open.feishu.cn/open-apis/bot/v2/hook/b6bf33ae-4239-4bef-a8a8-21896e0d1ba1" + if len(jira_list) != 0: + if type == 1: + message_data = {"msg_type": "text", + "content": {'text': '未搜集覆盖率已上线项目{}'.format(send_list)}} + rsp = requests.post(url=web_hook, json=message_data, headers=headers) + elif type == 2: + message_data = {"msg_type": "text", + "content": {'text': '未构建基线用例已上线项目{}'.format(send_list)}} + rsp = requests.post(url=web_hook, json=message_data, headers=headers) + + +if __name__ == '__main__': + A = ContractPairCheck() + A.send_reshult() + B = AutoInterfaceCheck() + # B.get_unfinished_interface() + # B.get_jira_qa(jira = 'PLATFORM-31791') + B.send_result() + C = CoverageCheck() + C.send_result(type=1) + C.send_result(type=2) \ No newline at end of file diff --git a/zhyy/library/CommonFun/course_package_common.py b/zhyy/library/CommonFun/course_package_common.py new file mode 100644 index 0000000..fe4694a --- /dev/null +++ b/zhyy/library/CommonFun/course_package_common.py @@ -0,0 +1,31 @@ +# -*- coding:utf-8 -*- + +""" +Author: 罗志鹏 +Email: luozhipeng@huohua.cn +Create Date: 2022/03/03 11:25 下午 +""" +from base_framework.public_tools import log +from base_framework.public_tools.sqlhelper import MySqLHelper + +obj_log = log.get_logger() +obj_my_sql_helper = MySqLHelper() + +class CoursePackageCommon: + def __init__(self): + pass + + def get_course_package_info_by_name(self, course_package_name): + """ + 功能:根据套餐名称获取套餐基本信息 + | 输入参数: | course_package_name string | 套餐名称 | + """ + sql = "SELECT id,code,name,price FROM `peppa`.`course_package` WHERE `name`='{}';".format(course_package_name) + return obj_my_sql_helper.select_one(sql) +if __name__ == '__main__': + user_common_obj = CoursePackageCommon() + user_common_obj.get_course_package_info_by_name('测试杰拉德0325-火花-直播逻辑思维') + + + + diff --git a/zhyy/library/CommonFun/drawPicture.py b/zhyy/library/CommonFun/drawPicture.py new file mode 100644 index 0000000..1165be7 --- /dev/null +++ b/zhyy/library/CommonFun/drawPicture.py @@ -0,0 +1,31 @@ +import matplotlib.pyplot as plt +import matplotlib + +# 设置字体为支持中文的字体(如SimHei) +matplotlib.rcParams['font.sans-serif'] = ['SimHei'] # Windows系统 +# matplotlib.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # macOS系统 +matplotlib.rcParams['axes.unicode_minus'] = False # 解决负号显示问题 + +# 数据 +categories = ['供应链', '天窗', 'cc', 'la', '题库', '练测', '教务', '教师', '基础', '学生端', '家长端', '活字', '转介绍'] +values = [8, 4, 13, 20, 11, 2, 22, 10, 1, 1, 14, 1, 17] + +# 创建柱状图 +plt.figure(figsize=(10, 6)) +bars = plt.bar(categories, values, color='skyblue') + +# 添加标题和标签 +plt.title('各类别数量统计', fontsize=16) +plt.xlabel('类别', fontsize=12) +plt.ylabel('数量', fontsize=12) +plt.xticks(rotation=45, ha='right') # 旋转X轴标签 + +# 在每个柱子上方显示数量 +for bar in bars: + height = bar.get_height() + plt.text(bar.get_x() + bar.get_width() / 2, height, str(height), + ha='center', va='bottom', fontsize=10) + +# 显示图表 +plt.tight_layout() +plt.show() \ No newline at end of file diff --git a/zhyy/library/CommonFun/env_check.ini b/zhyy/library/CommonFun/env_check.ini new file mode 100644 index 0000000..e1a6cb1 --- /dev/null +++ b/zhyy/library/CommonFun/env_check.ini @@ -0,0 +1,7 @@ +[QA] +check_list = ['PEPPA-STUDENT-API','PEPPA-LEARNING-PLAN-LISTENER','PEPPA-LEARNING-PLAN-SERVER','PEPPA-TEACH-BIZ','PEPPA-TEACH-LISTENER','PEPPA-TEACH-TIMETABLE-SERVER','PEPPA-USER-AUTH-API','PEPPA-USER-CENTER-SERVER'] + + + +[SIM] +check_list = ['PEPPA-STUDENT-API','PEPPA-LEARNING-PLAN-LISTENER','PEPPA-LEARNING-PLAN-SERVER','PEPPA-TEACH-BIZ','PEPPA-TEACH-LISTENER','PEPPA-TEACH-TIMETABLE-SERVER','PEPPA-USER-AUTH-API','PEPPA-USER-CENTER-SERVER'] \ No newline at end of file diff --git a/zhyy/library/CommonFun/env_check.py b/zhyy/library/CommonFun/env_check.py new file mode 100644 index 0000000..dc696c7 --- /dev/null +++ b/zhyy/library/CommonFun/env_check.py @@ -0,0 +1,141 @@ +# -*- coding:utf-8 -*- + +""" +Author: 罗志鹏 +Email: luozhipeng@huohua.cn +Create Date: 2022/07/25 11:25 下午 +""" + +import requests +import re +import json + + + + +class EnvCheck(): + def __init__(self): + self.container_ip_list = [] + + + + def get_env_container_ip(self,env): + dis_server =[] + dis_ip = [] + headers = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9','Accept-Encoding':'gzip, deflate, br'} + if env =='QA': + req = requests.get(url="http://eureka.qa.huohua.cn/",headers=headers) + elif env == 'SIM': + req = requests.get(url="http://eureka.sim.huohua.cn/", headers=headers) + else: + raise RuntimeError("环境选择异常") + req_list =req.text.split('tbody') + # print(req_list[1]) + server_str_list = req_list[1].split('') + check_list = self.get_check_lsit(env=env) + + for server in check_list: + if req.text.find(server) ==-1: + dis_server.append(server) + + + for i in check_list: + for str_ser in server_str_list: + if str_ser.find(i) != -1: + dict_sever = {} + # ip_list = [] + str_sername_start = '' + str_sername_end = '' + server_name = str_ser[str_ser.find(str_sername_start) + 7:str_ser.find(str_sername_end)] + ip_list = re.findall(r'[a-z]://(.*?):8080', str_ser) + dict_sever[server_name] = ip_list + # print(dict_sever) + for ip_ch in ip_list: + url_check = "http://"+ip_ch+':8080/actuator/health' + resp = requests.get(url=url_check) + # print(resp.json()) + if resp.json()['status'] != 'UP': + dict_ip = {server_name:ip_ch} + dis_ip.append(dict_ip) + return dis_ip,dis_server + + + # re_list = [] + # server_list = [] + # for str_ser in server_check_list: + # print(str_ser) + + # print(server_name) + + # pattern = re.compile(r'') + # str_server = pattern.search(str_ser) + # server_name = str_ser[str_ser.start():str_ser.end()] + # print(server_name) + # for server in check_list : + # for string_server in server_list: + # if string_server.find(server)!= -1: + # # print(string_server.find(server)) + # re_list.append(string_server) + + # print(re_list) + # ip_list = [] + # for string_server in re_list : + # if string_server.find('http://'): + # pattern = re.compile(r'[a-zA-z]+://[^\s]*:8080') + # str_ip = pattern.search(string_server) + # # print(str_ip.start()) + # url = string_server[str_ip.start():str_ip.end()] + # print(url) + + def send_result(self): + dis_ip,dis_server = self.get_env_container_ip(env='SIM') + at_user_list = [{"tag": "at", "user_id": "{}".format("7020366259502153730")}] + + message_data_SIM_server = {"msg_type": "text", "content": {'text':'SIM服务未找到部署IP{}'.format(dis_server)}} + message_data_SIM_IP = {"msg_type": "text", "content": {'text':'SIM服务部署IP不可用{}'.format(dis_ip)}} + + headers = {"Content-Type": "application/json;charset=UTF-8"} + web_hook = "https://open.feishu.cn/open-apis/bot/v2/hook/28b775e7-a863-4807-b280-c82e09be0a80" + if len(dis_server) != 0: + json_data = json.dumps(message_data_SIM_server) + rsp = requests.post(url=web_hook, json=message_data_SIM_server, headers=headers) + + if len(dis_ip) != 0: + json_data = json.dumps(message_data_SIM_IP) + rsp = requests.post(url=web_hook, json=message_data_SIM_IP, headers=headers) + dis_ip,dis_server = self.get_env_container_ip(env='QA') + message_data_QA_server = {"msg_type": "text", "content": {'text':'QA服务未找到部署IP{}'.format(dis_server)}} + message_data_QA_IP = {"msg_type": "text", "content": {'text':'QA服务部署IP不可用{}'.format(dis_ip)}} + + if len(dis_server) != 0: + json_data = json.dumps(message_data_QA_server) + rsp = requests.post(url=web_hook, data=json_data, headers=headers) + if len(dis_ip) != 0: + json_data = json.dumps(message_data_QA_IP) + rsp = requests.post(url=web_hook, json=message_data_QA_IP, headers=headers) + + + + + + def get_check_lsit(self,env): + if env == 'QA': + check_list = ['PEPPA-STUDENT-API','PEPPA-LEARNING-PLAN-LISTENER','PEPPA-LEARNING-PLAN-SERVER','PEPPA-TEACH-BIZ','PEPPA-TEACH-LISTENER','PEPPA-TEACH-TIMETABLE-SERVER','PEPPA-USER-AUTH-API','PEPPA-USER-CENTER-SERVER'] + return check_list + if env == 'SIM': + check_list = ['PEPPA-STUDENT-API','PEPPA-LEARNING-PLAN-LISTENER','PEPPA-LEARNING-PLAN-SERVER','PEPPA-TEACH-BIZ','PEPPA-TEACH-LISTENER','PEPPA-TEACH-TIMETABLE-SERVER','PEPPA-USER-AUTH-API','PEPPA-USER-CENTER-SERVER'] + return check_list + + + + +if __name__ == '__main__': + # o ='"http://10.251.187.248:8080/actuator/info" "http://10.251.187.248:8080/actuator/info"' + # i =re.findall(r'[a-z]://(.*?):8080',o) + # print(a) + A=EnvCheck() + A.send_result() + + + + diff --git a/zhyy/library/CommonFun/handle_aita.py b/zhyy/library/CommonFun/handle_aita.py new file mode 100644 index 0000000..a3951b9 --- /dev/null +++ b/zhyy/library/CommonFun/handle_aita.py @@ -0,0 +1,141 @@ +# encoding: UTF-8 +import time +import subprocess +from subprocess import PIPE,Popen +import os + + +class OSType: + WIN, LINUX, UNKNOWN = range(3) + + def __init__(self): + pass + + @staticmethod + def get_type(): + import platform + system_name = platform.system() + if system_name.lower() == 'windows': + return OSType.WIN + elif system_name.lower() == 'linux': + return OSType.LINUX + else: + return OSType.UNKNOWN + + +class tool(object): + def __init__(self): + self.env_port = 5011 + + def run_process(self, cmd_str, out_p=False): + """ + run command + cmd_str unicode string. + """ + if OSType.WIN == OSType.get_type(): + # cmd_str = cmd_str.encode('gbk') + cmd_str = cmd_str + elif OSType.LINUX == OSType.get_type(): + cmd_str = cmd_str.encode('utf-8') + else: + raise RuntimeError("your os is not support.") + close_fds = False if OSType.WIN == OSType.get_type() else True + if out_p: + p = subprocess.Popen(cmd_str, shell=True, close_fds=close_fds, stdout=PIPE) + p.wait() + return p.returncode, p.stdout.read() + else: + c = self.get_devnull() + p = subprocess.Popen(cmd_str, shell=True, close_fds=close_fds, stdout=c) + # for line in p.stdout.readline(): + # print(line) + # p.stdout.close() + p.communicate() + return p.returncode, None + + def adb_cmd(self,cmd): + process = Popen(cmd, shell=True, stderr=PIPE, stdout=PIPE) + (stdout, stdrr) = process.communicate() + stdout = stdout.decode('gbk') # 返回字段中存在中文,使用gbk + + return stdout, stdrr + + def get_devnull(self): + try: + return subprocess.DEVNULL + except AttributeError: + # Python 2.x or older + return open(os.devnull, 'r+') + + def _kills_pid(self): + if OSType.WIN == OSType.get_type(): + kill_pid_cmd = "taskkill /f /pid {}".format(self.pid) + elif OSType.LINUX == OSType.get_type(): + kill_pid_cmd = "kill -9 {}".format(self.pid) + else: + raise RuntimeError("your os is not support.") + + res_code, res_context = self.run_process(kill_pid_cmd) + if res_code: + raise RuntimeError("kill pid: {} failed. error: {}".format(self.pid, res_context)) + + def check_port(self): + if OSType.WIN == OSType.get_type(): + find_pid_win_cmd = 'netstat -ano | findstr {} | findstr LISTENING'.format(self.env_port) + print(find_pid_win_cmd) + res_code, res_context = self.run_process(find_pid_win_cmd, out_p=True) + if res_code == 0: + if len(res_context) > 0: + try: + self.pid = str(res_context).split()[-1].replace("\\r\\n'", "") + self._kills_pid() + except IndexError: + pass + + elif OSType.LINUX == OSType.get_type(): + find_pid_linux_cmd = "lsof -i:{}".format(self.env_port) + res_code, res_context = self.run_process(find_pid_linux_cmd, out_p=True) + if res_code == 0: + # 获取pid + if len(res_context) > 0: + try: + self.pid = str(res_context).split("\n")[1].split()[1] + self._kills_pid() + except IndexError: + pass + else: + raise RuntimeError("your os is not support.") + + def run_manage(self): + count = 3 + while count > 0: + self.run_manages() + find_pid_linux_cmd = "lsof -i:{}".format(self.env_port) + res_code, res_context = self.run_process(find_pid_linux_cmd, out_p=True) + print(res_code, "---res_code---", res_context, "---res_context---") + if len(res_context) > 0: + time.sleep(2) + pid = str(res_context).split("\n")[1].split()[1] + print(pid, "pid####") + count -= 1 + if pid: + break + else: + continue + else: + break + + def run_manages(self): + lod = "nohup python3 platform_tools/aida/manage.py &." + sh_lod = "./5011.sh" + + self.run_process(sh_lod) + # subprocess.call(["./5011.sh"]) + # self.run_process(lod) + + +if __name__ == '__main__': + test = tool() + test.check_port() + time.sleep(3) + test.run_manage() diff --git a/zhyy/library/CommonFun/handle_harmonyos.py b/zhyy/library/CommonFun/handle_harmonyos.py new file mode 100644 index 0000000..9836ccf --- /dev/null +++ b/zhyy/library/CommonFun/handle_harmonyos.py @@ -0,0 +1,105 @@ +import os +import subprocess +from urllib.parse import urlparse +import argparse + + +class HarmonyAppInstaller: + def __init__(self) -> None: + self.project_path = os.getcwd() + self.temp_dir = os.path.join(self.project_path, "temp") + + def _run_command(self, command: str) -> str: + result = subprocess.run(command, shell=True, capture_output=True, text=True) + if result.returncode != 0: + raise Exception(f"Command failed: {command}\nError: {result.stderr}") + return result.stdout + + def install(self, appUniqId, packageId): + import requests + import os + import shutil + + # Create or clean temp directory + if os.path.exists(self.temp_dir): + shutil.rmtree(self.temp_dir) + os.makedirs(self.temp_dir) + + url = "https://api.qa.huohua.cn/api/versions" + + headers = { + 'Content-Type': 'application/json', + 'huohua-podenv': 'HHC-111781' + } + + params = { + 'version': '25.1.2.1', + 'platform': 'harmonyos', + 'appUniqId': appUniqId, + 'packageId': packageId + } + + response = requests.get(url, headers=headers, params=params) + response_data = response.json() + + if not response_data.get('success'): + raise Exception(f"API request failed: {response_data.get('message')}") + + app_version = response_data.get('data', {}).get('app_version', {}) + download_url = app_version.get('url') + + if not download_url: + raise Exception("No download URL found in response") + + # Get the filename from the URL + hap_filename = os.path.basename(urlparse(download_url).path) + if not hap_filename.endswith('.hap'): + hap_filename = 'eduparent.hap' # fallback name if URL doesn't end with .hap + + # Download the HAP file + hap_file_path = os.path.join(self.temp_dir, hap_filename) + download_response = requests.get(download_url, stream=True) + download_response.raise_for_status() + + with open(hap_file_path, 'wb') as f: + for chunk in download_response.iter_content(chunk_size=8192): + if chunk: + f.write(chunk) + + # Execute HDC commands + device_path = f"data/local/tmp/{hap_filename}" + + print(f'hdc file send "{hap_file_path}" "{device_path}"') + # Push HAP file to device + self._run_command(f'hdc file send "{hap_file_path}" "{device_path}"') + + try: + # Install HAP package + print(f'hdc shell bm install -p "{device_path}"') + self._run_command(f'hdc shell bm install -p "{device_path}"') + finally: + # Clean up: Remove HAP file from device + print(f'hdc shell rm -rf "{device_path}"') + self._run_command(f'hdc shell rm -rf "{device_path}"') + + return hap_file_path + + +def main(): + parser = argparse.ArgumentParser(description='Install HarmonyOS application') + parser.add_argument('--app-uniq-id', required=True, help='Unique ID of the application') + parser.add_argument('--package-id', required=True, help='Package ID for the application') + + args = parser.parse_args() + + try: + installer = HarmonyAppInstaller() + hap_path = installer.install(args.app_uniq_id, args.package_id) + print(f"Successfully installed HAP from: {hap_path}") + except Exception as e: + print(f"Error: {str(e)}") + exit(1) + + +if __name__ == '__main__': + main() diff --git a/zhyy/library/CommonFun/handle_jenkins.py b/zhyy/library/CommonFun/handle_jenkins.py new file mode 100644 index 0000000..a6247ee --- /dev/null +++ b/zhyy/library/CommonFun/handle_jenkins.py @@ -0,0 +1,257 @@ +# -*- coding:utf-8 -*- +import sys +import xml.etree.ElementTree as ET +WORKSPACE = sys.argv[1] +sys.path.append(WORKSPACE) +import json +import requests +import os +import re +from bs4 import BeautifulSoup +from html.parser import HTMLParser + +handle_parser = HTMLParser() +BUILD_INFO = {} + + +class handle_to_jenkins(): + ''' + 处理关于jenkins的执行相关方法 + ''' + + def __init__(self): + self.dict_info = {} + self.list_info = [] + def GetJenkinsVar(self, key): + ''' + 获取jenkins上获得的参数信息 + ''' + try: + value = os.environ.get(key) + except Exception: + value = os.environ.get(key.upper()) + if (not value): + value = '' + return value + + def kw_to_get_dd_token(self, environment): + """ + 功能: 根据传入环境获取不同的token信息 + """ + sim = "40696c86-264a-4222-a40c-cfd64a05dffd" + product = "40696c86-264a-4222-a40c-cfd64a05dffd" + if environment.lower() == "sim" or environment.lower() == "qa": + return sim + else: + return product + + def send_message_by_dingding(self, data, environment="qa"): + ''' + 发送钉钉消息 + ''' + token = self.kw_to_get_dd_token(environment=environment) + # web_hook = "https://oapi.dingtalk.com/robot/send?access_token={}".format(token) + web_hook = "https://open.feishu.cn/open-apis/bot/v2/hook/{}".format(token) + headers = {"Content-Type": "application/json"} + json_data = json.dumps(data) + rsp = requests.post(url=web_hook, data=json_data, headers=headers) + return rsp + + # print(data) + def send_feishu(self,inferfaces): + ''' + 发送飞书 + :return: + ''' + at_user_list = [{"tag": "at", "user_id": "{}".format("7020366259502153730")}] + message_data = {"msg_type": "post", "content": { + "post": {"zh_cn": {"title": "有存在的接口未实现自动化哦", + "content": [[{"tag": "text", "text": "问题数据:"}], + [{"tag": "text", "text": "{}".format(inferfaces)}], + at_user_list]}}}} + web_hook = "https://open.feishu.cn/open-apis/bot/v2/hook/40696c86-264a-4222-a40c-cfd64a05dffd" + headers = {"Content-Type": "application/json"} + json_data = json.dumps(message_data) + print(json_data) + rsp = requests.post(url=web_hook, data=json_data, headers=headers) + return rsp + + def handle_send_message(self,root): + ''' + 处理对应所有人,根据job名称匹配来发送@的消息 + ''' + list_name_phone = {"陈江": "13458500234", "张楠": "", "罗志鹏": "", + "蒲思宇": "", "陈洁": "15328118883", "刘涛婷": "18328504751", "谯新久": "18202810506"} + job_name = self.GetJenkinsVar("JOB_TO_NAME") + job_url = self.GetJenkinsVar("JOB_TO_URL") + enviroment = self.GetJenkinsVar("JOB_ENVIRONMENT") + + # job_name = "123" + # job_url = "http://10.250.200.1:8080/jenkins/job/QA%E7%8E%AF%E5%A2%83%E5%B7%A1%E6%A3%80/2387/robot/report/report.html" + # enviroment = "PRODUCT" + + dict_info = self.get_fail_test_case(son_node=root) + BUILD_INFO = dict_info.get("BUILD_INFO") + fail_info = dict_info.get("INFO") + list_phone = [] + if BUILD_INFO: + for key, value in BUILD_INFO.items(): + list_phone.append(list_name_phone.get(key)) + print(list_phone) + print("+++++++{0}+++++{1}".format(job_name, job_url)) + if re.search("PRODUCT", enviroment): + # message_data = {"msg_type": "text", "content": { + # "text": "线上巡检:线上环境出现了问题,请点击进行查看{0},以下是错误日志:{1}".format(job_url + "robot/report/report.html",fail_info)}, + # "at": {"atMobiles": list_phone}} + + at_user_list = [{"tag": "at", "user_id": "{}".format("7020366258071715842")}] + message_data = {"msg_type": "post", "content": { + "post": {"zh_cn": {"title": "线上环境巡检", + "content": [[{"tag": "text", "text": "线上环境出现了问题:"}], + [{"tag": "a", "text": "点击查看","href":"".format(job_url + "robot/report/report.html")}], + at_user_list]}}}} + print(message_data) + self.send_message_by_dingding(message_data,environment="product") + + return enviroment + if re.search("QA", enviroment) or re.search("SIM", enviroment): + message_data = {"msg_type": "text", "content": { + "text": "{0}环境构建出问题了哦,请点击进行查看{1},以下是错误日志:{2}".format(enviroment, job_url + "robot/report/report.html",fail_info)}, + "at": {"atMobiles": list_phone}} + print(message_data) + self.send_message_by_dingding(message_data) + return enviroment + for key, value in list_name_phone.items(): + if re.search(key, job_name): + message_data = {"msg_type": "text", "content": { + "text": "亲爱的{0}同学,你的独立环境构建出问题了哦,请点击进行查看{1}".format(key, job_url + "robot/report/report.html")}, + "at": {"atMobiles": [value]}} + self.send_message_by_dingding(message_data) + return value + else: + return "这个job:{}没有找到人".format(job_name) + + def get_fail_test_case(self,son_node, father_node=None, grandpa_node=None): + """ + 功能:遍历xml文件的所有节点,搜索构建失败的用例信息 + """ + children_node = son_node.getchildren() + if len(children_node) == 0: + # if son_node.tag == 'doc': + # print(son_node.text) + # if son_node.tag == 'msg' and son_node.attrib['level'] == 'FAIL': + # # print(son_node.text) + # # print(son_node.attrib['timestamp']) + # print(children_node) + if son_node.tag == 'status' and son_node.attrib['status'] == 'FAIL' and 'critical' in son_node.attrib: + + # print(father_node.attrib["name"]) + tag = self.find_tags(father_node) + # a = self.find_error(father_node) + # print(a) + info_error = self.check_log_info(son_node.text) + INFO = "用例名称:" + father_node.attrib["name"] +"。人员:" + tag + "。失败日志:" + str(info_error) + run_time = son_node.attrib['endtime'] + # print(info_error) + # print(son_node.text) + # self.check_log_info(text=son_node.text) + run_time = "{}-{}-{} {}".format(run_time[0:4], run_time[4:6], run_time[6:8], run_time[9:17]) + reason = son_node.text.replace('"', '\\"') + # 满足构建时间,同时数据库中没有数据,才入库 + # print(run_time) + if tag in BUILD_INFO: + BUILD_INFO[tag] += 1 + else: + BUILD_INFO[tag] = 1 + # print(BUILD_INFO) + # print(tag) + self.list_info.append(INFO) + self.dict_info["BUILD_INFO"] = BUILD_INFO + self.dict_info["INFO"] = self.list_info + # print(tag) + + run_time = run_time + author = tag + case_name = father_node.attrib['name'] + file_path = grandpa_node.attrib['source'] + reason = reason[0:4500] + return self.dict_info + for child in children_node: + self.dict_info = self.get_fail_test_case(child, son_node, father_node) + return self.dict_info + + def check_log_info(self,text): + ''' + 过滤精确日志信息 + ''' + soup = BeautifulSoup(text, features="lxml") + list_info = [] + for a in soup.find_all(name='span'): + dict_info = {} + if a.text == "Old message:": + dict_info["old_message"] = a.nextSibling + list_info.append(dict_info) + elif a.text == "New message:": + dict_info["new_message"] = a.nextSibling + list_info.append(dict_info) + # print(a.nextSibling) + + if list_info: + return list_info + else: + return text + + + + def find_tags(self,root): + """ + 功能:在xml文件中搜索失败用对应的作者 + """ + children_node = root.getchildren() + for child in children_node: + if child.tag == 'tags': + tags = child.getchildren() + for tag in tags: + if 'qa-' in tag.text.lower(): + return tag.text[3:len(tag.text)] + return "无作者标签" + + def find_error(self,root): + """ + 功能:在xml文件中查询失败日志 + """ + children_node = root.getchildren() + for child in children_node: + if child.tag == 'kw': + msgs = child.getchildren() + for msg in msgs: + a = msg.tag + + # t = msg.attrib['level'] + l = msg.text + # print(msg.attrib['timestamp']) + if msg.tag == 'msg' and msg.attrib['level'] == 'FAIL': + print(msg.attrib) + s = msg.text + return msg.text + return "" + + def run(self): + ''' + 运行入口 + ''' + # FILE_PATH = "D:/output2.xml" + FILE_PATH = os.path.abspath(os.path.join(WORKSPACE, 'Report/out/output.xml')) + root = ET.parse(FILE_PATH).getroot() + # self.get_fail_test_case(son_node=root) + self.handle_send_message(root=root) + +if __name__ == '__main__': + test = handle_to_jenkins() + + # FILE_PATH = "D:/output1.xml" + # FILE_PATH = os.path.abspath(os.path.join(WORKSPACE, 'Report/out/output.xml')) + # root = ET.parse(FILE_PATH).getroot() + # print(test.get_fail_test_case(son_node=root)) + test.run() + # test.handle_send_message(root=root) \ No newline at end of file diff --git a/zhyy/library/CommonFun/handle_jira.py b/zhyy/library/CommonFun/handle_jira.py new file mode 100644 index 0000000..70fbe20 --- /dev/null +++ b/zhyy/library/CommonFun/handle_jira.py @@ -0,0 +1,331 @@ +# -*- coding:utf-8 -*- +""" +人员当日jira任务、故事状态流转 +""" +import os, sys + +file_dir = os.path.dirname(__file__) +project_dir = os.path.abspath(os.path.join(file_dir, "..", "..", "..")) +sys.path.append(project_dir) + +from jira import JIRA +from base_framework.public_tools.utils import Tools +from base_framework.public_tools.read_config import ReadConfig +from base_framework.platform_tools.Message_service.Feishu_api import FeiShuMessage,get_user_name_by_email_prefix,get_feishu_config_value +from configparser import ConfigParser +from base_framework.base_config.current_pth import * +import datetime + +tools = Tools() +ReadConfig = ReadConfig(filename=la_config_path) +FS_INFO = [{"team": "TO", "users": "wuyonggang,xuwenjun,luohong"}, + {"team": "TO-RD", "users": "fengtian,guosongchao,zhaoxiaofang," + "zhuliang,majincheng,gaozhijun,liuxuegang," + "zhangxiong"}, + {"team": "USER-FE", "users": "zhaofei,liuxinlin,qingchen,yefei,xiangming,jianghao,jianglingmin,xuchangle," + "libaicheng,gouyuheng,jixiang.dong,baiyang01"} + ] + + +class JiraApi: + def __init__(self): + self.jira = JIRA(server='https://jira.bg.huohua.cn/', + basic_auth=("wuyonggang", "Mima@123")) + + def close_jira_subtask(self, user_name): + """ + 功能: 关闭指定人员名下当天结束的子任务 + 请求参数 user_name (指定人员jira登录名,type: list) + """ + bad_name = [] + right_name = [] + for j in user_name: + if j not in ReadConfig.get_sections(): + bad_name.append(j) + elif j in ReadConfig.get_sections(): + right_name.append(j) + success = [] + false = [] + today_time = tools.get_format_date(r_type=1) + work_start_time = datetime.datetime.strptime('{0} 10:00:00'.format(today_time), '%Y-%m-%d %H:%M:%S') + for name in right_name: + jira = JIRA(server='https://jira.bg.huohua.cn/', basic_auth=(name, ReadConfig.get_value(name, 'password'))) + jql = 'project = HHC AND issuetype = 子任务 AND status in (重新打开, 启动, 需求池, 暂停, 计划, 产品需求设计,' \ + ' 产品需求内审, 需求评审, 排期, 执行, 开发, 待测试, QA测试, SIM验证, 验证中, 待办, 处理中, 关闭) AND resolution = Unresolved' \ + ' AND (QA in ({0}) OR assignee in ({1})) ORDER BY priority DESC, updated DESC'.format(name,name) + issues = jira.search_issues(jql, fields='') + # if len(issues) == 0: break + not_need_closed = 0 + for i in issues: + s = list(jira.transitions(i)) + close_status_id = [x['id'] for x in s if x['name'] == '关闭'] + if str(i.fields.status) == '处理中': + if i.fields.aggregatetimeestimate != None: + if i.fields.aggregatetimeestimate > 28800: + jira.add_worklog(i, timeSpent='7', started=work_start_time) + elif 0 < i.fields.aggregatetimeestimate <= 28800: + jira.add_worklog(i, timeSpent='{0}'.format(i.fields.aggregatetimeestimate / 3600), + started=work_start_time) + else: + jira.add_worklog(i, timeSpent='8', started=work_start_time) + if str(i.fields.status) == '处理中' and str(i.fields.customfield_13406) == today_time: + jira.transition_issue(i, int(close_status_id[0])) + else: + not_need_closed += 1 + issues2 = jira.search_issues(jql, fields='') + if not_need_closed == len(issues2): + success.append(name) + else: + false.append(name) + return "{0}今日结束的子任务已关闭成功; {1}今日结束的子任务关闭失败; {2}没有进行账号配置".format(success, false, bad_name) + + def start_jira_subtask(self, user_name): + """ + 功能: 将待开始为今天的子任务状态流转为“处理中” + 请求参数 user_name (指定人员jira登录名,type: list) + """ + bad_name = [] + right_name = [] + for j in user_name: + if j not in ReadConfig.get_sections(): + bad_name.append(j) + elif j in ReadConfig.get_sections(): + right_name.append(j) + success = [] + false = [] + for name in right_name: + jira = JIRA(server='https://jira.bg.huohua.cn/', basic_auth=(name, ReadConfig.get_value(name, 'password'))) + jql = 'project = HHC AND issuetype = 子任务 AND status in (重新打开, 启动, 需求池, 暂停, 计划, 产品需求设计, 产品需求内审, 需求评审, ' \ + '排期, 执行, 开发, 待测试, QA测试, SIM验证, 验证中, 待办, 关闭) AND resolution = Unresolved' \ + ' AND (QA in ({0}) OR assignee in ({1})) ORDER BY priority DESC, updated DESC'.format(name,name) + today_time = tools.get_format_date(r_type=1) + issues = jira.search_issues(jql, fields='') + # if len(issues) == 0: break + not_need_update = 0 + for i in issues: + s = list(jira.transitions(i)) + start_status_id = [x['id'] for x in s if x['name'] == '处理中'] + if str(i.fields.status) == '待办' and str(i.fields.customfield_12700) == today_time: + jira.transition_issue(i, int(start_status_id[0])) + else: + not_need_update += 1 + issues2 = jira.search_issues(jql, fields='') + if not_need_update == len(issues2): + success.append(name) + else: + false.append(name) + return "{0}今日开始的子任务已将状态修改为“处理中”‘; {1}今日开始的子任务状态修改失败; {2}没有进行账号配置".format(success, false, bad_name) + + def change_story(self, user_name, type): + """ + 功能: 将提测时间为今天的指定人员名下的故事状态修改至“QA测试” + 请求参数 user_name (指定人员jira登录名,type: list) + type (故事需要修改至的状态 1:提测日为今天,状态修改至【QA测试】 2:上线日为今天,状态修改至【关闭】) + """ + bad_name = [] + right_name = [] + for j in user_name: + if j not in ReadConfig.get_sections(): + bad_name.append(j) + elif j in ReadConfig.get_sections(): + right_name.append(j) + success = [] + false = [] + for name in right_name: + jira = JIRA(server='https://jira.bg.huohua.cn/', basic_auth=(name, ReadConfig.get_value(name, 'password'))) + jql = "project = HHC AND issuetype = 故事 AND " \ + "status in (重新打开, 启动, 需求池, 暂停, 计划, 产品需求设计, 产品需求内审, 需求评审, 排期, 执行, 开发, 待测试, 验证中, 待办, QA测试, SIM验证, 处理中, 关闭)" \ + " AND resolution = Unresolved AND QA in ({0}) ORDER BY priority DESC, updated DESC".format(name) + today_time = tools.get_format_date(r_type=1) + issues = jira.search_issues(jql, fields='') + # if len(issues) == 0 : break + x = 0 + need_update = 0 + if type == 1: # 将故事状态一直流转至“QA测试” + for i in issues: + if str(i.fields.status) == 'QA测试' or str(i.fields.status) == 'SIM验证': need_update += 1 + if str(i.fields.customfield_10504) == today_time: + if str(i.fields.status) != 'QA测试' and str(i.fields.status) != 'SIM验证': + need_update += 1 + for j in range(8): + jira.transition_issue(i, int(jira.transitions(i)[2]['id'])) + issues_2 = jira.search_issues(jql, fields='') # 重新获取所有故事 + status_now = issues_2[x].fields.status + print(status_now) + if str(status_now) == 'QA测试' or str(status_now) == 'SIM验证': + break + x += 1 + jql2 = "project = HHC AND issuetype = 故事 AND " \ + "status in (重新打开, 启动, 需求池, 暂停, 计划, 产品需求设计, 产品需求内审, 需求评审, 排期, 执行, 开发, 待测试, 验证中, 待办, 处理中, 关闭)" \ + " AND resolution = Unresolved AND QA in ({0}) ORDER BY priority DESC, updated DESC".format(name) + issues2 = jira.search_issues(jql2, fields='') + if len(issues2) == len(issues) - need_update: + success.append(name) + else: + false.append(name) + elif type == 2: # 直接将故事关闭 + for i in issues: + if str(i.fields.customfield_10606) == today_time: + jira.transition_issue(i, int(jira.transitions(i)[1]['id']), comment="已上线") # 将故事修改为关闭状态 + need_update += 1 + issues2 = jira.search_issues(jql, fields='') + if len(issues2) + need_update == len(issues): + success.append(name) + else: + false.append(name) + + return "{0}今日的故事已将状态修改成功!; {1}今日的故事状态修改失败; {2}没有进行账号配置".format(success, false, bad_name) + + def change_jira_status(self, user_name): + """ + 功能: 判断当前时间是否在下午18:00点以前,来选择打开或关闭【子任务】和【故事】 + 请求参数 user_name (指定人员jira登录名,type: list) + """ + time_now = tools.get_format_date(r_type=4) + time_now2 = '{0} 18:00:00'.format(time_now[0:10:1]) + if time_now < '{0} 18:00:00'.format(time_now2): + a = self.start_jira_subtask(user_name) + b = self.change_story(user_name, type=1) + return a + '\n' + b + elif time_now >= '{0} 18:00:00'.format(time_now2): + a = self.close_jira_subtask(user_name) + b = self.change_story(user_name, type=2) + return a + '\n' + b + + def query_jira_subtask(self, user_name, begin_date=0, end_date=0): + """ + | 功能 | 查询用户对应的jira子任务 | + | 入参 | user_name | 用户名字 | + | | begin_date | 开始时间:0-今天,X-未来X天,-X-过去的X天 | + | | end_date | 开始时间:0-今天,X-未来X天,-X-过去的X天 | + """ + b_date = tools.get_format_date(r_type=1, add_days=int(begin_date)) + e_date = tools.get_format_date(r_type=1, add_days=int(end_date)) + jql = "issuetype = 子任务 AND 计划开始时间 <= {} AND 计划结束时间 >= {} AND assignee in ({}) ORDER BY cf[12700] ASC"\ + .format(b_date, e_date, user_name) + issues = self.jira.search_issues(jql, fields='') + # print(jql) + return issues + # for item in issues: + # print("{0}:{1}, 当前状态:{2}".format(item.key, item.fields.summary, item.fields.status)) + + def query_jira_by_id(self, jira_id): + """ + | 功能 | 跟进jira的id查询具体详情 | + | 入参 | jira_id | jira的id | + """ + jql = "id={}".format(jira_id) + issues = self.jira.search_issues(jql, fields='') + if len(issues) == 0: + raise Exception("根据jira_id未查询到对应的jira信息,请检查....") + rd = issues[0].fields.assignee.displayName + qa = issues[0].fields.reporter.displayName + title = issues[0].fields.summary + status = issues[0].fields.status + j_time = issues[0].fields.created + j_time = j_time[0:10] + ' ' + j_time[11:19] + return {"jira_rd": rd, + "jira_qa": qa, + "jira_title": title, + "jira_time": j_time, + "jira_status": str(status)} + + def query_overdue_issues(self, user_name): + """ + | 功能 | 查询逾期的子任务和故事 | + | 入参 | 无 | + """ + today = tools.get_format_date() + check_time = tools.get_format_date(r_type=4) + if check_time < "{} 17:00:00".format(today): + jql = "issuetype in (故事,子任务) AND (计划结束时间 1: + dict_response[level] = list_level.count(level) + if dict_response: + return {"code":500,"data":dict_response} + else: + return {"code":200,"data":dict_response} + +class student_verity(): + def __init__(self): + self.get_url = auth_login() + + def get_schedule_count(self): + ''' + + :return: + ''' + m_token = self.get_url.get_m_online_token() + headers = self.get_url.get_m_headers(m_token=m_token) + url = self.get_url.get_student_config_info(url_name="scheduleCount_url") + print(url) + get_response_data = self.get_url.get_json_result(url=url, headers=headers, request_data={"subjectType": 4}) + + return get_response_data + + +if __name__ == '__main__': + test = auth_login() + test1 = m_parent() + test2 = student_verity() + # print(test.get_student_online_token()) + # print(test1.get_recommendedCourse()) + print(test1.get_trial_level(courseSubtype=22)) + # print(test2.get_schedule_count()) diff --git a/zhyy/library/CommonFun/handle_mq.py b/zhyy/library/CommonFun/handle_mq.py new file mode 100644 index 0000000..52edde6 --- /dev/null +++ b/zhyy/library/CommonFun/handle_mq.py @@ -0,0 +1,19 @@ +import pika + +# 设置RabbitMQ服务器的连接参数,使用自定义端口9876 +connection_params = pika.ConnectionParameters('rocketmq.qa.huohua.cn', 9876) +connection = pika.BlockingConnection(connection_params) +channel = connection.channel() + +# 声明一个队列,如果队列不存在则会创建 +queue_name = 'my_queue' +channel.queue_declare(queue=queue_name) + +# 发布消息到指定的队列 +message = 'Hello, World!' +channel.basic_publish(exchange='', + routing_key=queue_name, + body=message) +print(f" [x] Sent '{message}'") +# 关闭连接 +connection.close() diff --git a/zhyy/library/CommonFun/handle_picture.py b/zhyy/library/CommonFun/handle_picture.py new file mode 100644 index 0000000..24030f2 --- /dev/null +++ b/zhyy/library/CommonFun/handle_picture.py @@ -0,0 +1,56 @@ +# -*- coding:utf-8 -*- +""" +Author: qiaoxinjiu +Email: qiaoxinjiu@sparkedu.com +Create Date: 2022/05/08 5:58 下午 +""" +import os +from PIL import Image + +def image_gray(img): + # 打开图片 + img = Image.open(img) + + # 计算平均灰度值 + gray_sum = 0 + count = 0 + for x in range(img.width): + for y in range(img.height): + if img.mode == "RGB": + r, g, b = img.getpixel((x, y)) + gray_sum += (r + g + b) / 3 + elif img.mode == "L": + gray_value = img.getpixel((x, y)) + gray_sum += gray_value + count += 1 + + avg_gray = gray_sum / count + return avg_gray + + +def find_image(folder_path): + # 定义一个列表存储图片路径 + images = [] + # 遍历文件夹下的所有文件 + for root, dirs, files in os.walk(folder_path): + for file in files: + file_path = os.path.join(root, file) + # 处理每个文件,将其添加到列表中 + images.append(file_path) + return images + + +def assert_run(folder_path): + images = find_image(folder_path) + for img in images: + gray = image_gray(img) + # 灰度值小于50,将认为是黑图 + if gray < 50: + print(img, ":", gray) + + +if __name__ == "__main__": + # image_gray() + # find_image() + folder_path = r'D:\picture' + assert_run(folder_path) diff --git a/zhyy/library/CommonFun/handle_tools.py b/zhyy/library/CommonFun/handle_tools.py new file mode 100644 index 0000000..2d0d25f --- /dev/null +++ b/zhyy/library/CommonFun/handle_tools.py @@ -0,0 +1,211 @@ +# -*- coding:utf-8 -*- +""" +Author: qiaoxinjiu +Email: xinjiu.qiao@allschool.com +Create Date: 2022/04/26 5:58 下午 +""" +import os +import sys + +input_team_name = sys.argv + +BASIC_PATH = os.path.dirname(os.path.abspath(__file__)) +TEAM_PATH = os.path.abspath(os.path.join(BASIC_PATH, '../../../{}'.format("base_framework"))) +sys.path.append(TEAM_PATH) +PROJECT_PATH = os.path.abspath(os.path.join(BASIC_PATH, '../../..')) +sys.path.append(PROJECT_PATH) +from base_framework.public_tools.sqlhelper import MySqLHelper +import json +import requests + +obj_mysql_helper = MySqLHelper() + + +class Handle_tools: + def __init__(self): + pass + + def query_interface_sql(self): + ''' + 查询qa的余量接口信息 + :return: + ''' + query_sql = ''' SELECT in_url,controller_name + FROM + sparkatp.interface_info + WHERE + id IN ( + SELECT + a.id + FROM + (SELECT * from sparkatp.interface_info) a + WHERE + ( a.swagger_id in (SELECT id FROM sparkatp.swagger_info WHERE team="UBRD") ) + AND a.created_time > "2022-01-01 00:00:50" + AND is_used = 1 and at_numbers = 0 and offline=0 and jira_id is null) ORDER BY created_time''' + + query_sql_r = """SELECT + si.id AS interface_id, + rpgm.jira_id, + si.in_url +FROM + ( + SELECT + ii.id, + ii.in_url, + ii.jira_id + FROM + sparkatp.interface_info ii + WHERE + ii.swagger_id IN ( SELECT id FROM sparkatp.swagger_info WHERE team = "UBRD" ) + AND ii.created_time > "2022-01-01 00:00:50" + AND ii.is_used = 1 + AND ii.at_numbers = 0 + AND ii.offline = 0 + AND ii.jira_id IS NULL + ) si + INNER JOIN ( + SELECT + rp.interface_id, + rp.jira_id, + MAX( modified_time ) + FROM + request_parameters rp + WHERE + rp.jira_id IS NOT NULL + AND rp.jira_id <> 'None' + AND rp.jira_id <> '' + GROUP BY + rp.interface_id + ) rpgm ON rpgm.interface_id = si.id;""" + + # query_sql = " SELECT * FROM sparkatp.interface_info WHERE id IN (SELECT a.id FROM(SELECT * from sparkatp.interface_info) a WHERE( a.swagger_id in (SELECT id FROM sparkatp.swagger_info WHERE team='UBRD') ) AND a.created_time > '2022-01-01 00:00:50' AND is_used = 1 and at_numbers = 0 and offline=0 and jira_id is null) ORDER BY created_time" + query_sql_r_result = obj_mysql_helper.select_all(query_sql_r) + query_sql_dict = {} + query_sql_dict_temp = {} + # : {'interface_id': 924646, 'jira_id': 'HHC-48875', 'in_url': 'http://peppa-parent-api.qa.huohua.cn/classes/audition/enter'} + for qsrr in query_sql_r_result: + in_url = "{}_{}".format(str(qsrr.get('interface_id')).strip(' '), qsrr.get('in_url').strip(' ')) + if qsrr.get('jira_id') in query_sql_dict.keys(): + if in_url in query_sql_dict.get(qsrr.get('jira_id')): + pass + else: + query_sql_dict[qsrr.get('jira_id')].append(in_url) + else: + query_sql_dict[qsrr.get('jira_id')] = [in_url] + + query_sql_s = """SELECT + si.id AS interface_id, + rpgm.jira_id, + si.in_url +FROM + ( + SELECT + ii.id, + ii.in_url, + ii.jira_id + FROM + sparkatp.interface_info ii + WHERE + ii.swagger_id IN ( SELECT id FROM sparkatp.swagger_info WHERE team = "UBRD" ) + AND ii.created_time > "2022-01-01 00:00:50" + AND ii.is_used = 1 + AND ii.at_numbers = 0 + AND ii.offline = 0 + AND ii.jira_id IS NULL + ) si + INNER JOIN ( + SELECT + rp.interface_id, + rp.jira_id, + MAX( modified_time ) + FROM + response_parameters rp + WHERE + rp.jira_id IS NOT NULL + AND rp.jira_id <> 'None' + AND rp.jira_id <> '' + GROUP BY + rp.interface_id + ) rpgm ON rpgm.interface_id = si.id;""" + + query_sql_s_result = obj_mysql_helper.select_all(query_sql_s) + for qsrs in query_sql_s_result: + in_url = "{}_{}".format(str(qsrs.get('interface_id')).strip(' '), qsrs.get('in_url').strip(' ')) + if qsrs.get('jira_id') in query_sql_dict.keys(): + if in_url in query_sql_dict[qsrs.get('jira_id')]: + pass + else: + query_sql_dict[qsrs.get('jira_id')].append(in_url) + else: + query_sql_dict[qsrs.get('jira_id')] = [in_url] + return query_sql_dict + + def send_log_info(self): + ''' + 每天进行日志提醒 + :return: + ''' + feishu_name_id = {"罗志鹏": "7020366259502153730", "谯新久": "7020366258071715842", + "陈洁": "7020370251997069314", "蒲思宇": "7076270364313108481", "张楠": "7076270369128349697", + "刘涛婷": "7020366262240854017"} + order_list = ["谯新久", "陈洁", "刘涛婷", "罗志鹏", "蒲思宇", "张楠"] + # for name in order_list: + + def send_feishu(self, inferfaces): + ''' + 发送飞书 + :return: + ''' + at_user_list = [{"tag": "at", "user_id": "{}".format("7020366259502153730")}] + message_data = {"msg_type": "post", "content": { + "post": {"zh_cn": {"title": "有存在的接口未实现自动化哦", + "content": [[{"tag": "text", "text": "问题数据:"}], + [{"tag": "text", "text": "{}".format(inferfaces)}], + at_user_list]}}}} + web_hook = "https://open.feishu.cn/open-apis/bot/v2/hook/40696c86-264a-4222-a40c-cfd64a05dffd" + headers = {"Content-Type": "application/json"} + json_data = json.dumps(message_data) + print(json_data) + rsp = requests.post(url=web_hook, data=json_data, headers=headers) + return rsp + + def send_message_by_feishu(self, web_hook, data): + headers = {"Content-Type": "application/json"} + json_data = json.dumps(data) + rsp = requests.post(url=web_hook, data=json_data, headers=headers) + return rsp + + def compare_txt(self, su_t, st_t): + ''' + 对比两个文档,返回不同的信息 + :param su_t: + :param st_t: + :return: + ''' + file_object1 = open(su_t, 'r', encoding='utf-8') + file_object2 = open(st_t, 'r', encoding='utf-8') + try: + while True: + line = file_object1.readline() + lines = file_object2.readline() + if line.strip() != lines.strip(): + print("pc :", line) + print("right:", lines) + else: + continue + finally: + file_object1.close() + file_object2.close() + + +if __name__ == '__main__': + test = Handle_tools() + # get_interfaces = test.query_interface_sql() + # if get_interfaces: + # test.send_feishu(json.dumps(get_interfaces)) + # else: + # print("都已经完成") + su_t = r"D:\UiVispark\spark1.txt" + st_t = r"D:\UiVispark\spark2.txt" + test.compare_txt(st_t=st_t,su_t=su_t) \ No newline at end of file diff --git a/zhyy/library/CommonFun/host_update.ini b/zhyy/library/CommonFun/host_update.ini new file mode 100644 index 0000000..8e3f842 --- /dev/null +++ b/zhyy/library/CommonFun/host_update.ini @@ -0,0 +1 @@ +host_list = [{"ip":'127.0.0.1',"host_names":"ts.sim.huohua.cn"},{"ip":'127.0.0.1',"host_names":"student-api.sim.huohua.cn"}] \ No newline at end of file diff --git a/zhyy/library/CommonFun/host_update.py b/zhyy/library/CommonFun/host_update.py new file mode 100644 index 0000000..ab5cda7 --- /dev/null +++ b/zhyy/library/CommonFun/host_update.py @@ -0,0 +1,263 @@ +# -*- coding:utf-8 -*- +# @Time : 2023/7/5 10:27 +# @Author: luozhipeng +# @File : host_update.py +import os +from python_hosts import Hosts, HostsEntry +import sys +LOCAL_PATH = os.path.dirname(os.path.abspath(__file__)) +BASE_PROJECT_PATH = os.path.abspath(os.path.join(LOCAL_PATH, '../../../{}'.format("UBRD"))) +BASIC_PATH = os.path.abspath(os.path.join(LOCAL_PATH, '../../../')) +sys.path.append(BASE_PROJECT_PATH) +sys.path.append(BASIC_PATH) +from base_framework.public_tools.utils import Tools +from base_framework.public_tools.apollo import Apollo +import requests, time +import os +import sys +import socket +import subprocess +import requests +import urllib.parse +obj_apollo = Apollo() + +obj_tools = Tools() + +class HostUpdate: + def __init__(self): + if sys.platform.startswith('win'): + self.hosts_location = Hosts(path='C:\Windows\System32\drivers\etc\hosts') + elif sys.platform.startswith('darwin'): + self.hosts_location = Hosts(path='\etc\hosts') + + def host_update_by_host_dict(self, host_dict_list: list): + + entry_list = [] + for host in host_dict_list: + new_entry = HostsEntry(entry_type='ipv4', address=host['ip'], names=[host['host_name'], '']) + entry_list.append(new_entry) + self.hosts_location.add(entry_list) + self.hosts_location.write() + + def host_remove_intercept(self,address,name): + self.hosts_location.remove_all_matching(address,name) + self.hosts_location.write() + + def get_ip_by_host_domain(self,domain: str): + ip=socket.gethostbyname(domain) + return ip + + def wait_dns_flush(self,ip,domain): + count=1 + while True: + + cmd1="ipconfig /flushdns" + result1=subprocess.run(cmd1,capture_output=True,text=True) + output1=result1.stdout + print(output1) + cmd2="ping {}".format(domain) + result2=subprocess.run(cmd2,capture_output=True,text=True) + output2=result2.stdout + print(output2) + cmd="ipconfig /displaydns" + result=subprocess.run(cmd,capture_output=True,text=True) + output=result.stdout + print(output) + if output is None: + output='' + if ip in output: + break + if count >= 30: + break + count=count + 1 + host_list=[{"ip": ip,"host_name": domain}] + self.host_remove_intercept(ip,domain) + self.host_update_by_host_dict(host_list) + time.sleep(2) + + +class CoreApollo: + def __init__(self,show_username=None,password=None,current_evn=None): + if not show_username: + self.show_username="liuruiquan" + if not password: + self.password="lrq5823LRQ" + self.apollo_url="http://apollo.qa.huohua.cn/signin" + if not current_evn: + self.current_evn="SIM" + self.apollo_host="http://apollo.qa.huohua.cn" + self.session=self.core_apollo_login() + + def core_apollo_login(self): + post_data=dict() + post_data['login-submit']='登录' + post_data['username']=self.show_username + post_data['password']=self.password + req_session=requests.Session() + resp=req_session.post(url=self.apollo_url,data=post_data) + return req_session + + def get_key(self,server,cluster,key,project): + req_url="%s/apps/%s/envs/%s/clusters/%s/namespaces" % ( + self.apollo_host,server,self.current_evn,cluster) + resp=self.session.get(url=req_url).json() + for item in resp: + base_info=item.get('baseInfo') + items=item.get('items') + if not base_info['namespaceName'].lower() == project.lower(): + continue + for key_info in items: + item_detail=key_info["item"] + if item_detail["key"] == key: + return key_info["item"] + else: + return None + + def set_key(self,key,value,cluster='default',server="",project=None): + if server: + self.server=server + req_url="%s/apps/%s/envs/%s/clusters/%s/namespaces/%s/item" % ( + self.apollo_host,self.server,self.current_evn,cluster,project) + items=self.get_key(server,cluster,key,project) + if items is None: + items=dict() + items["value"]=value + items["key"]=key + items["tableViewOperType"]='create' + items["addItemBtnDisabled"]=True + + else: + items["value"]=value + items["tableViewOperType"]='update' + update_resp=self.session.put(url=req_url,json=items) + + assert update_resp.status_code == 200 + release_body=dict() + release_time_stamp=time.localtime() + release_time='%s-release' % time.strftime("%Y%m%d%H%M%S",release_time_stamp) + release_body["isEmergencyPublish"]=False + release_body["releaseComment"]="" + release_body["releaseTitle"]=release_time + release_url="%s/apps/%s/envs/%s/clusters/%s/namespaces/%s/releases" % ( + self.apollo_host,self.server,self.current_evn,cluster,project) + resp=self.session.post(url=release_url,json=release_body) + assert resp.status_code == 200 + time.sleep(5) + + def set_user_force_true(self,vlaue="true"): + self.set_key(value=vlaue,key="user-token.forceFallback",server="peppa-core-api",cluster="check" + ,project="application") + + def set_teacher_force_true(self,vlaue="true"): + self.set_key(value=vlaue,key="sso-token.forceFallback",server="peppa-core-api",cluster="check" + ,project="application") + + +class EduClassroom: + def kw_ubrd_get_online_test_classroom(self,post_data_input=None): + post_data = dict() + host = "sso.huohua.cn" + api_url = "https://{}/authentication/form".format(host) + show_username = "liuruiquan" + username = "liuruiquan" + password = "lrq5823LRQ" + post_data['showUsername'] = show_username + post_data['username'] = username + post_data['password'] = password + user_agent = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"} + deviceid = {"SSO_DEVICE_ID":"8dfbecf2-064a-4e3f-ac2b-fc09a2401416"} + req_session = requests.session() + req_session.cookies.update(deviceid) + req_session.headers.update(user_agent) + resp = req_session.post( + url=api_url, + data=post_data, + allow_redirects=False) + + # token_header = {"accesstoken": token} + # req_session.headers.update(token_header) + # print(token_header) + client_id = "tic-payment-admin" + origin_url="aHR0cHM6Ly9zc28uaHVvaHVhLmNuLw==" + # origin_url="aHR0cHM6Ly9zc28uc2ltLmh1b2h1YS5jbi8=" + + redirect_uri = "https://{}/uim/authorize_proxy?client_id={}&debug_mode=1&origin_url={}".format(host,client_id,origin_url) + print(redirect_uri) + token_code_url = "https://{}/oauth/authorize?client_id={}&response_type=code&".format(host,client_id,redirect_uri) + authorize_data = {"redirect_uri": redirect_uri} + authorize_data_uri =urllib.parse.urlencode(authorize_data) + resp = req_session.get( + url=token_code_url+authorize_data_uri, + data=authorize_data, + allow_redirects=False) + print(resp.url) + print(resp.headers) + print(resp) + + # token_url = "https://{}/uim/authorize_proxy?client_id={}&debug_mode=1&origin_url={}&code={}".format(host,client_id,origin_url,code) + token_url= resp.headers["Location"] + # authorize_code_data = {"client_id":"tic-payment-admin","debug_mode": 1,"origin_url": origin_url,"code":code} + + print(token_url,"token_url") + # print(authorize_code_data) + resp = req_session.get( + url=token_url, + # data=authorize_code_data, + allow_redirects=False) + print(resp) + print(resp.url) + for key, value in req_session.cookies.items(): + if key == 'peppa_sso_token': + token = value + token_header = {"accesstoken": token} + req_session.headers.update(token_header) + print(token_header) + print(value) + break + + + op_time = obj_tools.get_format_date(r_type=4,add_minutes=10) + print(op_time) + + create_classroom_json = {"courseId":1898,"unionFlag":0,"lessonId":49264,"planStudentCount":4,"timezoneName":"Asia/Shanghai","openTime":op_time,"overseasTag":0,"secondOpenTime":op_time,"teacherId":36573,"type":100} + api_url_created_classroom = "https://teach-api.huohua.cn/peppa-teach-api/classroom" + create_classroom_resp = req_session.post( + url=api_url_created_classroom, + json=create_classroom_json) + # print(req_session.headers) + # print(req_session.cookies) + # print(resp.content) + # print(resp.url) + print(create_classroom_resp.json()) + classroom_id = create_classroom_resp.json()['data']["classroomId"] + print(create_classroom_resp.json()) + # + # return req_session + add_student_json = {"classroomId":classroom_id,"studentId":274886,"userId":275774,"joinType":0,"ignoreClassHour":1} + api_url_add_student = "https://teach-api.huohua.cn/peppa-teach-api/classroom_student/add" + add_student_resp = req_session.post( + url=api_url_add_student, + json=add_student_json) + print(add_student_resp.json()) + + # return req_session + add_student_json1 = {"classroomId":classroom_id,"studentId":5635578,"userId":5646962,"joinType":0,"ignoreClassHour":1} + add_student_resp1 = req_session.post( + url=api_url_add_student, + json=add_student_json1) + print(add_student_resp1.json()) + +if __name__ == '__main__': + + + host_list = [{"ip": "127.0.0.1", "host_name": "ts.sim.huohua.cn"}, + {"ip": "127.0.0.1", "host_name": "its.sim.huohua.cn"}, + {"ip": "127.0.0.1", "host_name": "student-api.sim.huohua.cn"}, + {"ip": "127.0.0.1", "host_name": "sentry.sim.huohua.cn"}, + {"ip": "127.0.0.1", "host_name": "gray.sim.huohua.cn"}, + {"ip": "127.0.0.1", "host_name": "classroom-api.sim.huohua.cn"}, + {"ip": "127.0.0.1", "host_name": "logserver.sim.huohua.cn"}, + {"ip": "127.0.0.1", "host_name": "zipkin.sim.huohua.cn"}, + {"ip": "127.0.0.1", "host_name": "gs.sim.huohua.cn"}] + A = EduClassroom() + A.kw_ubrd_get_online_test_classroom() \ No newline at end of file diff --git a/zhyy/library/CommonFun/statistical_function.py b/zhyy/library/CommonFun/statistical_function.py new file mode 100644 index 0000000..e54072d --- /dev/null +++ b/zhyy/library/CommonFun/statistical_function.py @@ -0,0 +1,142 @@ +import os +import re +import threading + + +class StatisticalFunction: + def __init__(self): + self.java_dir =[] + self.zh_file = [] + self.python_dir = [] + self.robot_dir = [] + + + def check_lang_every_word(self,file_path): + """ 获取文件中注释外含中文的文档 """ + # file_path ='C:\\Users\\HuoH # a=time.time()ua\\Downloads\\peppa-parent-api\\src\\main\\java\\com\\peppa\\parent\\api\\common\\ApiErrorEnum.java' + with open(file_path,'r',encoding='utf-8') as text: + all_content = text.read() + all_content = all_content.encode('utf-8') + all_content = all_content.decode('utf-8','ignore') + yy= re.sub("\/\*\*.*?\*\*\/", "",all_content) # 去除多行注释 + yy = re.sub("\/\*[\w\W]*?\*\/|\/\/.*", "", yy) # 去除文本注释 + yy = re.sub("(? 0: + # cls_list_len = len(cls_list_all) + tmp_class_list = [] + del_list = [] + for index in range(len(class_list)): + if not cls_list_all: + # 先找出所有的父类是object的基类 + if class_list[index].__bases__[0] == object: + # while class_list[index].__bases__[0] == object: + tmp_class_list.append(class_list[index]) + del_list.append(class_list[index]) + else: + # 寻找前一步晒出基类的子类 + if class_list[index].__bases__[0] in cls_list_all[len(cls_list_all) - 1]: + tmp_class_list.append(class_list[index]) + del_list.append(class_list[index]) + if len(tmp_class_list) > 0: + # 如果父类在已选出的类中,则保存该类 + cls_list_all.append(tmp_class_list) + for item in del_list: + class_list.remove(item) + else: + # 如果父类都不在已选出的类中,则全部按父类是object处理 + cls_list_all.append(class_list) + class_list = [] + + +class_import(["base_framework", "public_tools"]) +class_import(["base_framework", "public_business"]) +class_import([team, "library"]) +# BaseLibrary = type('BaseLibrary', tuple(cls_list_father + cls_list_children), {}) +sort_class(t_c_list) +c_len = len(cls_list_all) +obj_cls_tuple = () +while c_len > 0: + obj_cls_tuple += tuple(cls_list_all[c_len - 1], ) + c_len = c_len - 1 +print(obj_cls_tuple) +BaseLibrary = type('BaseLibrary', obj_cls_tuple, {}) + + +class KwLibrary(BaseLibrary): + + def __init__(self): + class_len = 0 + for base in BaseLibrary.__bases__: + try: + if hasattr(base, '__init__'): + if '__init__' in base.__dict__.keys(): + if base.__bases__[0] != object or len(base.__init__.__code__.co_varnames) == 1: + base.__init__(self) + print(base) + # obj_log.info("init class:{} success...".format(base)) + class_len += 1 + except Exception as e: + if base.__bases__[0] == object: + print("INIT", e, base, base.__dict__) + # obj_log.info("import files num: {}".format(class_len)) + print("import files num: {}".format(class_len)) diff --git a/zhyy/library/__pycache__/ZZYY_interface.cpython-38.pyc b/zhyy/library/__pycache__/ZZYY_interface.cpython-38.pyc new file mode 100644 index 0000000..3e36819 Binary files /dev/null and b/zhyy/library/__pycache__/ZZYY_interface.cpython-38.pyc differ diff --git a/zhyy/library/__pycache__/__init__.cpython-38.pyc b/zhyy/library/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000..044ba08 Binary files /dev/null and b/zhyy/library/__pycache__/__init__.cpython-38.pyc differ diff --git a/zhyy/qa_helper/__init__.py b/zhyy/qa_helper/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/zhyy/qa_helper/__pycache__/__init__.cpython-38.pyc b/zhyy/qa_helper/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000..fdb43c5 Binary files /dev/null and b/zhyy/qa_helper/__pycache__/__init__.cpython-38.pyc differ diff --git a/zhyy/qa_helper/common/__init__.py b/zhyy/qa_helper/common/__init__.py new file mode 100644 index 0000000..0161749 --- /dev/null +++ b/zhyy/qa_helper/common/__init__.py @@ -0,0 +1 @@ +print("222222222222") \ No newline at end of file diff --git a/zhyy/qa_helper/common/__pycache__/__init__.cpython-38.pyc b/zhyy/qa_helper/common/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000..04be308 Binary files /dev/null and b/zhyy/qa_helper/common/__pycache__/__init__.cpython-38.pyc differ diff --git a/zhyy/qa_helper/common/__pycache__/order.cpython-38.pyc b/zhyy/qa_helper/common/__pycache__/order.cpython-38.pyc new file mode 100644 index 0000000..fe34ed0 Binary files /dev/null and b/zhyy/qa_helper/common/__pycache__/order.cpython-38.pyc differ diff --git a/zhyy/qa_helper/common/__pycache__/user.cpython-38.pyc b/zhyy/qa_helper/common/__pycache__/user.cpython-38.pyc new file mode 100644 index 0000000..f9253d2 Binary files /dev/null and b/zhyy/qa_helper/common/__pycache__/user.cpython-38.pyc differ diff --git a/zhyy/qa_helper/common/course_package.py b/zhyy/qa_helper/common/course_package.py new file mode 100644 index 0000000..cc001b8 --- /dev/null +++ b/zhyy/qa_helper/common/course_package.py @@ -0,0 +1,337 @@ +# -*- coding:utf-8 -*- +""" +Author: 陈江 +Email: chenjiang@sparkedu.com +Create Date: 2022/09/23 2:06 下午 +""" +from base_framework.public_tools.sqlhelper import MySqLHelper + +import json + +obj_mysql_helper = MySqLHelper() + + +class CoursePackage: + def __init__(self): + pass + + @staticmethod + def __get_data_name(data_list): + temp_data_list = [] + if data_list: + for data in data_list: + temp_data_list.append( + {"course_package_id": data.get('value'), "cc显示_course_package_name": data.get('name'), + "course_package_name": data.get('text')}) + return temp_data_list + return None + + def get_course_package_by_cc_crm(self, post_data_input): + """ + | 功能说明: | 获取套餐信息,根据cc/crm购买可选套餐 | + | 输入参数: | + | 作者信息: | 陈江 | 修改时间 | 2022-9-28 | + """ + sql_crm_vispark_subscribe_chinese = """SELECT + IF (abbreviation="",NAME,abbreviation) AS 'text',name, + id AS 'value', +type,business_line,business_line_type,teaching_method,sale_status,saleable_status,course_subject,course_type,sale_type,course_teaching_method,STATUS,view_authority_scope +FROM + peppa.course_package +WHERE + type = 1 + AND business_line = 1 + AND business_line_type = 2 + AND teaching_method = 0 + AND sale_status = 1 + AND saleable_status = 1 + AND course_subject = ',2,' + AND course_type IN ( ',5,', ',1,' ) + AND sale_type = 2 + AND course_teaching_method = 1 + AND STATUS = 1 + AND view_authority_scope LIKE '%,7,%' +ORDER BY + id DESC LIMIT 3;""" + crm_vispark_subscribe_chinese_list = self.__get_data_name( + obj_mysql_helper.select_all(sql_crm_vispark_subscribe_chinese)) + + sql_crm_vispark_subscribe_sg = """SELECT + IF (abbreviation="",NAME,abbreviation) AS 'text',name, + id AS 'value', + type,business_line,business_line_type,teaching_method,sale_status,saleable_status,course_subject,course_type,sale_type,course_teaching_method,STATUS,view_authority_scope +FROM + peppa.course_package +WHERE + type = 1 + AND business_line = 1 + AND business_line_type = 1 + AND teaching_method = 0 + AND sale_status = 1 + AND saleable_status = 1 + AND course_subject = ',1,' + AND course_type IN ( ',5,' ) + AND sale_type = 2 + AND course_teaching_method = 1 + AND STATUS = 1 + AND view_authority_scope LIKE '%,10,%' +ORDER BY + id DESC limit 3;""" + crm_vispark_subscribe_sg_list = self.__get_data_name( + obj_mysql_helper.select_all(sql_crm_vispark_subscribe_sg)) + + sql_crm_vispark_subscribe_oc = """SELECT + IF (abbreviation="",NAME,abbreviation) AS 'text',name, + id AS 'value', + type,business_line,business_line_type,teaching_method,sale_status,saleable_status,course_subject,course_type,sale_type,course_teaching_method,STATUS,view_authority_scope +FROM + peppa.course_package +WHERE + type = 1 + AND business_line = 1 + AND business_line_type in( 1,10 ) + AND teaching_method = 0 + AND sale_status = 1 + AND saleable_status = 1 + AND course_subject = ',1,' + AND course_type IN ( ',5,' ) + AND sale_type = 2 + AND course_teaching_method = 1 + AND STATUS = 1 + AND view_authority_scope LIKE '%,13,%' +ORDER BY + id DESC limit 3;""" + crm_vispark_subscribe_oc_list = self.__get_data_name( + obj_mysql_helper.select_all(sql_crm_vispark_subscribe_oc)) + + sql_crm_vispark_buy_chinese = """SELECT + IF (abbreviation="",NAME,abbreviation) AS 'text',name, + id AS 'value', + type,business_line,business_line_type,teaching_method,sale_status,saleable_status,course_subject,course_type,sale_type,course_teaching_method,STATUS,view_authority_scope +FROM + peppa.course_package +WHERE + type = 1 + AND business_line = 1 + AND business_line_type = 2 + AND teaching_method = 0 + AND sale_status = 1 + AND saleable_status = 1 + AND course_subject = ',2,' + AND course_type IN ( ',5,', ',1,', ',1,5,', ',5,1,') + AND sale_type = 1 + AND course_teaching_method = 1 + AND STATUS = 1 + AND view_authority_scope LIKE '%,7,%' +ORDER BY + IF (abbreviation="",NAME,abbreviation) asc limit 3;""" + crm_vispark_buy_chinese_list = self.__get_data_name( + obj_mysql_helper.select_all(sql_crm_vispark_buy_chinese)) + + sql_crm_vispark_buy_us = """SELECT + IF (abbreviation="",NAME,abbreviation) AS 'text',name, + id AS 'value', + type,business_line,business_line_type,teaching_method,sale_status,saleable_status,course_subject,course_type,sale_type,course_teaching_method,STATUS,view_authority_scope +FROM + peppa.course_package +WHERE + type = 1 + AND business_line = 1 + AND business_line_type = 10 + AND teaching_method = 0 + AND sale_status = 1 + AND saleable_status = 1 + AND course_subject = ',1,' + AND course_type IN ( ',5,', ',1,', ',1,5,', ',5,1,') + AND sale_type = 1 + AND course_teaching_method = 1 + AND STATUS = 1 + AND view_authority_scope LIKE '%,11,%' +ORDER BY + id DESC limit 3;""" + crm_vispark_buy_us_list = self.__get_data_name( + obj_mysql_helper.select_all(sql_crm_vispark_buy_us)) + + sql_crm_vispark_buy_canada = """SELECT + IF (abbreviation="",NAME,abbreviation) AS 'text',name, + id AS 'value', + type,business_line,business_line_type,teaching_method,sale_status,saleable_status,course_subject,course_type,sale_type,course_teaching_method,STATUS,view_authority_scope +FROM + peppa.course_package +WHERE + type = 1 + AND business_line = 1 + AND business_line_type = 10 + AND teaching_method = 0 + AND sale_status = 1 + AND saleable_status = 1 + AND course_subject = ',1,' + AND course_type IN ( ',5,', ',1,', ',1,5,', ',5,1,') + AND sale_type = 1 + AND course_teaching_method = 1 + AND STATUS = 1 + AND view_authority_scope LIKE '%,1012,%' +ORDER BY + id DESC limit 3;""" + crm_vispark_buy_canada_list = self.__get_data_name( + obj_mysql_helper.select_all(sql_crm_vispark_buy_canada)) + + sql_crm_vispark_buy_sg = """SELECT + IF (abbreviation="",NAME,abbreviation) AS 'text',name, + id AS 'value', + type,business_line,business_line_type,teaching_method,sale_status,saleable_status,course_subject,course_type,sale_type,course_teaching_method,STATUS,view_authority_scope +FROM + peppa.course_package +WHERE + type = 1 + AND business_line = 1 + AND business_line_type = 1 + AND teaching_method = 0 + AND sale_status = 1 + AND saleable_status = 1 + AND course_subject = ',1,' + AND course_type IN ( ',5,', ',1,', ',1,5,', ',5,1,') + AND sale_type = 1 + AND course_teaching_method = 1 + AND STATUS = 1 + AND view_authority_scope LIKE '%,10,%' +ORDER BY + id DESC limit 3;""" + crm_vispark_buy_sg_list = self.__get_data_name( + obj_mysql_helper.select_all(sql_crm_vispark_buy_sg)) + + sql_crm_vispark_buy_oc = """SELECT + IF (abbreviation="",NAME,abbreviation) AS 'text',name, + id AS 'value', + type,business_line,business_line_type,teaching_method,sale_status,saleable_status,course_subject,course_type,sale_type,course_teaching_method,STATUS,view_authority_scope +FROM + peppa.course_package +WHERE + type = 1 + AND business_line = 1 + AND business_line_type in( 1,10 ) + AND teaching_method = 0 + AND sale_status = 1 + AND saleable_status = 1 + AND course_subject = ',1,' + AND course_type IN ( ',5,', ',1,', ',1,5,', ',5,1,') + AND sale_type = 1 + AND course_teaching_method = 1 + AND STATUS = 1 + AND view_authority_scope LIKE '%,13,%' +ORDER BY + id DESC limit 3;""" + crm_vispark_buy_oc_list = self.__get_data_name( + obj_mysql_helper.select_all(sql_crm_vispark_buy_oc)) + + sql_crm_vispark_buy_huohua = """SELECT + IF (abbreviation="",NAME,abbreviation) AS 'text',name, + id AS 'value', + type,business_line,business_line_type,teaching_method,sale_status,saleable_status,course_subject,course_type,sale_type,course_teaching_method,STATUS,view_authority_scope +FROM + peppa.course_package +WHERE + teaching_method = 0 + AND business_line_type in( 0,4,9 ) +-- AND business_line = 0 (以及业务线) + AND sale_status = 1 + AND saleable_status = 1 + AND course_subject like '%,2,%' +-- AND course_type IN ( ',1,',',5,' ) (课程类型) + AND sale_type = 1 + AND course_teaching_method = 1 +-- AND STATUS = 1 (状态) + AND view_authority_scope LIKE '%,7,%' +ORDER BY + id DESC limit 3;""" + crm_vispark_buy_huohua_list = self.__get_data_name( + obj_mysql_helper.select_all(sql_crm_vispark_buy_huohua)) + + sql_crm_vispark_subscribe_huohua = """SELECT + IF (abbreviation="",NAME,abbreviation) AS 'text',name, + id AS 'value', + type,business_line,business_line_type,teaching_method,sale_status,saleable_status,course_subject,course_type,sale_type,course_teaching_method,STATUS,view_authority_scope +FROM + peppa.course_package +WHERE + teaching_method = 0 + AND business_line_type in( 0,4,9 ) + AND business_line = 1 + AND sale_status = 1 + AND saleable_status = 1 + AND course_subject like '%,2,%' +-- AND course_type IN ( ',1,',',5,' ) (课程类型) + AND sale_type = 2 + AND course_teaching_method = 1 +-- AND STATUS = 1 (状态) + AND view_authority_scope LIKE '%,7,%' +ORDER BY + id DESC limit 3;""" + crm_vispark_subscribe_huohua_list = self.__get_data_name( + obj_mysql_helper.select_all(sql_crm_vispark_subscribe_huohua)) + + sql_cc_vispark_subscribe_huohua = """SELECT + IF (abbreviation="",NAME,abbreviation) AS 'text',name, + id AS 'value', + type,business_line,business_line_type,teaching_method,sale_status,saleable_status,course_subject,course_type,sale_type,course_teaching_method,STATUS,view_authority_scope +FROM + peppa.course_package +WHERE + teaching_method = 0 + AND business_line_type in( 0,3,9 ) + AND business_line = 1 + AND sale_status = 1 + AND saleable_status = 1 + AND course_subject like '%,1,%' +-- AND course_type IN ( ',1,',',5,' ) (课程类型) + AND sale_type = 2 + AND course_teaching_method = 1 +-- AND STATUS = 1 (状态) + AND view_authority_scope LIKE '%,2,%' +ORDER BY + id DESC limit 3;""" + cc_vispark_subscribe_huohua_list = self.__get_data_name( + obj_mysql_helper.select_all(sql_cc_vispark_subscribe_huohua)) + + sql_cc_vispark_buy_huohua = """SELECT + IF (abbreviation="",NAME,abbreviation) AS 'text',name, + id AS 'value', + type,business_line,business_line_type,teaching_method,sale_status,saleable_status,course_subject,course_type,sale_type,course_teaching_method,STATUS,view_authority_scope +FROM + peppa.course_package +WHERE + teaching_method = 0 + AND business_line_type in( 0,3,9 ) + AND business_line = 1 + AND sale_status = 1 + AND saleable_status = 1 + AND course_subject like '%,1,%' +-- AND course_type IN ( ',1,',',5,' ) (课程类型) + AND sale_type = 1 + AND course_teaching_method = 1 +-- AND STATUS = 1 (状态) + AND view_authority_scope LIKE '%,2,%' +ORDER BY + id DESC limit 3;""" + cc_vispark_buy_huohua_list = self.__get_data_name( + obj_mysql_helper.select_all(sql_cc_vispark_buy_huohua)) + data = {"crm中订阅vispark业务的对外汉语-套餐": crm_vispark_subscribe_chinese_list, + "crm中订阅vispark业务的sg-套餐": crm_vispark_subscribe_sg_list, + "crm中订阅vispark业务的海华-套餐": crm_vispark_subscribe_oc_list, + "crm中直购vispark业务的对外汉语-套餐": crm_vispark_buy_chinese_list, + "crm中直购vispark业务的us-套餐": crm_vispark_buy_us_list, + "crm中直购vispark业务的加拿大-套餐": crm_vispark_buy_canada_list, + "crm中直购vispark业务的sg-套餐": crm_vispark_buy_sg_list, + "crm中直购vispark业务的海华-套餐": crm_vispark_buy_oc_list, + "crm中直购vispark业务的火花-套餐": crm_vispark_buy_huohua_list, + "crm中订阅vispark业务的火花-套餐": crm_vispark_subscribe_huohua_list, + "cc中订阅vispark业务的火花-套餐": cc_vispark_subscribe_huohua_list, + "cc中直购vispark业务的火花-套餐": cc_vispark_buy_huohua_list} + + return data + + +if __name__ == '__main__': + cc = CoursePackage() + bb = cc.get_course_package_by_cc_crm('') + print(bb) diff --git a/zhyy/qa_helper/common/order.py b/zhyy/qa_helper/common/order.py new file mode 100644 index 0000000..3c85ed5 --- /dev/null +++ b/zhyy/qa_helper/common/order.py @@ -0,0 +1,109 @@ +# -*- coding:utf-8 -*- +""" +Author: 陈江 +Email: chenjiang@sparkedu.com +Create Date: 2022/09/23 2:06 下午 +""" +from base_framework.public_business.common.UBRD.kw.coursePackage_keyword import CoursePackageKW +from base_framework.public_tools import utils +from base_framework.public_tools.sqlhelper import MySqLHelper +from base_framework.public_tools.mg_keyword import ManageKeyWord +from base_framework.public_business import manage_public_business + +import json + + +obj_course_package_kw = CoursePackageKW() +obj_get_way = utils.Tools() +obj_mysql_helper = MySqLHelper() +obj_manage_kw = ManageKeyWord() +obj_manage_business = manage_public_business.ManagePublicBusiness() + +class Order: + def __init__(self): + pass + + def order_done_pay(self, post_data_input): + """ + | 功能说明: | 订单完成支付 | + | 输入参数: | + |orderId/orderCode/prepayCode| 订单id/code/prepayCode | 必填其一 + | podenv | str | 独立环境编号| 非必填 + | 作者信息: | 陈江 | 修改时间 | 2022-9-28 | + """ + if not post_data_input.get('orderId') and not post_data_input.get('orderCode') and not post_data_input.get( + 'prepayCode'): + raise "orderId/orderCode/prepayCode必传1个" + + ordr_info = obj_course_package_kw.get_order_info(post_data_input) + order_id = ordr_info.get('orderId') + mq_body_dict = obj_course_package_kw.kw_ubrd_public_get_service_id_by_order_id({"orderId": order_id}) + message_body = {"orderId": order_id, "serviceId": int(mq_body_dict.get('serviceId'))} + if post_data_input.get('podenv'): + obj_get_way.mq_send(mq_body_dict.get('topic'), json.dumps(message_body), pro=post_data_input.get('podenv')) + else: + obj_get_way.mq_send(mq_body_dict.get('topic'), json.dumps(message_body)) + order_100_sql = f"SELECT id FROM `order_center`.`order_course_package` WHERE id='{order_id}' AND status=100" + obj_mysql_helper.check_result_exist(order_100_sql, retry_count=20) + subscribe_id_sql = f"SELECT subscribe_id FROM `order_center`.`subscribe_order_log` WHERE order_id={order_id}" + obj_mysql_helper.check_result_exist(subscribe_id_sql, retry_count=20) + subscribe_id = obj_mysql_helper.select_one(subscribe_id_sql).get('subscribe_id') + + return {"order_id": order_id, "subscribe_id": subscribe_id} + + def add_review_subscribe_order(self, post_data_input): + """ + | 功能说明: | 生成续订阅订单 | + | 输入参数: | + |subscribeIds| 订阅id | 传了用该订阅id生成续订阅订单,必传 比如[3355] + | 作者信息: | 张楠 | 修改时间 | 2022-11-10 | + """ + try: + post_data_input = eval(post_data_input) + except: + post_data_input = post_data_input + + if not post_data_input.get('subscribeIds'): + raise RuntimeWarning("订阅id必传") + # 构造生成续订阅订单请求参数 + response_data = obj_manage_kw.kw_execute_xxl_job(8306, para=json.dumps(post_data_input)) + return response_data + + def create_order(self, post_data_input): + """ + | 功能说明: | 创建直购订单 | + | 请求参数名 | 说明 | 类型 | 是否必填 | 如无要求时的值 | + | userId | 用户id | string | 0 | | + | phone | 用户手机号 | string | 0 | | + | coursePackageId | 套餐id | integer | 0 | | + | businessLine | 业务线一级类型,0:火花中国、1:vispark | integer | 0 | | + | businessLineType | 业务线二级类型,1:新加坡数学、2:对外汉语、3:直播逻辑思维、4:直播中文素养、5:直播双语素养、6:AI、7:围棋、8:国际象棋、9:其他、10:北美数学、11:越南数学、12:春风思维、13:口才课 | integer | 0 | | + | category | 业务线三级类型,K-SMAP:新加坡AP、P-CM:新加坡CP、A:北美AP、C-北美CP、V-越南AP、N-越南CP、ZHK-对外汉语综合课 | string | 0 | | + | teachingMethod | 授课方式, 0:直播、1:AI | integer | 0 | 0 | + | type | 套餐类型,1:正式课、2:体验课 | integer | 0 | 1 | + | courseSubjectList | 课程学科集合,1:数学、2:语文、3:科学、4:英语、5:编程、7:围棋、8:国际象棋、9:春风思维、10:口才课 | list | 0 | | + | courseId | 课程id | integer | 0 | | + | channel | 渠道,1:电商、0:非电商 | integer | 0 | 0 | + | channelValue | 渠道chi | string | 0 | 0 | + | classHourType | 课时类型,0:小班课、3:1v1 | integer | 0 | | + | offline | 线下,1:线下、0:线上 | integer | 0 | 1 | + | businessType | 业务类型,0:常规、1:补差、2:可补差 | integer | 0 | 0 | + | currency | 币种,0:人民币、1:美元 | integer | 0 | | + | saleType | 售卖方式,1:直购、2:订阅 | integer | 0 | 1 | + | teachingScope | 授课范围,1:所有课程、2:自定义课程 | integer | 0 | 1 | + | payPrice | 实付金额 | integer | 0 | | + | createdType | 订单类型,0:默认未选择、 10:团购订单、11:加价购-团购订单、12:加价购-普通订单、20:CC提单、30:CC提单、40:自助下单、50:微店购买| integer | 0| 0 | + Returns: + {orderId, orderCode} + | 作者信息: | lrq | 修改时间 | 2025-03-18 | + """ + try: + post_data_input = eval(post_data_input) + except: + post_data_input = post_data_input + return obj_manage_business.kw_public_manage_create_order(**post_data_input) + + +if __name__ == '__main__': + a = Order() + a.create_order({"userId":"2557864","channel":"35d75242534611e8b5325cb9018964ec","coursePackageId":"2503934602"}) diff --git a/zhyy/qa_helper/common/user.py b/zhyy/qa_helper/common/user.py new file mode 100644 index 0000000..a53d867 --- /dev/null +++ b/zhyy/qa_helper/common/user.py @@ -0,0 +1,208 @@ +# -*- coding:utf-8 -*- +""" +Author: 陈江 +Email: chenjiang@sparkedu.com +Create Date: 2022/09/23 2:06 下午 +""" +from base_framework.public_business.common.UBRD.UBRD_public_business import BaseLogic +from base_framework.public_business.common.UBRD.kw.coursePackage_keyword import CoursePackageKW +from base_framework.public_tools import utils +from base_framework.public_tools.sqlhelper import MySqLHelper +from base_framework.public_tools.log import get_logger +from base_framework.public_business.common.UBRD.kw.user_keyword import UserKW +from base_framework.public_tools.mg_keyword import ManageKeyWord +from library.CommonFun.handle_tools import Handle_tools +import requests + +obj_base_logic = BaseLogic() +obj_course_package_kw = CoursePackageKW() +obj_get_way = utils.Tools() +obj_mysql_helper = MySqLHelper() +log = get_logger() +obj_user_kw = UserKW() +obj_manage_kw = ManageKeyWord() +obj_handle_tools = Handle_tools() + + +class User: + def __init__(self): + pass + + def add_user_recharge(self, post_data_input): + """ + | 功能说明: | 新增用户,充值课时 | + | 输入参数: | + |phone/userId| 用户手机号/用户id | 非必填,不传会默认随机注册一个账号;传的手机号不存在会自动进行注册; + |coursePackageCode/courseId/coursePackageId/coursePackageName| 传一个即可 | 传了就给用充值课时,不传就不充 + |classHour | | 充值课时 | 不传,需要充值时默认充值100 + |new_user_count | | 生成新账号数量,存在这个参数时只会生成相应数量的账号 | + | 作者信息: | 陈江 | 修改时间 | 2022-9-23 | + """ + user_list = [] + try: + post_data_input = eval(post_data_input) + except: + post_data_input = post_data_input + + if post_data_input.get('new_user_count'): + new_user_count = post_data_input.pop('new_user_count') + for count in range(0, int(new_user_count)): + user_info = obj_base_logic.logic_public_add_user_recharge(post_data_input) + user_list.append(user_info) + return user_list + return obj_base_logic.logic_public_add_user_recharge(post_data_input) + + def add_new_user(self, post_data_input): + """ + | 功能说明: | 新增用户 | + | 输入参数: | + |fu_phone/fu_user_Id| 邀请人手机号或者user_id | 非必填,不传新增用户无邀请人; + |new_phone| 新增用户手机号 | 传了用该手机号注册,不传则随机一个手机号 + |new_user_count | | 生成新账号数量,存在这个参数时只会生成相应数量的账号 | + | 作者信息: | 陈江 | 修改时间 | 2022-11-01 | + """ + user_list = [] + try: + post_data_input = eval(post_data_input) + except: + post_data_input = post_data_input + + # 构造增加用户请求参数 + add_user_params = {} + # 判断是否需要进行邀请 + if post_data_input.get('fu_phone'): + user_info = obj_user_kw.kw_ubrd_public_get_user_info_by_phone(phone=post_data_input.get('fu_phone')) + add_user_params['fromUserId'] = user_info.get('user_id') + elif post_data_input.get('fu_user_Id'): + add_user_params['fromUserId'] = post_data_input.get('fu_user_Id') + else: + pass + + if post_data_input.get('new_phone'): + user_info = obj_user_kw.kw_ubrd_public_get_user_info_by_phone(phone=post_data_input.get('new_phone')) + if user_info: + raise RuntimeWarning(f"该手机号{post_data_input.get('new_phone')}已经存在") + add_user_params['phone'] = post_data_input.get('new_phone') + obj_user_kw.kw_ubrd_public_add_user_profile(post_data_input=add_user_params) + user_info = obj_user_kw.kw_ubrd_public_get_user_info_by_phone(phone=post_data_input.get('new_phone')) + return user_info + else: + if post_data_input.get('new_user_count'): + new_user_count = post_data_input.pop('new_user_count') + for count in range(0, int(new_user_count)): + phone = obj_user_kw.kw_ubrd_public_get_unregistered_phone() + add_user_params['phone'] = phone + obj_user_kw.kw_ubrd_public_add_user_profile(post_data_input=add_user_params) + user_info = obj_user_kw.kw_ubrd_public_get_user_info_by_phone(phone=phone) + user_list.append(user_info) + return user_list + else: + phone = obj_user_kw.kw_ubrd_public_get_unregistered_phone() + add_user_params['phone'] = phone + obj_user_kw.kw_ubrd_public_add_user_profile(post_data_input=add_user_params) + user_info = obj_user_kw.kw_ubrd_public_get_user_info_by_phone(phone=phone) + return user_info + + def add_new_haihua_user(self, post_data_input): + """ + | 功能说明: | 生成海华标签新用户 | + | 输入参数: | + |fu_phone/fu_user_Id| 邀请人手机号或者user_id | 非必填,不传新增用户无邀请人; + |new_phone| 新增用户手机号 | 传了用该手机号注册,不传则随机一个手机号 + |new_user_count | | 生成新账号数量,存在这个参数时只会生成相应数量的账号 | + | 作者信息: | 陈江 | 修改时间 | 2022-11-01 | + """ + user_info_list = self.add_new_user(post_data_input) + if user_info_list: + for user_info in user_info_list: + # 设置权益信息 + obj_mysql_helper.insert_one( + f"""INSERT INTO `operation`.`haibao_task_equity`(`user_id`, `order_id`, `order_business_side`, `course_package_id`, `course_package_name`, `equity_num`, `used_num`, `remain_num`, `lock_num`, `start_time`, `end_time`, `effect_period`, `generated_first_task`, `frozen_status`, `refund_status`, `equity_status`, `policy`, `subject_type`, `related_order_id`, `creator_id`, `creator_name`, `created_time`, `modifier_id`, `modifier_name`, `modified_time`) VALUES ({user_info.get('user_id')}, 8881050, 0, 23002, '逻辑思维直播系统课48课包', 20, 0, 20, 0, '2023-02-09 14:44:36', '2023-02-09 14:44:36', 360, 0, 0, 0, 0, 2, 0, 0, 0, '', '2023-02-01 00:00:01', 0, '', '2023-02-09 14:44:36');""") + # 设置海华标签 + obj_mysql_helper.insert_one( + f"""INSERT INTO `utag_mass`.`user_tag`(`user_id`, `tag_id`, `tag_name`, `creator_id`, `creator_name`, `created_time`, `status`, `is_del`, `source_type`, `expiration_time`, `modifier_id`, `modifier_name`, `modified_time`) VALUES ({user_info.get('user_id')}, 51, '海外及港澳台用户', 587312, '陈江', '2023-02-09 16:08:24', 1, 0, 0, NULL, 587312, '陈江', '2023-02-09 16:08:24');""") + return user_info_list + + def get_unregistered_user(self, post_data_input): + """ + | 功能说明: | 获取未注册手机号 | + | 输入参数: | + |user_count | | 返回未注册手机号数量 | + | 作者信息: | 刘涛婷 | 修改时间 | 2022-11-10 | + """ + user_list = [] + try: + post_data_input = eval(post_data_input) + except: + post_data_input = post_data_input + + if post_data_input.get('user_count'): + user_count = post_data_input.pop('user_count') + for count in range(0, int(user_count)): + user_phone = obj_user_kw.kw_ubrd_public_get_unregistered_phone() + user_list.append(user_phone) + return user_list + return obj_user_kw.kw_ubrd_public_get_unregistered_phone() + + def get_sms_code_by_phone(self, post_data_input): + """ + | 功能说明: | 发送验证码 | + | 输入参数: + | phone | 手机号| 必填 + | countryCode | 国家码 | 默认 86 + | authType | 验证码类型 | 默认2 + | verifyAppId | 验证appId | 默认2 + | 作者信息: | 陈江 | 2022/10/21 | + """ + # 发送验证码 + obj_base_logic.logic_public_send_auth_code(post_data_input) + # 获取验证码内容 + # 根据phone获取phone_code + phone_server_ip = obj_get_way.get_container_ip_from_eureka('PHONE-SERVER', need_jira_id='qa', + eureka_url='http://eureka.qa.huohua.cn') + if post_data_input.get('countryCode'): + phone = f"{post_data_input.get('countryCode')}-{post_data_input.get('phone')}" + else: + phone = post_data_input.get('phone') + + if phone_server_ip.get('container_ip'): + url = 'http://{}:8080/encrypt/regdata?biztype=phone&uid=123456&sourceData={}'.format( + phone_server_ip.get('container_ip'), phone) + response = requests.post(url=url) # 三个参数 + response_json = response.json() + else: + raise "获取phone server ip失败" + + if response_json.get('data'): + msg = obj_mysql_helper.select_all( + 'SELECT msg FROM `push_service`.`sms` WHERE `phone_code` = \'{}\' ORDER BY id DESC LIMIT 1 '.format( + response_json.get('data'))) + if msg: + web_hook = "https://open.feishu.cn/open-apis/bot/v2/hook/c9baf09d-55f1-46a6-8d67-9d4ff30b1be2" + message_data = {"msg_type": "post", "content": { + "post": {"zh_cn": {"title": "message", + "content": [[{"tag": "text", "text": msg[0].get('msg')}]]}}}} + obj_handle_tools.send_message_by_feishu(web_hook, message_data) + else: + raise f"{phone}获取验证码失败" + return msg[0].get('msg') + + else: + return response_json + + def register_user(self, post_data_input): + """ + | 功能说明: | 通过注册接口进行注册用户并设置密码A123456(支持邀请码,渠道) | + | 输入参数: | + | | | channelId | string | 渠道id | + | | | invitePhone | string | 手机号 | "12030690001/86-13400114600" + | | | registerPhone | string | 手机号 | "12030690001/86-13400114600" + | 作者信息: | 陈江 | 修改时间 | 2022-11-10 | + """ + return obj_base_logic.logic_public_register_user(post_data_input) + + +if __name__ == '__main__': + ff = {'phone': '13400234500','coursePackageId':29497} + # User().add_user_recharge(ff) + User().add_new_user(post_data_input={"new_user_count":"2"}) diff --git a/zhyy/test_case/JENKINS_SETUP.md b/zhyy/test_case/JENKINS_SETUP.md new file mode 100644 index 0000000..680924b --- /dev/null +++ b/zhyy/test_case/JENKINS_SETUP.md @@ -0,0 +1,190 @@ +# Jenkins + Allure 报告集成配置指南 + +## 前置要求 + +1. **Jenkins已安装并运行** +2. **安装Allure插件** + - 进入 Jenkins → Manage Jenkins → Manage Plugins + - 搜索并安装 "Allure Plugin" +3. **安装Allure命令行工具** + - 下载:https://github.com/allure-framework/allure2/releases + - 解压并添加到系统PATH环境变量 + - 或在Jenkins全局工具配置中配置Allure路径 + +## Jenkins配置步骤 + +### 方式一:使用Jenkinsfile(推荐) + +1. **在Jenkins中创建Pipeline任务** + - 新建任务 → 选择 "Pipeline" + - 任务名称:例如 "ZZYY_Test_Automation" + +2. **配置Pipeline** + - Pipeline definition → Pipeline script from SCM + - SCM: Git(或其他版本控制) + - Script Path: `zhyy/test_case/Jenkinsfile` + - 保存 + +3. **运行任务** + - 点击 "Build with Parameters" + - 选择运行方式(RUN_TYPE) + - 填写相应参数 + - 点击 "Build" + +### 方式二:自由风格项目配置 + +1. **创建自由风格项目** + - 新建任务 → 选择 "Freestyle project" + - 任务名称:例如 "ZZYY_Test_Automation" + +2. **配置源码管理** + - Source Code Management → Git + - Repository URL: 你的Git仓库地址 + - Branch: 分支名称 + +3. **配置构建步骤** + - Build → Add build step → Execute shell(Linux/Mac)或 Execute Windows batch command(Windows) + - 命令示例: + ```bash + # Linux/Mac + cd ${WORKSPACE} + python zhyy/test_case/run_tests.py --all --no-report + ``` + ```batch + # Windows + cd %WORKSPACE% + python zhyy\test_case\run_tests.py --all --no-report + ``` + +4. **配置Allure报告** + - Post-build Actions → Add post-build action → Allure Report + - Results path: `zhyy/test_case/reports/allure-results` + - Report path: `zhyy/test_case/reports/allure-report`(可选) + - 保存 + +5. **配置参数化构建(可选)** + - This project is parameterized → Add Parameter + - 添加Choice Parameter: + - Name: `RUN_TYPE` + - Choices: `all`, `feature`, `story`, `dir`, `file`, `keyword`, `marker` + - 添加String Parameter(根据需要): + - `FEATURE_NAME`, `STORY_NAME`, `DIR_PATH`, `FILE_PATH`, `KEYWORD`, `MARKER` + +6. **修改构建命令以使用参数** + ```bash + # Linux/Mac + cd ${WORKSPACE} + if [ "${RUN_TYPE}" = "all" ]; then + python zhyy/test_case/run_tests.py --all --no-report + elif [ "${RUN_TYPE}" = "feature" ]; then + python zhyy/test_case/run_tests.py --feature "${FEATURE_NAME}" --no-report + elif [ "${RUN_TYPE}" = "dir" ]; then + python zhyy/test_case/run_tests.py --dir "${DIR_PATH}" --no-report + # ... 其他条件 + fi + ``` + +## Allure插件配置 + +### 全局工具配置 + +1. **配置Allure命令行工具路径** + - Manage Jenkins → Global Tool Configuration + - Allure Commandline → Add Allure Commandline + - Name: `Allure`(或自定义名称) + - Installation directory: Allure安装路径(如:`C:\allure\bin` 或 `/usr/local/allure/bin`) + - 保存 + +### 项目配置 + +1. **在项目配置中添加Allure报告** + - Post-build Actions → Allure Report + - Results path: `zhyy/test_case/reports/allure-results` + - 勾选 "Keep allure results history" + +## 环境变量配置 + +### Jenkins全局环境变量 + +1. **配置Python路径(如需要)** + - Manage Jenkins → Configure System → Global properties + - Environment variables → Add + - Name: `PYTHONPATH` + - Value: `${WORKSPACE}` + +### 项目环境变量 + +在Pipeline或构建脚本中设置: +```groovy +environment { + PYTHONPATH = "${WORKSPACE}" + ALLURE_RESULTS = "${WORKSPACE}/zhyy/test_case/reports/allure-results" +} +``` + +## 使用示例 + +### 运行所有测试 +```bash +python zhyy/test_case/run_tests.py --all --no-report +``` + +### 按目录运行 +```bash +python zhyy/test_case/run_tests.py --dir "接口/SZPurchase" --no-report +``` + +### 按Feature标签运行 +```bash +python zhyy/test_case/run_tests.py --feature "深圳采购工作台采购订单页面" --no-report +``` + +## 报告查看 + +1. **在Jenkins中查看** + - 构建完成后,在项目页面左侧菜单会出现 "Allure Report" 链接 + - 点击即可查看详细的测试报告 + +2. **报告内容** + - 测试用例执行情况 + - 通过/失败统计 + - 执行时间 + - 测试步骤详情 + - 截图和日志(如果配置了) + +## 常见问题 + +### 1. Allure命令未找到 +- 确保Allure已安装并添加到PATH +- 或在Jenkins全局工具配置中指定Allure路径 + +### 2. 模块导入错误 +- 检查PYTHONPATH环境变量 +- 确保项目根目录在Python路径中 + +### 3. 报告未生成 +- 检查allure-results目录是否存在且包含数据 +- 检查Jenkins Allure插件配置的路径是否正确 + +### 4. 权限问题 +- 确保Jenkins有权限访问工作空间目录 +- 确保有权限执行Python和Allure命令 + +## 邮件通知配置(可选) + +在Post-build Actions中添加: +- Email Notification +- 配置收件人、主题等 +- 可以附加Allure报告链接 + +## 定时构建(可选) + +在项目配置中: +- Build Triggers → Build periodically +- 例如:`H 2 * * *`(每天凌晨2点执行) + +## 多节点执行(可选) + +如果有多台Jenkins节点: +- 在Pipeline中配置 `agent { label 'your-node-label' }` +- 或在自由风格项目中配置 "Restrict where this project can be run" diff --git a/zhyy/test_case/Jenkinsfile b/zhyy/test_case/Jenkinsfile new file mode 100644 index 0000000..721bf36 --- /dev/null +++ b/zhyy/test_case/Jenkinsfile @@ -0,0 +1,175 @@ +pipeline { + agent any + + options { + // 保留最近10次构建 + buildDiscarder(logRotator(numToKeepStr: '10')) + // 超时时间60分钟 + timeout(time: 60, unit: 'MINUTES') + } + + environment { + // Python路径 + PYTHONPATH = "${WORKSPACE}" + // Allure结果目录 + ALLURE_RESULTS = "${WORKSPACE}/zhyy/test_case/reports/allure-results" + // Allure报告目录 + ALLURE_REPORT = "${WORKSPACE}/zhyy/test_case/reports/allure-report" + } + + stages { + stage('Checkout') { + steps { + echo '检出代码...' + checkout scm + } + } + + stage('环境准备') { + steps { + echo '准备测试环境...' + script { + // 确保Python环境 + sh ''' + python --version + pip --version + ''' + } + } + } + + stage('运行测试') { + steps { + echo '执行测试用例...' + script { + // 根据参数选择运行方式 + def runType = params.RUN_TYPE ?: 'all' + def testCommand = '' + + switch(runType) { + case 'all': + testCommand = 'python zhyy/test_case/run_tests.py --all --no-report' + break + case 'feature': + def feature = params.FEATURE_NAME ?: '' + testCommand = "python zhyy/test_case/run_tests.py --feature \"${feature}\" --no-report" + break + case 'story': + def story = params.STORY_NAME ?: '' + testCommand = "python zhyy/test_case/run_tests.py --story \"${story}\" --no-report" + break + case 'dir': + def dir = params.DIR_PATH ?: '' + testCommand = "python zhyy/test_case/run_tests.py --dir \"${dir}\" --no-report" + break + case 'file': + def file = params.FILE_PATH ?: '' + testCommand = "python zhyy/test_case/run_tests.py --file \"${file}\" --no-report" + break + case 'keyword': + def keyword = params.KEYWORD ?: '' + testCommand = "python zhyy/test_case/run_tests.py --keyword \"${keyword}\" --no-report" + break + case 'marker': + def marker = params.MARKER ?: '' + testCommand = "python zhyy/test_case/run_tests.py --marker \"${marker}\" --no-report" + break + default: + testCommand = 'python zhyy/test_case/run_tests.py --all --no-report' + } + + sh """ + cd ${WORKSPACE} + ${testCommand} + """ + } + } + post { + always { + // 无论成功失败都收集测试结果 + echo '收集测试结果...' + } + } + } + + stage('生成Allure报告') { + steps { + echo '生成Allure报告...' + script { + sh """ + cd ${WORKSPACE} + allure generate ${ALLURE_RESULTS} -o ${ALLURE_REPORT} --clean || echo "Allure报告生成失败,但继续执行" + """ + } + } + } + } + + post { + always { + // 发布Allure报告 + allure([ + includeProperties: false, + jdk: '', + properties: [], + reportBuildPolicy: 'ALWAYS', + results: [[path: 'zhyy/test_case/reports/allure-results']] + ]) + + // 清理工作空间(可选) + // cleanWs() + } + success { + echo '✓ 测试执行成功' + // 可以在这里添加成功通知,如发送邮件、钉钉等 + } + failure { + echo '✗ 测试执行失败' + // 可以在这里添加失败通知 + } + unstable { + echo '⚠ 测试执行不稳定' + } + } +} + +// 参数化构建 +properties([ + parameters([ + choice( + name: 'RUN_TYPE', + choices: ['all', 'feature', 'story', 'dir', 'file', 'keyword', 'marker'], + description: '选择运行方式' + ), + string( + name: 'FEATURE_NAME', + defaultValue: '', + description: 'Feature标签名称(当RUN_TYPE=feature时使用)' + ), + string( + name: 'STORY_NAME', + defaultValue: '', + description: 'Story标签名称(当RUN_TYPE=story时使用)' + ), + string( + name: 'DIR_PATH', + defaultValue: '接口/SZPurchase', + description: '测试目录路径(当RUN_TYPE=dir时使用,相对于TestCase目录)' + ), + string( + name: 'FILE_PATH', + defaultValue: '接口/SZPurchase/PurchaseOrderManage.py', + description: '测试文件路径(当RUN_TYPE=file时使用,相对于TestCase目录)' + ), + string( + name: 'KEYWORD', + defaultValue: 'purchase', + description: '关键字(当RUN_TYPE=keyword时使用)' + ), + string( + name: 'MARKER', + defaultValue: 'smoke', + description: 'Pytest标记(当RUN_TYPE=marker时使用)' + ) + ]) +]) diff --git a/zhyy/test_case/README_JENKINS.md b/zhyy/test_case/README_JENKINS.md new file mode 100644 index 0000000..d13633f --- /dev/null +++ b/zhyy/test_case/README_JENKINS.md @@ -0,0 +1,84 @@ +# Jenkins + Allure 集成快速参考 + +## 快速开始 + +### 1. 安装Allure插件 +- Jenkins → Manage Jenkins → Manage Plugins +- 搜索 "Allure Plugin" 并安装 + +### 2. 配置Allure工具 +- Manage Jenkins → Global Tool Configuration +- Allure Commandline → 添加Allure安装路径 + +### 3. 创建Jenkins任务 + +#### 方式A:使用Jenkinsfile(推荐) +1. 新建Pipeline任务 +2. Pipeline script from SCM +3. Script Path: `zhyy/test_case/Jenkinsfile` +4. 保存并运行 + +#### 方式B:自由风格项目 +1. 新建Freestyle project +2. 构建步骤:执行 `jenkins_build.sh` 或 `jenkins_build.bat` +3. Post-build Actions → Allure Report +4. Results path: `zhyy/test_case/reports/allure-results` + +## 参数化构建 + +在Jenkins任务中配置以下参数: + +| 参数名 | 类型 | 说明 | 示例值 | +|--------|------|------|--------| +| RUN_TYPE | Choice | 运行方式 | all, feature, story, dir, file, keyword, marker | +| FEATURE_NAME | String | Feature标签 | 深圳采购工作台采购订单页面 | +| STORY_NAME | String | Story标签 | 验证采购工作台采购订单页面列表查询 | +| DIR_PATH | String | 目录路径 | 接口/SZPurchase | +| FILE_PATH | String | 文件路径 | 接口/SZPurchase/PurchaseOrderManage.py | +| KEYWORD | String | 关键字 | purchase | +| MARKER | String | Pytest标记 | smoke | + +## 常用命令 + +### 本地运行 +```bash +# 运行所有测试 +python run_tests.py + +# 按目录运行 +python run_tests.py --dir "接口/SZPurchase" + +# 按文件运行 +python run_tests.py --file "接口/SZPurchase/PurchaseOrderManage.py" + +# 生成并打开报告 +python run_tests.py --all --report --open +``` + +### Jenkins中运行 +```bash +# 使用构建脚本(自动检测参数) +bash jenkins_build.sh + +# 或直接使用run_tests.py +python run_tests.py --all --no-report +``` + +## Allure报告路径 + +- **结果目录**: `zhyy/test_case/reports/allure-results` +- **报告目录**: `zhyy/test_case/reports/allure-report` +- **Jenkins中查看**: 构建完成后点击左侧 "Allure Report" 链接 + +## 环境变量 + +Jenkins会自动设置以下环境变量: +- `WORKSPACE`: Jenkins工作空间路径 +- `BUILD_NUMBER`: 构建编号 +- `JENKINS_URL`: Jenkins服务器地址 + +`run_tests.py` 会自动检测Jenkins环境并调整路径。 + +## 详细文档 + +更多配置说明请参考:`JENKINS_SETUP.md` diff --git a/zhyy/test_case/README_RUN_TESTS.md b/zhyy/test_case/README_RUN_TESTS.md new file mode 100644 index 0000000..15762dc --- /dev/null +++ b/zhyy/test_case/README_RUN_TESTS.md @@ -0,0 +1,96 @@ +# 测试执行说明 + +## 统一测试执行工具 + +使用 `run_tests.py` 可以方便地执行各种测试用例。 + +## 安装依赖 + +```bash +pip install pytest +pip install pytest-allure-adaptor # 或 allure-pytest +pip install allure-python-commons +``` + +## 使用方法 + +### 1. 运行所有测试用例 + +```bash +python run_tests.py --all +``` + +### 2. 按 Allure Feature 标签运行 + +```bash +python run_tests.py --feature "深圳采购工作台采购订单页面" +``` + +### 3. 按 Allure Story 标签运行 + +```bash +python run_tests.py --story "验证采购工作台采购订单页面列表查询" +``` + +### 4. 按目录运行 + +```bash +python run_tests.py --dir "接口/SZPurchase" +``` + +### 5. 按文件运行 + +```bash +python run_tests.py --file "接口/SZPurchase/PurchaseOrderManage.py" +``` + +### 6. 按关键字运行(匹配文件名或类名) + +```bash +python run_tests.py --keyword "purchase" +``` + +### 7. 按 pytest 标记运行 + +```bash +python run_tests.py --marker "smoke" +``` + +### 8. 生成并打开 Allure 报告 + +```bash +python run_tests.py --all --report --open +``` + +## 快捷方式(Windows) + +- `run_all.bat` - 运行所有测试用例 +- `run_purchase.bat` - 运行采购相关测试用例 + +## 参数说明 + +- `--all`: 运行所有测试用例 +- `--feature `: 按allure feature标签运行 +- `--story `: 按allure story标签运行 +- `--dir `: 按目录运行(相对于TestCase目录) +- `--file `: 按文件运行(相对于TestCase目录) +- `--keyword `: 按关键字运行 +- `--marker `: 按pytest标记运行 +- `--report`: 生成Allure报告 +- `--open`: 打开Allure报告 +- `--no-report`: 不生成Allure报告 + +## 报告位置 + +- Allure结果: `reports/allure-results/` +- Allure报告: `reports/allure-report/` + +## 查看报告 + +生成报告后,可以使用以下命令打开: + +```bash +allure open reports/allure-report +``` + +或者直接使用 `--open` 参数自动打开。 diff --git a/zhyy/test_case/Resource/AdapterKws/hh-qa.robot b/zhyy/test_case/Resource/AdapterKws/hh-qa.robot new file mode 100644 index 0000000..750156a --- /dev/null +++ b/zhyy/test_case/Resource/AdapterKws/hh-qa.robot @@ -0,0 +1,9 @@ +*** Settings *** +*** Variables *** + +${parent_phone_1} 13400234900 # 增长业务使用(陈江)(陈洁-login、留资等也在使用),逻辑思维用户 +${parentphone_customerId_1} 6022462 #用户 13400234900逻辑思维线索id +${parent_phone_2} 12030990019 #张楠 course_package.robot(陈洁-login、留资等也在使用),中文素养用户 +${parent_phone_3} 13400234902 # 海报任务专用 + + diff --git a/zhyy/test_case/TestCase/__init__.py b/zhyy/test_case/TestCase/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/zhyy/test_case/TestCase/__pycache__/__init__.cpython-38.pyc b/zhyy/test_case/TestCase/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000..f93ff09 Binary files /dev/null and b/zhyy/test_case/TestCase/__pycache__/__init__.cpython-38.pyc differ diff --git a/zhyy/test_case/TestCase/__pycache__/test_sample.cpython-38-pytest-7.4.4.pyc b/zhyy/test_case/TestCase/__pycache__/test_sample.cpython-38-pytest-7.4.4.pyc new file mode 100644 index 0000000..0f3d6e4 Binary files /dev/null and b/zhyy/test_case/TestCase/__pycache__/test_sample.cpython-38-pytest-7.4.4.pyc differ diff --git a/zhyy/test_case/TestCase/接口/SZPurchase/ContractManage.py b/zhyy/test_case/TestCase/接口/SZPurchase/ContractManage.py new file mode 100644 index 0000000..e69de29 diff --git a/zhyy/test_case/TestCase/接口/SZPurchase/PurchaseOrderManage.py b/zhyy/test_case/TestCase/接口/SZPurchase/PurchaseOrderManage.py new file mode 100644 index 0000000..ebe8fb4 --- /dev/null +++ b/zhyy/test_case/TestCase/接口/SZPurchase/PurchaseOrderManage.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- + +import allure +import logging + +from zhyy.library.BusinessKw.SZPurchase.PurchaseOrderManage import PurchaseOrder + + +@allure.feature('深圳采购工作台采购订单页面') +class Test_purchase_order(object): + test_case = PurchaseOrder() + + def teardown_method(self): + logging.info("-----------------------------End-------------------------------") + + @allure.story("验证采购工作台采购订单页面列表查询") + def test_check_purchase_order_page(self): + purchase_order_code = 'PO251209048' # 采购单号 必填 + supplier_company_ids = ['334'] # 供应商id 非必填 + payment_status = '0' # 付款状态 非必填 + status = '0' # 采购单状态 非必填 + page_no = 1 # 页码 必填 + page_size = 10 # 每页条数 必填 + response_data = self.test_case.kw_zhyy_get_purchase_page_post( + note="采购工作台采购订单页面列表查询", + user='purchase', + order_sn=purchase_order_code, + supplier_company_ids=supplier_company_ids, + payment_status=payment_status, + status=status, + page_no=page_no, + page_size=page_size + ) + + # 断言检查 + assert response_data is not None, "响应数据不能为空" + assert 'code' in response_data, "响应数据中缺少code字段" + assert response_data['code'] == 0, "接口调用失败,code: {}, msg: {}".format( + response_data.get('code'), response_data.get('msg', '未知错误')) + assert 'data' in response_data, "响应数据中缺少data字段" + assert response_data['data'] is not None, "响应数据中的data字段不能为空" + + # 如果传入了采购单号,检查返回的数据中是否包含该采购单号 + if purchase_order_code: + data = response_data.get('data', {}) + order_found = False + + # 检查返回的数据结构,可能包含列表或其他结构 + if isinstance(data, dict): + # 如果data是字典,可能包含records、list、data等字段 + records = data.get('records') or data.get('list') or data.get('data') or [] + if isinstance(records, list) and len(records) > 0: + # 检查列表中是否包含指定的采购单号 + for item in records: + if isinstance(item, dict): + order_sn = item.get('order_sn') or item.get('orderSn') or item.get('orderSn') + if order_sn == purchase_order_code: + order_found = True + break + elif isinstance(data, list): + # 如果data本身就是列表 + for item in data: + if isinstance(item, dict): + order_sn = item.get('order_sn') or item.get('orderSn') or item.get('orderSn') + if order_sn == purchase_order_code: + order_found = True + break + + if order_found: + logging.info("✓ 断言通过:返回的数据中包含采购单号 {}".format(purchase_order_code)) + else: + logging.warning("⚠ 警告:返回的数据中未找到采购单号: {},但接口调用成功".format(purchase_order_code)) + + logging.info("✓ 所有断言检查通过") + print("✓ 查询成功,响应数据: {}".format(response_data)) \ No newline at end of file diff --git a/zhyy/test_case/TestCase/接口/SZPurchase/PurchasePlanManage.py b/zhyy/test_case/TestCase/接口/SZPurchase/PurchasePlanManage.py new file mode 100644 index 0000000..e69de29 diff --git a/zhyy/test_case/TestCase/接口/SZPurchase/__init__.py b/zhyy/test_case/TestCase/接口/SZPurchase/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/zhyy/test_case/TestCase/接口/SZPurchase/__pycache__/ContractManage.cpython-38-pytest-7.4.4.pyc b/zhyy/test_case/TestCase/接口/SZPurchase/__pycache__/ContractManage.cpython-38-pytest-7.4.4.pyc new file mode 100644 index 0000000..19a7fba Binary files /dev/null and b/zhyy/test_case/TestCase/接口/SZPurchase/__pycache__/ContractManage.cpython-38-pytest-7.4.4.pyc differ diff --git a/zhyy/test_case/TestCase/接口/SZPurchase/__pycache__/PurchaseOrderManage.cpython-38-pytest-7.4.4.pyc b/zhyy/test_case/TestCase/接口/SZPurchase/__pycache__/PurchaseOrderManage.cpython-38-pytest-7.4.4.pyc new file mode 100644 index 0000000..7e79365 Binary files /dev/null and b/zhyy/test_case/TestCase/接口/SZPurchase/__pycache__/PurchaseOrderManage.cpython-38-pytest-7.4.4.pyc differ diff --git a/zhyy/test_case/TestCase/接口/SZPurchase/__pycache__/PurchasePlanManage.cpython-38-pytest-7.4.4.pyc b/zhyy/test_case/TestCase/接口/SZPurchase/__pycache__/PurchasePlanManage.cpython-38-pytest-7.4.4.pyc new file mode 100644 index 0000000..4b83896 Binary files /dev/null and b/zhyy/test_case/TestCase/接口/SZPurchase/__pycache__/PurchasePlanManage.cpython-38-pytest-7.4.4.pyc differ diff --git a/zhyy/test_case/TestCase/接口/SZPurchase/__pycache__/__init__.cpython-38.pyc b/zhyy/test_case/TestCase/接口/SZPurchase/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000..3209a52 Binary files /dev/null and b/zhyy/test_case/TestCase/接口/SZPurchase/__pycache__/__init__.cpython-38.pyc differ diff --git a/zhyy/test_case/TestCase/接口/SZPurchase/__pycache__/index.cpython-38-pytest-7.4.4.pyc b/zhyy/test_case/TestCase/接口/SZPurchase/__pycache__/index.cpython-38-pytest-7.4.4.pyc new file mode 100644 index 0000000..997a65e Binary files /dev/null and b/zhyy/test_case/TestCase/接口/SZPurchase/__pycache__/index.cpython-38-pytest-7.4.4.pyc differ diff --git a/zhyy/test_case/TestCase/接口/SZPurchase/index.py b/zhyy/test_case/TestCase/接口/SZPurchase/index.py new file mode 100644 index 0000000..4db87c8 --- /dev/null +++ b/zhyy/test_case/TestCase/接口/SZPurchase/index.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +import allure +import logging + +from zhyy.library.BusinessKw.SZPurchase.index import PurchaseIndex + + +@allure.feature('深圳采购工作台首页') +class Test_purchase_index(object): + # config = ReadConfig.ReadConfig() # 调用读取配置文件的方法类 + test_case = PurchaseIndex() + + def teardown_method(self): + logging.info("-----------------------------End-------------------------------") + + @allure.story("验证采购工作台待办任务与在办任务功能") + def test_check_todo(self): + get_purchase_data = self.test_case.kw_zhyy_get_todo(note="采购工作台首页待办任务PO与在办任务PO", user='purchase') diff --git a/zhyy/test_case/TestCase/接口/__init__.py b/zhyy/test_case/TestCase/接口/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/zhyy/test_case/TestCase/接口/__pycache__/__init__.cpython-38.pyc b/zhyy/test_case/TestCase/接口/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000..9ce1ed7 Binary files /dev/null and b/zhyy/test_case/TestCase/接口/__pycache__/__init__.cpython-38.pyc differ diff --git a/zhyy/test_case/TestCase/接口/__pycache__/conftest.cpython-38-pytest-7.4.4.pyc b/zhyy/test_case/TestCase/接口/__pycache__/conftest.cpython-38-pytest-7.4.4.pyc new file mode 100644 index 0000000..676231a Binary files /dev/null and b/zhyy/test_case/TestCase/接口/__pycache__/conftest.cpython-38-pytest-7.4.4.pyc differ diff --git a/zhyy/test_case/TestCase/接口/conftest.py b/zhyy/test_case/TestCase/接口/conftest.py new file mode 100644 index 0000000..889acff --- /dev/null +++ b/zhyy/test_case/TestCase/接口/conftest.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +""" +测试配置文件 +用于设置测试环境的路径和配置 +""" +import os +import sys + +# 添加项目根目录到 Python 路径,确保能导入 zhyy 模块 +current_file_path = os.path.abspath(__file__) +# 从 test_case/TestCase/接口/conftest.py 向上找到项目根目录 d:\zhyy +project_root = os.path.abspath(os.path.join(os.path.dirname(current_file_path), '../../../..')) +if project_root not in sys.path: + sys.path.insert(0, project_root) diff --git a/zhyy/test_case/__init__.py b/zhyy/test_case/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/zhyy/test_case/__pycache__/__init__.cpython-38.pyc b/zhyy/test_case/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000..1dcb11e Binary files /dev/null and b/zhyy/test_case/__pycache__/__init__.cpython-38.pyc differ diff --git a/zhyy/test_case/jenkins_build.bat b/zhyy/test_case/jenkins_build.bat new file mode 100644 index 0000000..ca3212e --- /dev/null +++ b/zhyy/test_case/jenkins_build.bat @@ -0,0 +1,44 @@ +@echo off +REM Jenkins构建脚本(Windows) +REM 用于在Jenkins中执行测试并生成Allure报告 + +setlocal enabledelayedexpansion + +REM 设置工作目录 +if defined WORKSPACE ( + cd /d %WORKSPACE% +) else ( + cd /d %~dp0.. +) + +REM 进入测试目录 +cd zhyy\test_case + +REM 运行测试(根据参数选择运行方式) +if "%RUN_TYPE%"=="" set RUN_TYPE=all + +if "%RUN_TYPE%"=="all" ( + python run_tests.py --all --no-report +) else if "%RUN_TYPE%"=="feature" ( + python run_tests.py --feature "%FEATURE_NAME%" --no-report +) else if "%RUN_TYPE%"=="story" ( + python run_tests.py --story "%STORY_NAME%" --no-report +) else if "%RUN_TYPE%"=="dir" ( + python run_tests.py --dir "%DIR_PATH%" --no-report +) else if "%RUN_TYPE%"=="file" ( + python run_tests.py --file "%FILE_PATH%" --no-report +) else if "%RUN_TYPE%"=="keyword" ( + python run_tests.py --keyword "%KEYWORD%" --no-report +) else if "%RUN_TYPE%"=="marker" ( + python run_tests.py --marker "%MARKER%" --no-report +) else ( + echo 未知的运行类型: %RUN_TYPE% + exit /b 1 +) + +REM 生成Allure报告(Jenkins插件会自动处理,这里可选) +REM allure generate reports\allure-results -o reports\allure-report --clean + +echo 测试执行完成,Allure结果已保存到: reports\allure-results + +endlocal diff --git a/zhyy/test_case/jenkins_build.sh b/zhyy/test_case/jenkins_build.sh new file mode 100644 index 0000000..cba769a --- /dev/null +++ b/zhyy/test_case/jenkins_build.sh @@ -0,0 +1,47 @@ +#!/bin/bash +# Jenkins构建脚本(Linux/Mac) +# 用于在Jenkins中执行测试并生成Allure报告 + +set -e # 遇到错误立即退出 + +# 设置工作目录 +cd ${WORKSPACE:-$(pwd)} + +# 进入测试目录 +cd zhyy/test_case + +# 运行测试(根据参数选择运行方式) +RUN_TYPE=${RUN_TYPE:-all} + +case ${RUN_TYPE} in + all) + python run_tests.py --all --no-report + ;; + feature) + python run_tests.py --feature "${FEATURE_NAME}" --no-report + ;; + story) + python run_tests.py --story "${STORY_NAME}" --no-report + ;; + dir) + python run_tests.py --dir "${DIR_PATH}" --no-report + ;; + file) + python run_tests.py --file "${FILE_PATH}" --no-report + ;; + keyword) + python run_tests.py --keyword "${KEYWORD}" --no-report + ;; + marker) + python run_tests.py --marker "${MARKER}" --no-report + ;; + *) + echo "未知的运行类型: ${RUN_TYPE}" + exit 1 + ;; +esac + +# 生成Allure报告(Jenkins插件会自动处理,这里可选) +# allure generate reports/allure-results -o reports/allure-report --clean || true + +echo "测试执行完成,Allure结果已保存到: reports/allure-results" diff --git a/zhyy/test_case/reports/allure-report/app.js b/zhyy/test_case/reports/allure-report/app.js new file mode 100644 index 0000000..579e79d --- /dev/null +++ b/zhyy/test_case/reports/allure-report/app.js @@ -0,0 +1,60 @@ +!function(e){var t={};function __webpack_require__(n){if(t[n])return t[n].exports;var r=t[n]={i:n,l:!1,exports:{}};return e[n].call(r.exports,r,r.exports,__webpack_require__),r.l=!0,r.exports}__webpack_require__.m=e,__webpack_require__.c=t,__webpack_require__.d=function(e,t,n){__webpack_require__.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},__webpack_require__.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},__webpack_require__.t=function(e,t){if(1&t&&(e=__webpack_require__(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(__webpack_require__.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)__webpack_require__.d(n,r,function(t){return e[t]}.bind(null,r));return n},__webpack_require__.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return __webpack_require__.d(t,"a",t),t},__webpack_require__.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},__webpack_require__.p="/",__webpack_require__(__webpack_require__.s=197)}([function(e,t){function _getPrototypeOf(t){return e.exports=_getPrototypeOf=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)},_getPrototypeOf(t)}e.exports=_getPrototypeOf},function(e,t){e.exports=function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}},function(e,t){e.exports=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}},function(e,t,n){var r=n(113),i=n(1);e.exports=function(e,t){return!t||"object"!==r(t)&&"function"!=typeof t?i(e):t}},function(e,t,n){var r=n(114);e.exports=function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&r(e,t)}},function(e,t){function _defineProperties(e,t){for(var n=0;n1?n-1:0),i=1;i1?n-1:0),i=1;i1?n-1:0),i=1;i0)for(e=0;e1?r-1:0),a=1;a1&&void 0!==arguments[1]?arguments[1]:{},r=!e._isAttached&&o(this.el),i=void 0===n.replaceElement?!!t.result(this,"replaceElement"):!!n.replaceElement;r&&triggerMethodOn(e,"before:attach",e),i?this._replaceEl(e):this.attachHtml(e),r&&(e._isAttached=!0,triggerMethodOn(e,"attach",e))},_ensureElement:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if(t.isObject(this.el)||(this.$el=this.getEl(this.el),this.el=this.$el[0]),!this.$el||0===this.$el.length){var n=void 0===e.allowMissingEl?!!t.result(this,"allowMissingEl"):!!e.allowMissingEl;if(n)return!1;throw new d('An "el" must exist in DOM for this region '+this.cid)}return!0},_getView:function(t){if(!t)throw new d({name:"ViewNotValid",message:"The view passed is undefined and therefore invalid. You must pass a view instance to show."});if(t._isDestroyed)throw new d({name:"ViewDestroyedError",message:'View (cid: "'+t.cid+'") has already been destroyed and cannot be used.'});if(t instanceof e.View)return t;var n=this._getViewOptions(t);return new P(n)},_getViewOptions:function(e){return t.isFunction(e)?{template:e}:t.isObject(e)?e:{template:function(){return e}}},getEl:function(e){return this.findEls(e,t.result(this,"parentEl"))},_replaceEl:function(e){this._restoreEl(),e.on("before:destroy",this._restoreEl,this),this.replaceEl(e.el,this.el),this._isReplaced=!0},_restoreEl:function(){if(this._isReplaced){var e=this.currentView;e&&(this._detachView(e),this._isReplaced=!1)}},isReplaced:function(){return!!this._isReplaced},isSwappingView:function(){return!!this._isSwappingView},attachHtml:function(e){this.appendChildren(this.el,e.el)},empty:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{allowMissingEl:!0},t=this.currentView;if(!t)return this._ensureElement(e)&&this.detachHtml(),this;var n=!e.preventDestroy;return n||a("The preventDestroy option is deprecated. Use Region#detachView"),this._empty(t,n),this},_empty:function(e,t){e.off("destroy",this._empty,this),this.triggerMethod("before:empty",this,e),this._restoreEl(),delete this.currentView,e._isDestroyed||(t?this.removeView(e):this._detachView(e),this._stopChildViewEvents(e)),this.triggerMethod("empty",this,e)},_stopChildViewEvents:function(e){var t=this._parentView;t&&this._parentView.stopListening(e)},destroyView:function(e){return e._isDestroyed?e:(e.destroy?e.destroy():destroyBackboneView(e),e)},removeView:function(e){this.destroyView(e)},detachView:function(){var e=this.currentView;if(e)return this._empty(e),e},_detachView:function(e){var t=!!e._isAttached,n=this._isReplaced;t&&triggerMethodOn(e,"before:detach",e),n?this.replaceEl(this.el,e.el):this.detachHtml(),t&&(e._isAttached=!1,triggerMethodOn(e,"detach",e))},detachHtml:function(){this.detachContents(this.el)},hasView:function(){return!!this.currentView},reset:function(e){return this.empty(e),this.$el&&(this.el=this._initEl),delete this.$el,this},destroy:function(e){return this._isDestroyed?this:(this.reset(e),this._name&&this._parentView._removeReferences(this._name),delete this._parentView,delete this._name,v.prototype.destroy.apply(this,arguments))}});t.extend(R.prototype,b);var N=function(e,n){return e instanceof R?e:function(e,n){var r=t.extend({},n);if(t.isString(e))return t.extend(r,{el:e}),buildRegionFromObject(r);if(t.isFunction(e))return t.extend(r,{regionClass:e}),buildRegionFromObject(r);if(t.isObject(e))return e.selector&&a("The selector option on a Region definition object is deprecated. Use el to pass a selector string"),t.extend(r,{el:e.selector},e),buildRegionFromObject(r);throw new d({message:"Improper region configuration type.",url:"marionette.region.html#region-configuration-types"})}(e,n)};function buildRegionFromObject(e){var n=e.regionClass,r=t.omit(e,"regionClass");return new n(r)}var D={regionClass:R,_initRegions:function(){this.regions=this.regions||{},this._regions={},this.addRegions(t.result(this,"regions"))},_reInitRegions:function(){_(this._regions,"reset")},addRegion:function(e,t){var n={};return n[e]=t,this.addRegions(n)[e]},addRegions:function(e){if(!t.isEmpty(e))return e=this.normalizeUIValues(e,["selector","el"]),this.regions=t.extend({},this.regions,e),this._addRegions(e)},_addRegions:function(e){var n=this,r={regionClass:this.regionClass,parentEl:t.partial(t.result,this,"el")};return t.reduce(e,function(e,t,i){return e[i]=N(t,r),n._addRegion(e[i],i),e},{})},_addRegion:function(e,t){this.triggerMethod("before:add:region",this,t,e),e._parentView=this,e._name=t,this._regions[t]=e,this.triggerMethod("add:region",this,t,e)},removeRegion:function(e){var t=this._regions[e];return this._removeRegion(t,e),t},removeRegions:function(){var e=this._getRegions();return t.each(this._regions,t.bind(this._removeRegion,this)),e},_removeRegion:function(e,t){this.triggerMethod("before:remove:region",this,t,e),e.destroy(),this.triggerMethod("remove:region",this,t,e)},_removeReferences:function(e){delete this.regions[e],delete this._regions[e]},emptyRegions:function(){var e=this.getRegions();return _(e,"empty"),e},hasRegion:function(e){return!!this.getRegion(e)},getRegion:function(e){return this._isRendered||this.render(),this._regions[e]},_getRegions:function(){return t.clone(this._regions)},getRegions:function(){return this._isRendered||this.render(),this._getRegions()},showChildView:function(e,t){for(var n=this.getRegion(e),r=arguments.length,i=Array(r>2?r-2:0),a=2;a0&&void 0!==arguments[0]?arguments[0]:{},n=t.result(this,"templateContext");return t.extend(e,n)},attachElContent:function(e){return this.setInnerContent(this.el,e),this},_removeChildren:function(){this.removeRegions()},_getImmediateChildren:function(){return t.chain(this._getRegions()).map("currentView").compact().value()}},{setRenderer:function(e){this.prototype._renderHtml=e}});t.extend(P.prototype,A,D);var L=["forEach","each","map","find","detect","filter","select","reject","every","all","some","any","include","contains","invoke","toArray","first","initial","rest","last","without","isEmpty","pluck","reduce","partition"],I=function(e,n){t.each(L,function(r){e[r]=function(){var e=t.values(t.result(this,n)),i=[e].concat(t.toArray(arguments));return t[r].apply(t,i)}})},B=function(e){this._views={},this._indexByModel={},this._indexByCustom={},this._updateLength(),t.each(e,t.bind(this.add,this))};I(B.prototype,"_views"),t.extend(B.prototype,{add:function(e,t){return this._add(e,t)._updateLength()},_add:function(e,t){var n=e.cid;return this._views[n]=e,e.model&&(this._indexByModel[e.model.cid]=n),t&&(this._indexByCustom[t]=n),this},findByModel:function(e){return this.findByModelCid(e.cid)},findByModelCid:function(e){var t=this._indexByModel[e];return this.findByCid(t)},findByCustom:function(e){var t=this._indexByCustom[e];return this.findByCid(t)},findByIndex:function(e){return t.values(this._views)[e]},findByCid:function(e){return this._views[e]},remove:function(e){return this._remove(e)._updateLength()},_remove:function(e){var n=e.cid;return e.model&&delete this._indexByModel[e.model.cid],t.some(this._indexByCustom,t.bind(function(e,t){if(e===n)return delete this._indexByCustom[t],!0},this)),delete this._views[n],this},_updateLength:function(){return this.length=t.size(this._views),this}});var z=["behaviors","childView","childViewEventPrefix","childViewEvents","childViewOptions","childViewTriggers","collectionEvents","events","filter","emptyView","emptyViewOptions","modelEvents","reorderOnSort","sort","triggers","ui","viewComparator"],U=e.View.extend({sort:!0,constructor:function(n){this.render=t.bind(this.render,this),this._setOptions(n),this.mergeOptions(n,z),monitorViewEvents(this),this._initBehaviors(),this.once("render",this._initialEvents),this._initChildViewStorage(),this._bufferedChildren=[];var r=Array.prototype.slice.call(arguments);r[0]=this.options,e.View.prototype.constructor.apply(this,r),this.delegateEntityEvents(),this._triggerEventOnBehaviors("initialize",this)},_startBuffering:function(){this._isBuffering=!0},_endBuffering:function(){var e=!!this._isAttached,n=e?this._getImmediateChildren():[];this._isBuffering=!1,t.each(n,function(e){triggerMethodOn(e,"before:attach",e)}),this.attachBuffer(this,this._createBuffer()),t.each(n,function(e){e._isAttached=!0,triggerMethodOn(e,"attach",e)}),this._bufferedChildren=[]},_getImmediateChildren:function(){return t.values(this.children._views)},_initialEvents:function(){this.collection&&(this.listenTo(this.collection,"add",this._onCollectionAdd),this.listenTo(this.collection,"update",this._onCollectionUpdate),this.listenTo(this.collection,"reset",this.render),this.sort&&this.listenTo(this.collection,"sort",this._sortViews))},_onCollectionAdd:function(e,n,r){var i=void 0!==r.at&&(r.index||n.indexOf(e));(this.filter||!1===i)&&(i=t.indexOf(this._filteredSortedModels(i),e)),this._shouldAddChild(e,i)&&(this._destroyEmptyView(),this._addChild(e,i))},_onCollectionUpdate:function(e,t){var n=t.changes;this._removeChildModels(n.removed)},_removeChildModels:function(e){var t=this._getRemovedViews(e);t.length&&(this.children._updateLength(),this._updateIndices(t,!1),this.isEmpty()&&this._showEmptyView())},_getRemovedViews:function(e){var n=this;return t.reduce(e,function(e,t){var r=t&&n.children.findByModel(t);return!r||r._isDestroyed?e:(n._removeChildView(r),e.push(r),e)},[])},_removeChildView:function(e){this.triggerMethod("before:remove:child",this,e),this.children._remove(e),e.destroy?e.destroy():destroyBackboneView(e),this.stopListening(e),this.triggerMethod("remove:child",this,e)},setElement:function(){var t=!!this.el;return e.View.prototype.setElement.apply(this,arguments),t&&(this._isAttached=o(this.el)),this},render:function(){return this._isDestroyed?this:(this.triggerMethod("before:render",this),this._renderChildren(),this._isRendered=!0,this.triggerMethod("render",this),this)},setFilter:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.preventRender,r=this._isRendered&&!this._isDestroyed,i=this.filter!==e,a=r&&i&&!n;if(a){var o=this._filteredSortedModels();this.filter=e;var s=this._filteredSortedModels();this._applyModelDeltas(s,o)}else this.filter=e;return this},removeFilter:function(e){return this.setFilter(null,e)},_applyModelDeltas:function(e,n){var r=this,i={};t.each(e,function(e,t){var n=!r.children.findByModel(e);n&&r._onCollectionAdd(e,r.collection,{at:t}),i[e.cid]=!0});var a=t.filter(n,function(e){return!i[e.cid]&&r.children.findByModel(e)});this._removeChildModels(a)},reorder:function(){var e=this.children,n=this._filteredSortedModels();if(!n.length&&this._showingEmptyView)return this;var r=t.some(n,function(t){return!e.findByModel(t)});if(r)this.render();else{var i=[],a=e.reduce(function(e,r){var a=t.indexOf(n,r.model);return-1===a?(i.push(r.model),e):(r._index=a,e[a]=r.el,e)},new Array(n.length));this.triggerMethod("before:reorder",this),this._appendReorderedChildren(a),this._removeChildModels(i),this.triggerMethod("reorder",this)}return this},resortView:function(){return this.reorderOnSort?this.reorder():this._renderChildren(),this},_sortViews:function(){var e=this,n=this._filteredSortedModels(),r=t.find(n,function(t,n){var r=e.children.findByModel(t);return!r||r._index!==n});r&&this.resortView()},_emptyViewIndex:-1,_appendReorderedChildren:function(e){this.appendChildren(this.el,e)},_renderChildren:function(){this._isRendered&&(this._destroyEmptyView(),this._destroyChildren());var e=this._filteredSortedModels();this.isEmpty({processedModels:e})?this._showEmptyView():(this.triggerMethod("before:render:children",this),this._startBuffering(),this._showCollection(e),this._endBuffering(),this.triggerMethod("render:children",this))},_createView:function(e,t){var n=this._getChildView(e),r=this._getChildViewOptions(e,t),i=this.buildChildView(e,n,r);return i},_setupChildView:function(e,t){monitorViewEvents(e),this._proxyChildViewEvents(e),this.sort&&(e._index=t)},_showCollection:function(e){t.each(e,t.bind(this._addChild,this)),this.children._updateLength()},_filteredSortedModels:function(e){if(!this.collection||!this.collection.length)return[];var t=this.getViewComparator(),n=this.collection.models;if(e=Math.min(Math.max(e,0),n.length-1),t){var r=void 0;e&&(r=n[e],n=n.slice(0,e).concat(n.slice(e+1))),n=this._sortModelsBy(n,t),r&&n.splice(e,0,r)}return n=this._filterModels(n)},getViewComparator:function(){return this.viewComparator},_filterModels:function(e){var n=this;return this.filter&&(e=t.filter(e,function(e,t){return n._shouldAddChild(e,t)})),e},_sortModelsBy:function(e,n){return"string"==typeof n?t.sortBy(e,function(e){return e.get(n)}):1===n.length?t.sortBy(e,t.bind(n,this)):t.clone(e).sort(t.bind(n,this))},_showEmptyView:function(){var n=this._getEmptyView();if(n&&!this._showingEmptyView){this._showingEmptyView=!0;var r=new e.Model,i=this.emptyViewOptions||this.childViewOptions;t.isFunction(i)&&(i=i.call(this,r,this._emptyViewIndex));var a=this.buildChildView(r,n,i);this.triggerMethod("before:render:empty",this,a),this.addChildView(a,0),this.triggerMethod("render:empty",this,a)}},_destroyEmptyView:function(){this._showingEmptyView&&(this.triggerMethod("before:remove:empty",this),this._destroyChildren(),delete this._showingEmptyView,this.triggerMethod("remove:empty",this))},_getEmptyView:function(){var e=this.emptyView;if(e)return this._getView(e)},_getChildView:function(e){var t=this.childView;if(!t)throw new d({name:"NoChildViewError",message:'A "childView" must be specified'});if(!(t=this._getView(t,e)))throw new d({name:"InvalidChildViewError",message:'"childView" must be a view class or a function that returns a view class'});return t},_getView:function(n,r){return n.prototype instanceof e.View||n===e.View?n:t.isFunction(n)?n.call(this,r):void 0},_addChild:function(e,t){var n=this._createView(e,t);return this.addChildView(n,t),n},_getChildViewOptions:function(e,n){return t.isFunction(this.childViewOptions)?this.childViewOptions(e,n):this.childViewOptions},addChildView:function(e,t){return this.triggerMethod("before:add:child",this,e),this._setupChildView(e,t),this._isBuffering?this.children._add(e):(this._updateIndices(e,!0),this.children.add(e)),this._renderView(e),this._attachView(e,t),this.triggerMethod("add:child",this,e),e},_updateIndices:function(e,n){if(this.sort)if(n){var r=t.isArray(e)?t.max(e,"_index"):e;t.isObject(r)&&this.children.each(function(e){e._index>=r._index&&(e._index+=1)})}else t.each(t.sortBy(this.children._views,"_index"),function(e,t){e._index=t})},_renderView:function(e){e._isRendered||(e.supportsRenderLifecycle||triggerMethodOn(e,"before:render",e),e.render(),e.supportsRenderLifecycle||(e._isRendered=!0,triggerMethodOn(e,"render",e)))},_attachView:function(e,t){var n=!e._isAttached&&!this._isBuffering&&this._isAttached;n&&triggerMethodOn(e,"before:attach",e),this.attachHtml(this,e,t),n&&(e._isAttached=!0,triggerMethodOn(e,"attach",e))},buildChildView:function(e,n,r){var i=t.extend({model:e},r);return new n(i)},removeChildView:function(e){return!e||e._isDestroyed?e:(this._removeChildView(e),this.children._updateLength(),this._updateIndices(e,!1),e)},isEmpty:function(e){var n=void 0;return t.result(e,"processedModels")?n=e.processedModels:(n=this.collection?this.collection.models:[],n=this._filterModels(n)),0===n.length},attachBuffer:function(e,t){this.appendChildren(e.el,t)},_createBuffer:function(){var e=this,n=this.createBuffer();return t.each(this._bufferedChildren,function(t){e.appendChildren(n,t.el)}),n},attachHtml:function(e,t,n){e._isBuffering?e._bufferedChildren.splice(n,0,t):e._insertBefore(t,n)||e._insertAfter(t)},_insertBefore:function(e,t){var n=void 0,r=this.sort&&t1&&void 0!==arguments[1]?arguments[1]:this._views.length,n=e.cid;this._viewsByCid[n]=e,e.model&&(this._indexByModel[e.model.cid]=n),this._views.splice(t,0,e),this._updateLength()},_sort:function(e){return"string"==typeof e?(e=t.partial(stringComparator,e),this._sortBy(e)):1===e.length?this._sortBy(e):this._views.sort(e)},_sortBy:function(e){var n=t.sortBy(this._views,e);return this._set(n),n},_set:function(e){this._views.length=0,this._views.push.apply(this._views,e.slice(0)),this._updateLength()},findByModel:function(e){return this.findByModelCid(e.cid)},findByModelCid:function(e){var t=this._indexByModel[e];return this.findByCid(t)},findByIndex:function(e){return this._views[e]},findIndexByView:function(e){return this._views.indexOf(e)},findByCid:function(e){return this._viewsByCid[e]},_remove:function(e){if(this._viewsByCid[e.cid]){e.model&&delete this._indexByModel[e.model.cid],delete this._viewsByCid[e.cid];var t=this.findIndexByView(e);this._views.splice(t,1),this._updateLength()}},_updateLength:function(){this.length=this._views.length}});var H=["behaviors","childView","childViewEventPrefix","childViewEvents","childViewOptions","childViewTriggers","collectionEvents","emptyView","emptyViewOptions","events","modelEvents","sortWithCollection","triggers","ui","viewComparator","viewFilter"],q=e.View.extend({sortWithCollection:!0,constructor:function(t){this._setOptions(t),this.mergeOptions(t,H),monitorViewEvents(this),this.once("render",this._initialEvents),this._initChildViewStorage(),this._initBehaviors();var n=Array.prototype.slice.call(arguments);n[0]=this.options,e.View.prototype.constructor.apply(this,n),this._initEmptyRegion(),this.delegateEntityEvents(),this._triggerEventOnBehaviors("initialize",this)},_initChildViewStorage:function(){this.children=new F},_initEmptyRegion:function(){this.emptyRegion=new R({el:this.el}),this.emptyRegion._parentView=this},_initialEvents:function(){this.listenTo(this.collection,{sort:this._onCollectionSort,reset:this._onCollectionReset,update:this._onCollectionUpdate})},_onCollectionSort:function(){var e=this;if(this.sortWithCollection&&this.collection.length===this.children.length){var t=this.collection.some(function(t){return!e.children.findByModel(t)});t||this.sort()}},_onCollectionReset:function(){this.render()},_onCollectionUpdate:function(e,t){var n=t.changes,r=this._removeChildModels(n.removed);this._addChildModels(n.added),this._detachChildren(r),this._showChildren(),this._removeChildViews(r)},_removeChildModels:function(e){return t.map(e,t.bind(this._removeChildModel,this))},_removeChildModel:function(e){var t=this.children.findByModel(e);return this._removeChild(t),t},_removeChild:function(e){this.triggerMethod("before:remove:child",this,e),this.children._remove(e),this.triggerMethod("remove:child",this,e)},_addChildModels:function(e){return t.map(e,t.bind(this._addChildModel,this))},_addChildModel:function(e){var t=this._createChildView(e);return this._addChild(t),t},_createChildView:function(e){var t=this._getChildView(e),n=this._getChildViewOptions(e),r=this.buildChildView(e,t,n);return r},_addChild:function(e,t){this.triggerMethod("before:add:child",this,e),this._setupChildView(e),this.children._add(e,t),this.triggerMethod("add:child",this,e)},_getChildView:function(e){var t=this.childView;if(!t)throw new d({name:"NoChildViewError",message:'A "childView" must be specified'});if(!(t=this._getView(t,e)))throw new d({name:"InvalidChildViewError",message:'"childView" must be a view class or a function that returns a view class'});return t},_getView:function(n,r){return n.prototype instanceof e.View||n===e.View?n:t.isFunction(n)?n.call(this,r):void 0},_getChildViewOptions:function(e){return t.isFunction(this.childViewOptions)?this.childViewOptions(e):this.childViewOptions},buildChildView:function(e,n,r){var i=t.extend({model:e},r);return new n(i)},_setupChildView:function(e){monitorViewEvents(e),e.on("destroy",this.removeChildView,this),this._proxyChildViewEvents(e)},_getImmediateChildren:function(){return this.children._views},setElement:function(){var t=!!this.el;return e.View.prototype.setElement.apply(this,arguments),t&&(this._isAttached=o(this.el)),this},render:function(){return this._isDestroyed?this:(this.triggerMethod("before:render",this),this._destroyChildren(),this.children._init(),this.collection&&this._addChildModels(this.collection.models),this._showChildren(),this._isRendered=!0,this.triggerMethod("render",this),this)},sort:function(){return this._isDestroyed?this:this.children.length?(this._showChildren(),this):this},_showChildren:function(){this.isEmpty()?this._showEmptyView():(this._sortChildren(),this.filter())},isEmpty:function(e){return e||!this.children.length},_showEmptyView:function(){var e=this._getEmptyView();if(e){var t=this._getEmptyViewOptions();this.emptyRegion.show(new e(t))}},_getEmptyView:function(){var e=this.emptyView;if(e)return this._getView(e)},_destroyEmptyView:function(){this.emptyRegion.hasView()&&this.emptyRegion.empty()},_getEmptyViewOptions:function(){var e=this.emptyViewOptions||this.childViewOptions;return t.isFunction(e)?e.call(this):e},_sortChildren:function(){this.triggerMethod("before:sort",this);var e=this.getComparator();t.isFunction(e)&&(e=e.bind(this)),this.children._sort(e),this.triggerMethod("sort",this)},setComparator:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.preventRender,r=this.viewComparator!==e,i=r&&!n;return this.viewComparator=e,i&&this.sort(),this},removeComparator:function(e){return this.setComparator(null,e)},getComparator:function(){return this.viewComparator||this._viewComparator},_viewComparator:function(e){if(this.collection)return this.collection.indexOf(e.model)},filter:function(){if(this._isDestroyed)return this;if(!this.children.length)return this;var e=this._filterChildren();return this._renderChildren(e),this},_filterChildren:function(){var e=this._getFilter();if(!e)return this.children._views;this.triggerMethod("before:filter",this);var n=this.children.partition(t.bind(e,this));return this._detachChildren(n[1]),this.triggerMethod("filter",this),n[0]},_getFilter:function(){var e=this.getFilter();if(!e)return!1;if(t.isFunction(e))return e;if(t.isObject(e)){var n=t.matches(e);return function(e){return n(e.model&&e.model.attributes)}}if(t.isString(e))return function(t){return t.model&&t.model.get(e)};throw new d({name:"InvalidViewFilterError",message:'"viewFilter" must be a function, predicate object literal, a string indicating a model attribute, or falsy'})},getFilter:function(){return this.viewFilter},setFilter:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.preventRender,r=this.viewFilter!==e,i=r&&!n;return this.viewFilter=e,i&&this.filter(),this},removeFilter:function(e){return this.setFilter(null,e)},_detachChildren:function(e){t.each(e,t.bind(this._detachChildView,this))},_detachChildView:function(e){var t=!!e._isAttached;t&&triggerMethodOn(e,"before:detach",e),this.detachHtml(e),t&&(e._isAttached=!1,triggerMethodOn(e,"detach",e))},detachHtml:function(e){this.detachEl(e.el)},_renderChildren:function(e){if(this.isEmpty(!e.length))this._showEmptyView();else{this._destroyEmptyView(),this.triggerMethod("before:render:children",this,e);var t=this._getBuffer(e);this._attachChildren(t,e),this.triggerMethod("render:children",this,e)}},_attachChildren:function(e,n){var r=!!this._isAttached;n=r?n:[],t.each(n,function(e){e._isAttached||triggerMethodOn(e,"before:attach",e)}),this.attachHtml(this,e),t.each(n,function(e){e._isAttached||(e._isAttached=!0,triggerMethodOn(e,"attach",e))})},_getBuffer:function(e){var n=this,r=this.createBuffer();return t.each(e,function(e){n._renderChildView(e),n.appendChildren(r,e.el)}),r},_renderChildView:function(e){e._isRendered||(e.supportsRenderLifecycle||triggerMethodOn(e,"before:render",e),e.render(),e.supportsRenderLifecycle||(e._isRendered=!0,triggerMethodOn(e,"render",e)))},attachHtml:function(e,t){this.appendChildren(e.el,t)},addChildView:function(e,t){return!e||e._isDestroyed?e:(this._addChild(e,t),this._showChildren(),e)},detachChildView:function(e){return this.removeChildView(e,{shouldDetach:!0}),e},removeChildView:function(e,t){return e?(this._removeChildView(e,t),this._removeChild(e),this.isEmpty()&&this._showEmptyView(),e):e},_removeChildViews:function(e){t.each(e,t.bind(this._removeChildView,this))},_removeChildView:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.shouldDetach;e.off("destroy",this.removeChildView,this),n?this._detachChildView(e):this._destroyChildView(e),this.stopListening(e)},_destroyChildView:function(e){e._isDestroyed||(e.destroy?e.destroy():destroyBackboneView(e))},_removeChildren:function(){this._destroyChildren(),this.emptyRegion.destroy()},_destroyChildren:function(){this.children&&this.children.length&&(this.triggerMethod("before:destroy:children",this),this.children.each(t.bind(this._removeChildView,this)),this.triggerMethod("destroy:children",this))}});t.extend(q.prototype,A);var W=["childViewContainer","template","templateContext"],$=U.extend({constructor:function(e){a("CompositeView is deprecated. Convert to View at your earliest convenience"),this.mergeOptions(e,W),U.prototype.constructor.apply(this,arguments)},_initialEvents:function(){this.collection&&(this.listenTo(this.collection,"add",this._onCollectionAdd),this.listenTo(this.collection,"update",this._onCollectionUpdate),this.listenTo(this.collection,"reset",this.renderChildren),this.sort&&this.listenTo(this.collection,"sort",this._sortViews))},_getChildView:function(e){var t=this.childView;if(!t)return this.constructor;if(!(t=this._getView(t,e)))throw new d({name:"InvalidChildViewError",message:'"childView" must be a view class or a function that returns a view class'});return t},serializeData:function(){return this.serializeModel()},render:function(){return this._isDestroyed?this:(this._isRendering=!0,this.resetChildViewContainer(),this.triggerMethod("before:render",this),this._renderTemplate(),this.bindUIElements(),this.renderChildren(),this._isRendering=!1,this._isRendered=!0,this.triggerMethod("render",this),this)},renderChildren:function(){(this._isRendered||this._isRendering)&&U.prototype._renderChildren.call(this)},attachBuffer:function(e,t){var n=this.getChildViewContainer(e);this.appendChildren(n,t)},_insertAfter:function(e){var t=this.getChildViewContainer(this,e);this.appendChildren(t,e.el)},_appendReorderedChildren:function(e){var t=this.getChildViewContainer(this);this.appendChildren(t,e)},getChildViewContainer:function(e,n){if(e.$childViewContainer)return e.$childViewContainer;var r=void 0,i=e.childViewContainer;if(i){var a=t.result(e,"childViewContainer");if((r="@"===a.charAt(0)&&e.ui?e.ui[a.substr(4)]:this.findEls(a,e.$el)).length<=0)throw new d({name:"ChildViewContainerMissingError",message:'The specified "childViewContainer" was not found: '+e.childViewContainer})}else r=e.$el;return e.$childViewContainer=r,r},resetChildViewContainer:function(){this.$childViewContainer&&(this.$childViewContainer=void 0)}}),Y=t.pick(P.prototype,"serializeModel","getTemplate","_renderTemplate","_renderHtml","mixinTemplateContext","attachElContent");t.extend($.prototype,Y);var G=["collectionEvents","events","modelEvents","triggers","ui"],X=v.extend({cidPrefix:"mnb",constructor:function(e,n){this.view=n,this.defaults&&a("Behavior defaults are deprecated. For similar functionality set options on the Behavior class."),this.defaults=t.clone(t.result(this,"defaults",{})),this._setOptions(this.defaults,e),this.mergeOptions(this.options,G),this.ui=t.extend({},t.result(this,"ui"),t.result(n,"ui")),v.apply(this,arguments)},$:function(){return this.view.$.apply(this.view,arguments)},destroy:function(){return this.stopListening(),this.view._removeBehavior(this),this},proxyViewProperties:function(){return this.$el=this.view.$el,this.el=this.view.el,this},bindUIElements:function(){return this._bindUIElements(),this},unbindUIElements:function(){return this._unbindUIElements(),this},getUI:function(e){return this._getUI(e)},delegateEntityEvents:function(){return this._delegateEntityEvents(this.view.model,this.view.collection),this},undelegateEntityEvents:function(){return this._undelegateEntityEvents(this.view.model,this.view.collection),this},getEvents:function(){var e=this,n=this.normalizeUIKeys(t.result(this,"events"));return t.reduce(n,function(n,r,i){if(t.isFunction(r)||(r=e[r]),r)return i=C(i),n[i]=t.bind(r,e),n},{})},getTriggers:function(){if(this.triggers){var e=this.normalizeUIKeys(t.result(this,"triggers"));return this._getViewTriggers(this.view,e)}}});t.extend(X.prototype,x,T,M);var K=["region","regionClass"],Z=v.extend({cidPrefix:"mna",constructor:function(e){this._setOptions(e),this.mergeOptions(e,K),this._initRegion(),v.prototype.constructor.apply(this,arguments)},regionClass:R,_initRegion:function(){var e=this.region;if(e){var t={regionClass:this.regionClass};this._region=N(e,t)}},getRegion:function(){return this._region},showView:function(e){for(var t=this.getRegion(),n=arguments.length,r=Array(n>1?n-1:0),i=1;ithis.length&&(i=this.length),i<0&&(i+=this.length+1);var a,o,s=[],l=[],u=[],c=[],f={},h=t.add,d=t.merge,p=t.remove,g=!1,m=this.comparator&&null==i&&!1!==t.sort,v=n.isString(this.comparator)?this.comparator:null;for(o=0;o7),this._useHashChange=this._wantsHashChange&&this._hasHashChange,this._wantsPushState=!!this.options.pushState,this._hasPushState=!(!this.history||!this.history.pushState),this._usePushState=this._wantsPushState&&this._hasPushState,this.fragment=this.getFragment(),this.root=("/"+this.root+"/").replace(V,"/"),this._wantsHashChange&&this._wantsPushState){if(!this._hasPushState&&!this.atRoot()){var t=this.root.slice(0,-1)||"/";return this.location.replace(t+"#"+this.getPath()),!0}this._hasPushState&&this.atRoot()&&this.navigate(this.getHash(),{replace:!0})}if(!this._hasHashChange&&this._wantsHashChange&&!this._usePushState){this.iframe=document.createElement("iframe"),this.iframe.src="javascript:0",this.iframe.style.display="none",this.iframe.tabIndex=-1;var r=document.body,i=r.insertBefore(this.iframe,r.firstChild).contentWindow;i.document.open(),i.document.close(),i.location.hash="#"+this.fragment}var a=window.addEventListener||function(e,t){return attachEvent("on"+e,t)};if(this._usePushState?a("popstate",this.checkUrl,!1):this._useHashChange&&!this.iframe?a("hashchange",this.checkUrl,!1):this._wantsHashChange&&(this._checkUrlInterval=setInterval(this.checkUrl,this.interval)),!this.options.silent)return this.loadUrl()},stop:function(){var e=window.removeEventListener||function(e,t){return detachEvent("on"+e,t)};this._usePushState?e("popstate",this.checkUrl,!1):this._useHashChange&&!this.iframe&&e("hashchange",this.checkUrl,!1),this.iframe&&(document.body.removeChild(this.iframe),this.iframe=null),this._checkUrlInterval&&clearInterval(this._checkUrlInterval),N.started=!1},route:function(e,t){this.handlers.unshift({route:e,callback:t})},checkUrl:function(e){var t=this.getFragment();if(t===this.fragment&&this.iframe&&(t=this.getHash(this.iframe.contentWindow)),t===this.fragment)return!1;this.iframe&&this.navigate(t),this.loadUrl()},loadUrl:function(e){return!!this.matchRoot()&&(e=this.fragment=this.getFragment(e),n.some(this.handlers,function(t){if(t.route.test(e))return t.callback(e),!0}))},navigate:function(e,t){if(!N.started)return!1;t&&!0!==t||(t={trigger:!!t}),e=this.getFragment(e||"");var n=this.root;""!==e&&"?"!==e.charAt(0)||(n=n.slice(0,-1)||"/");var r=n+e;if(e=this.decodeFragment(e.replace(j,"")),this.fragment!==e){if(this.fragment=e,this._usePushState)this.history[t.replace?"replaceState":"pushState"]({},document.title,r);else{if(!this._wantsHashChange)return this.location.assign(r);if(this._updateHash(this.location,e,t.replace),this.iframe&&e!==this.getHash(this.iframe.contentWindow)){var i=this.iframe.contentWindow;t.replace||(i.document.open(),i.document.close()),this._updateHash(i.location,e,t.replace)}}return t.trigger?this.loadUrl(e):void 0}},_updateHash:function(e,t,n){if(n){var r=e.href.replace(/(javascript:|#).*$/,"");e.replace(r+"#"+t)}else e.hash="#"+t}}),t.history=new N,b.extend=y.extend=E.extend=k.extend=N.extend=function(e,t){var r,i=this;return r=e&&n.has(e,"constructor")?e.constructor:function(){return i.apply(this,arguments)},n.extend(r,i,t),r.prototype=n.create(i.prototype,e),r.prototype.constructor=r,r.__super__=i.prototype,r};var P=function(){throw new Error('A "url" property or function must be specified')},L=function(e,t){var n=t.error;t.error=function(r){n&&n.call(t.context,e,r,t),e.trigger("error",e,r,t)}};return t}(o,n,e,t)}.apply(t,i))||(e.exports=a)}).call(this,n(29))},function(e,t,n){(function(e,n){var r;!function(){var i="object"==typeof self&&self.self===self&&self||"object"==typeof e&&e.global===e&&e||this||{},a=i._,o=Array.prototype,s=Object.prototype,l="undefined"!=typeof Symbol?Symbol.prototype:null,u=o.push,c=o.slice,f=s.toString,h=s.hasOwnProperty,d=Array.isArray,p=Object.keys,g=Object.create,m=function(){},v=function(e){return e instanceof v?e:this instanceof v?void(this._wrapped=e):new v(e)};t.nodeType?i._=v:(!n.nodeType&&n.exports&&(t=n.exports=v),t._=v),v.VERSION="1.9.1";var b,y=function(e,t,n){if(void 0===t)return e;switch(null==n?3:n){case 1:return function(n){return e.call(t,n)};case 3:return function(n,r,i){return e.call(t,n,r,i)};case 4:return function(n,r,i,a){return e.call(t,n,r,i,a)}}return function(){return e.apply(t,arguments)}},_=function(e,t,n){return v.iteratee!==b?v.iteratee(e,t):null==e?v.identity:v.isFunction(e)?y(e,t,n):v.isObject(e)&&!v.isArray(e)?v.matcher(e):v.property(e)};v.iteratee=b=function(e,t){return _(e,t,1/0)};var w=function(e,t){return t=null==t?e.length-1:+t,function(){for(var n=Math.max(arguments.length-t,0),r=Array(n),i=0;i=0&&t<=T};v.each=v.forEach=function(e,t,n){var r,i;if(t=y(t,n),M(e))for(r=0,i=e.length;r=3;return function(t,n,r,i){var a=!M(t)&&v.keys(t),o=(a||t).length,s=e>0?0:o-1;for(i||(r=t[a?a[s]:s],s+=e);s>=0&&s=0},v.invoke=w(function(e,t,n){var r,i;return v.isFunction(t)?i=t:v.isArray(t)&&(r=t.slice(0,-1),t=t[t.length-1]),v.map(e,function(e){var a=i;if(!a){if(r&&r.length&&(e=S(e,r)),null==e)return;a=e[t]}return null==a?a:a.apply(e,n)})}),v.pluck=function(e,t){return v.map(e,v.property(t))},v.where=function(e,t){return v.filter(e,v.matcher(t))},v.findWhere=function(e,t){return v.find(e,v.matcher(t))},v.max=function(e,t,n){var r,i,a=-1/0,o=-1/0;if(null==t||"number"==typeof t&&"object"!=typeof e[0]&&null!=e)for(var s=0,l=(e=M(e)?e:v.values(e)).length;sa&&(a=r);else t=_(t,n),v.each(e,function(e,n,r){((i=t(e,n,r))>o||i===-1/0&&a===-1/0)&&(a=e,o=i)});return a},v.min=function(e,t,n){var r,i,a=1/0,o=1/0;if(null==t||"number"==typeof t&&"object"!=typeof e[0]&&null!=e)for(var s=0,l=(e=M(e)?e:v.values(e)).length;sr||void 0===n)return 1;if(n0?0:i-1;a>=0&&a0?o=a>=0?a:Math.max(a+s,o):s=a>=0?Math.min(a+1,s):a+s+1;else if(n&&a&&s)return r[a=n(r,i)]===i?a:-1;if(i!=i)return(a=t(c.call(r,o,s),v.isNaN))>=0?a+o:-1;for(a=e>0?o:s-1;a>=0&&at?(r&&(clearTimeout(r),r=null),s=u,o=e.apply(i,a),r||(i=a=null)):r||!1===n.trailing||(r=setTimeout(l,c)),o};return u.cancel=function(){clearTimeout(r),s=0,r=i=a=null},u},v.debounce=function(e,t,n){var r,i,a=function(t,n){r=null,n&&(i=e.apply(t,n))},o=w(function(o){if(r&&clearTimeout(r),n){var s=!r;r=setTimeout(a,t),s&&(i=e.apply(this,o))}else r=v.delay(a,t,this,o);return i});return o.cancel=function(){clearTimeout(r),r=null},o},v.wrap=function(e,t){return v.partial(t,e)},v.negate=function(e){return function(){return!e.apply(this,arguments)}},v.compose=function(){var e=arguments,t=e.length-1;return function(){for(var n=t,r=e[t].apply(this,arguments);n--;)r=e[n].call(this,r);return r}},v.after=function(e,t){return function(){if(--e<1)return t.apply(this,arguments)}},v.before=function(e,t){var n;return function(){return--e>0&&(n=t.apply(this,arguments)),e<=1&&(t=null),n}},v.once=v.partial(v.before,2),v.restArguments=w;var P=!{toString:null}.propertyIsEnumerable("toString"),L=["valueOf","isPrototypeOf","toString","propertyIsEnumerable","hasOwnProperty","toLocaleString"],I=function(e,t){var n=L.length,r=e.constructor,i=v.isFunction(r)&&r.prototype||s,a="constructor";for(C(e,a)&&!v.contains(t,a)&&t.push(a);n--;)(a=L[n])in e&&e[a]!==i[a]&&!v.contains(t,a)&&t.push(a)};v.keys=function(e){if(!v.isObject(e))return[];if(p)return p(e);var t=[];for(var n in e)C(e,n)&&t.push(n);return P&&I(e,t),t},v.allKeys=function(e){if(!v.isObject(e))return[];var t=[];for(var n in e)t.push(n);return P&&I(e,t),t},v.values=function(e){for(var t=v.keys(e),n=t.length,r=Array(n),i=0;i1&&(r=y(r,t[1])),t=v.allKeys(e)):(r=F,t=N(t,!1,!1),e=Object(e));for(var i=0,a=t.length;i1&&(n=t[1])):(t=v.map(N(t,!1,!1),String),r=function(e,n){return!v.contains(t,n)}),v.pick(e,r,n)}),v.defaults=B(v.allKeys,!0),v.create=function(e,t){var n=x(e);return t&&v.extendOwn(n,t),n},v.clone=function(e){return v.isObject(e)?v.isArray(e)?e.slice():v.extend({},e):e},v.tap=function(e,t){return t(e),e},v.isMatch=function(e,t){var n=v.keys(t),r=n.length;if(null==e)return!r;for(var i=Object(e),a=0;a":">",'"':""","'":"'","`":"`"},W=v.invert(q),$=function(e){var t=function(t){return e[t]},n="(?:"+v.keys(e).join("|")+")",r=RegExp(n),i=RegExp(n,"g");return function(e){return e=null==e?"":""+e,r.test(e)?e.replace(i,t):e}};v.escape=$(q),v.unescape=$(W),v.result=function(e,t,n){v.isArray(t)||(t=[t]);var r=t.length;if(!r)return v.isFunction(n)?n.call(e):n;for(var i=0;i/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var G=/(.)^/,X={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},K=/\\|'|\r|\n|\u2028|\u2029/g,Z=function(e){return"\\"+X[e]};v.template=function(e,t,n){!t&&n&&(t=n),t=v.defaults({},t,v.templateSettings);var r,i=RegExp([(t.escape||G).source,(t.interpolate||G).source,(t.evaluate||G).source].join("|")+"|$","g"),a=0,o="__p+='";e.replace(i,function(t,n,r,i,s){return o+=e.slice(a,s).replace(K,Z),a=s+t.length,n?o+="'+\n((__t=("+n+"))==null?'':_.escape(__t))+\n'":r?o+="'+\n((__t=("+r+"))==null?'':__t)+\n'":i&&(o+="';\n"+i+"\n__p+='"),t}),o+="';\n",t.variable||(o="with(obj||{}){\n"+o+"}\n"),o="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+o+"return __p;\n";try{r=new Function(t.variable||"obj","_",o)}catch(e){throw e.source=o,e}var s=function(e){return r.call(this,e,v)},l=t.variable||"obj";return s.source="function("+l+"){\n"+o+"}",s},v.chain=function(e){var t=v(e);return t._chain=!0,t};var J=function(e,t){return e._chain?v(t).chain():t};v.mixin=function(e){return v.each(v.functions(e),function(t){var n=v[t]=e[t];v.prototype[t]=function(){var e=[this._wrapped];return u.apply(e,arguments),J(this,n.apply(v,e))}}),v},v.mixin(v),v.each(["pop","push","reverse","shift","sort","splice","unshift"],function(e){var t=o[e];v.prototype[e]=function(){var n=this._wrapped;return t.apply(n,arguments),"shift"!==e&&"splice"!==e||0!==n.length||delete n[0],J(this,n)}}),v.each(["concat","join","slice"],function(e){var t=o[e];v.prototype[e]=function(){return J(this,t.apply(this._wrapped,arguments))}}),v.prototype.value=function(){return this._wrapped},v.prototype.valueOf=v.prototype.toJSON=v.prototype.value,v.prototype.toString=function(){return String(this._wrapped)},void 0===(r=function(){return v}.apply(t,[]))||(n.exports=r)}()}).call(this,n(29),n(42)(e))},function(e,t,n){var r; +/*! + * jQuery JavaScript Library v3.4.0 + * https://jquery.com/ + * + * Includes Sizzle.js + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2019-04-10T19:48Z + */ +/*! + * jQuery JavaScript Library v3.4.0 + * https://jquery.com/ + * + * Includes Sizzle.js + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2019-04-10T19:48Z + */ +!function(t,n){"use strict";"object"==typeof e.exports?e.exports=t.document?n(t,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return n(e)}:n(t)}("undefined"!=typeof window?window:this,function(n,i){"use strict";var a=[],o=n.document,s=Object.getPrototypeOf,l=a.slice,u=a.concat,c=a.push,f=a.indexOf,h={},d=h.toString,p=h.hasOwnProperty,g=p.toString,m=g.call(Object),v={},b=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},y=function(e){return null!=e&&e===e.window},_={type:!0,src:!0,nonce:!0,noModule:!0};function DOMEval(e,t,n){var r,i,a=(n=n||o).createElement("script");if(a.text=e,t)for(r in _)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&a.setAttribute(r,i);n.head.appendChild(a).parentNode.removeChild(a)}function toType(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?h[d.call(e)]||"object":typeof e}var w=function(e,t){return new w.fn.init(e,t)},x=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;function isArrayLike(e){var t=!!e&&"length"in e&&e.length,n=toType(e);return!b(e)&&!y(e)&&("array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e)}w.fn=w.prototype={jquery:"3.4.0",constructor:w,length:0,toArray:function(){return l.call(this)},get:function(e){return null==e?l.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=w.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return w.each(this,e)},map:function(e){return this.pushStack(w.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(l.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n+~]|"+L+")"+L+"*"),W=new RegExp(L+"|>"),$=new RegExp(z),Y=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+B),PSEUDO:new RegExp("^"+z),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+P+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},X=/HTML$/i,K=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,J=/^[^{]+\{\s*\[native \w/,Q=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),ne=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"�":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},ae=function(){h()},oe=addCombinator(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{D.apply(O=V.call(w.childNodes),w.childNodes),O[w.childNodes.length].nodeType}catch(e){D={apply:O.length?function(e,t){N.apply(e,V.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}function Sizzle(e,t,r,i){var a,s,u,c,f,p,v,b=t&&t.ownerDocument,x=t?t.nodeType:9;if(r=r||[],"string"!=typeof e||!e||1!==x&&9!==x&&11!==x)return r;if(!i&&((t?t.ownerDocument||t:w)!==d&&h(t),t=t||d,g)){if(11!==x&&(f=Q.exec(e)))if(a=f[1]){if(9===x){if(!(u=t.getElementById(a)))return r;if(u.id===a)return r.push(u),r}else if(b&&(u=b.getElementById(a))&&y(t,u)&&u.id===a)return r.push(u),r}else{if(f[2])return D.apply(r,t.getElementsByTagName(e)),r;if((a=f[3])&&n.getElementsByClassName&&t.getElementsByClassName)return D.apply(r,t.getElementsByClassName(a)),r}if(n.qsa&&!E[e+" "]&&(!m||!m.test(e))&&(1!==x||"object"!==t.nodeName.toLowerCase())){if(v=e,b=t,1===x&&W.test(e)){for((c=t.getAttribute("id"))?c=c.replace(re,ie):t.setAttribute("id",c=_),s=(p=o(e)).length;s--;)p[s]="#"+c+" "+toSelector(p[s]);v=p.join(","),b=ee.test(e)&&testContext(t.parentNode)||t}try{return D.apply(r,b.querySelectorAll(v)),r}catch(t){E(e,!0)}finally{c===_&&t.removeAttribute("id")}}}return l(e.replace(F,"$1"),t,r,i)}function createCache(){var e=[];return function cache(t,n){return e.push(t+" ")>r.cacheLength&&delete cache[e.shift()],cache[t+" "]=n}}function markFunction(e){return e[_]=!0,e}function assert(e){var t=d.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function addHandle(e,t){for(var n=e.split("|"),i=n.length;i--;)r.attrHandle[n[i]]=t}function siblingCheck(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function createInputPseudo(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function createButtonPseudo(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function createDisabledPseudo(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&oe(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function createPositionalPseudo(e){return markFunction(function(t){return t=+t,markFunction(function(n,r){for(var i,a=e([],n.length,t),o=a.length;o--;)n[i=a[o]]&&(n[i]=!(r[i]=n[i]))})})}function testContext(e){return e&&void 0!==e.getElementsByTagName&&e}for(t in n=Sizzle.support={},a=Sizzle.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!X.test(t||n&&n.nodeName||"HTML")},h=Sizzle.setDocument=function(e){var t,i,o=e?e.ownerDocument||e:w;return o!==d&&9===o.nodeType&&o.documentElement?(p=(d=o).documentElement,g=!a(d),w!==d&&(i=d.defaultView)&&i.top!==i&&(i.addEventListener?i.addEventListener("unload",ae,!1):i.attachEvent&&i.attachEvent("onunload",ae)),n.attributes=assert(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=assert(function(e){return e.appendChild(d.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=J.test(d.getElementsByClassName),n.getById=assert(function(e){return p.appendChild(e).id=_,!d.getElementsByName||!d.getElementsByName(_).length}),n.getById?(r.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},r.find.ID=function(e,t){if(void 0!==t.getElementById&&g){var n=t.getElementById(e);return n?[n]:[]}}):(r.filter.ID=function(e){var t=e.replace(te,ne);return function(e){var n=void 0!==e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},r.find.ID=function(e,t){if(void 0!==t.getElementById&&g){var n,r,i,a=t.getElementById(e);if(a){if((n=a.getAttributeNode("id"))&&n.value===e)return[a];for(i=t.getElementsByName(e),r=0;a=i[r++];)if((n=a.getAttributeNode("id"))&&n.value===e)return[a]}return[]}}),r.find.TAG=n.getElementsByTagName?function(e,t){return void 0!==t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,a=t.getElementsByTagName(e);if("*"===e){for(;n=a[i++];)1===n.nodeType&&r.push(n);return r}return a},r.find.CLASS=n.getElementsByClassName&&function(e,t){if(void 0!==t.getElementsByClassName&&g)return t.getElementsByClassName(e)},v=[],m=[],(n.qsa=J.test(d.querySelectorAll))&&(assert(function(e){p.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&m.push("[*^$]="+L+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||m.push("\\["+L+"*(?:value|"+P+")"),e.querySelectorAll("[id~="+_+"-]").length||m.push("~="),e.querySelectorAll(":checked").length||m.push(":checked"),e.querySelectorAll("a#"+_+"+*").length||m.push(".#.+[+~]")}),assert(function(e){e.innerHTML="";var t=d.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&m.push("name"+L+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&m.push(":enabled",":disabled"),p.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&m.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),m.push(",.*:")})),(n.matchesSelector=J.test(b=p.matches||p.webkitMatchesSelector||p.mozMatchesSelector||p.oMatchesSelector||p.msMatchesSelector))&&assert(function(e){n.disconnectedMatch=b.call(e,"*"),b.call(e,"[s!='']:x"),v.push("!=",z)}),m=m.length&&new RegExp(m.join("|")),v=v.length&&new RegExp(v.join("|")),t=J.test(p.compareDocumentPosition),y=t||J.test(p.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},M=t?function(e,t){if(e===t)return f=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===r?e===d||e.ownerDocument===w&&y(w,e)?-1:t===d||t.ownerDocument===w&&y(w,t)?1:c?j(c,e)-j(c,t):0:4&r?-1:1)}:function(e,t){if(e===t)return f=!0,0;var n,r=0,i=e.parentNode,a=t.parentNode,o=[e],s=[t];if(!i||!a)return e===d?-1:t===d?1:i?-1:a?1:c?j(c,e)-j(c,t):0;if(i===a)return siblingCheck(e,t);for(n=e;n=n.parentNode;)o.unshift(n);for(n=t;n=n.parentNode;)s.unshift(n);for(;o[r]===s[r];)r++;return r?siblingCheck(o[r],s[r]):o[r]===w?-1:s[r]===w?1:0},d):d},Sizzle.matches=function(e,t){return Sizzle(e,null,null,t)},Sizzle.matchesSelector=function(e,t){if((e.ownerDocument||e)!==d&&h(e),n.matchesSelector&&g&&!E[t+" "]&&(!v||!v.test(t))&&(!m||!m.test(t)))try{var r=b.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){E(t,!0)}return Sizzle(t,d,null,[e]).length>0},Sizzle.contains=function(e,t){return(e.ownerDocument||e)!==d&&h(e),y(e,t)},Sizzle.attr=function(e,t){(e.ownerDocument||e)!==d&&h(e);var i=r.attrHandle[t.toLowerCase()],a=i&&A.call(r.attrHandle,t.toLowerCase())?i(e,t,!g):void 0;return void 0!==a?a:n.attributes||!g?e.getAttribute(t):(a=e.getAttributeNode(t))&&a.specified?a.value:null},Sizzle.escape=function(e){return(e+"").replace(re,ie)},Sizzle.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},Sizzle.uniqueSort=function(e){var t,r=[],i=0,a=0;if(f=!n.detectDuplicates,c=!n.sortStable&&e.slice(0),e.sort(M),f){for(;t=e[a++];)t===e[a]&&(i=r.push(a));for(;i--;)e.splice(r[i],1)}return c=null,e},i=Sizzle.getText=function(e){var t,n="",r=0,a=e.nodeType;if(a){if(1===a||9===a||11===a){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===a||4===a)return e.nodeValue}else for(;t=e[r++];)n+=i(t);return n},(r=Sizzle.selectors={cacheLength:50,createPseudo:markFunction,match:G,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||Sizzle.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&Sizzle.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&$.test(n)&&(t=o(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=C[e+" "];return t||(t=new RegExp("(^|"+L+")"+e+"("+L+"|$)"))&&C(e,function(e){return t.test("string"==typeof e.className&&e.className||void 0!==e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=Sizzle.attr(r,e);return null==i?"!="===t:!t||(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i.replace(U," ")+" ").indexOf(n)>-1:"|="===t&&(i===n||i.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,i){var a="nth"!==e.slice(0,3),o="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,f,h,d,p,g=a!==o?"nextSibling":"previousSibling",m=t.parentNode,v=s&&t.nodeName.toLowerCase(),b=!l&&!s,y=!1;if(m){if(a){for(;g;){for(h=t;h=h[g];)if(s?h.nodeName.toLowerCase()===v:1===h.nodeType)return!1;p=g="only"===e&&!p&&"nextSibling"}return!0}if(p=[o?m.firstChild:m.lastChild],o&&b){for(y=(d=(u=(c=(f=(h=m)[_]||(h[_]={}))[h.uniqueID]||(f[h.uniqueID]={}))[e]||[])[0]===x&&u[1])&&u[2],h=d&&m.childNodes[d];h=++d&&h&&h[g]||(y=d=0)||p.pop();)if(1===h.nodeType&&++y&&h===t){c[e]=[x,d,y];break}}else if(b&&(y=d=(u=(c=(f=(h=t)[_]||(h[_]={}))[h.uniqueID]||(f[h.uniqueID]={}))[e]||[])[0]===x&&u[1]),!1===y)for(;(h=++d&&h&&h[g]||(y=d=0)||p.pop())&&((s?h.nodeName.toLowerCase()!==v:1!==h.nodeType)||!++y||(b&&((c=(f=h[_]||(h[_]={}))[h.uniqueID]||(f[h.uniqueID]={}))[e]=[x,y]),h!==t)););return(y-=i)===r||y%r==0&&y/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||Sizzle.error("unsupported pseudo: "+e);return i[_]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?markFunction(function(e,n){for(var r,a=i(e,t),o=a.length;o--;)e[r=j(e,a[o])]=!(n[r]=a[o])}):function(e){return i(e,0,n)}):i}},pseudos:{not:markFunction(function(e){var t=[],n=[],r=s(e.replace(F,"$1"));return r[_]?markFunction(function(e,t,n,i){for(var a,o=r(e,null,i,[]),s=e.length;s--;)(a=o[s])&&(e[s]=!(t[s]=a))}):function(e,i,a){return t[0]=e,r(t,null,a,n),t[0]=null,!n.pop()}}),has:markFunction(function(e){return function(t){return Sizzle(e,t).length>0}}),contains:markFunction(function(e){return e=e.replace(te,ne),function(t){return(t.textContent||i(t)).indexOf(e)>-1}}),lang:markFunction(function(e){return Y.test(e||"")||Sizzle.error("unsupported lang: "+e),e=e.replace(te,ne).toLowerCase(),function(t){var n;do{if(n=g?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===p},focus:function(e){return e===d.activeElement&&(!d.hasFocus||d.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:createDisabledPseudo(!1),disabled:createDisabledPseudo(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return Z.test(e.nodeName)},input:function(e){return K.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:createPositionalPseudo(function(){return[0]}),last:createPositionalPseudo(function(e,t){return[t-1]}),eq:createPositionalPseudo(function(e,t,n){return[n<0?n+t:n]}),even:createPositionalPseudo(function(e,t){for(var n=0;nt?t:n;--r>=0;)e.push(r);return e}),gt:createPositionalPseudo(function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function condense(e,t,n,r,i){for(var a,o=[],s=0,l=e.length,u=null!=t;s-1&&(a[u]=!(o[u]=f))}}else v=condense(v===o?v.splice(p,v.length):v),i?i(null,o,v,l):D.apply(o,v)})}function matcherFromTokens(e){for(var t,n,i,a=e.length,o=r.relative[e[0].type],s=o||r.relative[" "],l=o?1:0,c=addCombinator(function(e){return e===t},s,!0),f=addCombinator(function(e){return j(t,e)>-1},s,!0),h=[function(e,n,r){var i=!o&&(r||n!==u)||((t=n).nodeType?c(e,n,r):f(e,n,r));return t=null,i}];l1&&elementMatcher(h),l>1&&toSelector(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(F,"$1"),n,l0,i=e.length>0,a=function(a,o,s,l,c){var f,p,m,v=0,b="0",y=a&&[],_=[],w=u,k=a||i&&r.find.TAG("*",c),C=x+=null==w?1:Math.random()||.1,S=k.length;for(c&&(u=o===d||o||c);b!==S&&null!=(f=k[b]);b++){if(i&&f){for(p=0,o||f.ownerDocument===d||(h(f),s=!g);m=e[p++];)if(m(f,o||d,s)){l.push(f);break}c&&(x=C)}n&&((f=!m&&f)&&v--,a&&y.push(f))}if(v+=b,n&&b!==v){for(p=0;m=t[p++];)m(y,_,o,s);if(a){if(v>0)for(;b--;)y[b]||_[b]||(_[b]=R.call(l));_=condense(_)}D.apply(l,_),c&&!a&&_.length>0&&v+t.length>1&&Sizzle.uniqueSort(l)}return c&&(x=C,u=w),y};return n?markFunction(a):a}(a,i))).selector=e}return s},l=Sizzle.select=function(e,t,n,i){var a,l,u,c,f,h="function"==typeof e&&e,d=!i&&o(e=h.selector||e);if(n=n||[],1===d.length){if((l=d[0]=d[0].slice(0)).length>2&&"ID"===(u=l[0]).type&&9===t.nodeType&&g&&r.relative[l[1].type]){if(!(t=(r.find.ID(u.matches[0].replace(te,ne),t)||[])[0]))return n;h&&(t=t.parentNode),e=e.slice(l.shift().value.length)}for(a=G.needsContext.test(e)?0:l.length;a--&&(u=l[a],!r.relative[c=u.type]);)if((f=r.find[c])&&(i=f(u.matches[0].replace(te,ne),ee.test(l[0].type)&&testContext(t.parentNode)||t))){if(l.splice(a,1),!(e=i.length&&toSelector(l)))return D.apply(n,i),n;break}}return(h||s(e,d))(i,t,!g,n,!t||ee.test(e)&&testContext(t.parentNode)||t),n},n.sortStable=_.split("").sort(M).join("")===_,n.detectDuplicates=!!f,h(),n.sortDetached=assert(function(e){return 1&e.compareDocumentPosition(d.createElement("fieldset"))}),assert(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||addHandle("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),n.attributes&&assert(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||addHandle("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),assert(function(e){return null==e.getAttribute("disabled")})||addHandle(P,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),Sizzle}(n);w.find=k,w.expr=k.selectors,w.expr[":"]=w.expr.pseudos,w.uniqueSort=w.unique=k.uniqueSort,w.text=k.getText,w.isXMLDoc=k.isXML,w.contains=k.contains,w.escapeSelector=k.escape;var C=function(e,t,n){for(var r=[],i=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(i&&w(e).is(n))break;r.push(e)}return r},S=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},T=w.expr.match.needsContext;function nodeName(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var E=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function winnow(e,t,n){return b(t)?w.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?w.grep(e,function(e){return e===t!==n}):"string"!=typeof t?w.grep(e,function(e){return f.call(t,e)>-1!==n}):w.filter(t,e,n)}w.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?w.find.matchesSelector(r,e)?[r]:[]:w.find.matches(e,w.grep(t,function(e){return 1===e.nodeType}))},w.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(w(e).filter(function(){for(t=0;t1?w.uniqueSort(n):n},filter:function(e){return this.pushStack(winnow(this,e||[],!1))},not:function(e){return this.pushStack(winnow(this,e||[],!0))},is:function(e){return!!winnow(this,"string"==typeof e&&T.test(e)?w(e):e||[],!1).length}});var M,A=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(w.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||M,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:A.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof w?t[0]:t,w.merge(this,w.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:o,!0)),E.test(r[1])&&w.isPlainObject(t))for(r in t)b(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=o.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):b(e)?void 0!==n.ready?n.ready(e):e(w):w.makeArray(e,this)}).prototype=w.fn,M=w(o);var O=/^(?:parents|prev(?:Until|All))/,R={children:!0,contents:!0,next:!0,prev:!0};function sibling(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}w.fn.extend({has:function(e){var t=w(e,this),n=t.length;return this.filter(function(){for(var e=0;e-1:1===n.nodeType&&w.find.matchesSelector(n,e))){a.push(n);break}return this.pushStack(a.length>1?w.uniqueSort(a):a)},index:function(e){return e?"string"==typeof e?f.call(w(e),this[0]):f.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(w.uniqueSort(w.merge(this.get(),w(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),w.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return C(e,"parentNode")},parentsUntil:function(e,t,n){return C(e,"parentNode",n)},next:function(e){return sibling(e,"nextSibling")},prev:function(e){return sibling(e,"previousSibling")},nextAll:function(e){return C(e,"nextSibling")},prevAll:function(e){return C(e,"previousSibling")},nextUntil:function(e,t,n){return C(e,"nextSibling",n)},prevUntil:function(e,t,n){return C(e,"previousSibling",n)},siblings:function(e){return S((e.parentNode||{}).firstChild,e)},children:function(e){return S(e.firstChild)},contents:function(e){return void 0!==e.contentDocument?e.contentDocument:(nodeName(e,"template")&&(e=e.content||e),w.merge([],e.childNodes))}},function(e,t){w.fn[e]=function(n,r){var i=w.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=w.filter(r,i)),this.length>1&&(R[e]||w.uniqueSort(i),O.test(e)&&i.reverse()),this.pushStack(i)}});var N=/[^\x20\t\r\n\f]+/g;function Identity(e){return e}function Thrower(e){throw e}function adoptValue(e,t,n,r){var i;try{e&&b(i=e.promise)?i.call(e).done(t).fail(n):e&&b(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}w.Callbacks=function(e){e="string"==typeof e?function(e){var t={};return w.each(e.match(N)||[],function(e,n){t[n]=!0}),t}(e):w.extend({},e);var t,n,r,i,a=[],o=[],s=-1,l=function(){for(i=i||e.once,r=t=!0;o.length;s=-1)for(n=o.shift();++s-1;)a.splice(n,1),n<=s&&s--}),this},has:function(e){return e?w.inArray(e,a)>-1:a.length>0},empty:function(){return a&&(a=[]),this},disable:function(){return i=o=[],a=n="",this},disabled:function(){return!a},lock:function(){return i=o=[],n||t||(a=n=""),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=[e,(n=n||[]).slice?n.slice():n],o.push(n),t||l()),this},fire:function(){return u.fireWith(this,arguments),this},fired:function(){return!!r}};return u},w.extend({Deferred:function(e){var t=[["notify","progress",w.Callbacks("memory"),w.Callbacks("memory"),2],["resolve","done",w.Callbacks("once memory"),w.Callbacks("once memory"),0,"resolved"],["reject","fail",w.Callbacks("once memory"),w.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return a.done(arguments).fail(arguments),this},catch:function(e){return i.then(null,e)},pipe:function(){var e=arguments;return w.Deferred(function(n){w.each(t,function(t,r){var i=b(e[r[4]])&&e[r[4]];a[r[1]](function(){var e=i&&i.apply(this,arguments);e&&b(e.promise)?e.promise().progress(n.notify).done(n.resolve).fail(n.reject):n[r[0]+"With"](this,i?[e]:arguments)})}),e=null}).promise()},then:function(e,r,i){var a=0;function resolve(e,t,r,i){return function(){var o=this,s=arguments,l=function(){var n,l;if(!(e=a&&(r!==Thrower&&(o=void 0,s=[n]),t.rejectWith(o,s))}};e?u():(w.Deferred.getStackHook&&(u.stackTrace=w.Deferred.getStackHook()),n.setTimeout(u))}}return w.Deferred(function(n){t[0][3].add(resolve(0,n,b(i)?i:Identity,n.notifyWith)),t[1][3].add(resolve(0,n,b(e)?e:Identity)),t[2][3].add(resolve(0,n,b(r)?r:Thrower))}).promise()},promise:function(e){return null!=e?w.extend(e,i):i}},a={};return w.each(t,function(e,n){var o=n[2],s=n[5];i[n[1]]=o.add,s&&o.add(function(){r=s},t[3-e][2].disable,t[3-e][3].disable,t[0][2].lock,t[0][3].lock),o.add(n[3].fire),a[n[0]]=function(){return a[n[0]+"With"](this===a?void 0:this,arguments),this},a[n[0]+"With"]=o.fireWith}),i.promise(a),e&&e.call(a,a),a},when:function(e){var t=arguments.length,n=t,r=Array(n),i=l.call(arguments),a=w.Deferred(),o=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?l.call(arguments):n,--t||a.resolveWith(r,i)}};if(t<=1&&(adoptValue(e,a.done(o(n)).resolve,a.reject,!t),"pending"===a.state()||b(i[n]&&i[n].then)))return a.then();for(;n--;)adoptValue(i[n],o(n),a.reject);return a.promise()}});var D=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;w.Deferred.exceptionHook=function(e,t){n.console&&n.console.warn&&e&&D.test(e.name)&&n.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},w.readyException=function(e){n.setTimeout(function(){throw e})};var V=w.Deferred();function completed(){o.removeEventListener("DOMContentLoaded",completed),n.removeEventListener("load",completed),w.ready()}w.fn.ready=function(e){return V.then(e).catch(function(e){w.readyException(e)}),this},w.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--w.readyWait:w.isReady)||(w.isReady=!0,!0!==e&&--w.readyWait>0||V.resolveWith(o,[w]))}}),w.ready.then=V.then,"complete"===o.readyState||"loading"!==o.readyState&&!o.documentElement.doScroll?n.setTimeout(w.ready):(o.addEventListener("DOMContentLoaded",completed),n.addEventListener("load",completed));var j=function(e,t,n,r,i,a,o){var s=0,l=e.length,u=null==n;if("object"===toType(n))for(s in i=!0,n)j(e,t,s,n[s],!0,a,o);else if(void 0!==r&&(i=!0,b(r)||(o=!0),u&&(o?(t.call(e,r),t=null):(u=t,t=function(e,t,n){return u.call(w(e),n)})),t))for(;s1,null,!0)},removeData:function(e){return this.each(function(){z.remove(this,e)})}}),w.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=B.get(e,t),n&&(!r||Array.isArray(n)?r=B.access(e,t,w.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=w.queue(e,t),r=n.length,i=n.shift(),a=w._queueHooks(e,t);"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete a.stop,i.call(e,function(){w.dequeue(e,t)},a)),!r&&a&&a.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return B.get(e,n)||B.access(e,n,{empty:w.Callbacks("once memory").add(function(){B.remove(e,[t+"queue",n])})})}}),w.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]*)/i,ee=/^$|^module$|\/(?:java|ecma)script/i,te={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function getAll(e,t){var n;return n=void 0!==e.getElementsByTagName?e.getElementsByTagName(t||"*"):void 0!==e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&nodeName(e,t)?w.merge([e],n):n}function setGlobalEval(e,t){for(var n=0,r=e.length;n-1)i&&i.push(a);else if(u=Y(a),o=getAll(f.appendChild(a),"script"),u&&setGlobalEval(o),n)for(c=0;a=o[c++];)ee.test(a.type||"")&&n.push(a);return f}ne=o.createDocumentFragment().appendChild(o.createElement("div")),(re=o.createElement("input")).setAttribute("type","radio"),re.setAttribute("checked","checked"),re.setAttribute("name","t"),ne.appendChild(re),v.checkClone=ne.cloneNode(!0).cloneNode(!0).lastChild.checked,ne.innerHTML="",v.noCloneChecked=!!ne.cloneNode(!0).lastChild.defaultValue;var ae=/^key/,oe=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,se=/^([^.]*)(?:\.(.+)|)/;function returnTrue(){return!0}function returnFalse(){return!1}function expectSync(e,t){return e===function(){try{return o.activeElement}catch(e){}}()==("focus"===t)}function on(e,t,n,r,i,a){var o,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)on(e,s,n,r,t[s],a);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=returnFalse;else if(!i)return e;return 1===a&&(o=i,(i=function(e){return w().off(e),o.apply(this,arguments)}).guid=o.guid||(o.guid=w.guid++)),e.each(function(){w.event.add(this,t,i,r,n)})}function leverageNative(e,t,n){n?(B.set(e,t,!1),w.event.add(e,t,{namespace:!1,handler:function(e){var r,i,a=B.get(this,t);if(1&e.isTrigger&&this[t]){if(a)(w.event.special[t]||{}).delegateType&&e.stopPropagation();else if(a=l.call(arguments),B.set(this,t,a),r=n(this,t),this[t](),a!==(i=B.get(this,t))||r?B.set(this,t,!1):i=void 0,a!==i)return e.stopImmediatePropagation(),e.preventDefault(),i}else a&&(B.set(this,t,w.event.trigger(w.extend(a.shift(),w.Event.prototype),a,this)),e.stopImmediatePropagation())}})):w.event.add(e,t,returnTrue)}w.event={global:{},add:function(e,t,n,r,i){var a,o,s,l,u,c,f,h,d,p,g,m=B.get(e);if(m)for(n.handler&&(n=(a=n).handler,i=a.selector),i&&w.find.matchesSelector($,i),n.guid||(n.guid=w.guid++),(l=m.events)||(l=m.events={}),(o=m.handle)||(o=m.handle=function(t){return void 0!==w&&w.event.triggered!==t.type?w.event.dispatch.apply(e,arguments):void 0}),u=(t=(t||"").match(N)||[""]).length;u--;)d=g=(s=se.exec(t[u])||[])[1],p=(s[2]||"").split(".").sort(),d&&(f=w.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=w.event.special[d]||{},c=w.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&w.expr.match.needsContext.test(i),namespace:p.join(".")},a),(h=l[d])||((h=l[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,p,o)||e.addEventListener&&e.addEventListener(d,o)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?h.splice(h.delegateCount++,0,c):h.push(c),w.event.global[d]=!0)},remove:function(e,t,n,r,i){var a,o,s,l,u,c,f,h,d,p,g,m=B.hasData(e)&&B.get(e);if(m&&(l=m.events)){for(u=(t=(t||"").match(N)||[""]).length;u--;)if(d=g=(s=se.exec(t[u])||[])[1],p=(s[2]||"").split(".").sort(),d){for(f=w.event.special[d]||{},h=l[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),o=a=h.length;a--;)c=h[a],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(h.splice(a,1),c.selector&&h.delegateCount--,f.remove&&f.remove.call(e,c));o&&!h.length&&(f.teardown&&!1!==f.teardown.call(e,p,m.handle)||w.removeEvent(e,d,m.handle),delete l[d])}else for(d in l)w.event.remove(e,d+t[u],n,r,!0);w.isEmptyObject(l)&&B.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,a,o,s=w.event.fix(e),l=new Array(arguments.length),u=(B.get(this,"events")||{})[s.type]||[],c=w.event.special[s.type]||{};for(l[0]=s,t=1;t=1))for(;u!==this;u=u.parentNode||this)if(1===u.nodeType&&("click"!==e.type||!0!==u.disabled)){for(a=[],o={},n=0;n-1:w.find(i,this,null,[u]).length),o[i]&&a.push(r);a.length&&s.push({elem:u,handlers:a})}return u=this,l\x20\t\r\n\f]*)[^>]*)\/>/gi,ue=/\s*$/g;function manipulationTarget(e,t){return nodeName(e,"table")&&nodeName(11!==t.nodeType?t:t.firstChild,"tr")&&w(e).children("tbody")[0]||e}function disableScript(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function restoreScript(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function cloneCopyEvent(e,t){var n,r,i,a,o,s,l,u;if(1===t.nodeType){if(B.hasData(e)&&(a=B.access(e),o=B.set(t,a),u=a.events))for(i in delete o.handle,o.events={},u)for(n=0,r=u[i].length;n1&&"string"==typeof p&&!v.checkClone&&ce.test(p))return e.each(function(i){var a=e.eq(i);g&&(t[0]=p.call(this,i,a.html())),domManip(a,t,n,r)});if(h&&(a=(i=buildFragment(t,e[0].ownerDocument,!1,e,r)).firstChild,1===i.childNodes.length&&(i=a),a||r)){for(s=(o=w.map(getAll(i,"script"),disableScript)).length;f")},clone:function(e,t,n){var r,i,a,o,s,l,u,c=e.cloneNode(!0),f=Y(e);if(!(v.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||w.isXMLDoc(e)))for(o=getAll(c),r=0,i=(a=getAll(e)).length;r0&&setGlobalEval(o,!f&&getAll(e,"script")),c},cleanData:function(e){for(var t,n,r,i=w.event.special,a=0;void 0!==(n=e[a]);a++)if(I(n)){if(t=n[B.expando]){if(t.events)for(r in t.events)i[r]?w.event.remove(n,r):w.removeEvent(n,r,t.handle);n[B.expando]=void 0}n[z.expando]&&(n[z.expando]=void 0)}}}),w.fn.extend({detach:function(e){return remove(this,e,!0)},remove:function(e){return remove(this,e)},text:function(e){return j(this,function(e){return void 0===e?w.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return domManip(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||manipulationTarget(this,e).appendChild(e)})},prepend:function(){return domManip(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=manipulationTarget(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return domManip(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return domManip(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(w.cleanData(getAll(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return w.clone(this,e,t)})},html:function(e){return j(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!ue.test(e)&&!te[(Q.exec(e)||["",""])[1].toLowerCase()]){e=w.htmlPrefilter(e);try{for(;n=0&&(l+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-a-l-s-.5))||0),l}function getWidthOrHeight(e,t,n){var r=de(e),i=(!v.boxSizingReliable()||n)&&"border-box"===w.css(e,"boxSizing",!1,r),a=i,o=curCSS(e,t,r),s="offset"+t[0].toUpperCase()+t.slice(1);if(he.test(o)){if(!n)return o;o="auto"}return(!v.boxSizingReliable()&&i||"auto"===o||!parseFloat(o)&&"inline"===w.css(e,"display",!1,r))&&e.getClientRects().length&&(i="border-box"===w.css(e,"boxSizing",!1,r),(a=s in e)&&(o=e[s])),(o=parseFloat(o)||0)+boxModelAdjustment(e,t,n||(i?"border":"content"),a,r,o)+"px"}function Tween(e,t,n,r,i){return new Tween.prototype.init(e,t,n,r,i)}w.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=curCSS(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,a,o,s=camelCase(t),l=ye.test(t),u=e.style;if(l||(t=finalPropName(s)),o=w.cssHooks[t]||w.cssHooks[s],void 0===n)return o&&"get"in o&&void 0!==(i=o.get(e,!1,r))?i:u[t];"string"===(a=typeof n)&&(i=q.exec(n))&&i[1]&&(n=adjustCSS(e,t,i),a="number"),null!=n&&n==n&&("number"!==a||l||(n+=i&&i[3]||(w.cssNumber[s]?"":"px")),v.clearCloneStyle||""!==n||0!==t.indexOf("background")||(u[t]="inherit"),o&&"set"in o&&void 0===(n=o.set(e,n,r))||(l?u.setProperty(t,n):u[t]=n))}},css:function(e,t,n,r){var i,a,o,s=camelCase(t);return ye.test(t)||(t=finalPropName(s)),(o=w.cssHooks[t]||w.cssHooks[s])&&"get"in o&&(i=o.get(e,!0,n)),void 0===i&&(i=curCSS(e,t,r)),"normal"===i&&t in we&&(i=we[t]),""===n||n?(a=parseFloat(i),!0===n||isFinite(a)?a||0:i):i}}),w.each(["height","width"],function(e,t){w.cssHooks[t]={get:function(e,n,r){if(n)return!be.test(w.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?getWidthOrHeight(e,t,r):K(e,_e,function(){return getWidthOrHeight(e,t,r)})},set:function(e,n,r){var i,a=de(e),o=!v.scrollboxSize()&&"absolute"===a.position,s=(o||r)&&"border-box"===w.css(e,"boxSizing",!1,a),l=r?boxModelAdjustment(e,t,r,s,a):0;return s&&o&&(l-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(a[t])-boxModelAdjustment(e,t,"border",!1,a)-.5)),l&&(i=q.exec(n))&&"px"!==(i[3]||"px")&&(e.style[t]=n,n=w.css(e,t)),setPositiveNumber(0,n,l)}}}),w.cssHooks.marginLeft=addGetHookIf(v.reliableMarginLeft,function(e,t){if(t)return(parseFloat(curCSS(e,"marginLeft"))||e.getBoundingClientRect().left-K(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),w.each({margin:"",padding:"",border:"Width"},function(e,t){w.cssHooks[e+t]={expand:function(n){for(var r=0,i={},a="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+W[r]+t]=a[r]||a[r-2]||a[0];return i}},"margin"!==e&&(w.cssHooks[e+t].set=setPositiveNumber)}),w.fn.extend({css:function(e,t){return j(this,function(e,t,n){var r,i,a={},o=0;if(Array.isArray(t)){for(r=de(e),i=t.length;o1)}}),w.Tween=Tween,Tween.prototype={constructor:Tween,init:function(e,t,n,r,i,a){this.elem=e,this.prop=n,this.easing=i||w.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=a||(w.cssNumber[n]?"":"px")},cur:function(){var e=Tween.propHooks[this.prop];return e&&e.get?e.get(this):Tween.propHooks._default.get(this)},run:function(e){var t,n=Tween.propHooks[this.prop];return this.options.duration?this.pos=t=w.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):Tween.propHooks._default.set(this),this}},Tween.prototype.init.prototype=Tween.prototype,Tween.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=w.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){w.fx.step[e.prop]?w.fx.step[e.prop](e):1!==e.elem.nodeType||!w.cssHooks[e.prop]&&null==e.elem.style[finalPropName(e.prop)]?e.elem[e.prop]=e.now:w.style(e.elem,e.prop,e.now+e.unit)}}},Tween.propHooks.scrollTop=Tween.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},w.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},w.fx=Tween.prototype.init,w.fx.step={};var xe,ke,Ce=/^(?:toggle|show|hide)$/,Se=/queueHooks$/;function schedule(){ke&&(!1===o.hidden&&n.requestAnimationFrame?n.requestAnimationFrame(schedule):n.setTimeout(schedule,w.fx.interval),w.fx.tick())}function createFxNow(){return n.setTimeout(function(){xe=void 0}),xe=Date.now()}function genFx(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=W[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function createTween(e,t,n){for(var r,i=(Animation.tweeners[t]||[]).concat(Animation.tweeners["*"]),a=0,o=i.length;a1)},removeAttr:function(e){return this.each(function(){w.removeAttr(this,e)})}}),w.extend({attr:function(e,t,n){var r,i,a=e.nodeType;if(3!==a&&8!==a&&2!==a)return void 0===e.getAttribute?w.prop(e,t,n):(1===a&&w.isXMLDoc(e)||(i=w.attrHooks[t.toLowerCase()]||(w.expr.match.bool.test(t)?Te:void 0)),void 0!==n?null===n?void w.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=w.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!v.radioValue&&"radio"===t&&nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(N);if(i&&1===e.nodeType)for(;n=i[r++];)e.removeAttribute(n)}}),Te={set:function(e,t,n){return!1===t?w.removeAttr(e,n):e.setAttribute(n,n),n}},w.each(w.expr.match.bool.source.match(/\w+/g),function(e,t){var n=Ee[t]||w.find.attr;Ee[t]=function(e,t,r){var i,a,o=t.toLowerCase();return r||(a=Ee[o],Ee[o]=i,i=null!=n(e,t,r)?o:null,Ee[o]=a),i}});var Me=/^(?:input|select|textarea|button)$/i,Ae=/^(?:a|area)$/i;function stripAndCollapse(e){return(e.match(N)||[]).join(" ")}function getClass(e){return e.getAttribute&&e.getAttribute("class")||""}function classesToArray(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(N)||[]}w.fn.extend({prop:function(e,t){return j(this,w.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[w.propFix[e]||e]})}}),w.extend({prop:function(e,t,n){var r,i,a=e.nodeType;if(3!==a&&8!==a&&2!==a)return 1===a&&w.isXMLDoc(e)||(t=w.propFix[t]||t,i=w.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=w.find.attr(e,"tabindex");return t?parseInt(t,10):Me.test(e.nodeName)||Ae.test(e.nodeName)&&e.href?0:-1}}},propFix:{for:"htmlFor",class:"className"}}),v.optSelected||(w.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),w.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){w.propFix[this.toLowerCase()]=this}),w.fn.extend({addClass:function(e){var t,n,r,i,a,o,s,l=0;if(b(e))return this.each(function(t){w(this).addClass(e.call(this,t,getClass(this)))});if((t=classesToArray(e)).length)for(;n=this[l++];)if(i=getClass(n),r=1===n.nodeType&&" "+stripAndCollapse(i)+" "){for(o=0;a=t[o++];)r.indexOf(" "+a+" ")<0&&(r+=a+" ");i!==(s=stripAndCollapse(r))&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,i,a,o,s,l=0;if(b(e))return this.each(function(t){w(this).removeClass(e.call(this,t,getClass(this)))});if(!arguments.length)return this.attr("class","");if((t=classesToArray(e)).length)for(;n=this[l++];)if(i=getClass(n),r=1===n.nodeType&&" "+stripAndCollapse(i)+" "){for(o=0;a=t[o++];)for(;r.indexOf(" "+a+" ")>-1;)r=r.replace(" "+a+" "," ");i!==(s=stripAndCollapse(r))&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"==typeof t&&r?t?this.addClass(e):this.removeClass(e):b(e)?this.each(function(n){w(this).toggleClass(e.call(this,n,getClass(this),t),t)}):this.each(function(){var t,i,a,o;if(r)for(i=0,a=w(this),o=classesToArray(e);t=o[i++];)a.hasClass(t)?a.removeClass(t):a.addClass(t);else void 0!==e&&"boolean"!==n||((t=getClass(this))&&B.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":B.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;for(t=" "+e+" ";n=this[r++];)if(1===n.nodeType&&(" "+stripAndCollapse(getClass(n))+" ").indexOf(t)>-1)return!0;return!1}});var Oe=/\r/g;w.fn.extend({val:function(e){var t,n,r,i=this[0];return arguments.length?(r=b(e),this.each(function(n){var i;1===this.nodeType&&(null==(i=r?e.call(this,n,w(this).val()):e)?i="":"number"==typeof i?i+="":Array.isArray(i)&&(i=w.map(i,function(e){return null==e?"":e+""})),(t=w.valHooks[this.type]||w.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))})):i?(t=w.valHooks[i.type]||w.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:"string"==typeof(n=i.value)?n.replace(Oe,""):null==n?"":n:void 0}}),w.extend({valHooks:{option:{get:function(e){var t=w.find.attr(e,"value");return null!=t?t:stripAndCollapse(w.text(e))}},select:{get:function(e){var t,n,r,i=e.options,a=e.selectedIndex,o="select-one"===e.type,s=o?null:[],l=o?a+1:i.length;for(r=a<0?l:o?a:0;r-1)&&(n=!0);return n||(e.selectedIndex=-1),a}}}}),w.each(["radio","checkbox"],function(){w.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=w.inArray(w(e).val(),t)>-1}},v.checkOn||(w.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),v.focusin="onfocusin"in n;var Re=/^(?:focusinfocus|focusoutblur)$/,Ne=function(e){e.stopPropagation()};w.extend(w.event,{trigger:function(e,t,r,i){var a,s,l,u,c,f,h,d,g=[r||o],m=p.call(e,"type")?e.type:e,v=p.call(e,"namespace")?e.namespace.split("."):[];if(s=d=l=r=r||o,3!==r.nodeType&&8!==r.nodeType&&!Re.test(m+w.event.triggered)&&(m.indexOf(".")>-1&&(v=m.split("."),m=v.shift(),v.sort()),c=m.indexOf(":")<0&&"on"+m,(e=e[w.expando]?e:new w.Event(m,"object"==typeof e&&e)).isTrigger=i?2:3,e.namespace=v.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+v.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=r),t=null==t?[e]:w.makeArray(t,[e]),h=w.event.special[m]||{},i||!h.trigger||!1!==h.trigger.apply(r,t))){if(!i&&!h.noBubble&&!y(r)){for(u=h.delegateType||m,Re.test(u+m)||(s=s.parentNode);s;s=s.parentNode)g.push(s),l=s;l===(r.ownerDocument||o)&&g.push(l.defaultView||l.parentWindow||n)}for(a=0;(s=g[a++])&&!e.isPropagationStopped();)d=s,e.type=a>1?u:h.bindType||m,(f=(B.get(s,"events")||{})[e.type]&&B.get(s,"handle"))&&f.apply(s,t),(f=c&&s[c])&&f.apply&&I(s)&&(e.result=f.apply(s,t),!1===e.result&&e.preventDefault());return e.type=m,i||e.isDefaultPrevented()||h._default&&!1!==h._default.apply(g.pop(),t)||!I(r)||c&&b(r[m])&&!y(r)&&((l=r[c])&&(r[c]=null),w.event.triggered=m,e.isPropagationStopped()&&d.addEventListener(m,Ne),r[m](),e.isPropagationStopped()&&d.removeEventListener(m,Ne),w.event.triggered=void 0,l&&(r[c]=l)),e.result}},simulate:function(e,t,n){var r=w.extend(new w.Event,n,{type:e,isSimulated:!0});w.event.trigger(r,null,t)}}),w.fn.extend({trigger:function(e,t){return this.each(function(){w.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return w.event.trigger(e,t,n,!0)}}),v.focusin||w.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){w.event.simulate(t,e.target,w.event.fix(e))};w.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=B.access(r,t);i||r.addEventListener(e,n,!0),B.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=B.access(r,t)-1;i?B.access(r,t,i):(r.removeEventListener(e,n,!0),B.remove(r,t))}}});var De=n.location,Ve=Date.now(),je=/\?/;w.parseXML=function(e){var t;if(!e||"string"!=typeof e)return null;try{t=(new n.DOMParser).parseFromString(e,"text/xml")}catch(e){t=void 0}return t&&!t.getElementsByTagName("parsererror").length||w.error("Invalid XML: "+e),t};var Pe=/\[\]$/,Le=/\r?\n/g,Ie=/^(?:submit|button|image|reset|file)$/i,Be=/^(?:input|select|textarea|keygen)/i;function buildParams(e,t,n,r){var i;if(Array.isArray(t))w.each(t,function(t,i){n||Pe.test(e)?r(e,i):buildParams(e+"["+("object"==typeof i&&null!=i?t:"")+"]",i,n,r)});else if(n||"object"!==toType(t))r(e,t);else for(i in t)buildParams(e+"["+i+"]",t[i],n,r)}w.param=function(e,t){var n,r=[],i=function(e,t){var n=b(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(null==e)return"";if(Array.isArray(e)||e.jquery&&!w.isPlainObject(e))w.each(e,function(){i(this.name,this.value)});else for(n in e)buildParams(n,e[n],t,i);return r.join("&")},w.fn.extend({serialize:function(){return w.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=w.prop(this,"elements");return e?w.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!w(this).is(":disabled")&&Be.test(this.nodeName)&&!Ie.test(e)&&(this.checked||!J.test(e))}).map(function(e,t){var n=w(this).val();return null==n?null:Array.isArray(n)?w.map(n,function(e){return{name:t.name,value:e.replace(Le,"\r\n")}}):{name:t.name,value:n.replace(Le,"\r\n")}}).get()}});var ze=/%20/g,Ue=/#.*$/,Fe=/([?&])_=[^&]*/,He=/^(.*?):[ \t]*([^\r\n]*)$/gm,qe=/^(?:GET|HEAD)$/,We=/^\/\//,$e={},Ye={},Ge="*/".concat("*"),Xe=o.createElement("a");function addToPrefiltersOrTransports(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,a=t.toLowerCase().match(N)||[];if(b(n))for(;r=a[i++];)"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function inspectPrefiltersOrTransports(e,t,n,r){var i={},a=e===Ye;function inspect(o){var s;return i[o]=!0,w.each(e[o]||[],function(e,o){var l=o(t,n,r);return"string"!=typeof l||a||i[l]?a?!(s=l):void 0:(t.dataTypes.unshift(l),inspect(l),!1)}),s}return inspect(t.dataTypes[0])||!i["*"]&&inspect("*")}function ajaxExtend(e,t){var n,r,i=w.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&w.extend(!0,e,r),e}Xe.href=De.href,w.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:De.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(De.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Ge,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":w.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?ajaxExtend(ajaxExtend(e,w.ajaxSettings),t):ajaxExtend(w.ajaxSettings,e)},ajaxPrefilter:addToPrefiltersOrTransports($e),ajaxTransport:addToPrefiltersOrTransports(Ye),ajax:function(e,t){"object"==typeof e&&(t=e,e=void 0),t=t||{};var r,i,a,s,l,u,c,f,h,d,p=w.ajaxSetup({},t),g=p.context||p,m=p.context&&(g.nodeType||g.jquery)?w(g):w.event,v=w.Deferred(),b=w.Callbacks("once memory"),y=p.statusCode||{},_={},x={},k="canceled",C={readyState:0,getResponseHeader:function(e){var t;if(c){if(!s)for(s={};t=He.exec(a);)s[t[1].toLowerCase()+" "]=(s[t[1].toLowerCase()+" "]||[]).concat(t[2]);t=s[e.toLowerCase()+" "]}return null==t?null:t.join(", ")},getAllResponseHeaders:function(){return c?a:null},setRequestHeader:function(e,t){return null==c&&(e=x[e.toLowerCase()]=x[e.toLowerCase()]||e,_[e]=t),this},overrideMimeType:function(e){return null==c&&(p.mimeType=e),this},statusCode:function(e){var t;if(e)if(c)C.always(e[C.status]);else for(t in e)y[t]=[y[t],e[t]];return this},abort:function(e){var t=e||k;return r&&r.abort(t),done(0,t),this}};if(v.promise(C),p.url=((e||p.url||De.href)+"").replace(We,De.protocol+"//"),p.type=t.method||t.type||p.method||p.type,p.dataTypes=(p.dataType||"*").toLowerCase().match(N)||[""],null==p.crossDomain){u=o.createElement("a");try{u.href=p.url,u.href=u.href,p.crossDomain=Xe.protocol+"//"+Xe.host!=u.protocol+"//"+u.host}catch(e){p.crossDomain=!0}}if(p.data&&p.processData&&"string"!=typeof p.data&&(p.data=w.param(p.data,p.traditional)),inspectPrefiltersOrTransports($e,p,t,C),c)return C;for(h in(f=w.event&&p.global)&&0==w.active++&&w.event.trigger("ajaxStart"),p.type=p.type.toUpperCase(),p.hasContent=!qe.test(p.type),i=p.url.replace(Ue,""),p.hasContent?p.data&&p.processData&&0===(p.contentType||"").indexOf("application/x-www-form-urlencoded")&&(p.data=p.data.replace(ze,"+")):(d=p.url.slice(i.length),p.data&&(p.processData||"string"==typeof p.data)&&(i+=(je.test(i)?"&":"?")+p.data,delete p.data),!1===p.cache&&(i=i.replace(Fe,"$1"),d=(je.test(i)?"&":"?")+"_="+Ve+++d),p.url=i+d),p.ifModified&&(w.lastModified[i]&&C.setRequestHeader("If-Modified-Since",w.lastModified[i]),w.etag[i]&&C.setRequestHeader("If-None-Match",w.etag[i])),(p.data&&p.hasContent&&!1!==p.contentType||t.contentType)&&C.setRequestHeader("Content-Type",p.contentType),C.setRequestHeader("Accept",p.dataTypes[0]&&p.accepts[p.dataTypes[0]]?p.accepts[p.dataTypes[0]]+("*"!==p.dataTypes[0]?", "+Ge+"; q=0.01":""):p.accepts["*"]),p.headers)C.setRequestHeader(h,p.headers[h]);if(p.beforeSend&&(!1===p.beforeSend.call(g,C,p)||c))return C.abort();if(k="abort",b.add(p.complete),C.done(p.success),C.fail(p.error),r=inspectPrefiltersOrTransports(Ye,p,t,C)){if(C.readyState=1,f&&m.trigger("ajaxSend",[C,p]),c)return C;p.async&&p.timeout>0&&(l=n.setTimeout(function(){C.abort("timeout")},p.timeout));try{c=!1,r.send(_,done)}catch(e){if(c)throw e;done(-1,e)}}else done(-1,"No Transport");function done(e,t,o,s){var u,h,d,_,x,k=t;c||(c=!0,l&&n.clearTimeout(l),r=void 0,a=s||"",C.readyState=e>0?4:0,u=e>=200&&e<300||304===e,o&&(_=function(e,t,n){for(var r,i,a,o,s=e.contents,l=e.dataTypes;"*"===l[0];)l.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){l.unshift(i);break}if(l[0]in n)a=l[0];else{for(i in n){if(!l[0]||e.converters[i+" "+l[0]]){a=i;break}o||(o=i)}a=a||o}if(a)return a!==l[0]&&l.unshift(a),n[a]}(p,C,o)),_=function(e,t,n,r){var i,a,o,s,l,u={},c=e.dataTypes.slice();if(c[1])for(o in e.converters)u[o.toLowerCase()]=e.converters[o];for(a=c.shift();a;)if(e.responseFields[a]&&(n[e.responseFields[a]]=t),!l&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),l=a,a=c.shift())if("*"===a)a=l;else if("*"!==l&&l!==a){if(!(o=u[l+" "+a]||u["* "+a]))for(i in u)if((s=i.split(" "))[1]===a&&(o=u[l+" "+s[0]]||u["* "+s[0]])){!0===o?o=u[i]:!0!==u[i]&&(a=s[0],c.unshift(s[1]));break}if(!0!==o)if(o&&e.throws)t=o(t);else try{t=o(t)}catch(e){return{state:"parsererror",error:o?e:"No conversion from "+l+" to "+a}}}return{state:"success",data:t}}(p,_,C,u),u?(p.ifModified&&((x=C.getResponseHeader("Last-Modified"))&&(w.lastModified[i]=x),(x=C.getResponseHeader("etag"))&&(w.etag[i]=x)),204===e||"HEAD"===p.type?k="nocontent":304===e?k="notmodified":(k=_.state,h=_.data,u=!(d=_.error))):(d=k,!e&&k||(k="error",e<0&&(e=0))),C.status=e,C.statusText=(t||k)+"",u?v.resolveWith(g,[h,k,C]):v.rejectWith(g,[C,k,d]),C.statusCode(y),y=void 0,f&&m.trigger(u?"ajaxSuccess":"ajaxError",[C,p,u?h:d]),b.fireWith(g,[C,k]),f&&(m.trigger("ajaxComplete",[C,p]),--w.active||w.event.trigger("ajaxStop")))}return C},getJSON:function(e,t,n){return w.get(e,t,n,"json")},getScript:function(e,t){return w.get(e,void 0,t,"script")}}),w.each(["get","post"],function(e,t){w[t]=function(e,n,r,i){return b(n)&&(i=i||r,r=n,n=void 0),w.ajax(w.extend({url:e,type:t,dataType:i,data:n,success:r},w.isPlainObject(e)&&e))}}),w._evalUrl=function(e,t){return w.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(e){w.globalEval(e,t)}})},w.fn.extend({wrapAll:function(e){var t;return this[0]&&(b(e)&&(e=e.call(this[0])),t=w(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstElementChild;)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return b(e)?this.each(function(t){w(this).wrapInner(e.call(this,t))}):this.each(function(){var t=w(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=b(e);return this.each(function(n){w(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){w(this).replaceWith(this.childNodes)}),this}}),w.expr.pseudos.hidden=function(e){return!w.expr.pseudos.visible(e)},w.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},w.ajaxSettings.xhr=function(){try{return new n.XMLHttpRequest}catch(e){}};var Ke={0:200,1223:204},Ze=w.ajaxSettings.xhr();v.cors=!!Ze&&"withCredentials"in Ze,v.ajax=Ze=!!Ze,w.ajaxTransport(function(e){var t,r;if(v.cors||Ze&&!e.crossDomain)return{send:function(i,a){var o,s=e.xhr();if(s.open(e.type,e.url,e.async,e.username,e.password),e.xhrFields)for(o in e.xhrFields)s[o]=e.xhrFields[o];for(o in e.mimeType&&s.overrideMimeType&&s.overrideMimeType(e.mimeType),e.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest"),i)s.setRequestHeader(o,i[o]);t=function(e){return function(){t&&(t=r=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?a(0,"error"):a(s.status,s.statusText):a(Ke[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=t(),r=s.onerror=s.ontimeout=t("error"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&n.setTimeout(function(){t&&r()})},t=t("abort");try{s.send(e.hasContent&&e.data||null)}catch(e){if(t)throw e}},abort:function(){t&&t()}}}),w.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),w.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return w.globalEval(e),e}}}),w.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),w.ajaxTransport("script",function(e){var t,n;if(e.crossDomain||e.scriptAttrs)return{send:function(r,i){t=w("