Commit 67a278b6 authored by 邱阿朋's avatar 邱阿朋

refactor(vc): 重构 Vendor Central 工具

- 重新设计了 GUI 界面,增加了更多功能选项和更好的用户体验
-重构了 main.py 和各个功能模块,提高了代码的可维护性和可扩展性
- 新增了日志记录功能,方便调试和追踪程序运行过程
- 优化了浏览器初始化和资源清理逻辑,提高了程序的稳定性
- 移除了未使用的 ec.py 文件,简化了项目结构
parent d097d515
# coding: utf-8
\ No newline at end of file
# coding: utf-8
import logging
from app.logger.logger import Logger
class ColoredFormatter(logging.Formatter):
def format(self, record):
# 为不同的日志级别定义一些颜色
colors = {
'DEBUG': '\033[94m', # blue
'INFO': '\033[92m', # green
'WARNING': '\033[93m', # yellow
'ERROR': '\033[91m', # red
'CRITICAL': '\033[95m' # magenta
}
# 从记录中获取原始消息
message = super().format(record)
# 如果日志级别定义了颜色,则添加颜色代码
if record.levelname in colors:
color_code = colors[record.levelname]
message = f"{color_code}{message}\033[0m"
return message
class ConsoleLog(Logger):
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(ConsoleLog, cls).__new__(cls)
cls._instance._initialize()
return cls._instance
def _initialize(self):
self.logger = logging.getLogger(__name__)
self.logger.setLevel(logging.DEBUG)
# 使用彩色格式化程序创建控制台处理程序
console_handler = logging.StreamHandler()
console_handler.setFormatter(ColoredFormatter('%(asctime)s %(message)s'))
# 将控制台处理程序添加到记录器
self.logger.addHandler(console_handler)
def info(self, arg):
self.logger.info(arg)
def debug(self, arg):
self.logger.debug(arg)
def warning(self, arg):
self.logger.warning(arg)
def error(self, arg):
self.logger.error(arg)
\ No newline at end of file
# coding: utf-8
import queue
import time
from app.logger.logger import Logger
class GuiLog(Logger):
_instance = None
_log_queue = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(GuiLog, cls).__new__(cls)
# 全局队列,用于传递日志消息
cls._queue = queue.Queue()
return cls._instance
def set_console(self, log_queue):
self._log_queue = log_queue
def info(self, arg):
self.__message("INFO", arg)
def debug(self, arg):
self.__message("DEBUG", arg)
def warning(self, arg):
self.__message("WARN", arg)
def error(self, arg):
self.__message("ERROR", arg)
def __message(self, t, message):
timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
self._log_queue.put(f"[{t}] [{timestamp}] {message}")
# coding: utf-8
from abc import ABC,abstractmethod
class Logger(ABC):
@abstractmethod
def info(self, arg):
pass
@abstractmethod
def debug(self, arg):
pass
@abstractmethod
def warning(self, arg):
pass
@abstractmethod
def error(self, arg):
pass
\ No newline at end of file
......@@ -8,17 +8,19 @@ from datetime import datetime
from decimal import Decimal
import pandas as pd
from DrissionPage import ChromiumPage as Page
from DrissionPage.errors import ElementNotFoundError
from lxml import etree
from app.helper import domain, excel, file
from app.vc import log, rdb, rabbit
from app.logger.logger import Logger
from app.vc import rdb, rabbit
from app.vc.interface import AutoInterface
from DrissionPage import ChromiumPage as Page
class Payment(AutoInterface):
def __init__(self, page: Page, country: str, payee_code: str, shop_code: str):
def __init__(self, logger: Logger, page: Page, country: str, payee_code: str, shop_code: str):
self.logger = logger
self.page = page
self.country = country
self.payeeCode = payee_code
......@@ -105,7 +107,7 @@ class Payment(AutoInterface):
except ElementNotFoundError:
if count == 3: return None
count += 1
log.warning("导出按钮不存在刷新网页")
self.logger.warning("导出按钮不存在刷新网页")
self.page.refresh()
return pd.read_csv(file_name)
......@@ -183,7 +185,6 @@ class Payment(AutoInterface):
'RESOLUTION_COST': row.xpath('string(./td[@data-column="RESOLUTION_COST"])').strip().replace('$', '')
}
# 如果字段为空则设为空字符串
data = {k: "" if not v else v for k, v in data.items()}
data_list.append(data)
......@@ -306,7 +307,7 @@ class Payment(AutoInterface):
# 拼接新的文件名
new_file_name = f"{current_datetime}_{self.country}_{file_name}"
excel.save_xls(list_data, new_file_name, "Remittance payments")
log.info(f"共计:{len(list_data)} 订单")
self.logger.info(f"共计:{len(list_data)} 订单")
all_normal_pay_data = []
all_price_pay_data = []
......@@ -318,7 +319,7 @@ class Payment(AutoInterface):
# 获取当前订单的Payee和优惠比例
vendor_payment_terms = self.__get_po_code(i, invoice_number)
log.info(vendor_payment_terms)
self.logger.info(vendor_payment_terms)
vendor = vendor_payment_terms['vendor']
deduction_points = int(vendor_payment_terms['payment_terms'])
......@@ -338,7 +339,7 @@ class Payment(AutoInterface):
self.__invoice_details(invoice_number, last_two, last_three)
# 点击争议价tab
self.page.ele("#pd").click()
log.debug("等待争议数据加载,10秒后获取表单数据")
self.logger.debug("等待争议数据加载,10秒后获取表单数据")
self.page.wait(10)
table_html = self.page.ele("#priceDiscrepancyWithDMSGridForm", timeout=5).html
# 抓取表单数据
......@@ -413,4 +414,4 @@ class Payment(AutoInterface):
# 推送数据
rabbit.send_message(push_data)
rabbit.close()
\ No newline at end of file
rabbit.close()
......@@ -2,13 +2,16 @@
# 回款明细
import pandas as pd
from app.vc import log, rabbit
from app.logger.logger import Logger
from app.vc import rabbit
from app.vc.interface import AutoInterface
class PaymentPush(AutoInterface):
def __init__(self, country: str, shop_code: str):
def __init__(self,logger: Logger, country: str, shop_code: str):
self.logger = logger
self.country = country
self.shop_code = shop_code
......@@ -47,7 +50,7 @@ class PaymentPush(AutoInterface):
# 转换为字典,orient='index' 表示以索引为键
payments_map = payments.to_dict(orient='index')
log.info(f"共计:{len(invoices)} 订单")
self.logger.info(f"共计:{len(invoices)} 订单")
rabbit.connection()
rabbit.connect(queue='refund_robot', routing_key='refund_robot', exchange='reports')
......
......@@ -4,16 +4,18 @@ import os
from datetime import datetime
import pandas as pd
from DrissionPage import ChromiumPage as Page
from DrissionPage.errors import ElementNotFoundError
from app.helper import domain, api, excel, rabbitmq, file
from app.vc import log, rabbit
from app.helper import domain, api, excel, file
from app.logger.logger import Logger
from app.vc import rabbit
from app.vc.interface import AutoInterface
from DrissionPage import ChromiumPage as Page
class ReturnGoods(AutoInterface):
def __init__(self, page: Page, country: str,shop_code: str):
def __init__(self,logger: Logger, page: Page, country: str,shop_code: str):
self.logger = logger
self.page = page
self.country = country
self.shop_code = shop_code
......@@ -41,7 +43,7 @@ class ReturnGoods(AutoInterface):
file.wait_for_downloads(file_name)
break
except ElementNotFoundError:
log.warning("元素未找到,刷新网页")
self.logger.warning("元素未找到,刷新网页")
self.page.refresh()
# 读取回退商品详情
......@@ -56,14 +58,14 @@ class ReturnGoods(AutoInterface):
# 读取list数据
list_data = pd.read_excel(file_name)
log.info(f"共计:{len(list_data)} 订单")
self.logger.info(f"共计:{len(list_data)} 订单")
new_list_data = []
i = 0
for _, data in list_data.iterrows():
i += 1
return_id = data.get('Return ID')
log.info({"index": i, "return_id": return_id})
self.logger.info({"index": i, "return_id": return_id})
# 下载退货详情表格读取数据
item_data = self.__export_item_read_data(return_id)
......
......@@ -8,13 +8,16 @@ from datetime import datetime
from urllib.parse import urlparse, parse_qs
from DrissionPage import ChromiumPage as Page
from DrissionPage.errors import ElementNotFoundError
from app.vc import log, rabbit
from app.logger.logger import Logger
from app.vc import rabbit
from app.vc.interface import AutoInterface
from app.helper import domain, file, excel, rabbitmq, api
from app.helper import domain, file, excel, api
class Spa(AutoInterface):
def __init__(self, page: Page, country: str, shop_code: str):
def __init__(self, logger: Logger, page: Page, country: str, shop_code: str):
self.logger = logger
self.page = page
self.country = country
self.shop_code = shop_code
......@@ -46,7 +49,7 @@ class Spa(AutoInterface):
continue
return report_table_html
except ElementNotFoundError:
log.warning("元素未找到,刷新网页")
self.logger.warning("元素未找到,刷新网页")
self.page.refresh()
def __export_item_read_data(self, invoice_id):
......@@ -78,7 +81,7 @@ class Spa(AutoInterface):
self.page.download(host + link, report_file_tmp_dir, show_msg=False)
is_down = file.wait_for_downloads(report_file, 60)
if is_down: break
log.warning(f"下载 {invoice_id} 失败,重新下载")
self.logger.warning(f"下载 {invoice_id} 失败,重新下载")
try:
df = pd.read_excel(report_file)
......@@ -172,7 +175,7 @@ class Spa(AutoInterface):
if not isinstance(sheet_name, str):
sheet_name = str(sheet_name)
log.info(f"开始写入 {sheet_name}, 共计 {len(data)} 条")
self.logger.info(f"开始写入 {sheet_name}, 共计 {len(data)} 条")
df = pd.DataFrame(data) # 将数据转换为 DataFrame
df.to_excel(writer, sheet_name=sheet_name, index=False)
......@@ -182,26 +185,26 @@ class Spa(AutoInterface):
with pd.ExcelWriter(new_file_name) as writer:
# 写入小数据
if sheet_data:
log.info(f"保存小数据,共计 {len(sheet_data)} 条")
self.logger.info(f"保存小数据,共计 {len(sheet_data)} 条")
self.__write_sheet(writer, sheet_data, "Sheet1")
# 写入大数据(使用多线程并行写入不同表)
if large_sheet_data:
log.info(f"保存大数据,共计 {sum(len(data) for data in large_sheet_data.values())} 条")
self.logger.info(f"保存大数据,共计 {sum(len(data) for data in large_sheet_data.values())} 条")
for sheet_name, data in large_sheet_data.items():
self.__write_sheet(writer, data, sheet_name)
# with ThreadPoolExecutor() as executor:
# for sheet_name, data in large_sheet_data.items():
# executor.submit(write_sheet, writer, data, sheet_name)
log.info(f"文件 {new_file_name} 保存完成,路径:{os.path.abspath(new_file_name)}")
self.logger.info(f"文件 {new_file_name} 保存完成,路径:{os.path.abspath(new_file_name)}")
def push_data_queue(self):
rabbit.connection()
rabbit.connect(queue='spa_robot', routing_key='spa_robot', exchange='reports')
log.info("开始读取数据....")
self.logger.info("开始读取数据....")
data_dict = pd.read_excel(self.result_file_name, sheet_name=None, keep_default_na=False, na_values=[])
log.info("开始推送消息....")
self.logger.info("开始推送消息....")
for sheet_name, values in data_dict.items():
for _, item_row in values.iterrows():
if sheet_name == "Sheet1":
......@@ -238,7 +241,7 @@ class Spa(AutoInterface):
# 读取数据列表
coop_list = pd.read_excel(file_name)
log.info(f"共计: {len(coop_list)} 条数据")
self.logger.info(f"共计: {len(coop_list)} 条数据")
sheet_data = [] # 用于保存小数据
large_sheet_data = {} # 保存大数据(需要分 Sheet)
......@@ -246,17 +249,17 @@ class Spa(AutoInterface):
for index, coop in coop_list.iterrows():
index += 1
invoice_id = coop.get("Invoice ID") # 获取发票 ID
log.info({"index": index, "invoice_id": invoice_id})
self.logger.info({"index": index, "invoice_id": invoice_id})
if not invoice_id:
log.warning(f"缺少 Invoice ID,跳过第 {index} 条数据")
self.logger.warning(f"缺少 Invoice ID,跳过第 {index} 条数据")
continue
# 获取当前发票的 item 列表
item_dict = self.__export_item_read_data(invoice_id)
if item_dict is None:
sheet_data.append(coop)
log.warning(f"{invoice_id} 暂无报告信息")
self.logger.warning(f"{invoice_id} 暂无报告信息")
continue
if len(item_dict) > 1:
......
pip.exe install -i https://mirrors.cloud.tencent.com/pypi/simple -r requirements.txt
pyinstaller -F -n amazon_vc.exe main.py
pyinstaller -F -n amazon.exe --noconsole gui.py
pyinstaller -F -n amazon_cmd.exe main.py
pyinstaller -F -n amazon_gui.exe --noconsole gui.py
pyinstaller -F -n easy_gui.exe --noconsole easy.py
rd /s /q build
del *.spec
\ No newline at end of file
# coding: utf-8
import json
import requests
import xmltodict
import pandas as pd
from datetime import datetime
class YcClient:
@classmethod
def __init__(cls, app_key, app_token):
cls.app_key = app_key
cls.app_token = app_token
cls.base_url = "http://8.210.223.221/default/svc/web-service"
@classmethod
def call_service(cls, service, params_json):
"""
调用 SOAP 服务的封装函数。
:param params_json: 请求的数据内容,Python 字典
:param service: 要调用的接口方法
:return: 响应内容
"""
# 构造 SOAP 请求的 XML 数据
payload = f"""<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www.example.org/Ec/">
<SOAP-ENV:Body>
<ns1:callService>
<appToken>{cls.app_token}</appToken>
<appKey>{cls.app_key}</appKey>
<service>{service}</service>
<paramsJson>{json.dumps(params_json)}</paramsJson>
</ns1:callService>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>"""
# 发送请求
response = requests.post(cls.base_url, data=payload, headers={"Content-Type": "text/xml;charset=UTF-8"})
# 检查响应状态码
if response.status_code != 200:
raise Exception(f"HTTP 错误: {response.status_code}, 内容: {response.text}")
# 将 XML 转换为字典
response_dict = xmltodict.parse(response.text)
resp = (response_dict.get("SOAP-ENV:Envelope", {}).
get("SOAP-ENV:Body", {}).
get("ns1:callServiceResponse", {}).
get('response', {}))
json_res = resp
# 如果 resp 是字符串,则解析为 JSON 字典
if isinstance(resp, str):
json_res = json.loads(resp)
if json_res.get("ask", "Failure") != "Success":
raise Exception(json_res.get("message", "未知错误"))
return json_res.get("data", {})
@classmethod
def get_products(cls, sku: str) -> dict:
"""
获取商品列表
:param sku: 商品编码
"""
params_json = {
"pageSize": 10,
"page": 1,
"product_sku": sku,
}
return cls.call_service("getProductList", params_json)
@classmethod
def create_asn(cls, warehouse_code, desc, tracking_number, order_item_list):
"""创建入库单"""
params_json = {
"reference_no": tracking_number, # 参考号
"warehouse_code": warehouse_code, # 目的仓
"tracking_number": tracking_number, # 跟踪号
"bulk_cargo_type": 1, # 散货类型(托)
"pallet_cnt": 1, # 散货托数量
"receiving_desc": desc, # 描述
"items": order_item_list
}
return cls.call_service("createAsn", params_json)
class Process(YcClient):
def __init__(self):
# 调用 API
key = "c906cc46bda8cea593c0b6b20eadb1f2"
token = "2832175c4ee5c6efd79e129e919f2dfd"
super().__init__(key, token)
@classmethod
def read_data(cls, file):
# 读取 Excel 文件
result = pd.read_excel(file)
# 按入库单跟踪号和 SKU 去重并汇总数量
result = result.groupby(['Shipment Request ID', 'SKU'], as_index=False).agg({'Return quantity': 'sum'})
# 组装结果数据
result_list = {}
for _, item in result.iterrows():
tracking_number = item.get('Shipment Request ID', '')
order_item = {
"product_sku": item.get('SKU', ''),
"quantity": item.get('Return quantity', 0),
"box_no": 1
}
# 初始化或追加数据
if tracking_number not in result_list:
result_list[tracking_number] = {"item": []}
result_list[tracking_number]["item"].append(order_item)
return result_list
@classmethod
def send_data(cls, code, result_list):
# 获取当前时间
desc = datetime.now().strftime("%Y-%m")
result = {}
for tracking_number, order_data in result_list.items():
try:
resp = cls.create_asn(code, desc, tracking_number, order_data["item"])
print(f"跟踪号:{tracking_number}, 入库单号:{resp.get('receiving_code')}")
result[tracking_number] = resp.get('receiving_code')
except Exception as e:
print(e)
return result
@classmethod
def save_excel(cls, file, result):
# 读取 Excel 文件
data = pd.read_excel(file)
# 通过映射方式直接添加新列 'Receiving code'
data['Receiving code'] = data['Shipment Request ID'].map(result)
# 保存修改后的数据到原文件或另一个文件
output_file = file.replace('.xlsx', '_updated.xlsx')
data.to_excel(output_file, index=False)
print(f"结果已保存到 {output_file}")
# 仓库编码和名称的映射
warehouse_map = {
"USLAX01": "美西CA仓库",
"KHDCN": "国内仓-练习使用",
"USNJ01": "美东NJ仓库",
"USWA03": "Sumner仓",
"NOVILAND": "Noviland",
"USHOU01": "美南TX仓库",
"JPTOKYO01": "日本关东仓库",
"CNFUJ01": "CNFUJ01",
}
if __name__ == '__main__':
for warehouse_code, warehouse_name in warehouse_map.items():
print(f"仓库编码:{warehouse_code} , 名称:{warehouse_name}")
while True:
# 提示用户输入
warehouse_code = input("\n请输入仓库编码:").strip()
# 根据输入查找仓库名称
warehouse_name = warehouse_map.get(warehouse_code)
if warehouse_name:
break
else:
print(f"仓库编码 [{warehouse_code}] 未找到对应的仓库名称,请重新输入。")
print(f"\n仓库编码 [{warehouse_code}] 对应的仓库名称是:{warehouse_name}")
# 提示用户输入文件路径
file_path = input("请输入 Excel 文件路径:").strip()
process = Process()
# 处理文件
data_list = process.read_data(file_path)
# 发送数据
send_result = process.send_data(warehouse_code, data_list)
# 保存结果到 Excel 文件
process.save_excel(file_path, send_result)
# coding: utf-8
import json
import queue
import threading
from datetime import datetime
from tkinter import filedialog, scrolledtext
import pandas as pd
import requests
import ttkbootstrap as ttk
import xmltodict
from ttkbootstrap.constants import *
# 原有的YcClient和Process类重构为实例方法
class YcClient:
def __init__(self, app_key, app_token):
self.app_key = app_key
self.app_token = app_token
self.base_url = "http://8.210.223.221/default/svc/web-service"
def call_service(self, service, params_json):
"""调用SOAP服务的封装函数"""
payload = f"""<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www.example.org/Ec/">
<SOAP-ENV:Body>
<ns1:callService>
<appToken>{self.app_token}</appToken>
<appKey>{self.app_key}</appKey>
<service>{service}</service>
<paramsJson>{json.dumps(params_json)}</paramsJson>
</ns1:callService>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>"""
response = requests.post(
self.base_url,
data=payload,
headers={"Content-Type": "text/xml;charset=UTF-8"}
)
if response.status_code != 200:
raise Exception(f"HTTP错误: {response.status_code}, 内容: {response.text}")
response_dict = xmltodict.parse(response.text)
resp = (response_dict.get("SOAP-ENV:Envelope", {})
.get("SOAP-ENV:Body", {})
.get("ns1:callServiceResponse", {})
.get('response', {}))
json_res = json.loads(resp) if isinstance(resp, str) else resp
if json_res.get("ask", "Failure") != "Success":
raise Exception(json_res.get("message", "未知错误"))
return json_res.get("data", {})
def get_products(self, sku: str) -> dict:
"""获取商品列表"""
params_json = {"pageSize": 10, "page": 1, "product_sku": sku}
return self.call_service("getProductList", params_json)
def create_asn(self, warehouse_code, desc, tracking_number, order_item_list):
"""创建入库单"""
params_json = {
"reference_no": tracking_number,
"warehouse_code": warehouse_code,
"tracking_number": tracking_number,
"bulk_cargo_type": 1,
"pallet_cnt": 1,
"receiving_desc": desc,
"items": order_item_list
}
return self.call_service("createAsn", params_json)
class Process(YcClient):
def __init__(self, app_key, app_token):
super().__init__(app_key, app_token)
self.log_message = None
@staticmethod
def read_data(file):
"""读取并处理Excel数据"""
result = pd.read_excel(file)
result = result.groupby(['Shipment Request ID', 'SKU'], as_index=False).agg({'Return quantity': 'sum'})
result_list = {}
for _, item in result.iterrows():
tracking_number = item['Shipment Request ID']
order_item = {
"product_sku": item['SKU'],
"quantity": item['Return quantity'],
"box_no": 1
}
result_list.setdefault(tracking_number, {"item": []})["item"].append(order_item)
return result_list
def send_data(self, warehouse_code, data_list):
"""发送数据到API"""
desc = datetime.now().strftime("%Y-%m")
result = {}
for tracking_number, order_data in data_list.items():
try:
resp = self.create_asn(warehouse_code, desc, tracking_number, order_data["item"])
result[tracking_number] = resp.get('receiving_code')
self.log_message(f"跟踪号:{tracking_number}, 入库单号:{resp.get('receiving_code')}")
except Exception as e:
self.log_message(f"错误: {str(e)}")
return result
@staticmethod
def save_excel(file, result):
"""保存结果到Excel"""
data = pd.read_excel(file)
data['Receiving code'] = data['Shipment Request ID'].map(result)
output_file = file.replace('.xlsx', '_updated.xlsx')
data.to_excel(output_file, index=False)
return output_file
class Application(ttk.Window):
def __init__(self):
super().__init__(themename="cosmo")
self.log_area = None
self.run_btn = None
self.file_entry = None
self.warehouse_combo = None
self.title("入库单创建工具")
self.geometry("600x500")
self.resizable(False, False)
# 仓库编码映射
self.warehouse_map = {
"USLAX01": "美西CA仓库",
"KHDCN": "国内仓-练习使用",
"USNJ01": "美东NJ仓库",
"USWA03": "Sumner仓",
"NOVILAND": "Noviland",
"USHOU01": "美南TX仓库",
"JPTOKYO01": "日本关东仓库",
"CNFUJ01": "CNFUJ01",
}
# 设置窗口尺寸并居中
self._center_window()
# 创建日志队列
self.log_queue = queue.Queue()
self.create_widgets()
self.after(100, self.process_log_queue)
def _center_window(self):
"""设置窗口居中"""
window_width = 600
window_height = 500
# 获取屏幕尺寸
screen_width = self.winfo_screenwidth()
screen_height = self.winfo_screenheight()
# 计算居中坐标
x = (screen_width - window_width) // 2
y = (screen_height - window_height) // 2
# 设置窗口位置
self.geometry(f"{window_width}x{window_height}+{x}+{y}")
def create_widgets(self):
"""创建界面组件"""
main_frame = ttk.Frame(self)
main_frame.pack(fill=BOTH, expand=True, padx=10, pady=10)
# 仓库选择
warehouse_frame = ttk.Labelframe(main_frame, text="仓库选择", padding=10)
warehouse_frame.pack(fill=X, pady=5)
self.warehouse_combo = ttk.Combobox(
warehouse_frame,
values=[f"{code} - {name}" for code, name in self.warehouse_map.items()],
state="readonly"
)
self.warehouse_combo.pack(fill=X, padx=5)
# 文件选择
file_frame = ttk.Labelframe(main_frame, text="Excel文件", padding=10)
file_frame.pack(fill=X, pady=5)
self.file_entry = ttk.Entry(file_frame)
self.file_entry.pack(side=LEFT, fill=X, expand=True, padx=5)
ttk.Button(file_frame, text="浏览", command=self.select_file).pack(side=LEFT, padx=5)
# 控制按钮
btn_frame = ttk.Frame(main_frame)
btn_frame.pack(fill=X, pady=10)
self.run_btn = ttk.Button(btn_frame, text="开始执行", command=self.start_process, style=PRIMARY)
self.run_btn.pack(pady=5)
# 日志显示
log_frame = ttk.Labelframe(main_frame, text="操作日志", padding=10)
log_frame.pack(fill=BOTH, expand=True)
self.log_area = scrolledtext.ScrolledText(log_frame, state=DISABLED)
self.log_area.pack(fill=BOTH, expand=True)
def select_file(self):
"""选择Excel文件"""
path = filedialog.askopenfilename(filetypes=[("Excel文件", "*.xlsx *.xls")])
if path:
self.file_entry.delete(0, END)
self.file_entry.insert(0, path)
def start_process(self):
"""启动处理线程"""
app_key = "c906cc46bda8cea593c0b6b20eadb1f2"
app_token = "2832175c4ee5c6efd79e129e919f2dfd"
warehouse = self.warehouse_combo.get().split(" - ")[0]
file_path = self.file_entry.get().strip()
if not warehouse:
self.log("错误:请选择仓库")
return
if not file_path:
self.log("错误:请选择Excel文件")
return
self.run_btn.config(state=DISABLED)
threading.Thread(
target=self.process_data,
args=(app_key, app_token, warehouse, file_path),
daemon=True
).start()
def process_data(self, app_key, app_token, warehouse, file_path):
"""后台数据处理线程"""
try:
processor = Process(app_key, app_token)
processor.log_message = self.log # 传递日志方法
self.log("开始处理数据...")
data_list = processor.read_data(file_path)
if not data_list:
self.log("没有需要处理的数据")
return
self.log("开始创建入库单...")
result = processor.send_data(warehouse, data_list)
self.log("正在保存结果文件...")
output_path = processor.save_excel(file_path, result)
self.log(f"处理完成!结果已保存至:{output_path}")
except Exception as e:
self.log(f"处理出错:{str(e)}")
finally:
self.run_btn.config(state=NORMAL)
def log(self, message):
"""线程安全的日志记录"""
self.log_queue.put(message)
def process_log_queue(self):
"""处理日志队列"""
while not self.log_queue.empty():
msg = self.log_queue.get()
self.log_area.config(state=NORMAL)
self.log_area.insert(END, msg + "\n")
self.log_area.config(state=DISABLED)
self.log_area.see(END)
self.after(100, self.process_log_queue)
if __name__ == "__main__":
app = Application()
app.mainloop()
This diff is collapsed.
......@@ -2,6 +2,7 @@
import os
from app.helper import file, domain, helper
from app.helper.logger import ConsoleLog
from app.vc.payment import Payment
from app.vc.payment_push import PaymentPush
from app.vc.return_goods import ReturnGoods
......@@ -29,15 +30,17 @@ if __name__ == '__main__':
if action == "":
raise Exception("请输入要执行的功能")
logger = ConsoleLog()
if action.lower() == "payment":
payee_code = helper.get_input_with_default("回款Code: [ 详情页url参数 payeeCode ]", "VECET")
object_instate = Payment(page, country, payee_code, shop_code)
object_instate = Payment(logger, page, country, payee_code, shop_code)
if action.lower() == "payment_erp":
object_instate = PaymentPush(country, shop_code)
object_instate = PaymentPush(logger, country, shop_code)
elif action.lower() == "return":
object_instate = ReturnGoods(page, country, shop_code)
object_instate = ReturnGoods(logger, page, country, shop_code)
elif action.lower() == "spa":
object_instate = Spa(page, country, shop_code)
object_instate = Spa(logger, page, country, shop_code)
else:
raise Exception("请输入正确的功能")
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment