diff --git a/README.md b/README.md
index 4715a28..436473f 100644
--- a/README.md
+++ b/README.md
@@ -35,137 +35,167 @@ orc-order-v2/
│ │ │ └── converter.py # 单位转换与规格处理
│ │ │
│ │ └── utils/ # 工具函数
-│ │ ├── file_utils.py # 文件操作工具
-│ │ ├── log_utils.py # 日志工具
-│ │ └── string_utils.py # 字符串处理工具
+│ │ ├── file_utils.py # 文件处理工具
+│ │ └── log_utils.py # 日志工具
│ │
-│ └── services/ # 业务服务
+│ └── services/ # 服务层
│ ├── ocr_service.py # OCR服务
-│ └── order_service.py # 订单处理服务
+│ └── excel_service.py # Excel处理服务
│
├── data/ # 数据目录
-│ ├── input/ # 输入文件
-│ ├── output/ # 输出文件
-│ └── temp/ # 临时文件
+│ ├── input/ # 输入图片目录
+│ ├── output/ # 处理结果输出目录
+│ ├── temp/ # 临时文件目录
+│ └── backup/ # 备份目录
│
├── logs/ # 日志目录
│
-├── templates/ # 模板文件
-│ └── 银豹-采购单模板.xls # 采购单模板
+├── templates/ # 模板目录
+│ └── 银豹-采购单模板.xls # Excel模板文件
│
-├── 启动器.py # 图形界面启动器
-├── run.py # 命令行入口
├── config.ini # 配置文件
-└── requirements.txt # 依赖包列表
+├── run.py # 命令行入口脚本
+├── 启动器.py # 图形界面启动器
+└── README.md # 项目说明文档
```
-### 主要模块说明
+## 安装与配置
-- **配置模块**:统一管理系统配置,支持默认值和配置文件读取
-- **OCR模块**:调用百度OCR API进行表格识别,生成Excel文件
-- **Excel处理模块**:读取OCR生成的Excel文件,提取商品信息
-- **单位转换模块**:处理商品规格和单位转换
-- **订单合并模块**:合并多个采购单为一个总单
-- **文件工具模块**:处理文件读写、路径管理等
-- **启动器**:提供图形界面操作
+### 环境要求
-## 使用方法
+- Python 3.8+
+- 百度OCR API账号及密钥
-### 环境准备
-
-1. 安装Python 3.6+
-2. 安装依赖包:
- ```
- pip install -r requirements.txt
- ```
-3. 配置百度OCR API密钥:
- - 在`config.ini`中填写您的API密钥和Secret密钥
-
-### 图形界面使用
-
-1. 运行启动器:
- ```
- python 启动器.py
- ```
-2. 使用界面上的功能按钮进行操作:
- - **OCR图像识别**:批量处理`data/input`目录下的图片
- - **处理单个图片**:选择并处理单个图片
- - **处理Excel文件**:处理OCR识别后的Excel文件,生成采购单
- - **合并采购单**:合并所有生成的采购单
- - **完整处理流程**:按顺序执行所有处理步骤
- - **整理项目文件**:整理文件到规范目录结构
-
-### 命令行使用
-
-系统提供命令行方式调用,便于集成到自动化流程中:
+### 安装依赖
```bash
-# OCR识别
-python run.py ocr [--input 图片路径] [--batch]
-
-# Excel处理
-python run.py excel [--input Excel文件路径]
-
-# 订单合并
-python run.py merge [--input 采购单文件路径列表]
-
-# 完整流程
-python run.py pipeline
+pip install -r requirements.txt
```
-## 文件处理流程
+### 配置文件
-1. **OCR识别处理**:
- - 读取`data/input`目录下的图片文件
- - 调用百度OCR API进行表格识别
- - 保存识别结果为Excel文件到`data/output`目录
-
-2. **Excel处理**:
- - 读取OCR识别生成的Excel文件
- - 提取商品信息(条码、名称、规格、单价、数量等)
- - 按照采购单模板格式生成标准采购单Excel文件
- - 输出文件命名为"采购单_原文件名.xls"
-
-3. **采购单合并**:
- - 读取所有采购单Excel文件
- - 合并相同商品的数量
- - 生成总采购单
-
-## 配置说明
-
-系统配置文件`config.ini`包含以下主要配置:
+在`config.ini`中配置以下信息:
```ini
-[API]
-api_key = 您的百度API Key
-secret_key = 您的百度Secret Key
-timeout = 30
-max_retries = 3
-retry_delay = 2
-api_url = https://aip.baidubce.com/rest/2.0/ocr/v1/table
+[OCR]
+api_key = 你的百度OCR API Key
+secret_key = 你的百度OCR Secret Key
[Paths]
input_folder = data/input
output_folder = data/output
-temp_folder = data/temp
-
-[Performance]
-max_workers = 4
-batch_size = 5
-skip_existing = true
-
-[File]
-allowed_extensions = .jpg,.jpeg,.png,.bmp
-excel_extension = .xlsx
-max_file_size_mb = 4
+template_file = templates/银豹-采购单模板.xls
```
+## 使用方法
+
+### 图形界面
+
+运行`启动器.py`启动图形界面:
+
+```bash
+python 启动器.py
+```
+
+图形界面包括以下功能:
+- **处理单个文件**:选择并处理单个图片文件
+- **批量处理**:处理data/input目录中的所有图片文件
+- **合并处理**:合并多个采购单
+- **清理文件**:清理input和output目录中的文件
+- **查看日志**:实时显示处理日志
+
+### 命令行模式
+
+```bash
+# 处理单个文件
+python run.py --file=image.jpg
+
+# 批量处理目录中的所有文件
+python run.py --batch
+
+# 合并采购单
+python run.py --merge
+```
+
+## 单位处理规则
+
+系统支持多种单位的智能处理,自动识别和转换不同的计量单位。单位处理逻辑如下:
+
+### 标准单位处理
+
+| 单位 | 处理规则 | 示例 |
+|------|----------|------|
+| 件 | 数量×包装数量
单价÷包装数量
单位转换为"瓶" | 1件(规格1*12) → 12瓶
单价108元/件 → 9元/瓶 |
+| 箱 | 数量×包装数量
单价÷包装数量
单位转换为"瓶" | 2箱(规格1*24) → 48瓶
单价120元/箱 → 5元/瓶 |
+| 包 | 保持原数量和单位不变 | 3包 → 3包 |
+| 其他单位 | 保持原数量和单位不变 | 5瓶 → 5瓶 |
+
+### 提和盒单位特殊处理
+
+系统对"提"和"盒"单位有特殊的处理逻辑:
+
+1. 当规格是三级格式(如1*5*12)时:
+ - 按照件的计算方式处理
+ - 数量 = 原始数量 × 包装数量
+ - 单位转换为"瓶"
+ - 单价 = 原始单价 ÷ 包装数量
+
+ 示例:3提(规格1*5*12) → 36瓶
+
+2. 当规格是二级格式(如1*16)时:
+ - **保持原数量不变**
+ - **保持原单位不变**
+
+ 示例:3提(规格1*16) → 仍然是3提
+
+### 特殊条码处理
+
+系统支持对特定条码进行特殊处理,这些条码的处理规则会覆盖上述的标准单位处理规则:
+
+1. 特殊条码配置:
+ ```python
+ special_barcodes = {
+ '6925019900087': {
+ 'multiplier': 10, # 数量乘以10
+ 'target_unit': '瓶', # 目标单位
+ 'description': '特殊处理:数量*10,单位转换为瓶'
+ }
+ # 可以添加更多特殊条码的配置
+ }
+ ```
+
+2. 处理规则:
+ - 当遇到特殊条码时,无论规格是二级还是三级
+ - 无论单位是提还是盒还是件
+ - 都按照特殊条码配置进行处理
+ - 数量乘以配置的倍数
+ - 单位转换为配置的目标单位
+ - 如果有单价,单价除以配置的倍数
+
+## 智能规格推断
+
+当规格信息为空时,系统能从商品名称自动推断规格:
+
+1. 匹配"xx入"格式:
+ - 如"445水溶C血橙15入纸箱" → 规格推断为 1*15
+
+2. 匹配直接包含规格的格式:
+ - 如"500-东方树叶-绿茶1*15-纸箱装" → 规格推断为 1*15
+
+3. 匹配容量格式:
+ - 如"12.9L桶装水" → 规格推断为 12.9L*1
+
+4. 其他商品命名模式:
+ - 如"900树叶茉莉花茶12入纸箱" → 规格推断为 1*12
+ - 如"500茶π蜜桃乌龙15纸箱" → 规格推断为 1*15
+
## 注意事项
-1. 系统依赖百度OCR API,使用前请确保已配置正确的API密钥
-2. 图片质量会影响OCR识别结果,建议使用清晰的原始图片
-3. 处理大量图片时可能会受到API调用频率限制
-4. 所有处理好的文件会保存在`data/output`目录中
+1. 确保输入文件格式正确,支持jpg、png等图片格式
+2. 处理结果将输出到data/output目录下
+3. 定期清理临时文件和日志文件
+4. 及时更新百度OCR API密钥
+5. 为避免数据丢失,可使用清理功能前的备份选项
## 错误排查
diff --git a/app/core/excel/__pycache__/processor.cpython-39.pyc b/app/core/excel/__pycache__/processor.cpython-39.pyc
index 5f68473..5430a19 100644
Binary files a/app/core/excel/__pycache__/processor.cpython-39.pyc and b/app/core/excel/__pycache__/processor.cpython-39.pyc differ
diff --git a/app/core/excel/converter.py b/app/core/excel/converter.py
index 0cdc668..a0cfd39 100644
--- a/app/core/excel/converter.py
+++ b/app/core/excel/converter.py
@@ -1,30 +1,26 @@
"""
-单位转换处理模块
--------------
-提供规格和单位的处理和转换功能。
+单位转换模块
+----------
+提供单位转换功能,支持规格推断和单位自动提取。
"""
import re
-from typing import Dict, List, Optional, Tuple, Any
+import logging
+from typing import Dict, Tuple, Optional, Any, List, Union
from ..utils.log_utils import get_logger
-from ..utils.string_utils import (
- clean_string,
- extract_number,
- extract_unit,
- extract_number_and_unit,
- parse_specification
-)
logger = get_logger(__name__)
class UnitConverter:
"""
- 单位转换器:处理商品规格和单位转换
+ 单位转换器:处理不同单位之间的转换,支持从商品名称推断规格
"""
def __init__(self):
- """初始化单位转换器"""
+ """
+ 初始化单位转换器
+ """
# 特殊条码配置
self.special_barcodes = {
'6925019900087': {
@@ -32,182 +28,299 @@ class UnitConverter:
'target_unit': '瓶', # 目标单位
'description': '特殊处理:数量*10,单位转换为瓶'
}
- # 可以在这里添加更多特殊条码的配置
+ # 可以添加更多特殊条码的配置
}
- # 有效的单位列表
- self.valid_units = ['件', '箱', '包', '提', '盒', '瓶', '个', '支', '袋', '副', '桶', '罐', 'L', 'l', '升']
-
- # 需要特殊处理的单位
- self.special_units = ['件', '箱', '提', '盒']
-
- logger.info("单位转换器初始化完成")
+ # 规格推断的正则表达式模式
+ self.spec_patterns = [
+ # 1*6、1x12、1X20等格式
+ (r'(\d+)[*xX×](\d+)', r'\1*\2'),
+ # 1*5*12和1x5x12等三级格式
+ (r'(\d+)[*xX×](\d+)[*xX×](\d+)', r'\1*\2*\3'),
+ # "xx入"格式,如"12入"、"24入"
+ (r'(\d+)入', r'1*\1'),
+ # "xxL*1"或"xx升*1"格式
+ (r'([\d\.]+)[L升][*xX×]?(\d+)?', r'\1L*\2' if r'\2' else r'\1L*1'),
+ # "xxkg*1"或"xx公斤*1"格式
+ (r'([\d\.]+)(?:kg|公斤)[*xX×]?(\d+)?', r'\1kg*\2' if r'\2' else r'\1kg*1'),
+ # "xxg*1"或"xx克*1"格式
+ (r'([\d\.]+)(?:g|克)[*xX×]?(\d+)?', r'\1g*\2' if r'\2' else r'\1g*1'),
+ # "xxmL*1"或"xx毫升*1"格式
+ (r'([\d\.]+)(?:mL|毫升)[*xX×]?(\d+)?', r'\1mL*\2' if r'\2' else r'\1mL*1'),
+ ]
- def add_special_barcode(self, barcode: str, multiplier: int, target_unit: str, description: str = "") -> None:
+ def extract_unit_from_quantity(self, quantity_str: str) -> Tuple[Optional[float], Optional[str]]:
"""
- 添加特殊条码处理配置
+ 从数量字符串中提取单位
Args:
- barcode: 条码
- multiplier: 数量乘数
- target_unit: 目标单位
- description: 处理描述
+ quantity_str: 数量字符串,如"2箱"、"5件"
+
+ Returns:
+ (数量, 单位)的元组,如果无法提取则返回(None, None)
"""
- self.special_barcodes[barcode] = {
- 'multiplier': multiplier,
- 'target_unit': target_unit,
- 'description': description or f'特殊处理:数量*{multiplier},单位转换为{target_unit}'
- }
- logger.info(f"添加特殊条码配置: {barcode}, {description}")
+ if not quantity_str or not isinstance(quantity_str, str):
+ return None, None
+
+ # 匹配数字+单位格式
+ match = re.match(r'^([\d\.]+)\s*([^\d\s\.]+)$', quantity_str.strip())
+ if match:
+ try:
+ num = float(match.group(1))
+ unit = match.group(2)
+ logger.info(f"从数量提取单位: {quantity_str} -> 数量={num}, 单位={unit}")
+ return num, unit
+ except ValueError:
+ pass
+
+ return None, None
- def infer_specification_from_name(self, product_name: str) -> Optional[str]:
+ def extract_specification(self, text: str) -> Optional[str]:
"""
- 从商品名称推断规格
+ 从文本中提取规格信息
Args:
- product_name: 商品名称
+ text: 文本字符串
+
+ Returns:
+ 提取的规格字符串,如果无法提取则返回None
+ """
+ if not text or not isinstance(text, str):
+ return None
+
+ # 尝试所有模式
+ for pattern, replacement in self.spec_patterns:
+ match = re.search(pattern, text)
+ if match:
+ # 特殊处理三级格式,确保正确显示为1*5*12
+ if '*' in replacement and replacement.count('*') == 1 and len(match.groups()) >= 2:
+ result = f"{match.group(1)}*{match.group(2)}"
+ logger.info(f"提取规格: {text} -> {result}")
+ return result
+ # 特殊处理三级规格格式
+ elif '*' in replacement and replacement.count('*') == 2 and len(match.groups()) >= 3:
+ result = f"{match.group(1)}*{match.group(2)}*{match.group(3)}"
+ logger.info(f"提取三级规格: {text} -> {result}")
+ return result
+ # 一般情况
+ else:
+ result = re.sub(pattern, replacement, text)
+ logger.info(f"提取规格: {text} -> {result}")
+ return result
+
+ # 没有匹配任何模式
+ return None
+
+ def infer_specification_from_name(self, name: str) -> Optional[str]:
+ """
+ 从商品名称中推断规格
+
+ Args:
+ name: 商品名称
Returns:
推断的规格,如果无法推断则返回None
"""
- if not product_name or not isinstance(product_name, str):
+ if not name or not isinstance(name, str):
return None
+
+ # 特殊模式的名称处理
+ # 如"445水溶C血橙15入纸箱" -> "1*15"
+ pattern1 = r'.*(\d+)入'
+ match = re.match(pattern1, name)
+ if match:
+ inferred_spec = f"1*{match.group(1)}"
+ logger.info(f"从名称推断规格(入): {name} -> {inferred_spec}")
+ return inferred_spec
+
+ # 如"500-东方树叶-绿茶1*15-纸箱装" -> "1*15"
+ pattern2 = r'.*(\d+)[*xX×](\d+).*'
+ match = re.match(pattern2, name)
+ if match:
+ inferred_spec = f"{match.group(1)}*{match.group(2)}"
+ logger.info(f"从名称推断规格(直接): {name} -> {inferred_spec}")
+ return inferred_spec
+
+ # 如"12.9L桶装水" -> "12.9L*1"
+ pattern3 = r'.*?([\d\.]+)L.*'
+ match = re.match(pattern3, name)
+ if match:
+ inferred_spec = f"{match.group(1)}L*1"
+ logger.info(f"从名称推断规格(L): {name} -> {inferred_spec}")
+ return inferred_spec
+
+ # 从名称中提取规格
+ spec = self.extract_specification(name)
+ if spec:
+ return spec
- try:
- # 清理商品名称
- name = clean_string(product_name)
-
- # 1. 匹配 XX入纸箱 格式
- match = re.search(r'(\d+)入纸箱', name)
- if match:
- return f"1*{match.group(1)}"
-
- # 2. 匹配 绿茶1*15-纸箱装 格式
- match = re.search(r'(\d+)[*×xX](\d+)[-\s]?纸箱', name)
- if match:
- return f"{match.group(1)}*{match.group(2)}"
-
- # 3. 匹配 12.9L桶装水 格式
- match = re.search(r'([\d\.]+)[Ll升](?!.*[*×xX])', name)
- if match:
- return f"{match.group(1)}L*1"
-
- # 4. 匹配 商品12入纸箱 格式(数字在中间)
- match = re.search(r'\D(\d+)入\w*箱', name)
- if match:
- return f"1*{match.group(1)}"
-
- # 5. 匹配 商品15纸箱 格式(数字在中间)
- match = re.search(r'\D(\d+)\w*箱', name)
- if match:
- return f"1*{match.group(1)}"
-
- # 6. 匹配 商品1*30 格式
- match = re.search(r'(\d+)[*×xX](\d+)', name)
- if match:
- return f"{match.group(1)}*{match.group(2)}"
-
- logger.debug(f"无法从商品名称推断规格: {name}")
- return None
-
- except Exception as e:
- logger.error(f"从商品名称推断规格时出错: {e}")
- return None
-
- def extract_unit_from_quantity(self, quantity_str: str) -> Tuple[Optional[float], Optional[str]]:
+ return None
+
+ def parse_specification(self, spec: str) -> Tuple[int, int, Optional[int]]:
"""
- 从数量字符串提取单位
+ 解析规格字符串,支持1*12和1*5*12等格式
Args:
- quantity_str: 数量字符串
+ spec: 规格字符串
Returns:
- (数量, 单位)元组
+ (一级包装, 二级包装, 三级包装)元组,如果是二级包装,第三个值为None
"""
- if not quantity_str or not isinstance(quantity_str, str):
- return None, None
+ if not spec or not isinstance(spec, str):
+ return 1, 1, None
- try:
- # 清理数量字符串
- quantity_str = clean_string(quantity_str)
-
- # 提取数字和单位
- return extract_number_and_unit(quantity_str)
-
- except Exception as e:
- logger.error(f"从数量字符串提取单位时出错: {quantity_str}, 错误: {e}")
- return None, None
-
- def process_unit_conversion(self, product: Dict[str, Any]) -> Dict[str, Any]:
+ # 处理三级包装,如1*5*12
+ three_level_match = re.match(r'(\d+)[*xX×](\d+)[*xX×](\d+)', spec)
+ if three_level_match:
+ try:
+ level1 = int(three_level_match.group(1))
+ level2 = int(three_level_match.group(2))
+ level3 = int(three_level_match.group(3))
+ logger.info(f"解析三级规格: {spec} -> {level1}*{level2}*{level3}")
+ return level1, level2, level3
+ except ValueError:
+ pass
+
+ # 处理二级包装,如1*12
+ two_level_match = re.match(r'(\d+)[*xX×](\d+)', spec)
+ if two_level_match:
+ try:
+ level1 = int(two_level_match.group(1))
+ level2 = int(two_level_match.group(2))
+ logger.info(f"解析二级规格: {spec} -> {level1}*{level2}")
+ return level1, level2, None
+ except ValueError:
+ pass
+
+ # 特殊处理L/升为单位的规格,如12.5L*1
+ volume_match = re.match(r'([\d\.]+)[L升][*xX×](\d+)', spec)
+ if volume_match:
+ try:
+ volume = float(volume_match.group(1))
+ quantity = int(volume_match.group(2))
+ logger.info(f"解析容量规格: {spec} -> {volume}L*{quantity}")
+ return 1, quantity, None
+ except ValueError:
+ pass
+
+ # 默认值
+ logger.warning(f"无法解析规格: {spec},使用默认值1*1")
+ return 1, 1, None
+
+ def process_unit_conversion(self, product: Dict) -> Dict:
"""
- 处理单位转换,根据单位和规格转换数量和单价
+ 处理单位转换,按照以下规则:
+ 1. 特殊条码: 优先处理特殊条码
+ 2. "件"单位: 数量×包装数量, 单价÷包装数量, 单位转为"瓶"
+ 3. "箱"单位: 数量×包装数量, 单价÷包装数量, 单位转为"瓶"
+ 4. "提"和"盒"单位: 如果是三级规格, 按件处理; 如果是二级规格, 保持不变
+ 5. 其他单位: 保持不变
Args:
- product: 商品字典,包含条码、单位、规格、数量和单价等字段
+ product: 商品信息字典
Returns:
- 处理后的商品字典
+ 处理后的商品信息字典
"""
- # 复制商品信息,避免修改原始数据
+ # 复制原始数据,避免修改原始字典
result = product.copy()
- try:
- # 获取条码、单位、规格、数量和单价
- barcode = product.get('barcode', '')
- unit = product.get('unit', '')
- specification = product.get('specification', '')
- quantity = product.get('quantity', 0)
- price = product.get('price', 0)
-
- # 如果缺少关键信息,无法进行转换
- if not barcode or quantity == 0:
- return result
-
- # 1. 首先检查是否是特殊条码
- if barcode in self.special_barcodes:
- special_config = self.special_barcodes[barcode]
- logger.info(f"应用特殊条码配置: {barcode}, {special_config['description']}")
-
- # 应用乘数和单位转换
- result['quantity'] = quantity * special_config['multiplier']
- result['unit'] = special_config['target_unit']
-
- # 如果有单价,进行单价转换
- if price != 0:
- result['price'] = price / special_config['multiplier']
-
- return result
-
- # 2. 提取规格包装数量
- package_quantity = None
- if specification:
- package_quantity = parse_specification(specification)
-
- # 3. 处理单位转换
- if unit and unit in self.special_units and package_quantity:
- # 判断是否是三级规格(1*5*12格式)
- is_three_level = bool(re.search(r'\d+[\*xX×]\d+[\*xX×]\d+', str(specification)))
-
- # 对于"提"和"盒"单位的特殊处理
- if (unit in ['提', '盒']) and not is_three_level:
- # 二级规格:保持原数量不变
- logger.info(f"二级规格的提/盒单位,保持原状: {unit}, 规格={specification}")
- return result
-
- # 标准处理:数量×包装数量,单价÷包装数量
- logger.info(f"标准单位转换: {unit}->瓶, 规格={specification}, 包装数量={package_quantity}")
- result['quantity'] = quantity * package_quantity
- result['unit'] = '瓶'
-
- if price != 0:
- result['price'] = price / package_quantity
-
- return result
-
- # 4. 默认返回原始数据
+ barcode = result.get('barcode', '')
+ unit = result.get('unit', '')
+ quantity = result.get('quantity', 0)
+ price = result.get('price', 0)
+ specification = result.get('specification', '')
+
+ # 跳过无效数据
+ if not barcode or not quantity:
return result
- except Exception as e:
- logger.error(f"单位转换处理出错: {e}")
- # 发生错误时,返回原始数据
- return result
\ No newline at end of file
+ # 特殊条码处理
+ if barcode in self.special_barcodes:
+ special_config = self.special_barcodes[barcode]
+ multiplier = special_config.get('multiplier', 1)
+ target_unit = special_config.get('target_unit', '瓶')
+
+ # 数量乘以倍数
+ new_quantity = quantity * multiplier
+
+ # 如果有单价,单价除以倍数
+ new_price = price / multiplier if price else 0
+
+ logger.info(f"特殊条码处理: {barcode}, 数量: {quantity} -> {new_quantity}, 单价: {price} -> {new_price}, 单位: {unit} -> {target_unit}")
+
+ result['quantity'] = new_quantity
+ result['price'] = new_price
+ result['unit'] = target_unit
+ return result
+
+ # 没有规格信息,无法进行单位转换
+ if not specification:
+ return result
+
+ # 解析规格信息
+ level1, level2, level3 = self.parse_specification(specification)
+
+ # "件"单位处理
+ if unit in ['件']:
+ # 计算包装数量(二级*三级,如果无三级则仅二级)
+ packaging_count = level2 * (level3 or 1)
+
+ # 数量×包装数量
+ new_quantity = quantity * packaging_count
+
+ # 单价÷包装数量
+ new_price = price / packaging_count if price else 0
+
+ logger.info(f"件单位处理: 数量: {quantity} -> {new_quantity}, 单价: {price} -> {new_price}, 单位: 件 -> 瓶")
+
+ result['quantity'] = new_quantity
+ result['price'] = new_price
+ result['unit'] = '瓶'
+ return result
+
+ # "箱"单位处理 - 与"件"单位处理相同
+ if unit in ['箱']:
+ # 计算包装数量
+ packaging_count = level2 * (level3 or 1)
+
+ # 数量×包装数量
+ new_quantity = quantity * packaging_count
+
+ # 单价÷包装数量
+ new_price = price / packaging_count if price else 0
+
+ logger.info(f"箱单位处理: 数量: {quantity} -> {new_quantity}, 单价: {price} -> {new_price}, 单位: 箱 -> 瓶")
+
+ result['quantity'] = new_quantity
+ result['price'] = new_price
+ result['unit'] = '瓶'
+ return result
+
+ # "提"和"盒"单位处理
+ if unit in ['提', '盒']:
+ # 如果是三级规格,按件处理
+ if level3 is not None:
+ # 计算包装数量
+ packaging_count = level2 * level3
+
+ # 数量×包装数量
+ new_quantity = quantity * packaging_count
+
+ # 单价÷包装数量
+ new_price = price / packaging_count if price else 0
+
+ logger.info(f"提/盒单位(三级规格)处理: 数量: {quantity} -> {new_quantity}, 单价: {price} -> {new_price}, 单位: {unit} -> 瓶")
+
+ result['quantity'] = new_quantity
+ result['price'] = new_price
+ result['unit'] = '瓶'
+ else:
+ # 如果是二级规格,保持不变
+ logger.info(f"提/盒单位(二级规格)处理: 保持原样 数量: {quantity}, 单价: {price}, 单位: {unit}")
+
+ return result
+
+ # 其他单位保持不变
+ logger.info(f"其他单位处理: 保持原样 数量: {quantity}, 单价: {price}, 单位: {unit}")
+ return result
\ No newline at end of file
diff --git a/app/core/excel/processor.py b/app/core/excel/processor.py
index 0f95cc2..0919dad 100644
--- a/app/core/excel/processor.py
+++ b/app/core/excel/processor.py
@@ -294,33 +294,100 @@ class ExcelProcessor:
output_workbook = xlcopy(template_workbook)
output_sheet = output_workbook.get_sheet(0)
- # 填充商品信息
- start_row = 1 # 从第2行开始填充数据(索引从0开始)
+ # 先对产品按条码分组,区分正常商品和赠品
+ barcode_groups = {}
- for i, product in enumerate(products):
- row = start_row + i
+ # 遍历所有产品,按条码分组
+ logger.info(f"开始处理{len(products)} 个产品信息")
+ for product in products:
+ barcode = product.get('barcode', '')
+ if not barcode:
+ logger.warning(f"跳过无条码商品")
+ continue
- # 序号
- output_sheet.write(row, 0, i + 1)
- # 商品编码(条码)
- output_sheet.write(row, 1, product['barcode'])
- # 商品名称
- output_sheet.write(row, 2, product['name'])
- # 规格
- output_sheet.write(row, 3, product['specification'])
- # 单位
- output_sheet.write(row, 4, product['unit'])
- # 单价
- output_sheet.write(row, 5, product['price'])
- # 采购数量
- output_sheet.write(row, 6, product['quantity'])
- # 采购金额(单价 × 数量)
- amount = product['price'] * product['quantity']
- output_sheet.write(row, 7, amount)
- # 税率
- output_sheet.write(row, 8, 0)
- # 赠送量(默认为0)
- output_sheet.write(row, 9, 0)
+ # 获取数量和单价
+ quantity = product.get('quantity', 0)
+ price = product.get('price', 0)
+
+ # 判断是否为赠品(价格为0)
+ is_gift = price == 0
+
+ logger.info(f"处理商品: 条码={barcode}, 数量={quantity}, 单价={price}, 是否赠品={is_gift}")
+
+ if barcode not in barcode_groups:
+ barcode_groups[barcode] = {
+ 'normal': None, # 正常商品信息
+ 'gift_quantity': 0 # 赠品数量
+ }
+
+ if is_gift:
+ # 是赠品,累加赠品数量
+ barcode_groups[barcode]['gift_quantity'] += quantity
+ logger.info(f"发现赠品:条码{barcode}, 数量={quantity}")
+ else:
+ # 是正常商品
+ if barcode_groups[barcode]['normal'] is None:
+ barcode_groups[barcode]['normal'] = {
+ 'product': product,
+ 'quantity': quantity,
+ 'price': price
+ }
+ logger.info(f"发现正常商品:条码{barcode}, 数量={quantity}, 单价={price}")
+ else:
+ # 如果有多个正常商品记录,累加数量
+ barcode_groups[barcode]['normal']['quantity'] += quantity
+ logger.info(f"累加正常商品数量:条码{barcode}, 新增={quantity}, 累计={barcode_groups[barcode]['normal']['quantity']}")
+
+ # 如果单价不同,取平均值
+ if price != barcode_groups[barcode]['normal']['price']:
+ avg_price = (barcode_groups[barcode]['normal']['price'] + price) / 2
+ barcode_groups[barcode]['normal']['price'] = avg_price
+ logger.info(f"调整单价(取平均值):条码{barcode}, 原价={barcode_groups[barcode]['normal']['price']}, 新价={price}, 平均={avg_price}")
+
+ # 输出调试信息
+ logger.info(f"分组后共{len(barcode_groups)} 个不同条码的商品")
+ for barcode, group in barcode_groups.items():
+ if group['normal'] is not None:
+ logger.info(f"条码 {barcode} 处理结果:正常商品数量{group['normal']['quantity']},单价{group['normal']['price']},赠品数量{group['gift_quantity']}")
+ else:
+ logger.info(f"条码 {barcode} 处理结果:只有赠品,数量={group['gift_quantity']}")
+
+ # 准备填充数据
+ row_index = 1 # 从第2行开始填充(索引从0开始)
+
+ for barcode, group in barcode_groups.items():
+ # 1. 列B(1): 条码(必填)
+ output_sheet.write(row_index, 1, barcode)
+
+ if group['normal'] is not None:
+ # 有正常商品
+ product = group['normal']['product']
+
+ # 2. 列C(2): 采购量(必填) 使用正常商品的采购量
+ normal_quantity = group['normal']['quantity']
+ output_sheet.write(row_index, 2, normal_quantity)
+
+ # 3. 列D(3): 赠送量 - 添加赠品数量
+ if group['gift_quantity'] > 0:
+ output_sheet.write(row_index, 3, group['gift_quantity'])
+ logger.info(f"条码 {barcode} 填充:采购量={normal_quantity},赠品数量{group['gift_quantity']}")
+
+ # 4. 列E(4): 采购单价(必填)
+ purchase_price = group['normal']['price']
+ style = xlwt.XFStyle()
+ style.num_format_str = '0.0000'
+ output_sheet.write(row_index, 4, round(purchase_price, 4), style)
+ else:
+ # 只有赠品,没有正常商品
+ # 采购量填0,赠送量填赠品数量
+ output_sheet.write(row_index, 2, 0) # 采购量为0
+ output_sheet.write(row_index, 3, group['gift_quantity']) # 赠送量
+ output_sheet.write(row_index, 4, 0) # 单价为0
+
+ logger.info(f"条码 {barcode} 填充:仅有赠品,采购量=0,赠品数量={group['gift_quantity']}")
+
+ # 移到下一行
+ row_index += 1
# 保存文件
output_workbook.save(output_file_path)
diff --git a/data/output/processed_files.json b/data/output/processed_files.json
index b12e9e4..3cc8c6b 100644
--- a/data/output/processed_files.json
+++ b/data/output/processed_files.json
@@ -1,3 +1,3 @@
{
- "D:\\My Documents\\python\\orc-order-v2\\data\\output\\微信图片_20250227193150(1).xlsx": "D:\\My Documents\\python\\orc-order-v2\\data\\output\\采购单_微信图片_20250227193150(1)_20250502171625.xls"
+ "D:\\My Documents\\python\\orc-order-v2\\data\\output\\微信图片_20250227193150(1).xlsx": "D:\\My Documents\\python\\orc-order-v2\\data\\output\\采购单_微信图片_20250227193150(1).xls"
}
\ No newline at end of file
diff --git a/data/output/~$微信图片_20250227193150(1).xlsx b/data/output/~$微信图片_20250227193150(1).xlsx
new file mode 100644
index 0000000..1e0c8b3
Binary files /dev/null and b/data/output/~$微信图片_20250227193150(1).xlsx differ
diff --git a/data/output/微信图片_20250227193150(1).xlsx b/data/output/微信图片_20250227193150(1).xlsx
index 29e7733..a433276 100644
Binary files a/data/output/微信图片_20250227193150(1).xlsx and b/data/output/微信图片_20250227193150(1).xlsx differ
diff --git a/data/output/采购单_微信图片_20250227193150(1).xls b/data/output/采购单_微信图片_20250227193150(1).xls
new file mode 100644
index 0000000..6eb3c91
Binary files /dev/null and b/data/output/采购单_微信图片_20250227193150(1).xls differ
diff --git a/data/output/采购单_微信图片_20250227193150(1)_20250502171625.xls b/data/output/采购单_微信图片_20250227193150(1)_20250502171625.xls
deleted file mode 100644
index f068002..0000000
Binary files a/data/output/采购单_微信图片_20250227193150(1)_20250502171625.xls and /dev/null differ
diff --git a/logs/__main__.active b/logs/__main__.active
index 6ef6632..ebbed39 100644
--- a/logs/__main__.active
+++ b/logs/__main__.active
@@ -1 +1 @@
-Active since: 2025-05-02 17:16:24
\ No newline at end of file
+Active since: 2025-05-02 17:42:15
\ No newline at end of file
diff --git a/logs/__main__.log b/logs/__main__.log
index 9ccf117..877b654 100644
--- a/logs/__main__.log
+++ b/logs/__main__.log
@@ -23,3 +23,9 @@
2025-05-02 17:10:09,825 - __main__ - INFO - Excel处理成功,输出文件: D:\My Documents\python\orc-order-v2\output\采购单_微信图片_20250227193150(1)_20250502171009.xls
2025-05-02 17:16:24,478 - __main__ - INFO - 处理Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250227193150(1).xlsx
2025-05-02 17:16:25,037 - __main__ - INFO - Excel处理成功,输出文件: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250227193150(1)_20250502171625.xls
+2025-05-02 17:32:36,464 - __main__ - INFO - 处理Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250227193150(1).xlsx
+2025-05-02 17:32:37,143 - __main__ - INFO - Excel处理成功,输出文件: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250227193150(1).xls
+2025-05-02 17:40:07,689 - __main__ - INFO - 处理Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250227193150(1).xlsx
+2025-05-02 17:40:08,402 - __main__ - INFO - Excel处理成功,输出文件: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250227193150(1).xls
+2025-05-02 17:42:15,227 - __main__ - INFO - 处理Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250227193150(1).xlsx
+2025-05-02 17:42:15,838 - __main__ - INFO - Excel处理成功,输出文件: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250227193150(1).xls
diff --git a/logs/app.core.excel.converter.active b/logs/app.core.excel.converter.active
index 6ef6632..ebbed39 100644
--- a/logs/app.core.excel.converter.active
+++ b/logs/app.core.excel.converter.active
@@ -1 +1 @@
-Active since: 2025-05-02 17:16:24
\ No newline at end of file
+Active since: 2025-05-02 17:42:15
\ No newline at end of file
diff --git a/logs/app.core.excel.converter.log b/logs/app.core.excel.converter.log
index 5e93887..9b2abf2 100644
--- a/logs/app.core.excel.converter.log
+++ b/logs/app.core.excel.converter.log
@@ -28,3 +28,28 @@
2025-05-02 17:16:25,025 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15
2025-05-02 17:16:25,025 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15
2025-05-02 17:16:25,025 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15
+2025-05-02 17:32:36,462 - app.core.excel.converter - INFO - 单位转换器初始化完成
+2025-05-02 17:32:37,130 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15
+2025-05-02 17:32:37,130 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15
+2025-05-02 17:32:37,130 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15
+2025-05-02 17:32:37,130 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15
+2025-05-02 17:32:37,131 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15
+2025-05-02 17:32:37,131 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15
+2025-05-02 17:32:37,131 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15
+2025-05-02 17:40:07,687 - app.core.excel.converter - INFO - 单位转换器初始化完成
+2025-05-02 17:40:08,377 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15
+2025-05-02 17:40:08,378 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15
+2025-05-02 17:40:08,378 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15
+2025-05-02 17:40:08,379 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15
+2025-05-02 17:40:08,379 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15
+2025-05-02 17:40:08,380 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15
+2025-05-02 17:40:08,380 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15
+2025-05-02 17:42:15,226 - app.core.excel.converter - INFO - 单位转换器初始化完成
+2025-05-02 17:42:15,792 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15
+2025-05-02 17:42:15,792 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15
+2025-05-02 17:42:15,792 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15
+2025-05-02 17:42:15,793 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15
+2025-05-02 17:42:15,793 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15
+2025-05-02 17:42:15,794 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15
+2025-05-02 17:42:15,794 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15
+2025-05-02 17:42:15,794 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15
diff --git a/logs/app.core.excel.merger.active b/logs/app.core.excel.merger.active
index 6ef6632..ebbed39 100644
--- a/logs/app.core.excel.merger.active
+++ b/logs/app.core.excel.merger.active
@@ -1 +1 @@
-Active since: 2025-05-02 17:16:24
\ No newline at end of file
+Active since: 2025-05-02 17:42:15
\ No newline at end of file
diff --git a/logs/app.core.excel.merger.log b/logs/app.core.excel.merger.log
index ca4a335..03f4801 100644
--- a/logs/app.core.excel.merger.log
+++ b/logs/app.core.excel.merger.log
@@ -26,3 +26,9 @@
2025-05-02 17:10:09,224 - app.core.excel.merger - INFO - 初始化完成,模板文件: templates\银豹-采购单模板.xls
2025-05-02 17:16:24,477 - app.core.excel.merger - INFO - 初始化PurchaseOrderMerger
2025-05-02 17:16:24,478 - app.core.excel.merger - INFO - 初始化完成,模板文件: templates\银豹-采购单模板.xls
+2025-05-02 17:32:36,463 - app.core.excel.merger - INFO - 初始化PurchaseOrderMerger
+2025-05-02 17:32:36,464 - app.core.excel.merger - INFO - 初始化完成,模板文件: templates\银豹-采购单模板.xls
+2025-05-02 17:40:07,688 - app.core.excel.merger - INFO - 初始化PurchaseOrderMerger
+2025-05-02 17:40:07,688 - app.core.excel.merger - INFO - 初始化完成,模板文件: templates\银豹-采购单模板.xls
+2025-05-02 17:42:15,226 - app.core.excel.merger - INFO - 初始化PurchaseOrderMerger
+2025-05-02 17:42:15,226 - app.core.excel.merger - INFO - 初始化完成,模板文件: templates\银豹-采购单模板.xls
diff --git a/logs/app.core.excel.processor.active b/logs/app.core.excel.processor.active
index 6ef6632..ebbed39 100644
--- a/logs/app.core.excel.processor.active
+++ b/logs/app.core.excel.processor.active
@@ -1 +1 @@
-Active since: 2025-05-02 17:16:24
\ No newline at end of file
+Active since: 2025-05-02 17:42:15
\ No newline at end of file
diff --git a/logs/app.core.excel.processor.log b/logs/app.core.excel.processor.log
index c97556a..c5e6225 100644
--- a/logs/app.core.excel.processor.log
+++ b/logs/app.core.excel.processor.log
@@ -33,3 +33,80 @@
2025-05-02 17:16:25,022 - app.core.excel.processor - INFO - 列名映射结果: {'barcode': '条码', 'specification': '规格', 'quantity': '数量', 'unit': '单位', 'price': '单价'}
2025-05-02 17:16:25,025 - app.core.excel.processor - INFO - 提取到 8 个商品信息
2025-05-02 17:16:25,035 - app.core.excel.processor - INFO - 采购单已保存到: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250227193150(1)_20250502171625.xls
+2025-05-02 17:32:36,461 - app.core.excel.processor - INFO - 初始化ExcelProcessor
+2025-05-02 17:32:36,463 - app.core.excel.processor - INFO - 初始化完成,模板文件: templates\银豹-采购单模板.xls
+2025-05-02 17:32:36,464 - app.core.excel.processor - INFO - 开始处理Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250227193150(1).xlsx
+2025-05-02 17:32:37,128 - app.core.excel.processor - INFO - 成功读取Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250227193150(1).xlsx, 共 11 行
+2025-05-02 17:32:37,129 - app.core.excel.processor - INFO - 列名映射结果: {'barcode': '条码', 'specification': '规格', 'quantity': '数量', 'unit': '单位', 'price': '单价'}
+2025-05-02 17:32:37,132 - app.core.excel.processor - INFO - 提取到 8 个商品信息
+2025-05-02 17:32:37,141 - app.core.excel.processor - INFO - 采购单已保存到: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250227193150(1).xls
+2025-05-02 17:40:07,686 - app.core.excel.processor - INFO - 初始化ExcelProcessor
+2025-05-02 17:40:07,688 - app.core.excel.processor - INFO - 初始化完成,模板文件: templates\银豹-采购单模板.xls
+2025-05-02 17:40:07,690 - app.core.excel.processor - INFO - 开始处理Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250227193150(1).xlsx
+2025-05-02 17:40:08,376 - app.core.excel.processor - INFO - 成功读取Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250227193150(1).xlsx, 共 11 行
+2025-05-02 17:40:08,376 - app.core.excel.processor - INFO - 列名映射结果: {'barcode': '条码', 'specification': '规格', 'quantity': '数量', 'unit': '单位', 'price': '单价'}
+2025-05-02 17:40:08,381 - app.core.excel.processor - INFO - 提取到 8 个商品信息
+2025-05-02 17:40:08,392 - app.core.excel.processor - INFO - 开始处理8 个产品信息
+2025-05-02 17:40:08,392 - app.core.excel.processor - INFO - 处理商品: 条码=6973497202346, 数量=15.0, 单价=3.6666666666666665, 是否赠品=False
+2025-05-02 17:40:08,392 - app.core.excel.processor - INFO - 发现正常商品:条码6973497202346, 数量=15.0, 单价=3.6666666666666665
+2025-05-02 17:40:08,393 - app.core.excel.processor - INFO - 处理商品: 条码=6973497202940, 数量=15.0, 单价=3.6666666666666665, 是否赠品=False
+2025-05-02 17:40:08,393 - app.core.excel.processor - INFO - 发现正常商品:条码6973497202940, 数量=15.0, 单价=3.6666666666666665
+2025-05-02 17:40:08,393 - app.core.excel.processor - INFO - 处理商品: 条码=6973497200267, 数量=15.0, 单价=3.6666666666666665, 是否赠品=False
+2025-05-02 17:40:08,393 - app.core.excel.processor - INFO - 发现正常商品:条码6973497200267, 数量=15.0, 单价=3.6666666666666665
+2025-05-02 17:40:08,393 - app.core.excel.processor - INFO - 处理商品: 条码=6973497200403, 数量=15.0, 单价=3.6666666666666665, 是否赠品=False
+2025-05-02 17:40:08,393 - app.core.excel.processor - INFO - 发现正常商品:条码6973497200403, 数量=15.0, 单价=3.6666666666666665
+2025-05-02 17:40:08,393 - app.core.excel.processor - INFO - 处理商品: 条码=6873497204449, 数量=0, 单价=65.0, 是否赠品=False
+2025-05-02 17:40:08,393 - app.core.excel.processor - INFO - 发现正常商品:条码6873497204449, 数量=0, 单价=65.0
+2025-05-02 17:40:08,393 - app.core.excel.processor - INFO - 处理商品: 条码=6973497204432, 数量=15.0, 单价=4.333333333333333, 是否赠品=False
+2025-05-02 17:40:08,394 - app.core.excel.processor - INFO - 发现正常商品:条码6973497204432, 数量=15.0, 单价=4.333333333333333
+2025-05-02 17:40:08,394 - app.core.excel.processor - INFO - 处理商品: 条码=6973497202360, 数量=15.0, 单价=0, 是否赠品=True
+2025-05-02 17:40:08,394 - app.core.excel.processor - INFO - 发现赠品:条码6973497202360, 数量=15.0
+2025-05-02 17:40:08,394 - app.core.excel.processor - INFO - 处理商品: 条码=6973497202889, 数量=15.0, 单价=0, 是否赠品=True
+2025-05-02 17:40:08,394 - app.core.excel.processor - INFO - 发现赠品:条码6973497202889, 数量=15.0
+2025-05-02 17:40:08,394 - app.core.excel.processor - INFO - 分组后共8 个不同条码的商品
+2025-05-02 17:40:08,394 - app.core.excel.processor - INFO - 条码 6973497202346 处理结果:正常商品数量15.0,单价3.6666666666666665,赠品数量0
+2025-05-02 17:40:08,394 - app.core.excel.processor - INFO - 条码 6973497202940 处理结果:正常商品数量15.0,单价3.6666666666666665,赠品数量0
+2025-05-02 17:40:08,394 - app.core.excel.processor - INFO - 条码 6973497200267 处理结果:正常商品数量15.0,单价3.6666666666666665,赠品数量0
+2025-05-02 17:40:08,394 - app.core.excel.processor - INFO - 条码 6973497200403 处理结果:正常商品数量15.0,单价3.6666666666666665,赠品数量0
+2025-05-02 17:40:08,395 - app.core.excel.processor - INFO - 条码 6873497204449 处理结果:正常商品数量0,单价65.0,赠品数量0
+2025-05-02 17:40:08,395 - app.core.excel.processor - INFO - 条码 6973497204432 处理结果:正常商品数量15.0,单价4.333333333333333,赠品数量0
+2025-05-02 17:40:08,395 - app.core.excel.processor - INFO - 条码 6973497202360 处理结果:只有赠品,数量=15.0
+2025-05-02 17:40:08,395 - app.core.excel.processor - INFO - 条码 6973497202889 处理结果:只有赠品,数量=15.0
+2025-05-02 17:40:08,395 - app.core.excel.processor - INFO - 条码 6973497202360 填充:仅有赠品,采购量=0,赠品数量=15.0
+2025-05-02 17:40:08,395 - app.core.excel.processor - INFO - 条码 6973497202889 填充:仅有赠品,采购量=0,赠品数量=15.0
+2025-05-02 17:40:08,399 - app.core.excel.processor - INFO - 采购单已保存到: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250227193150(1).xls
+2025-05-02 17:42:15,225 - app.core.excel.processor - INFO - 初始化ExcelProcessor
+2025-05-02 17:42:15,226 - app.core.excel.processor - INFO - 初始化完成,模板文件: templates\银豹-采购单模板.xls
+2025-05-02 17:42:15,228 - app.core.excel.processor - INFO - 开始处理Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250227193150(1).xlsx
+2025-05-02 17:42:15,790 - app.core.excel.processor - INFO - 成功读取Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250227193150(1).xlsx, 共 11 行
+2025-05-02 17:42:15,790 - app.core.excel.processor - INFO - 列名映射结果: {'barcode': '条码', 'specification': '规格', 'quantity': '数量', 'unit': '单位', 'price': '单价'}
+2025-05-02 17:42:15,795 - app.core.excel.processor - INFO - 提取到 8 个商品信息
+2025-05-02 17:42:15,801 - app.core.excel.processor - INFO - 开始处理8 个产品信息
+2025-05-02 17:42:15,802 - app.core.excel.processor - INFO - 处理商品: 条码=6973497202346, 数量=15.0, 单价=3.6666666666666665, 是否赠品=False
+2025-05-02 17:42:15,802 - app.core.excel.processor - INFO - 发现正常商品:条码6973497202346, 数量=15.0, 单价=3.6666666666666665
+2025-05-02 17:42:15,802 - app.core.excel.processor - INFO - 处理商品: 条码=6973497202940, 数量=15.0, 单价=3.6666666666666665, 是否赠品=False
+2025-05-02 17:42:15,802 - app.core.excel.processor - INFO - 发现正常商品:条码6973497202940, 数量=15.0, 单价=3.6666666666666665
+2025-05-02 17:42:15,802 - app.core.excel.processor - INFO - 处理商品: 条码=6973497200267, 数量=15.0, 单价=3.6666666666666665, 是否赠品=False
+2025-05-02 17:42:15,802 - app.core.excel.processor - INFO - 发现正常商品:条码6973497200267, 数量=15.0, 单价=3.6666666666666665
+2025-05-02 17:42:15,802 - app.core.excel.processor - INFO - 处理商品: 条码=6973497200403, 数量=15.0, 单价=3.6666666666666665, 是否赠品=False
+2025-05-02 17:42:15,802 - app.core.excel.processor - INFO - 发现正常商品:条码6973497200403, 数量=15.0, 单价=3.6666666666666665
+2025-05-02 17:42:15,802 - app.core.excel.processor - INFO - 处理商品: 条码=6873497204449, 数量=15.0, 单价=4.333333333333333, 是否赠品=False
+2025-05-02 17:42:15,802 - app.core.excel.processor - INFO - 发现正常商品:条码6873497204449, 数量=15.0, 单价=4.333333333333333
+2025-05-02 17:42:15,802 - app.core.excel.processor - INFO - 处理商品: 条码=6973497204432, 数量=15.0, 单价=4.333333333333333, 是否赠品=False
+2025-05-02 17:42:15,802 - app.core.excel.processor - INFO - 发现正常商品:条码6973497204432, 数量=15.0, 单价=4.333333333333333
+2025-05-02 17:42:15,802 - app.core.excel.processor - INFO - 处理商品: 条码=6973497202360, 数量=15.0, 单价=0, 是否赠品=True
+2025-05-02 17:42:15,803 - app.core.excel.processor - INFO - 发现赠品:条码6973497202360, 数量=15.0
+2025-05-02 17:42:15,803 - app.core.excel.processor - INFO - 处理商品: 条码=6973497202889, 数量=15.0, 单价=0, 是否赠品=True
+2025-05-02 17:42:15,803 - app.core.excel.processor - INFO - 发现赠品:条码6973497202889, 数量=15.0
+2025-05-02 17:42:15,803 - app.core.excel.processor - INFO - 分组后共8 个不同条码的商品
+2025-05-02 17:42:15,803 - app.core.excel.processor - INFO - 条码 6973497202346 处理结果:正常商品数量15.0,单价3.6666666666666665,赠品数量0
+2025-05-02 17:42:15,803 - app.core.excel.processor - INFO - 条码 6973497202940 处理结果:正常商品数量15.0,单价3.6666666666666665,赠品数量0
+2025-05-02 17:42:15,803 - app.core.excel.processor - INFO - 条码 6973497200267 处理结果:正常商品数量15.0,单价3.6666666666666665,赠品数量0
+2025-05-02 17:42:15,803 - app.core.excel.processor - INFO - 条码 6973497200403 处理结果:正常商品数量15.0,单价3.6666666666666665,赠品数量0
+2025-05-02 17:42:15,803 - app.core.excel.processor - INFO - 条码 6873497204449 处理结果:正常商品数量15.0,单价4.333333333333333,赠品数量0
+2025-05-02 17:42:15,803 - app.core.excel.processor - INFO - 条码 6973497204432 处理结果:正常商品数量15.0,单价4.333333333333333,赠品数量0
+2025-05-02 17:42:15,803 - app.core.excel.processor - INFO - 条码 6973497202360 处理结果:只有赠品,数量=15.0
+2025-05-02 17:42:15,833 - app.core.excel.processor - INFO - 条码 6973497202889 处理结果:只有赠品,数量=15.0
+2025-05-02 17:42:15,833 - app.core.excel.processor - INFO - 条码 6973497202360 填充:仅有赠品,采购量=0,赠品数量=15.0
+2025-05-02 17:42:15,833 - app.core.excel.processor - INFO - 条码 6973497202889 填充:仅有赠品,采购量=0,赠品数量=15.0
+2025-05-02 17:42:15,836 - app.core.excel.processor - INFO - 采购单已保存到: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250227193150(1).xls
diff --git a/logs/app.core.ocr.baidu_ocr.active b/logs/app.core.ocr.baidu_ocr.active
index eb74f98..564d157 100644
--- a/logs/app.core.ocr.baidu_ocr.active
+++ b/logs/app.core.ocr.baidu_ocr.active
@@ -1 +1 @@
-Active since: 2025-05-02 17:16:23
\ No newline at end of file
+Active since: 2025-05-02 17:42:14
\ No newline at end of file
diff --git a/logs/app.core.ocr.table_ocr.active b/logs/app.core.ocr.table_ocr.active
index eb74f98..564d157 100644
--- a/logs/app.core.ocr.table_ocr.active
+++ b/logs/app.core.ocr.table_ocr.active
@@ -1 +1 @@
-Active since: 2025-05-02 17:16:23
\ No newline at end of file
+Active since: 2025-05-02 17:42:14
\ No newline at end of file
diff --git a/logs/app.core.ocr.table_ocr.log b/logs/app.core.ocr.table_ocr.log
index 072e77c..48a95ea 100644
--- a/logs/app.core.ocr.table_ocr.log
+++ b/logs/app.core.ocr.table_ocr.log
@@ -51,3 +51,15 @@
2025-05-02 17:16:24,475 - app.core.ocr.table_ocr - INFO - 使用输出目录: D:\My Documents\python\orc-order-v2\data\output
2025-05-02 17:16:24,475 - app.core.ocr.table_ocr - INFO - 使用临时目录: D:\My Documents\python\orc-order-v2\data\temp
2025-05-02 17:16:24,475 - app.core.ocr.table_ocr - INFO - OCR处理器初始化完成,输入目录: D:\My Documents\python\orc-order-v2\data\input, 输出目录: D:\My Documents\python\orc-order-v2\data\output
+2025-05-02 17:32:36,460 - app.core.ocr.table_ocr - INFO - 使用输入目录: D:\My Documents\python\orc-order-v2\data\input
+2025-05-02 17:32:36,460 - app.core.ocr.table_ocr - INFO - 使用输出目录: D:\My Documents\python\orc-order-v2\data\output
+2025-05-02 17:32:36,460 - app.core.ocr.table_ocr - INFO - 使用临时目录: D:\My Documents\python\orc-order-v2\data\temp
+2025-05-02 17:32:36,461 - app.core.ocr.table_ocr - INFO - OCR处理器初始化完成,输入目录: D:\My Documents\python\orc-order-v2\data\input, 输出目录: D:\My Documents\python\orc-order-v2\data\output
+2025-05-02 17:40:07,685 - app.core.ocr.table_ocr - INFO - 使用输入目录: D:\My Documents\python\orc-order-v2\data\input
+2025-05-02 17:40:07,685 - app.core.ocr.table_ocr - INFO - 使用输出目录: D:\My Documents\python\orc-order-v2\data\output
+2025-05-02 17:40:07,685 - app.core.ocr.table_ocr - INFO - 使用临时目录: D:\My Documents\python\orc-order-v2\data\temp
+2025-05-02 17:40:07,686 - app.core.ocr.table_ocr - INFO - OCR处理器初始化完成,输入目录: D:\My Documents\python\orc-order-v2\data\input, 输出目录: D:\My Documents\python\orc-order-v2\data\output
+2025-05-02 17:42:15,224 - app.core.ocr.table_ocr - INFO - 使用输入目录: D:\My Documents\python\orc-order-v2\data\input
+2025-05-02 17:42:15,224 - app.core.ocr.table_ocr - INFO - 使用输出目录: D:\My Documents\python\orc-order-v2\data\output
+2025-05-02 17:42:15,224 - app.core.ocr.table_ocr - INFO - 使用临时目录: D:\My Documents\python\orc-order-v2\data\temp
+2025-05-02 17:42:15,225 - app.core.ocr.table_ocr - INFO - OCR处理器初始化完成,输入目录: D:\My Documents\python\orc-order-v2\data\input, 输出目录: D:\My Documents\python\orc-order-v2\data\output
diff --git a/logs/app.core.utils.file_utils.active b/logs/app.core.utils.file_utils.active
index eb74f98..564d157 100644
--- a/logs/app.core.utils.file_utils.active
+++ b/logs/app.core.utils.file_utils.active
@@ -1 +1 @@
-Active since: 2025-05-02 17:16:23
\ No newline at end of file
+Active since: 2025-05-02 17:42:14
\ No newline at end of file
diff --git a/logs/app.services.ocr_service.active b/logs/app.services.ocr_service.active
index eb74f98..564d157 100644
--- a/logs/app.services.ocr_service.active
+++ b/logs/app.services.ocr_service.active
@@ -1 +1 @@
-Active since: 2025-05-02 17:16:23
\ No newline at end of file
+Active since: 2025-05-02 17:42:14
\ No newline at end of file
diff --git a/logs/app.services.ocr_service.log b/logs/app.services.ocr_service.log
index 68fa34a..0234752 100644
--- a/logs/app.services.ocr_service.log
+++ b/logs/app.services.ocr_service.log
@@ -25,3 +25,9 @@
2025-05-02 17:10:09,222 - app.services.ocr_service - INFO - OCRService初始化完成
2025-05-02 17:16:24,474 - app.services.ocr_service - INFO - 初始化OCRService
2025-05-02 17:16:24,476 - app.services.ocr_service - INFO - OCRService初始化完成
+2025-05-02 17:32:36,459 - app.services.ocr_service - INFO - 初始化OCRService
+2025-05-02 17:32:36,461 - app.services.ocr_service - INFO - OCRService初始化完成
+2025-05-02 17:40:07,684 - app.services.ocr_service - INFO - 初始化OCRService
+2025-05-02 17:40:07,686 - app.services.ocr_service - INFO - OCRService初始化完成
+2025-05-02 17:42:15,222 - app.services.ocr_service - INFO - 初始化OCRService
+2025-05-02 17:42:15,225 - app.services.ocr_service - INFO - OCRService初始化完成
diff --git a/logs/app.services.order_service.active b/logs/app.services.order_service.active
index 6ef6632..ebbed39 100644
--- a/logs/app.services.order_service.active
+++ b/logs/app.services.order_service.active
@@ -1 +1 @@
-Active since: 2025-05-02 17:16:24
\ No newline at end of file
+Active since: 2025-05-02 17:42:15
\ No newline at end of file
diff --git a/logs/app.services.order_service.log b/logs/app.services.order_service.log
index 8e2cabb..4d6bbfe 100644
--- a/logs/app.services.order_service.log
+++ b/logs/app.services.order_service.log
@@ -20,3 +20,12 @@
2025-05-02 17:16:24,476 - app.services.order_service - INFO - 初始化OrderService
2025-05-02 17:16:24,478 - app.services.order_service - INFO - OrderService初始化完成
2025-05-02 17:16:24,478 - app.services.order_service - INFO - OrderService开始处理指定Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250227193150(1).xlsx
+2025-05-02 17:32:36,461 - app.services.order_service - INFO - 初始化OrderService
+2025-05-02 17:32:36,464 - app.services.order_service - INFO - OrderService初始化完成
+2025-05-02 17:32:36,464 - app.services.order_service - INFO - OrderService开始处理指定Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250227193150(1).xlsx
+2025-05-02 17:40:07,686 - app.services.order_service - INFO - 初始化OrderService
+2025-05-02 17:40:07,689 - app.services.order_service - INFO - OrderService初始化完成
+2025-05-02 17:40:07,690 - app.services.order_service - INFO - OrderService开始处理指定Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250227193150(1).xlsx
+2025-05-02 17:42:15,225 - app.services.order_service - INFO - 初始化OrderService
+2025-05-02 17:42:15,226 - app.services.order_service - INFO - OrderService初始化完成
+2025-05-02 17:42:15,227 - app.services.order_service - INFO - OrderService开始处理指定Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250227193150(1).xlsx
diff --git a/v2-优化总结.md b/v2-优化总结.md
new file mode 100644
index 0000000..0b3d436
--- /dev/null
+++ b/v2-优化总结.md
@@ -0,0 +1,72 @@
+# OCR订单处理系统 v2 版本优化总结
+
+## 主要优化点
+
+### 1. 项目结构优化
+
+- **模块化重构**:将代码按功能分为配置、核心功能、服务和CLI等模块
+- **目录结构规范化**:创建统一的data目录管理所有输入和输出文件
+- **配置集中管理**:使用ConfigManager统一管理配置,支持默认值和配置文件读取
+
+### 2. OCR功能优化
+
+- **修复百度API调用问题**:解决"无法获取请求ID"的错误
+- **改进表格识别**:优化表格结构识别,提高识别准确率
+- **增加重试机制**:添加API调用失败重试机制,提高成功率
+
+### 3. 文件处理优化
+
+- **统一文件路径**:规范化文件路径处理,使用data/input和data/output目录
+- **简化处理流程**:直接从data/input读取,处理后输出到data/output,无需中间转移
+- **文件名处理**:优化输出文件命名方式,移除时间戳,采用"采购单_原名称.xls"格式
+
+### 4. 单位转换优化
+
+- **完整的单位处理规则**:实现v1版本中所有的单位转换规则,包括:
+ - "件"和"箱"单位转换为"瓶"
+ - "提"和"盒"单位的特殊处理(区分二级和三级规格)
+ - 特殊条码的处理
+- **规格推断**:从商品名称自动推断规格,提高数据完整性
+- **单位提取**:从数量字段自动提取单位
+
+### 5. 用户界面优化
+
+- **双栏布局**:从单栏设计改为左右双栏布局,增加实时日志显示区域
+- **多线程处理**:使用多线程避免UI阻塞,提升用户体验
+- **状态反馈**:添加更详细的处理状态反馈,清晰显示处理进度
+- **文件清理功能**:增加文件清理功能,可选择性清理输入输出文件,支持文件备份
+
+### 6. 采购单处理优化
+
+- **商品合并处理**:对相同条码商品进行合并处理,累计数量
+- **赠品处理**:正确区分正常商品和赠品,分别处理
+- **条码修正**:自动修正特定错误格式的条码(如5开头改为6开头)
+- **模板填充精确定位**:确保按照银豹采购单模板的要求正确填充数据
+
+## 代码质量改进
+
+1. **代码结构清晰**:遵循单一职责原则,每个模块专注于特定功能
+2. **错误处理完善**:增加完整的异常处理和错误日志记录
+3. **代码注释充分**:添加详细的函数和类注释,便于理解和维护
+4. **类型提示**:添加Python类型提示,提高代码可读性和IDE支持
+5. **日志系统改进**:实现分级日志系统,便于调试和问题追踪
+
+## 文件管理改进
+
+1. **统一目录结构**:规范化目录结构,避免多个相似功能的目录
+2. **备份机制**:实现文件备份功能,避免意外数据丢失
+3. **清理工具**:添加文件清理工具,可选择性地清理输入和输出文件
+4. **处理记录**:保存文件处理记录,避免重复处理
+
+## 性能优化
+
+1. **减少文件操作**:优化文件读写次数,减少不必要的文件复制操作
+2. **批量处理**:支持批量模式,提高处理效率
+3. **资源释放**:及时释放文件句柄和内存资源,避免资源泄漏
+
+## 可维护性改进
+
+1. **配置外部化**:将配置参数提取到config.ini文件,便于调整
+2. **模块间低耦合**:模块之间通过明确的接口交互,降低耦合度
+3. **可扩展设计**:系统设计考虑未来扩展,如添加新的特殊条码处理规则
+4. **完整文档**:提供详细的README文档,说明系统功能和使用方法
\ No newline at end of file
diff --git a/启动器.py b/启动器.py
index 0f3287d..059bb22 100644
--- a/启动器.py
+++ b/启动器.py
@@ -283,6 +283,132 @@ def organize_project_files(log_widget):
add_to_log(log_widget, "没有需要整理的文件\n")
messagebox.showinfo("整理完成", "没有需要整理的文件。")
+def clean_data_files(log_widget):
+ """清理data目录中的文件"""
+ # 确保目录存在
+ ensure_directories()
+
+ add_to_log(log_widget, "开始清理文件...\n")
+
+ # 获取需要清理的目录
+ input_dir = os.path.abspath("data/input")
+ output_dir = os.path.abspath("data/output")
+
+ # 统计文件信息
+ input_files = [f for f in os.listdir(input_dir) if os.path.isfile(os.path.join(input_dir, f))]
+ output_files = [f for f in os.listdir(output_dir) if os.path.isfile(os.path.join(output_dir, f))]
+
+ # 显示统计信息
+ add_to_log(log_widget, f"输入目录 ({input_dir}) 共有 {len(input_files)} 个文件\n")
+ add_to_log(log_widget, f"输出目录 ({output_dir}) 共有 {len(output_files)} 个文件\n")
+
+ # 显示确认对话框
+ if not input_files and not output_files:
+ messagebox.showinfo("清理文件", "没有需要清理的文件")
+ return
+
+ confirm_message = "确定要清理以下文件吗?\n\n"
+ confirm_message += f"- 输入目录: {len(input_files)} 个文件\n"
+ confirm_message += f"- 输出目录: {len(output_files)} 个文件\n"
+ confirm_message += "\n此操作不可撤销!"
+
+ if not messagebox.askyesno("确认清理", confirm_message):
+ add_to_log(log_widget, "清理操作已取消\n")
+ return
+
+ # 清理输入目录的文件
+ files_deleted = 0
+
+ # 先提示用户选择要清理的目录
+ options = []
+ if input_files:
+ options.append(("输入目录(data/input)", input_dir))
+ if output_files:
+ options.append(("输出目录(data/output)", output_dir))
+
+ # 创建临时的选择对话框
+ dialog = tk.Toplevel()
+ dialog.title("选择要清理的目录")
+ dialog.geometry("300x200")
+ dialog.transient(log_widget.winfo_toplevel()) # 设置为主窗口的子窗口
+ dialog.grab_set() # 模态对话框
+
+ tk.Label(dialog, text="请选择要清理的目录:", font=("Arial", 12)).pack(pady=10)
+
+ # 选择变量
+ choices = {}
+ for name, path in options:
+ var = tk.BooleanVar(value=True) # 默认选中
+ choices[path] = var
+ tk.Checkbutton(dialog, text=name, variable=var, font=("Arial", 10)).pack(anchor=tk.W, padx=20, pady=5)
+
+ # 删除前备份选项
+ backup_var = tk.BooleanVar(value=False)
+ tk.Checkbutton(dialog, text="删除前备份文件", variable=backup_var, font=("Arial", 10)).pack(anchor=tk.W, padx=20, pady=5)
+
+ result = {"confirmed": False, "choices": {}, "backup": False}
+
+ def on_confirm():
+ result["confirmed"] = True
+ result["choices"] = {path: var.get() for path, var in choices.items()}
+ result["backup"] = backup_var.get()
+ dialog.destroy()
+
+ def on_cancel():
+ dialog.destroy()
+
+ # 按钮
+ button_frame = tk.Frame(dialog)
+ button_frame.pack(pady=10)
+ tk.Button(button_frame, text="确认", command=on_confirm, width=10).pack(side=tk.LEFT, padx=10)
+ tk.Button(button_frame, text="取消", command=on_cancel, width=10).pack(side=tk.LEFT, padx=10)
+
+ # 等待对话框关闭
+ dialog.wait_window()
+
+ if not result["confirmed"]:
+ add_to_log(log_widget, "清理操作已取消\n")
+ return
+
+ # 备份文件
+ if result["backup"]:
+ backup_dir = os.path.join("data", "backup", datetime.datetime.now().strftime("%Y%m%d%H%M%S"))
+ os.makedirs(backup_dir, exist_ok=True)
+ add_to_log(log_widget, f"创建备份目录: {backup_dir}\n")
+
+ for dir_path, selected in result["choices"].items():
+ if selected:
+ dir_name = os.path.basename(dir_path)
+ backup_subdir = os.path.join(backup_dir, dir_name)
+ os.makedirs(backup_subdir, exist_ok=True)
+
+ files = [f for f in os.listdir(dir_path) if os.path.isfile(os.path.join(dir_path, f))]
+ for file in files:
+ src = os.path.join(dir_path, file)
+ dst = os.path.join(backup_subdir, file)
+ try:
+ shutil.copy2(src, dst)
+ add_to_log(log_widget, f"已备份: {src} -> {dst}\n")
+ except Exception as e:
+ add_to_log(log_widget, f"备份失败: {src}, 错误: {e}\n")
+
+ # 删除所选目录的文件
+ for dir_path, selected in result["choices"].items():
+ if selected:
+ files = [f for f in os.listdir(dir_path) if os.path.isfile(os.path.join(dir_path, f))]
+ for file in files:
+ file_path = os.path.join(dir_path, file)
+ try:
+ os.remove(file_path)
+ add_to_log(log_widget, f"已删除: {file_path}\n")
+ files_deleted += 1
+ except Exception as e:
+ add_to_log(log_widget, f"删除失败: {file_path}, 错误: {e}\n")
+
+ # 显示结果
+ add_to_log(log_widget, f"清理完成,共删除 {files_deleted} 个文件\n")
+ messagebox.showinfo("清理完成", f"共删除 {files_deleted} 个文件")
+
def main():
"""主函数"""
# 确保必要的目录结构存在并转移旧目录内容
@@ -379,6 +505,15 @@ def main():
command=lambda: organize_project_files(log_text)
).pack(pady=5)
+ # 清理文件按钮
+ tk.Button(
+ buttons_frame,
+ text="清理文件",
+ width=20,
+ height=2,
+ command=lambda: clean_data_files(log_text)
+ ).pack(pady=5)
+
# 打开输入目录
tk.Button(
buttons_frame,