# 益选-OCR订单处理系统 - 技术架构文档 ## 系统架构设计 ### 整体架构概述 益选-OCR订单处理系统采用分层架构设计,遵循单一职责原则和开闭原则,确保系统的可维护性、可扩展性和可测试性。系统架构分为四个主要层次:用户界面层、业务逻辑层、核心处理层和数据访问层。 ### 架构设计原则 #### 1. 分层解耦 - 各层之间通过明确定义的接口进行通信 - 上层依赖下层,下层不依赖上层 - 层与层之间保持松耦合关系 #### 2. 模块化设计 - 每个模块具有明确的职责边界 - 模块内部高内聚,模块之间低耦合 - 支持模块的独立开发和测试 #### 3. 配置驱动 - 系统行为通过配置文件控制 - 支持运行时参数调整 - 提供灵活的配置管理机制 #### 4. 错误处理 - 统一的异常处理机制 - 详细的日志记录和错误追踪 - 优雅的错误恢复机制 ## 模块划分和职责 ### 用户界面层 (UI Layer) #### 启动器模块 (启动器.py) **职责**: - 提供图形用户界面 - 协调各个功能模块的调用 - 显示处理进度和结果 - 管理用户交互流程 **主要组件**: - `OCR订单处理系统`类:主应用程序类 - `StatusBar`类:状态栏组件 - `LogRedirector`类:日志重定向器 - 各种对话框和预览窗口 #### 命令行接口 (app/cli/) **职责**: - 提供命令行操作方式 - 支持脚本化操作 - 实现批量处理功能 **子模块**: - `ocr_cli.py`:OCR识别命令行接口 - `excel_cli.py`:Excel处理命令行接口 - `merge_cli.py`:合并功能命令行接口 ### 业务逻辑层 (Service Layer) #### OCR服务 (app/services/ocr_service.py) **职责**: - 协调OCR识别流程 - 管理OCR处理器的生命周期 - 提供OCR相关的业务逻辑 - 处理OCR结果的验证和转换 **核心方法**: - `process_image()`:处理单个图片 - `process_images_batch()`:批量处理图片 - `get_unprocessed_images()`:获取待处理图片列表 - `validate_image()`:验证图片有效性 #### 订单服务 (app/services/order_service.py) **职责**: - 处理Excel订单文件 - 提取和标准化商品信息 - 应用条码映射规则 - 执行规格单位转换 **核心功能**: - Excel文件读取和解析 - 商品信息提取和清洗 - 条码映射和转换 - 规格单位智能识别 #### 烟草服务 (app/services/tobacco_service.py) **职责**: - 专门处理烟草行业订单 - 适配烟草公司的特殊格式 - 处理烟草订单的特定规则 #### 合并服务 **职责**: - 合并多个采购单文件 - 汇总相同商品信息 - 处理合并冲突和重复项 ### 核心处理层 (Core Layer) #### OCR处理核心 (app/core/ocr/) ##### 表格OCR处理器 (table_ocr.py) **职责**: - 协调OCR识别流程 - 管理处理记录 - 控制批量处理逻辑 - 处理文件I/O操作 **核心组件**: - `OCRProcessor`类:主要的OCR处理器 - `ProcessedRecordManager`类:处理记录管理器 **处理流程**: 1. 图片验证和预处理 2. 调用百度OCR API进行识别 3. 解析OCR返回结果 4. 生成Excel文件 5. 更新处理记录 ##### 百度OCR客户端 (baidu_ocr.py) **职责**: - 封装百度OCR API调用 - 处理API认证和授权 - 管理API请求和响应 - 实现重试和错误处理机制 **核心功能**: - API密钥管理 - 请求签名生成 - 表格识别API调用 - 结果获取和解析 #### Excel处理核心 (app/core/excel/) ##### Excel处理器 **职责**: - 读取和解析Excel文件 - 提取商品信息 - 数据清洗和标准化 - 生成标准采购单格式 **处理逻辑**: 1. 读取Excel文件 2. 识别商品数据区域 3. 提取商品属性(条码、名称、规格、数量、单价) 4. 应用数据清洗规则 5. 生成标准化输出 ##### 单位转换器 (converter.py) **职责**: - 智能识别商品规格单位 - 执行单位换算 - 处理复杂的规格描述 **支持的单位**: - 数量单位:个、只、条、包、箱、件等 - 重量单位:克、千克、斤、公斤等 - 体积单位:毫升、升、立方米等 #### 工具模块 (app/core/utils/) ##### 文件工具 (file_utils.py) **职责**: - 文件系统操作封装 - 路径处理和验证 - 文件类型检查 - 批量文件操作 ##### 日志工具 (log_utils.py) **职责**: - 日志配置和管理 - 日志级别控制 - 日志文件轮转 - 错误追踪和记录 ##### 对话框工具 (dialog_utils.py) **职责**: - 自定义对话框实现 - 用户交互界面组件 - 配置界面管理 ### 数据访问层 (Data Access Layer) #### 配置管理 (app/config/) ##### 配置管理器 (settings.py) **职责**: - 配置文件加载和解析 - 配置项访问和修改 - 配置验证和默认值处理 - 配置持久化 ##### 默认配置 (defaults.py) **职责**: - 定义系统默认配置 - 提供配置模板 - 确保配置完整性 #### 数据存储 ##### 文件系统接口 - **输入目录**:`data/input/` - 存放待处理的图片文件 - **输出目录**:`data/output/` - 存放处理结果和生成的Excel文件 - **临时目录**:`data/temp/` - 存放临时文件 - **模板目录**:`templates/` - 存放Excel模板文件 - **配置目录**:`config/` - 存放配置文件和映射规则 ##### 处理记录管理 - **JSON记录文件**:`data/output/processed_files.json` - **记录内容**:已处理文件的映射关系 - **更新机制**:处理完成后自动更新记录 ## 核心算法和流程 ### OCR识别算法流程 ```mermaid graph TD A[开始] --> B[图片验证] B --> C{图片有效?} C -->|是| D[检查是否已处理] C -->|否| E[返回错误] D --> F{已处理?} F -->|是| G[返回现有结果] F -->|否| H[调用百度OCR API] H --> I[解析OCR结果] I --> J[生成Excel文件] J --> K[更新处理记录] K --> L[返回成功] E --> M[结束] G --> M L --> M ``` #### 图片验证算法 ```python def validate_image(image_path: str) -> bool: # 1. 文件存在性检查 if not os.path.exists(image_path): return False # 2. 文件扩展名验证 ext = get_file_extension(image_path) if ext not in ALLOWED_EXTENSIONS: return False # 3. 文件大小检查 if not is_file_size_valid(image_path, MAX_SIZE_MB): return False # 4. 图片格式验证(可选) try: with Image.open(image_path) as img: img.verify() except: return False return True ``` #### OCR结果解析算法 ```python def parse_ocr_result(ocr_response: dict) -> dict: result = { 'tables': [], 'text': '', 'excel_data': None } # 1. 提取表格数据 if 'tables_result' in ocr_response: for table in ocr_response['tables_result']: table_data = extract_table_data(table) result['tables'].append(table_data) # 2. 提取文本内容 if 'words_result' in ocr_response: result['text'] = extract_text_content(ocr_response['words_result']) # 3. 提取Excel数据 excel_base64 = find_excel_data(ocr_response) if excel_base64: result['excel_data'] = base64.b64decode(excel_base64) return result ``` ### Excel处理算法流程 #### 商品信息提取算法 ```python def extract_product_info(excel_data: pd.DataFrame) -> List[Dict]: products = [] # 1. 识别表头行 header_row = identify_header_row(excel_data) # 2. 确定列映射 column_mapping = map_columns(excel_data.iloc[header_row]) # 3. 提取商品数据 for row_idx in range(header_row + 1, len(excel_data)): row_data = excel_data.iloc[row_idx] product = { 'barcode': extract_barcode(row_data, column_mapping), 'name': extract_product_name(row_data, column_mapping), 'specification': extract_specification(row_data, column_mapping), 'quantity': extract_quantity(row_data, column_mapping), 'unit_price': extract_unit_price(row_data, column_mapping), 'total_price': extract_total_price(row_data, column_mapping) } # 4. 数据验证和清洗 if validate_product(product): cleaned_product = clean_product_data(product) products.append(cleaned_product) return products ``` #### 规格单位识别算法 ```python def parse_specification(spec_text: str) -> Dict: result = { 'original': spec_text, 'quantity': 1, 'unit': '个', 'parsed': False } # 1. 预定义单位模式 unit_patterns = { r'(\d+)\s*个': ('个', 1), r'(\d+)\s*只': ('只', 1), r'(\d+)\s*条': ('条', 1), r'(\d+)\s*包': ('包', 1), r'(\d+)\s*箱': ('箱', 1), r'(\d+)\s*件': ('件', 1), r'(\d+)\s*克': ('克', 1), r'(\d+)\s*千克': ('千克', 1), r'(\d+)\s*斤': ('斤', 1), r'(\d+)\s*公斤': ('公斤', 1) } # 2. 模式匹配 for pattern, (unit, multiplier) in unit_patterns.items(): match = re.search(pattern, spec_text, re.IGNORECASE) if match: result['quantity'] = int(match.group(1)) result['unit'] = unit result['parsed'] = True break # 3. 复杂规格处理 if not result['parsed']: result = parse_complex_specification(spec_text) return result ``` ### 采购单合并算法 #### 商品汇总算法 ```python def merge_products(products_list: List[List[Dict]]) -> List[Dict]: merged_products = {} # 1. 收集所有商品 for products in products_list: for product in products: key = generate_product_key(product) if key in merged_products: # 2. 合并相同商品 merged_products[key]['quantity'] += product['quantity'] merged_products[key]['total_price'] += product['total_price'] merged_products[key]['source_files'].append(product.get('source_file', '')) else: # 3. 添加新商品 merged_products[key] = product.copy() merged_products[key]['source_files'] = [product.get('source_file', '')] # 4. 转换回列表格式 result = list(merged_products.values()) # 5. 排序(按条码或名称) result.sort(key=lambda x: x.get('barcode', x.get('name', ''))) return result ``` ## 数据流设计 ### 主要数据流 #### 1. OCR识别数据流 ``` 输入图片 → 图片验证 → OCR API调用 → 结果解析 → Excel生成 → 输出文件 ``` #### 2. Excel处理数据流 ``` Excel文件 → 数据读取 → 商品提取 → 数据清洗 → 格式转换 → 标准采购单 ``` #### 3. 合并处理数据流 ``` 多个采购单 → 商品提取 → 去重汇总 → 冲突处理 → 合并结果 → 输出文件 ``` ### 数据结构设计 #### 商品数据结构 ```python { 'barcode': str, # 商品条码 'name': str, # 商品名称 'specification': str, # 商品规格 'quantity': int, # 数量 'unit': str, # 单位 'unit_price': float, # 单价 'total_price': float, # 总价 'source_file': str, # 来源文件 'category': str, # 商品类别 'brand': str # 品牌 } ``` #### 处理记录数据结构 ```python { 'image_file': str, # 输入图片路径 'output_file': str, # 输出文件路径 'processing_time': str, # 处理时间 'status': str, # 处理状态 'error_message': str # 错误信息(如果有) } ``` #### OCR结果数据结构 ```python { 'tables': List[Dict], # 表格数据 'text': str, # 文本内容 'excel_data': bytes, # Excel文件数据 'confidence': float, # 识别置信度 'processing_time': float # 处理耗时 } ``` ## 关键技术实现 ### 并发处理机制 #### 多线程批量处理 ```python class BatchProcessor: def __init__(self, max_workers: int = 4): self.max_workers = max_workers self.executor = ThreadPoolExecutor(max_workers=max_workers) def process_batch(self, items: List[Any], processor_func) -> List[Any]: # 使用线程池并发处理 futures = [self.executor.submit(processor_func, item) for item in items] # 收集处理结果 results = [] for future in as_completed(futures): try: result = future.result() results.append(result) except Exception as e: logger.error(f"处理失败: {e}") results.append(None) return results ``` ### 错误处理和重试机制 #### API调用重试机制 ```python def call_with_retry(func, max_retries=3, retry_delay=2): for attempt in range(max_retries): try: result = func() return result except Exception as e: logger.warning(f"第{attempt + 1}次尝试失败: {e}") if attempt < max_retries - 1: time.sleep(retry_delay) else: logger.error(f"所有重试尝试都失败") raise ``` ### 内存优化策略 #### 大文件处理 ```python def process_large_file(file_path: str, chunk_size: int = 1000): # 使用生成器避免一次性加载大文件 def read_in_chunks(): with pd.read_excel(file_path, chunksize=chunk_size) as reader: for chunk in reader: yield chunk # 逐块处理 for chunk in read_in_chunks(): process_chunk(chunk) # 及时清理内存 del chunk gc.collect() ``` ### 配置管理实现 #### 动态配置加载 ```python class ConfigManager: def __init__(self, config_file: str): self.config_file = config_file self.config = configparser.ConfigParser() self.load_config() def load_config(self): if os.path.exists(self.config_file): self.config.read(self.config_file, encoding='utf-8') else: self.create_default_config() def get(self, section: str, option: str, fallback: Any = None) -> Any: return self.config.get(section, option, fallback=fallback) def getint(self, section: str, option: str, fallback: int = 0) -> int: return self.config.getint(section, option, fallback=fallback) def getboolean(self, section: str, option: str, fallback: bool = False) -> bool: return self.config.getboolean(section, option, fallback=fallback) ``` ### 日志系统设计 #### 结构化日志记录 ```python import logging from logging.handlers import RotatingFileHandler def setup_logging(log_file: str = 'logs/app.log'): # 创建logger logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) # 创建文件处理器(带轮转) file_handler = RotatingFileHandler( log_file, maxBytes=10*1024*1024, backupCount=5 ) file_handler.setLevel(logging.DEBUG) # 创建控制台处理器 console_handler = logging.StreamHandler() console_handler.setLevel(logging.INFO) # 创建格式化器 formatter = logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) # 添加格式化器到处理器 file_handler.setFormatter(formatter) console_handler.setFormatter(formatter) # 添加处理器到logger logger.addHandler(file_handler) logger.addHandler(console_handler) return logger ``` ### 性能监控和优化 #### 处理时间统计 ```python import time from functools import wraps def timing_decorator(func): @wraps(func) def wrapper(*args, **kwargs): start_time = time.time() result = func(*args, **kwargs) end_time = time.time() processing_time = end_time - start_time logger.info(f"{func.__name__} 执行耗时: {processing_time:.2f}秒") return result return wrapper ``` ### 安全性考虑 #### API密钥管理 ```python import os from cryptography.fernet import Fernet class SecureConfig: def __init__(self, encryption_key: str = None): self.cipher = Fernet(encryption_key or self._get_or_create_key()) def _get_or_create_key(self) -> str: key_file = 'config/.key' if os.path.exists(key_file): with open(key_file, 'rb') as f: return f.read() else: key = Fernet.generate_key() with open(key_file, 'wb') as f: f.write(key) return key def encrypt(self, data: str) -> str: return self.cipher.encrypt(data.encode()).decode() def decrypt(self, encrypted_data: str) -> str: return self.cipher.decrypt(encrypted_data.encode()).decode() ``` 这个技术架构文档详细描述了益选-OCR订单处理系统的技术实现细节,包括系统架构设计、模块职责划分、核心算法流程、数据流设计以及关键技术实现。系统设计遵循软件工程最佳实践,确保系统的可靠性、可维护性和可扩展性。