From 7b7d491663920cc457cf1913fbec52443b974344 Mon Sep 17 00:00:00 2001 From: houhuan Date: Thu, 8 May 2025 21:16:58 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=B9=8B=E5=90=8E=EF=BC=8C?= =?UTF-8?q?=E6=88=91=E4=B9=9F=E4=B8=8D=E7=9F=A5=E9=81=93=E6=9C=89=E6=B2=A1?= =?UTF-8?q?=E6=9C=89=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 21 + .../__pycache__/converter.cpython-39.pyc | Bin 12176 -> 11124 bytes .../__pycache__/processor.cpython-39.pyc | Bin 21671 -> 21824 bytes .../__pycache__/validators.cpython-39.pyc | Bin 0 -> 4932 bytes app/core/excel/converter.py | 203 +++------- app/core/excel/handlers/__init__.py | 11 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 552 bytes .../__pycache__/barcode_mapper.cpython-39.pyc | Bin 0 -> 2167 bytes .../unit_converter_handlers.cpython-39.pyc | Bin 0 -> 7765 bytes app/core/excel/handlers/barcode_mapper.py | 83 ++++ .../excel/handlers/unit_converter_handlers.py | 284 +++++++++++++ app/core/excel/processor.py | 9 +- app/core/excel/test_converter.py | 355 ++++++++++++++++ app/core/excel/validators.py | 215 ++++++++++ logs/__main__.log | 18 + logs/app.core.excel.converter.log | 39 ++ ...app.core.excel.handlers.barcode_mapper.log | 0 ...excel.handlers.unit_converter_handlers.log | 35 ++ logs/app.core.excel.merger.log | 12 + logs/app.core.excel.processor.log | 379 ++++++++++++++++++ logs/app.core.excel.validators.log | 9 + logs/app.core.ocr.baidu_ocr.log | 1 + logs/app.core.ocr.table_ocr.log | 26 ++ logs/app.services.ocr_service.log | 11 + logs/app.services.order_service.log | 15 + v2-优化总结.md | 259 +++++++++++- 26 files changed, 1840 insertions(+), 145 deletions(-) create mode 100644 app/core/excel/__pycache__/validators.cpython-39.pyc create mode 100644 app/core/excel/handlers/__init__.py create mode 100644 app/core/excel/handlers/__pycache__/__init__.cpython-39.pyc create mode 100644 app/core/excel/handlers/__pycache__/barcode_mapper.cpython-39.pyc create mode 100644 app/core/excel/handlers/__pycache__/unit_converter_handlers.cpython-39.pyc create mode 100644 app/core/excel/handlers/barcode_mapper.py create mode 100644 app/core/excel/handlers/unit_converter_handlers.py create mode 100644 app/core/excel/test_converter.py create mode 100644 app/core/excel/validators.py create mode 100644 logs/app.core.excel.handlers.barcode_mapper.log create mode 100644 logs/app.core.excel.handlers.unit_converter_handlers.log create mode 100644 logs/app.core.excel.validators.log diff --git a/README.md b/README.md index 39cc7d2..d16cf22 100644 --- a/README.md +++ b/README.md @@ -267,6 +267,27 @@ MIT License ## 更新日志 +### v1.3 (2025-07-20) + +#### 功能优化 +- 采购单赠品处理逻辑优化:修改了银豹采购单中赠品的处理方式 + - ~~之前:赠品数量单独填写在"赠送量"列,与正常采购量分开处理~~ + - ~~现在:将赠品数量合并到采购量中,赠送量列留空~~ + - ~~有正常商品且有赠品的情况:采购量 = 正常商品数量 + 赠品数量,单价 = 原单价 × 正常商品数量 ÷ 总数量~~ + - ~~只有赠品的情况:采购量填写赠品数量,单价为0~~ +- 更新说明:经用户反馈,赠品处理逻辑已还原为原始方式,正常商品数量和赠品数量分开填写 + +### v1.2 (2025-07-15) + +#### 功能优化 +- 规格提取优化:改进了从商品名称中提取规格的逻辑,优先识别"容量*数量"格式 + - 例如从"美汁源果粒橙1.8L*8瓶"能准确提取"1.8L*8"而非错误的"1.8L*1" +- 规格解析增强:优化`parse_specification`方法,能正确解析"1.8L*8"格式规格,确保准确提取包装数量 +- 单位推断增强:在`extract_product_info`方法中增加新逻辑,当单位为空且有条码、规格、数量、单价时,根据规格格式(如容量*数量格式或简单数量*数量格式)自动推断单位为"件" +- 件单位处理优化:确保当设置单位为"件"时,正确触发UnitConverter单位处理逻辑,将数量乘以包装数量,单价除以包装数量,单位转为"瓶" +- 整体改进:提高了系统处理复杂格式商品名称和规格的能力,使单位转换更加准确可靠 +- 规格提取逻辑修正:修复了在Excel中已有规格信息时仍会从商品名称推断规格的问题,现在系统会优先使用Excel中的数据,只有在规格为空时才尝试从商品名称推断 + ### v1.1 (2025-05-07) #### 功能更新 diff --git a/app/core/excel/__pycache__/converter.cpython-39.pyc b/app/core/excel/__pycache__/converter.cpython-39.pyc index 2183d84c9be1081ed91263d06e99c8ad2ba0d5d2..3e66958453045fd7d8ca5db54f9176f130deecaa 100644 GIT binary patch delta 2346 zcma)7U2GIp6rMXXJ3IUTLutE9p}Um=+v2uJA+Z!h3bcrnpIX2H(rkC8?MipI+}$NW zX06z!7@_>+27N%|u7L;!#ZV0jl4yMJ(Fb2}^u?q<5Fd<*FD5>C&TLz@JUN^B_TF>O zz31FB=Y02a@81XAF_+8E!IK`W8SLME#a%-#UTJLKr%I}I$eDC0F2ccB9&#r=iYMt+ zyh)$pOZpXma*49U)Y*nAl1r7Pgwq^atEFG0H2|D&NgToLAoceB=oM zKM`WMw#8^Xt!jH>!^0YlSPaiAiCC&9mB@C+QtFUK4fnS6sq(q!wM3`3NYIh!&z1`u zWfO4W-AB`EE}lIY8%n6LY?{V7ZUm3{^pRk=72eEy02-$dl~Z_?D1yqX!WgHBs;FAv z-=ayHRg*PafCH2QTt6RxlzhOeR#hGo6+7rnxoQI$9$Zu$3o<9jT&f#n&XwjyF3knz zF4#tZZ9Jg%sy|{cMJOMDDV2zUbAt0$=0^mlZ}io$D3ZYN3_nSfB9A>Iw*% zE(K5={fSeW+B-}Wam{dLhP8O2KM{{*6X_JKWPRdJyCNlH!@ZD#%E`OpO@Ub05jnip zaQ7x4;k|>UjAy71b~9`z=ks-o7-B{n>c7R&TK10IB?UpB>0`giF;Vi%0^4k>?u_FH zoqupF!rKmSW)(mdqJD#n5{RA0Gh{`0E$~XC+<*vDt4g3W_}ufXNvIsTJU08u#Ps#C z*~{azH_uPszhn>1JzWu*I$%Q@05IHrrRwR0V?p^)4{DqUE(AB)xN?imwj33;3lPH5 zGJqt!8C=R)oU28Vn7@C|)L<>Or&A|2n$>7@aiS)BY~2D+?USE6*AQt1tfed2BiB~% z;{A;vJMRt>owd7rp+~S=~vR|3@Spuq5K8W+ep<+dvwGJ(cmChB2PKqQCh<%&q zb-o~|MCFToUNFbNEHD;zA=9D*DZ(1DV9|M4!|P&Z9Qs`{?L@Q#sYSQYw{?p?0)*7Y zWvvBS1#{TcG`I#Nq+70WABsgnxRhAhvS54asz{Csc`0wr%izksojX>Sx9PTm16|VV zFmmd=F6mZXE{Z&yqMJK$-SlEL_mEXvkq0mKHRjYYH-$wIf(v5==Mp+c*AaK_H2Wu# z-TC;d!ZbTuYUI|?^qp^}?q4j){`~gB?1L|7E}WgY`o)uTmmWX7Ju~s~)Wh-ViK|l& z?oEAtzj>kM*R$gdJ*0(w54eFoy&x&;*dsWNJ%?uYcq}zXu7*@1*@(T=K}XSQJHjyp^E9vG=pe!&gu?)a zb&i|MixTBkY)iDN=^`A^q*>1A6lC5>oC4670Lc4+$Yc`YMWI@-6FaFCS_q3q508X# zZzR4F^9VRX%BJfOS`o0-OUK%eBlAR9`FMerbEru}A(Z(6@G8o90c0YIa<}XWt*dCP zh_C22wUGcFKuglZq^8ahrE31ti9SQ+*iuSpL2HtDe4dA&UR8_9zH|YgbH%=JeU5qozYt9aK{0rC(21ayX~{F+k{tfAPmaY$Z*ol2$26YgDD$xRTawk8Rjq z+l(qeJyGSD8da$NBb9onF|Mb5FfR4f3p$hfKxZxMEYSIB0CaxoXGg#efO!DiXy7IY z!yy`m;h-I|Lo@>NI@$no*p)x3O=_X0&cc(t%_9yC4e~F#b07kk&Ct%yHIzxI2}FX--N7*!&`B9CK=mL?)(y& z?1m|+2$QMYC#tPoq*MH{)h73fp0>oNGCJC}naql3+ukwuVyxDgyEgt^OJGJat${J^ zNO2pBgY4j^_1M#gfB))Hwn=m+y8Y4$vt##&;lxI=OPo#IBK@MU`Ov;qN%o=@R;uQq z_K-8oI+52#@+G&3vVM1<&U)bffddEk?)k>xUX~PAd&1qay`rZ*yYX9SVSd-i=1U`` zXKKWD_s`u#@$>c;5*L4Hf2-08b~zQu(u3a^{`bJY_)nrJoK%%5rL1zbN+_Y~lv>s# zuTdR%UGg5udn9j28-p9geh%%&m8!~(DRM+Bd&_3o$2ICbrLgPVVj-W*?; zcy7LWZtmQ3a~EE@F?-?W>^t)pu6%s``5Pa;_pfJ9FHHXe=={YAz^R--w{WP58v;NJBVqnID4Tt4vym6cgGjRdn^D0l|}!Ms)MfKy|(LBZoqEQ zO-hvs!Ybt();Zu{`+;Hy2(Gu}jStQ*T)TSn>iB&1da5q%ewlY&%9x|$h53u_ov+^R z>)MMezC{+OHvzRhI>S7)7i!dl1P0 zk^?}};n8gFcy@RsKb*-GoP4Pk$lKq`EQ5s)C3l|?FDiA=p8$O!XBUf^Vkw)aSw=Gs zo*g-Pm=s3}`66s{5ko0{QwCZET_?e$fd`ku zQtzVO!(iu)0#r~hz@}0KJ<3z09O9vg3Sm2W2nHMbTqJQFHMvHJPdo%JCE6?^5w5Sf z4yjwXAy*8cX>WA---FTXu^`P$s{4*>}(5|L^Vx3lc!X1d+jMGAld z{3ZdtUOapAjk7MyeO*FPeXu1waa`nEM#Doempy`cnCv^qR}LdZKwQBFh5*AJLxP}@ z3a^HWBRI4MUZg|CQ9C!1J%N3FY-Bj?M?@IPvRr}MZl$o6>`^3XBu6Vq>n?C0)|!`E zbQMJW2_a%1SSXQYh#&wB00;qM!P_Jo2#lxz52y#|uuumypoWM6d|chR3?gG&0U5;A z)L;c+-o1I^!NPPTNhCc;@T$i!EZdJn;wavH+zLI8+{ywZ*Md5%JZ@DH@dI+y%?IN> zP1~AsUm1)%Do3HPLug4l#8}tw?sC>7faR z5rZQXnd6R%Q9*w=4@F5B6w#AD_25HKDMk_m!61k_yW2p5?!)D~zwdnC?|dIO_YjsJ zLCsiowH@)NDd!)L-g#a#pa@MidR95%cy~@PWW$!0r^eZ1WmLW>;xr5kEB$xsY(zqIkXK=`HB|WB>xHxC$ zO2KdwH+TF@+-5CSvS4^D6z0fFJPQan(>XQNu(x=ge>aj^{%#?=7SN(C3xF;y<5s$& z*29=1sVi+LHX`d7AK(*5 zslRBE<+nb#C|aqz@FBxzDSbt*sZ=Ik_&+I~@rOdTNePgl0-EYb!UYY-h>R7bP)MAp zyb1|-;7+>YZ&SLGej;20&Kxc-CBiUj>1Lk#Pr`1M|2$t_-`u!uu0Kny&)(Y1-P_2d zH*={CbE&(LZ8lt&V3q2@^Aa=)UbY$x+u;h!w~xxOhn|ToIobq-^il3I6{TabR+fmr z1;{gd=N}1#%ZZD3LmMRxjB3gA6k8}v&MWci_9fG|5Ud=dTveO2sg delta 693 zcmZuvOK4L;6rKC}UeTlp`82-fO`#gmvJWVWv9k{qo+*Lo#;pZ6&hM zSOFmCmRYx@J5%*dQ{S^)I6>q7-J98Xo3_yy56L`D6Hdwj9+De<+-Iv<8PTE?y6`&< zd1iQ1Sz~P#u+`iQ&Kf~{LjzZ6m`D{nxlgK4YbxX*#Ktgu53|3j6_|4(Ic zT#1;bcIgHX%K%d7&%g7xEp=w3aUk8tHG$jU%{Cs)QvcS_7g z-znl^>zoQNaJ~Jcm@>D!U`p7%lNv;@+V>1P#KXi7fT~zbuB(s~i|IQ6c`-a#)SxI{ z4$Z6XPYNN~N%@ZogZOc558fIs!e@+*T!$~XG;$Zd;^9#Xs`zSj4pwj?a}8GULuTH) m_J=p^m1I1hIjj6q#J595fafAN7EvK8>?1<}JH*@ZGw>TLaL+*i diff --git a/app/core/excel/__pycache__/validators.cpython-39.pyc b/app/core/excel/__pycache__/validators.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ba087fb24b287d972df4bd2c0448ae81a43990dc GIT binary patch literal 4932 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 literal 0 HcmV?d00001 diff --git a/app/core/excel/converter.py b/app/core/excel/converter.py index 04eadf9..9cd51ca 100644 --- a/app/core/excel/converter.py +++ b/app/core/excel/converter.py @@ -9,6 +9,12 @@ import logging from typing import Dict, Tuple, Optional, Any, List, Union from ..utils.log_utils import get_logger +from .handlers.barcode_mapper import BarcodeMapper +from .handlers.unit_converter_handlers import ( + JianUnitHandler, BoxUnitHandler, TiHeUnitHandler, + GiftUnitHandler, UnitHandler +) +from .validators import ProductValidator logger = get_logger(__name__) @@ -93,7 +99,28 @@ class UnitConverter: # "xxmL*1"或"xx毫升*1"格式 (r'([\d\.]+)(?:mL|毫升)[*xX×]?(\d+)?', r'\1mL*\2' if r'\2' else r'\1mL*1'), ] - + + # 初始化处理程序 + self._init_handlers() + + # 初始化验证器 + self.validator = ProductValidator() + + def _init_handlers(self): + """ + 初始化各种处理程序 + """ + # 创建条码处理程序 + self.barcode_mapper = BarcodeMapper(self.special_barcodes) + + # 创建单位处理程序列表,优先级从高到低 + self.unit_handlers: List[UnitHandler] = [ + GiftUnitHandler(), # 首先处理赠品,优先级最高 + JianUnitHandler(), # 处理"件"单位 + BoxUnitHandler(), # 处理"箱"单位 + TiHeUnitHandler() # 处理"提"和"盒"单位 + ] + def extract_unit_from_quantity(self, quantity_str: str) -> Tuple[Optional[float], Optional[str]]: """ 从数量字符串中提取单位 @@ -368,103 +395,15 @@ class UnitConverter: logger.error(f"解析规格时出错: {e}") return 1, 1, None - def _process_standard_unit_conversion(self, product: Dict) -> Dict: - """ - 处理标准单位转换(件、箱、提、盒等单位) - - Args: - product: 商品信息字典 - - Returns: - 处理后的商品信息字典 - """ - # 复制原始数据,避免修改原始字典 - result = product.copy() - - unit = result.get('unit', '') - quantity = result.get('quantity', 0) - price = result.get('price', 0) - specification = result.get('specification', '') - - # 跳过无效数据 - 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 = 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 - def process_unit_conversion(self, product: Dict) -> Dict: """ 处理单位转换,按照以下规则: 1. 特殊条码: 优先处理特殊条码 - 2. "件"单位: 数量×包装数量, 单价÷包装数量, 单位转为"瓶" - 3. "箱"单位: 数量×包装数量, 单价÷包装数量, 单位转为"瓶" - 4. "提"和"盒"单位: 如果是三级规格, 按件处理; 如果是二级规格, 保持不变 - 5. 其他单位: 保持不变 + 2. 赠品处理: 对于赠品,维持数量转换但单价为0 + 3. "件"单位: 数量×包装数量, 单价÷包装数量, 单位转为"瓶" + 4. "箱"单位: 数量×包装数量, 单价÷包装数量, 单位转为"瓶" + 5. "提"和"盒"单位: 如果是三级规格, 按件处理; 如果是二级规格, 保持不变 + 6. 其他单位: 保持不变 Args: product: 商品信息字典 @@ -472,62 +411,40 @@ class UnitConverter: Returns: 处理后的商品信息字典 """ + # 首先验证商品数据 + product = self.validator.validate_product(product) + # 复制原始数据,避免修改原始字典 result = product.copy() 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: + if not barcode: return result - # 特殊条码处理 - if barcode in self.special_barcodes: - special_config = self.special_barcodes[barcode] - - # 处理条码映射情况 - if 'map_to' in special_config: - new_barcode = special_config['map_to'] - logger.info(f"条码映射: {barcode} -> {new_barcode}") - result['barcode'] = new_barcode - # 如果只是条码映射且没有其他特殊处理,继续执行标准单位处理 - if len(special_config) == 2: # 只有map_to和description两个字段 - # 继续标准处理流程,不提前返回 - return self._process_standard_unit_conversion(result) - - 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 - - # 如果有固定单价,优先使用 - if 'fixed_price' in special_config: - new_price = special_config['fixed_price'] - logger.info(f"特殊条码({barcode})使用固定单价: {new_price}") - - # 如果有固定规格,设置规格 - if 'specification' in special_config: - result['specification'] = special_config['specification'] - # 解析规格以获取包装数量 - package_quantity = self.parse_specification(special_config['specification']) - if package_quantity: - result['package_quantity'] = package_quantity - logger.info(f"特殊条码({barcode})使用固定规格: {special_config['specification']}, 包装数量={package_quantity}") - - 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 + # 先处理条码映射 + result = self.barcode_mapper.map_barcode(result) - # 没有特殊条码,使用标准单位处理 - return self._process_standard_unit_conversion(result) \ No newline at end of file + # 如果没有规格信息,无法进行单位转换 + if not specification: + # 尝试从商品名称推断规格 + inferred_spec = self.infer_specification_from_name(result.get('name', '')) + if inferred_spec: + result['specification'] = inferred_spec + logger.info(f"从商品名称推断规格: {result.get('name', '')} -> {inferred_spec}") + else: + return result + + # 解析规格信息 + level1, level2, level3 = self.parse_specification(result.get('specification', '')) + + # 使用单位处理程序处理单位转换 + for handler in self.unit_handlers: + if handler.can_handle(result): + return handler.handle(result, level1, level2, level3) + + # 没有找到适用的处理程序,保持不变 + logger.info(f"其他单位处理: 保持原样 数量: {result.get('quantity', 0)}, 单价: {result.get('price', 0)}, 单位: {result.get('unit', '')}") + return result \ No newline at end of file diff --git a/app/core/excel/handlers/__init__.py b/app/core/excel/handlers/__init__.py new file mode 100644 index 0000000..fe768ae --- /dev/null +++ b/app/core/excel/handlers/__init__.py @@ -0,0 +1,11 @@ +""" +单位转换处理程序包 +----------------- +提供单位转换和条码处理的各种处理程序 +""" + +from typing import Dict, Any + +# 导出所有处理程序类 +from .barcode_mapper import BarcodeMapper +from .unit_converter_handlers import JianUnitHandler, BoxUnitHandler, TiHeUnitHandler, GiftUnitHandler, UnitHandler \ No newline at end of file diff --git a/app/core/excel/handlers/__pycache__/__init__.cpython-39.pyc b/app/core/excel/handlers/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1fe00c132e1ecc4f541daaa46835739b375909bd GIT binary patch literal 552 zcmZ9JO-tiI7{@0`leFoZH!mK%=FmNOTozF&C_+8x?qvuJ+01AInM|0bVva(0FSfJ@ zUKC+n7FNMeAm|5~>za1`3Z9(NT}XXk{yhI@c%I+9`1QI2WK_FL-CYfUKgtwCmu5|_ zo)Qcoz#7&gjp|UR1~fE)4P1&!)P&}1ZblZhp-s!MOe?TL9q7<1tjaxW54d)JUygC| z(b?nu=y4PKVRWtJ#m^f6eEEd@`8* z9u;RfPm%;&U6k5kkP2fx&Yt82fvbu7hR*|rNykqT!d+7|w?jYv5{K!QALEE{(b!-| zMc@1!ZjrgdX1Jdg3)O3;P tw+>f4N%I26iARnC5_$g#9zvmFqYp_Y%AY91{fMlpr|Kzo$yzXK+6%r7x6=Rs literal 0 HcmV?d00001 diff --git a/app/core/excel/handlers/__pycache__/barcode_mapper.cpython-39.pyc b/app/core/excel/handlers/__pycache__/barcode_mapper.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eec0dd529c3c86140c7ecab0d67f7c6e2b5c5feb GIT binary patch literal 2167 zcmaJ?&2QX96d!-=+Uwm-Qktd;6=*>Su@dQl1BX?pT8QA3Ai>RA)ml7GYUH)IW4kn! zbBL0Zk0vytgpZ^V2_UrWArG4DOs zibaFKc*kg8 z-c@6)e;A#kkAH9OeA2$sT3_tk`fPb`_jYsTTVo#p$y8b4>V#`YTz|U}xq)BvxN^py z-A5@-#;?2ISn(Mg2f@=A2DSrnTn%288Nfd@+iSczj}6ukYRcX7BEIt^e@0 z9Sd>wmc)m`hGV-m&w8(o9@;VhcL&lq-+&noz>-LftVb1%5SP_E^BE!^SSWQiVWP?SMH|=c4U+)pIqU zdJYS#olsbH5$ThSS*}}_>${O<4Fh;6BBWg!Iq)DSyCPtZ)?fjRA&0WeiDKp-h-m>J zj=&T#Ju6{Mx5ze`lM{IhD?;9u=afWY&nHSk-j^>(?8O8!9_d6GC37lO67>RMXA_`c zpNW($m8yxdL1-q)Y>-WLTarkUNg(&ep>j}qRbpnM0zT9c-~$h6o#uengkFQ@fz}f} z$qtjboZuA%&qxe*J28^%3F6B%KbNOPq5m(?Uj+Smp?^T=A58LH{dB0K7j)5~Q3BQs zjgvmvBW-?HUYtB7P>v?L@FfT9aAF7=aOp8&)d)sN@HDtPAbbUTsWDEHJbVlA1)t~$ ze6dp;kN<>d_mZYl^=p^YYxC_ieRO~MKz~?RN-zK1lkX8vrflbxE2VUKDP8@c*H}^k z{_w$ebN;78DT|rjLwrXh$DulCo06`63LWZyfVkGlQcidl64i==7z?z&@+zfEYH93L zi5vCUi`<6iI&2>jj_`a`W7x=INa`3nTkF~{Tt6Sz{K$=FnUKjem}@&cKkZ(0s3kUW z5k>p@%1iMv;FKTkK4^V@Gu^nC?yRRPYY+eUwNm0mQPI<`UBfo|q~P9-#pc%iN-4&g z>IZE{01oi4@^Y!Uc4z;?tG#U(zjKP)-4#%$qA%qPSM2}_RsrusL*UtVv*xPnPY2}! zLr{j1!r}}eE^G)k=0Laq*nvOo&X{@M`Jmeq*-^ZMp0W{?E#O%%SjUl-Lzak<=s)cR zE1l=@aXN4H3^2l1VIhVt6_f!@NWeJKQ4tow4 g42i(%(byb$E{>>l6018C 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 literal 0 HcmV?d00001 diff --git a/app/core/excel/handlers/barcode_mapper.py b/app/core/excel/handlers/barcode_mapper.py new file mode 100644 index 0000000..b51c761 --- /dev/null +++ b/app/core/excel/handlers/barcode_mapper.py @@ -0,0 +1,83 @@ +""" +条码映射处理程序 +------------- +处理特殊条码的映射和转换 +""" + +import logging +from typing import Dict, Optional, Any + +from ...utils.log_utils import get_logger + +logger = get_logger(__name__) + + +class BarcodeMapper: + """ + 条码映射器:负责特殊条码的映射和处理 + """ + + def __init__(self, special_barcodes: Dict[str, Dict[str, Any]]): + """ + 初始化条码映射器 + + Args: + special_barcodes: 特殊条码配置字典 + """ + self.special_barcodes = special_barcodes or {} + + def map_barcode(self, product: Dict[str, Any]) -> Dict[str, Any]: + """ + 映射商品条码,处理特殊情况 + + Args: + product: 包含条码的商品信息字典 + + Returns: + 处理后的商品信息字典 + """ + result = product.copy() + barcode = result.get('barcode', '') + + # 如果条码不在特殊条码列表中,直接返回 + if not barcode or barcode not in self.special_barcodes: + return result + + special_config = self.special_barcodes[barcode] + + # 处理条码映射 + if 'map_to' in special_config: + new_barcode = special_config['map_to'] + logger.info(f"条码映射: {barcode} -> {new_barcode}") + result['barcode'] = new_barcode + + # 处理特殊倍数 + if 'multiplier' in special_config: + multiplier = special_config.get('multiplier', 1) + target_unit = special_config.get('target_unit', '瓶') + + # 数量乘以倍数 + quantity = result.get('quantity', 0) + new_quantity = quantity * multiplier + + # 单价除以倍数 + price = result.get('price', 0) + new_price = price / multiplier if price else 0 + + # 如果有固定单价,优先使用 + if 'fixed_price' in special_config: + new_price = special_config['fixed_price'] + logger.info(f"特殊条码({barcode})使用固定单价: {new_price}") + + # 如果有固定规格,设置规格 + if 'specification' in special_config: + result['specification'] = special_config['specification'] + logger.info(f"特殊条码({barcode})使用固定规格: {special_config['specification']}") + + logger.info(f"特殊条码处理: {barcode}, 数量: {quantity} -> {new_quantity}, 单价: {price} -> {new_price}, 单位: {result.get('unit', '')} -> {target_unit}") + + result['quantity'] = new_quantity + result['price'] = new_price + result['unit'] = target_unit + + return result \ No newline at end of file diff --git a/app/core/excel/handlers/unit_converter_handlers.py b/app/core/excel/handlers/unit_converter_handlers.py new file mode 100644 index 0000000..7aac214 --- /dev/null +++ b/app/core/excel/handlers/unit_converter_handlers.py @@ -0,0 +1,284 @@ +""" +单位转换处理程序 +------------- +处理不同单位的转换逻辑 +""" + +import logging +from typing import Dict, Optional, Any, Tuple, Protocol +from abc import ABC, abstractmethod + +from ...utils.log_utils import get_logger + +logger = get_logger(__name__) + + +class UnitHandler(ABC): + """ + 单位处理器基类:定义单位处理接口 + """ + + @abstractmethod + def can_handle(self, product: Dict[str, Any]) -> bool: + """ + 检查是否可以处理该商品 + + Args: + product: 商品信息字典 + + Returns: + 是否可以处理 + """ + pass + + @abstractmethod + def handle(self, product: Dict[str, Any], level1: int, level2: int, level3: Optional[int]) -> Dict[str, Any]: + """ + 处理单位转换 + + Args: + product: 商品信息字典 + level1: 一级包装数量 + level2: 二级包装数量 + level3: 三级包装数量,可能为None + + Returns: + 处理后的商品信息字典 + """ + pass + + +class JianUnitHandler(UnitHandler): + """ + 处理"件"单位的转换 + """ + + def can_handle(self, product: Dict[str, Any]) -> bool: + """ + 检查是否可以处理该商品(单位为"件") + + Args: + product: 商品信息字典 + + Returns: + 是否可以处理 + """ + unit = product.get('unit', '') + return unit == '件' + + def handle(self, product: Dict[str, Any], level1: int, level2: int, level3: Optional[int]) -> Dict[str, Any]: + """ + 处理"件"单位转换:数量×包装数量,单价÷包装数量,单位转为"瓶" + + Args: + product: 商品信息字典 + level1: 一级包装数量 + level2: 二级包装数量 + level3: 三级包装数量,可能为None + + Returns: + 处理后的商品信息字典 + """ + result = product.copy() + + quantity = result.get('quantity', 0) + price = result.get('price', 0) + + # 计算包装数量(二级*三级,如果无三级则仅二级) + 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 + + +class BoxUnitHandler(UnitHandler): + """ + 处理"箱"单位的转换 + """ + + def can_handle(self, product: Dict[str, Any]) -> bool: + """ + 检查是否可以处理该商品(单位为"箱") + + Args: + product: 商品信息字典 + + Returns: + 是否可以处理 + """ + unit = product.get('unit', '') + return unit == '箱' + + def handle(self, product: Dict[str, Any], level1: int, level2: int, level3: Optional[int]) -> Dict[str, Any]: + """ + 处理"箱"单位转换:数量×包装数量,单价÷包装数量,单位转为"瓶" + + Args: + product: 商品信息字典 + level1: 一级包装数量 + level2: 二级包装数量 + level3: 三级包装数量,可能为None + + Returns: + 处理后的商品信息字典 + """ + result = product.copy() + + quantity = result.get('quantity', 0) + price = result.get('price', 0) + + # 计算包装数量(二级*三级,如果无三级则仅二级) + 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 + + +class TiHeUnitHandler(UnitHandler): + """ + 处理"提"和"盒"单位的转换 + """ + + def can_handle(self, product: Dict[str, Any]) -> bool: + """ + 检查是否可以处理该商品(单位为"提"或"盒") + + Args: + product: 商品信息字典 + + Returns: + 是否可以处理 + """ + unit = product.get('unit', '') + return unit in ['提', '盒'] + + def handle(self, product: Dict[str, Any], level1: int, level2: int, level3: Optional[int]) -> Dict[str, Any]: + """ + 处理"提"和"盒"单位转换: + - 如果是三级规格,按件处理(数量×包装数量,单价÷包装数量,单位转为"瓶") + - 如果是二级规格,保持不变 + + Args: + product: 商品信息字典 + level1: 一级包装数量 + level2: 二级包装数量 + level3: 三级包装数量,可能为None + + Returns: + 处理后的商品信息字典 + """ + result = product.copy() + + quantity = result.get('quantity', 0) + price = result.get('price', 0) + unit = result.get('unit', '') + + # 如果是三级规格,按件处理 + if level3 is not None: + # 计算包装数量 - 只乘以最后一级数量 + packaging_count = 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 + + +class GiftUnitHandler(UnitHandler): + """ + 处理赠品的特殊情况 + """ + + def can_handle(self, product: Dict[str, Any]) -> bool: + """ + 检查是否可以处理该商品(是否为赠品) + + Args: + product: 商品信息字典 + + Returns: + 是否可以处理 + """ + return product.get('is_gift', False) is True + + def handle(self, product: Dict[str, Any], level1: int, level2: int, level3: Optional[int]) -> Dict[str, Any]: + """ + 处理赠品的单位转换: + - 对于件/箱单位,数量仍然需要转换,但赠品的单价保持为0 + + Args: + product: 商品信息字典 + level1: 一级包装数量 + level2: 二级包装数量 + level3: 三级包装数量,可能为None + + Returns: + 处理后的商品信息字典 + """ + result = product.copy() + + unit = result.get('unit', '') + quantity = result.get('quantity', 0) + + # 根据单位类型选择适当的包装数计算 + if unit in ['件', '箱']: + # 计算包装数量(二级*三级,如果无三级则仅二级) + packaging_count = level2 * (level3 or 1) + + # 数量×包装数量 + new_quantity = quantity * packaging_count + + logger.info(f"赠品{unit}单位处理: 数量: {quantity} -> {new_quantity}, 单价: 0, 单位: {unit} -> 瓶") + + result['quantity'] = new_quantity + result['unit'] = '瓶' + elif unit in ['提', '盒'] and level3 is not None: + # 对于三级规格的提/盒,类似件处理 + new_quantity = quantity * level3 + + logger.info(f"赠品{unit}单位(三级规格)处理: 数量: {quantity} -> {new_quantity}, 单价: 0, 单位: {unit} -> 瓶") + + result['quantity'] = new_quantity + result['unit'] = '瓶' + else: + # 其他情况保持不变 + logger.info(f"赠品{unit}单位处理: 保持原样 数量: {quantity}, 单价: 0, 单位: {unit}") + + # 确保单价为0 + result['price'] = 0 + + return result \ No newline at end of file diff --git a/app/core/excel/processor.py b/app/core/excel/processor.py index 22577f4..87231ea 100644 --- a/app/core/excel/processor.py +++ b/app/core/excel/processor.py @@ -295,8 +295,15 @@ class ExcelProcessor: if package_quantity: product['package_quantity'] = package_quantity logger.info(f"解析规格: {product['specification']} -> 包装数量={package_quantity}") + elif column_mapping.get('specification') and not pd.isna(row[column_mapping['specification']]): + # 添加这段逻辑以处理通过列映射找到的规格列 + product['specification'] = str(row[column_mapping['specification']]) + package_quantity = self.parse_specification(product['specification']) + if package_quantity: + product['package_quantity'] = package_quantity + logger.info(f"从映射列解析规格: {product['specification']} -> 包装数量={package_quantity}") else: - # 逻辑1: 如果规格为空,尝试从商品名称推断规格 + # 只有在无法从Excel获取规格时,才尝试从商品名称推断规格 if product['name']: # 特殊处理:优先检查名称中是否包含"容量*数量"格式 container_pattern = r'.*?(\d+(?:\.\d+)?)\s*(?:ml|[mM][lL]|[lL]|升|毫升)[*×xX](\d+).*' diff --git a/app/core/excel/test_converter.py b/app/core/excel/test_converter.py new file mode 100644 index 0000000..2032f85 --- /dev/null +++ b/app/core/excel/test_converter.py @@ -0,0 +1,355 @@ +""" +单位转换器测试模块 +--------------- +测试单位转换和条码映射逻辑 +""" + +import os +import sys +import unittest +from typing import Dict, Any + +# 添加项目根目录到Python路径 +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../..'))) + +from app.core.excel.converter import UnitConverter +from app.core.excel.validators import ProductValidator + + +class TestUnitConverter(unittest.TestCase): + """ + 测试单位转换器功能 + """ + + def setUp(self): + """ + 测试前的准备工作 + """ + self.converter = UnitConverter() + + def test_jian_unit_conversion(self): + """ + 测试"件"单位的转换 + """ + # 准备测试数据 + product = { + 'barcode': '6954767400129', + 'name': '美汁源果粒橙1.8L*8瓶', + 'specification': '1.8L*8', + 'quantity': 1.0, + 'unit': '件', + 'price': 65.0 + } + + # 执行转换 + result = self.converter.process_unit_conversion(product) + + # 验证结果 + self.assertEqual(result['quantity'], 8.0) + self.assertEqual(result['price'], 8.125) + self.assertEqual(result['unit'], '瓶') + + def test_box_unit_conversion(self): + """ + 测试"箱"单位的转换 + """ + # 准备测试数据 + product = { + 'barcode': '6925303721244', + 'name': '统一鲜橙多2L*6瓶', + 'specification': '2L*6', + 'quantity': 1.0, + 'unit': '箱', + 'price': 43.0 + } + + # 执行转换 + result = self.converter.process_unit_conversion(product) + + # 验证结果 + self.assertEqual(result['quantity'], 6.0) + self.assertEqual(result['price'], 7.1666666666666667) + self.assertEqual(result['unit'], '瓶') + + def test_tihe_unit_conversion_level3(self): + """ + 测试"提"单位的转换(三级规格) + """ + # 准备测试数据(三级规格:1*6*4,表示1排6提,每提4瓶) + product = { + 'barcode': '6921168509347', + 'name': '农夫山泉550ml*24瓶', + 'specification': '1*6*4', + 'quantity': 2.0, + 'unit': '提', + 'price': 16.0 + } + + # 执行转换 + result = self.converter.process_unit_conversion(product) + + # 验证结果:三级规格,提单位特殊处理,数量*最后一级 + self.assertEqual(result['quantity'], 8.0) # 2提 * 4瓶/提 + self.assertEqual(result['price'], 4.0) # 16元/提 ÷ 4瓶/提 + self.assertEqual(result['unit'], '瓶') + + def test_tihe_unit_conversion_level2(self): + """ + 测试"提"单位的转换(二级规格) + """ + # 准备测试数据(二级规格:1*4,表示每件4提) + product = { + 'barcode': '6921168509347', + 'name': '农夫山泉550ml*4瓶', + 'specification': '1*4', + 'quantity': 5.0, + 'unit': '提', + 'price': 10.0 + } + + # 执行转换 + result = self.converter.process_unit_conversion(product) + + # 验证结果:二级规格,提单位保持不变 + self.assertEqual(result['quantity'], 5.0) + self.assertEqual(result['price'], 10.0) + self.assertEqual(result['unit'], '提') + + def test_barcode_mapping(self): + """ + 测试条码映射 + """ + # 准备测试数据(使用需要被映射的条码) + product = { + 'barcode': '6920584471055', # 这个条码应映射到6920584471017 + 'name': '测试映射条码商品', + 'specification': '1*12', + 'quantity': 1.0, + 'unit': '件', + 'price': 60.0 + } + + # 执行转换 + result = self.converter.process_unit_conversion(product) + + # 验证结果:条码应该被映射 + self.assertEqual(result['barcode'], '6920584471017') + self.assertEqual(result['quantity'], 12.0) # 同时处理件单位转换 + self.assertEqual(result['price'], 5.0) # 60元/件 ÷ 12瓶/件 + self.assertEqual(result['unit'], '瓶') + + def test_special_barcode_multiplier(self): + """ + 测试特殊条码的倍数处理 + """ + # 准备测试数据(使用特殊条码) + product = { + 'barcode': '6925019900087', # 特殊条码:数量*10,单位转瓶 + 'name': '特殊条码商品', + 'specification': '1*10', + 'quantity': 2.0, + 'unit': '箱', + 'price': 100.0 + } + + # 执行转换 + result = self.converter.process_unit_conversion(product) + + # 验证结果:特殊条码乘数应该生效 + self.assertEqual(result['quantity'], 20.0) # 2箱 * 10倍数 + self.assertEqual(result['price'], 5.0) # 100元/箱 ÷ 10倍数/箱 + self.assertEqual(result['unit'], '瓶') + + +class TestProductValidator(unittest.TestCase): + """ + 测试商品数据验证器功能 + """ + + def setUp(self): + """ + 测试前的准备工作 + """ + self.validator = ProductValidator() + + def test_validate_barcode(self): + """ + 测试条码验证 + """ + # 测试有效条码 + is_valid, barcode, error = self.validator.validate_barcode('6925303721244') + self.assertTrue(is_valid) + self.assertEqual(barcode, '6925303721244') + self.assertIsNone(error) + + # 测试包含非数字字符的条码 + is_valid, barcode, error = self.validator.validate_barcode('6925303-721244') + self.assertTrue(is_valid) + self.assertEqual(barcode, '6925303721244') + self.assertIsNone(error) + + # 测试5开头的条码修正 + is_valid, barcode, error = self.validator.validate_barcode('5925303721244') + self.assertTrue(is_valid) + self.assertEqual(barcode, '6925303721244') + self.assertIsNone(error) + + # 测试过短的条码 + is_valid, barcode, error = self.validator.validate_barcode('12345') + self.assertFalse(is_valid) + self.assertEqual(barcode, '12345') + self.assertIn("条码长度异常", error) + + # 测试仓库标识 + is_valid, barcode, error = self.validator.validate_barcode('仓库') + self.assertFalse(is_valid) + self.assertEqual(barcode, '仓库') + self.assertEqual(error, "条码为仓库标识") + + # 测试空值 + is_valid, barcode, error = self.validator.validate_barcode(None) + self.assertFalse(is_valid) + self.assertEqual(barcode, "") + self.assertEqual(error, "条码为空") + + def test_validate_quantity(self): + """ + 测试数量验证 + """ + # 测试有效数量 + is_valid, quantity, error = self.validator.validate_quantity(10) + self.assertTrue(is_valid) + self.assertEqual(quantity, 10.0) + self.assertIsNone(error) + + # 测试字符串数量 + is_valid, quantity, error = self.validator.validate_quantity("25.5") + self.assertTrue(is_valid) + self.assertEqual(quantity, 25.5) + self.assertIsNone(error) + + # 测试带单位的数量 + is_valid, quantity, error = self.validator.validate_quantity("30瓶") + self.assertTrue(is_valid) + self.assertEqual(quantity, 30.0) + self.assertIsNone(error) + + # 测试零数量 + is_valid, quantity, error = self.validator.validate_quantity(0) + self.assertFalse(is_valid) + self.assertEqual(quantity, 0.0) + self.assertIn("数量必须大于0", error) + + # 测试负数量 + is_valid, quantity, error = self.validator.validate_quantity(-5) + self.assertFalse(is_valid) + self.assertEqual(quantity, 0.0) + self.assertIn("数量必须大于0", error) + + # 测试非数字 + is_valid, quantity, error = self.validator.validate_quantity("abc") + self.assertFalse(is_valid) + self.assertEqual(quantity, 0.0) + self.assertIn("数量不包含数字", error) + + # 测试空值 + is_valid, quantity, error = self.validator.validate_quantity(None) + self.assertFalse(is_valid) + self.assertEqual(quantity, 0.0) + self.assertEqual(error, "数量为空") + + def test_validate_price(self): + """ + 测试单价验证 + """ + # 测试有效单价 + is_valid, price, is_gift, error = self.validator.validate_price(12.5) + self.assertTrue(is_valid) + self.assertEqual(price, 12.5) + self.assertFalse(is_gift) + self.assertIsNone(error) + + # 测试字符串单价 + is_valid, price, is_gift, error = self.validator.validate_price("8.0") + self.assertTrue(is_valid) + self.assertEqual(price, 8.0) + self.assertFalse(is_gift) + self.assertIsNone(error) + + # 测试零单价(赠品) + is_valid, price, is_gift, error = self.validator.validate_price(0) + self.assertTrue(is_valid) + self.assertEqual(price, 0.0) + self.assertTrue(is_gift) + self.assertIsNone(error) + + # 测试"赠品"标记 + is_valid, price, is_gift, error = self.validator.validate_price("赠品") + self.assertTrue(is_valid) + self.assertEqual(price, 0.0) + self.assertTrue(is_gift) + self.assertIsNone(error) + + # 测试负单价 + is_valid, price, is_gift, error = self.validator.validate_price(-5) + self.assertFalse(is_valid) + self.assertEqual(price, 0.0) + self.assertTrue(is_gift) + self.assertIn("单价不能为负数", error) + + # 测试空值 + is_valid, price, is_gift, error = self.validator.validate_price(None) + self.assertFalse(is_valid) + self.assertEqual(price, 0.0) + self.assertTrue(is_gift) + self.assertEqual(error, "单价为空,视为赠品") + + def test_validate_product(self): + """ + 测试商品数据验证 + """ + # 准备测试数据(有效商品) + product = { + 'barcode': '6954767400129', + 'name': '测试商品', + 'specification': '1*12', + 'quantity': 3.0, + 'price': 36.0, + 'unit': '件', + 'is_gift': False + } + + # 验证有效商品 + result = self.validator.validate_product(product) + self.assertEqual(result['barcode'], '6954767400129') + self.assertEqual(result['quantity'], 3.0) + self.assertEqual(result['price'], 36.0) + self.assertFalse(result['is_gift']) + + # 验证赠品商品 + gift_product = product.copy() + gift_product['price'] = 0 + result = self.validator.validate_product(gift_product) + self.assertEqual(result['price'], 0.0) + self.assertTrue(result['is_gift']) + + # 验证需要修复的商品 + invalid_product = { + 'barcode': '5954767-400129', # 需要修复前缀和移除非数字 + 'name': '测试商品', + 'specification': '1*12', + 'quantity': '2件', # 需要提取数字 + 'price': '赠品', # 赠品标记 + 'unit': '件', + 'is_gift': False + } + + result = self.validator.validate_product(invalid_product) + self.assertEqual(result['barcode'], '6954767400129') # 5->6,移除 '-' + self.assertEqual(result['quantity'], 2.0) # 提取数字 + self.assertEqual(result['price'], 0.0) # 赠品价格为0 + self.assertTrue(result['is_gift']) # 标记为赠品 + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/app/core/excel/validators.py b/app/core/excel/validators.py new file mode 100644 index 0000000..0898973 --- /dev/null +++ b/app/core/excel/validators.py @@ -0,0 +1,215 @@ +""" +数据验证器模块 +---------- +提供对商品数据的验证和修复功能 +""" + +import re +import logging +from typing import Dict, Any, Optional, List, Tuple, Union + +from ..utils.log_utils import get_logger + +logger = get_logger(__name__) + + +class ProductValidator: + """ + 商品数据验证器:验证和修复商品数据 + """ + + def __init__(self): + """ + 初始化商品数据验证器 + """ + # 仓库标识列表 + self.warehouse_identifiers = ["仓库", "仓库全名", "warehouse"] + + def validate_barcode(self, barcode: Any) -> Tuple[bool, str, Optional[str]]: + """ + 验证并修复条码 + + Args: + barcode: 原始条码值 + + Returns: + (是否有效, 修复后的条码, 错误信息)元组 + """ + error_message = None + + # 处理空值 + if barcode is None: + return False, "", "条码为空" + + # 转为字符串 + barcode_str = str(barcode).strip() + + # 处理"仓库"特殊情况 + if barcode_str in self.warehouse_identifiers: + return False, barcode_str, "条码为仓库标识" + + # 清理条码格式(移除非数字字符) + barcode_clean = re.sub(r'\D', '', barcode_str) + + # 如果清理后为空,无效 + if not barcode_clean: + return False, barcode_str, "条码不包含数字" + + # 对特定的错误条码进行修正(5开头改为6开头) + if len(barcode_clean) > 8 and barcode_clean.startswith('5') and not barcode_clean.startswith('53'): + original_barcode = barcode_clean + barcode_clean = '6' + barcode_clean[1:] + logger.info(f"修正条码前缀 5->6: {original_barcode} -> {barcode_clean}") + + # 验证条码长度 + if len(barcode_clean) < 8 or len(barcode_clean) > 13: + error_message = f"条码长度异常: {barcode_clean}, 长度={len(barcode_clean)}" + logger.warning(error_message) + return False, barcode_clean, error_message + + # 验证条码是否全为数字 + if not barcode_clean.isdigit(): + error_message = f"条码包含非数字字符: {barcode_clean}" + logger.warning(error_message) + return False, barcode_clean, error_message + + # 对于序号9的特殊情况,允许其条码格式 + if barcode_clean == "5321545613": + logger.info(f"特殊条码验证通过: {barcode_clean}") + return True, barcode_clean, None + + logger.debug(f"条码验证通过: {barcode_clean}") + return True, barcode_clean, None + + def validate_quantity(self, quantity: Any) -> Tuple[bool, float, Optional[str]]: + """ + 验证并修复数量 + + Args: + quantity: 原始数量值 + + Returns: + (是否有效, 修复后的数量, 错误信息)元组 + """ + # 处理空值 + if quantity is None: + return False, 0.0, "数量为空" + + # 如果是字符串,尝试解析 + if isinstance(quantity, str): + # 去除空白和非数字字符(保留小数点) + quantity_clean = re.sub(r'[^\d\.]', '', quantity.strip()) + if not quantity_clean: + return False, 0.0, "数量不包含数字" + + try: + quantity_value = float(quantity_clean) + except ValueError: + return False, 0.0, f"无法将数量 '{quantity}' 转换为数字" + else: + # 尝试直接转换 + try: + quantity_value = float(quantity) + except (ValueError, TypeError): + return False, 0.0, f"无法将数量 '{quantity}' 转换为数字" + + # 数量必须大于0 + if quantity_value <= 0: + return False, 0.0, f"数量必须大于0,当前值: {quantity_value}" + + return True, quantity_value, None + + def validate_price(self, price: Any) -> Tuple[bool, float, bool, Optional[str]]: + """ + 验证并修复单价 + + Args: + price: 原始单价值 + + Returns: + (是否有效, 修复后的单价, 是否为赠品, 错误信息)元组 + """ + # 初始化不是赠品 + is_gift = False + + # 处理空值 + if price is None: + return False, 0.0, True, "单价为空,视为赠品" + + # 如果是字符串,检查赠品标识 + if isinstance(price, str): + price_str = price.strip().lower() + if price_str in ["赠品", "gift", "赠送", "0", ""]: + return True, 0.0, True, None + + # 去除空白和非数字字符(保留小数点) + price_clean = re.sub(r'[^\d\.]', '', price_str) + if not price_clean: + return False, 0.0, True, "单价不包含数字,视为赠品" + + try: + price_value = float(price_clean) + except ValueError: + return False, 0.0, True, f"无法将单价 '{price}' 转换为数字,视为赠品" + else: + # 尝试直接转换 + try: + price_value = float(price) + except (ValueError, TypeError): + return False, 0.0, True, f"无法将单价 '{price}' 转换为数字,视为赠品" + + # 单价为0视为赠品 + if price_value == 0: + return True, 0.0, True, None + + # 单价必须大于0 + if price_value < 0: + return False, 0.0, True, f"单价不能为负数: {price_value},视为赠品" + + return True, price_value, False, None + + def validate_product(self, product: Dict[str, Any]) -> Dict[str, Any]: + """ + 验证并修复商品数据 + + Args: + product: 商品数据字典 + + Returns: + 修复后的商品数据字典 + """ + # 创建新字典,避免修改原始数据 + validated_product = product.copy() + + # 验证条码 + barcode = product.get('barcode', '') + is_valid, fixed_barcode, error_msg = self.validate_barcode(barcode) + if is_valid: + validated_product['barcode'] = fixed_barcode + else: + logger.warning(f"条码验证失败: {error_msg}") + if fixed_barcode: + # 即使验证失败,但如果有修复后的条码仍然使用它 + validated_product['barcode'] = fixed_barcode + + # 验证数量 + quantity = product.get('quantity', 0) + is_valid, fixed_quantity, error_msg = self.validate_quantity(quantity) + if is_valid: + validated_product['quantity'] = fixed_quantity + else: + logger.warning(f"数量验证失败: {error_msg}") + validated_product['quantity'] = 0.0 + + # 验证单价 + price = product.get('price', 0) + is_valid, fixed_price, is_gift, error_msg = self.validate_price(price) + validated_product['price'] = fixed_price + + # 如果单价验证结果表示为赠品,更新赠品标识 + if is_gift: + validated_product['is_gift'] = True + if error_msg: + logger.info(error_msg) + + return validated_product \ No newline at end of file diff --git a/logs/__main__.log b/logs/__main__.log index 31d0d7b..86403d2 100644 --- a/logs/__main__.log +++ b/logs/__main__.log @@ -367,3 +367,21 @@ 2025-05-07 22:28:53,640 - __main__ - INFO - 发现 1 个采购单文件 2025-05-07 22:28:53,640 - __main__ - WARNING - 只有1个采购单文件 D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250507222846.xls,无需合并 2025-05-07 22:28:53,641 - __main__ - INFO - === 完整流程处理成功(只有一个文件,跳过合并)=== +2025-05-08 19:45:41,035 - __main__ - INFO - === 流程步骤 1: OCR识别 === +2025-05-08 19:45:41,036 - __main__ - INFO - 批量处理所有图片 +2025-05-08 19:45:48,795 - __main__ - INFO - OCR处理完成,总计: 1,成功: 1 +2025-05-08 19:45:48,795 - __main__ - INFO - === 流程步骤 2: Excel处理 === +2025-05-08 19:45:48,797 - __main__ - INFO - 处理最新的Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250508194532.xlsx +2025-05-08 19:45:56,356 - __main__ - INFO - Excel处理成功,输出文件: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250508194532.xls +2025-05-08 19:45:56,357 - __main__ - INFO - === 流程步骤 3: 订单合并 === +2025-05-08 19:45:56,358 - __main__ - INFO - 发现 1 个采购单文件 +2025-05-08 19:45:56,358 - __main__ - WARNING - 只有1个采购单文件 D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250508194532.xls,无需合并 +2025-05-08 19:45:56,358 - __main__ - INFO - === 完整流程处理成功(只有一个文件,跳过合并)=== +2025-05-08 19:47:28,886 - __main__ - INFO - 处理Excel文件: D:/My Documents/python/orc-order-v2/data/output/微信图片_20250508194532.xlsx +2025-05-08 19:47:34,495 - __main__ - INFO - Excel处理成功,输出文件: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250508194532.xls +2025-05-08 19:51:23,190 - __main__ - INFO - 处理Excel文件: D:/My Documents/python/orc-order-v2/data/output/微信图片_20250508194532.xlsx +2025-05-08 19:51:27,459 - __main__ - INFO - Excel处理成功,输出文件: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250508194532.xls +2025-05-08 20:04:06,909 - __main__ - INFO - 处理Excel文件: D:/My Documents/python/orc-order-v2/data/output/微信图片_20250508194532.xlsx +2025-05-08 20:04:14,985 - __main__ - INFO - Excel处理成功,输出文件: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250508194532.xls +2025-05-08 20:46:00,701 - __main__ - INFO - 处理Excel文件: D:/My Documents/python/orc-order-v2/data/output/微信图片_20250508194532.xlsx +2025-05-08 20:46:07,564 - __main__ - INFO - Excel处理成功,输出文件: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250508194532.xls diff --git a/logs/app.core.excel.converter.log b/logs/app.core.excel.converter.log index 2aed929..9acdd9e 100644 --- a/logs/app.core.excel.converter.log +++ b/logs/app.core.excel.converter.log @@ -1832,3 +1832,42 @@ 2025-05-07 22:28:53,518 - app.core.excel.converter - INFO - 件单位处理: 数量: 1.0 -> 15.0, 单价: 55.0 -> 3.6666666666666665, 单位: 件 -> 瓶 2025-05-07 22:28:53,518 - app.core.excel.converter - INFO - 解析容量(ml)规格: 600ml*15 -> 1*15 2025-05-07 22:28:53,518 - app.core.excel.converter - INFO - 件单位处理: 数量: 1.0 -> 15.0, 单价: 55.0 -> 3.6666666666666665, 单位: 件 -> 瓶 +2025-05-08 19:45:49,632 - app.core.excel.converter - INFO - 提取规格: 外星人-维B水阳光青提500mL -> 500*None +2025-05-08 19:45:49,635 - app.core.excel.converter - INFO - 从名称推断规格(通用模式): 外星人-维B水阳光青提500mL -> 500*None +2025-05-08 19:45:49,639 - app.core.excel.converter - WARNING - 无法解析规格: 500*None,使用默认值1*1 +2025-05-08 19:47:29,598 - app.core.excel.converter - INFO - 提取规格: 外星人-维B水阳光青提500mL -> 500*None +2025-05-08 19:47:29,599 - app.core.excel.converter - INFO - 从名称推断规格(通用模式): 外星人-维B水阳光青提500mL -> 500*None +2025-05-08 19:47:29,601 - app.core.excel.converter - WARNING - 无法解析规格: 500*None,使用默认值1*1 +2025-05-08 19:51:24,077 - app.core.excel.converter - INFO - 解析容量(ml)规格: 500ml*15 -> 1*15 +2025-05-08 19:51:24,078 - app.core.excel.converter - INFO - 解析容量(ml)规格: 600ml*15 -> 1*15 +2025-05-08 19:51:24,081 - app.core.excel.converter - INFO - 解析容量(ml)规格: 500ml*15 -> 1*15 +2025-05-08 19:51:24,086 - app.core.excel.converter - INFO - 解析容量(ml)规格: 400mL*15 -> 1*15 +2025-05-08 19:51:24,088 - app.core.excel.converter - INFO - 解析容量(ml)规格: 400mL*15 -> 1*15 +2025-05-08 19:51:24,089 - app.core.excel.converter - INFO - 解析容量(ml)规格: 600ml*15 -> 1*15 +2025-05-08 19:51:24,104 - app.core.excel.converter - INFO - 解析容量(ml)规格: 900ml*12 -> 1*12 +2025-05-08 19:51:24,106 - app.core.excel.converter - INFO - 解析容量(ml)规格: 900ml*12 -> 1*12 +2025-05-08 19:51:24,106 - app.core.excel.converter - INFO - 解析容量(ml)规格: 400mL*15 -> 1*15 +2025-05-08 19:51:24,107 - app.core.excel.converter - INFO - 解析容量(ml)规格: 480ml*15 -> 1*15 +2025-05-08 19:51:24,108 - app.core.excel.converter - INFO - 解析容量(ml)规格: 500ml*15 -> 1*15 +2025-05-08 20:04:07,790 - app.core.excel.converter - INFO - 解析容量(ml)规格: 500ml*15 -> 1*15 +2025-05-08 20:04:07,793 - app.core.excel.converter - INFO - 解析容量(ml)规格: 600ml*15 -> 1*15 +2025-05-08 20:04:07,794 - app.core.excel.converter - INFO - 解析容量(ml)规格: 500ml*15 -> 1*15 +2025-05-08 20:04:07,798 - app.core.excel.converter - INFO - 解析容量(ml)规格: 400mL*15 -> 1*15 +2025-05-08 20:04:07,799 - app.core.excel.converter - INFO - 解析容量(ml)规格: 400mL*15 -> 1*15 +2025-05-08 20:04:07,822 - app.core.excel.converter - INFO - 解析容量(ml)规格: 600ml*15 -> 1*15 +2025-05-08 20:04:07,823 - app.core.excel.converter - INFO - 解析容量(ml)规格: 900ml*12 -> 1*12 +2025-05-08 20:04:07,824 - app.core.excel.converter - INFO - 解析容量(ml)规格: 900ml*12 -> 1*12 +2025-05-08 20:04:07,825 - app.core.excel.converter - INFO - 解析容量(ml)规格: 400mL*15 -> 1*15 +2025-05-08 20:04:07,827 - app.core.excel.converter - INFO - 解析容量(ml)规格: 480ml*15 -> 1*15 +2025-05-08 20:04:07,843 - app.core.excel.converter - INFO - 解析容量(ml)规格: 500ml*15 -> 1*15 +2025-05-08 20:46:01,837 - app.core.excel.converter - INFO - 解析容量(ml)规格: 500ml*15 -> 1*15 +2025-05-08 20:46:01,839 - app.core.excel.converter - INFO - 解析容量(ml)规格: 600ml*15 -> 1*15 +2025-05-08 20:46:01,840 - app.core.excel.converter - INFO - 解析容量(ml)规格: 500ml*15 -> 1*15 +2025-05-08 20:46:01,867 - app.core.excel.converter - INFO - 解析容量(ml)规格: 400mL*15 -> 1*15 +2025-05-08 20:46:01,868 - app.core.excel.converter - INFO - 解析容量(ml)规格: 400mL*15 -> 1*15 +2025-05-08 20:46:01,869 - app.core.excel.converter - INFO - 解析容量(ml)规格: 600ml*15 -> 1*15 +2025-05-08 20:46:01,869 - app.core.excel.converter - INFO - 解析容量(ml)规格: 900ml*12 -> 1*12 +2025-05-08 20:46:01,870 - app.core.excel.converter - INFO - 解析容量(ml)规格: 900ml*12 -> 1*12 +2025-05-08 20:46:01,871 - app.core.excel.converter - INFO - 解析容量(ml)规格: 400mL*15 -> 1*15 +2025-05-08 20:46:01,872 - app.core.excel.converter - INFO - 解析容量(ml)规格: 480ml*15 -> 1*15 +2025-05-08 20:46:01,873 - app.core.excel.converter - INFO - 解析容量(ml)规格: 500ml*15 -> 1*15 diff --git a/logs/app.core.excel.handlers.barcode_mapper.log b/logs/app.core.excel.handlers.barcode_mapper.log new file mode 100644 index 0000000..e69de29 diff --git a/logs/app.core.excel.handlers.unit_converter_handlers.log b/logs/app.core.excel.handlers.unit_converter_handlers.log new file mode 100644 index 0000000..6831ead --- /dev/null +++ b/logs/app.core.excel.handlers.unit_converter_handlers.log @@ -0,0 +1,35 @@ +2025-05-08 19:45:49,639 - app.core.excel.handlers.unit_converter_handlers - INFO - 件单位处理: 数量: 0.0 -> 0.0, 单价: 56.0 -> 56.0, 单位: 件 -> 瓶 +2025-05-08 19:47:29,601 - app.core.excel.handlers.unit_converter_handlers - INFO - 件单位处理: 数量: 1.0 -> 1.0, 单价: 56.0 -> 56.0, 单位: 件 -> 瓶 +2025-05-08 19:51:24,077 - app.core.excel.handlers.unit_converter_handlers - INFO - 件单位处理: 数量: 1.0 -> 15.0, 单价: 68.0 -> 4.533333333333333, 单位: 件 -> 瓶 +2025-05-08 19:51:24,078 - app.core.excel.handlers.unit_converter_handlers - INFO - 件单位处理: 数量: 1.0 -> 15.0, 单价: 68.0 -> 4.533333333333333, 单位: 件 -> 瓶 +2025-05-08 19:51:24,082 - app.core.excel.handlers.unit_converter_handlers - INFO - 件单位处理: 数量: 1.0 -> 15.0, 单价: 56.0 -> 3.7333333333333334, 单位: 件 -> 瓶 +2025-05-08 19:51:24,086 - app.core.excel.handlers.unit_converter_handlers - INFO - 件单位处理: 数量: 1.0 -> 15.0, 单价: 56.0 -> 3.7333333333333334, 单位: 件 -> 瓶 +2025-05-08 19:51:24,089 - app.core.excel.handlers.unit_converter_handlers - INFO - 件单位处理: 数量: 1.0 -> 15.0, 单价: 56.0 -> 3.7333333333333334, 单位: 件 -> 瓶 +2025-05-08 19:51:24,090 - app.core.excel.handlers.unit_converter_handlers - INFO - 件单位处理: 数量: 1.0 -> 15.0, 单价: 30.0 -> 2.0, 单位: 件 -> 瓶 +2025-05-08 19:51:24,105 - app.core.excel.handlers.unit_converter_handlers - INFO - 赠品件单位处理: 数量: 1.0 -> 12.0, 单价: 0, 单位: 件 -> 瓶 +2025-05-08 19:51:24,106 - app.core.excel.handlers.unit_converter_handlers - INFO - 赠品件单位处理: 数量: 1.0 -> 12.0, 单价: 0, 单位: 件 -> 瓶 +2025-05-08 19:51:24,106 - app.core.excel.handlers.unit_converter_handlers - INFO - 赠品瓶单位处理: 保持原样 数量: 2.0, 单价: 0, 单位: 瓶 +2025-05-08 19:51:24,107 - app.core.excel.handlers.unit_converter_handlers - INFO - 赠品瓶单位处理: 保持原样 数量: 1.0, 单价: 0, 单位: 瓶 +2025-05-08 19:51:24,108 - app.core.excel.handlers.unit_converter_handlers - INFO - 赠品瓶单位处理: 保持原样 数量: 9.0, 单价: 0, 单位: 瓶 +2025-05-08 20:04:07,791 - app.core.excel.handlers.unit_converter_handlers - INFO - 件单位处理: 数量: 1.0 -> 15.0, 单价: 68.0 -> 4.533333333333333, 单位: 件 -> 瓶 +2025-05-08 20:04:07,793 - app.core.excel.handlers.unit_converter_handlers - INFO - 件单位处理: 数量: 1.0 -> 15.0, 单价: 68.0 -> 4.533333333333333, 单位: 件 -> 瓶 +2025-05-08 20:04:07,794 - app.core.excel.handlers.unit_converter_handlers - INFO - 件单位处理: 数量: 1.0 -> 15.0, 单价: 56.0 -> 3.7333333333333334, 单位: 件 -> 瓶 +2025-05-08 20:04:07,798 - app.core.excel.handlers.unit_converter_handlers - INFO - 件单位处理: 数量: 1.0 -> 15.0, 单价: 56.0 -> 3.7333333333333334, 单位: 件 -> 瓶 +2025-05-08 20:04:07,799 - app.core.excel.handlers.unit_converter_handlers - INFO - 件单位处理: 数量: 1.0 -> 15.0, 单价: 56.0 -> 3.7333333333333334, 单位: 件 -> 瓶 +2025-05-08 20:04:07,822 - app.core.excel.handlers.unit_converter_handlers - INFO - 件单位处理: 数量: 1.0 -> 15.0, 单价: 30.0 -> 2.0, 单位: 件 -> 瓶 +2025-05-08 20:04:07,823 - app.core.excel.handlers.unit_converter_handlers - INFO - 赠品件单位处理: 数量: 1.0 -> 12.0, 单价: 0, 单位: 件 -> 瓶 +2025-05-08 20:04:07,825 - app.core.excel.handlers.unit_converter_handlers - INFO - 赠品件单位处理: 数量: 1.0 -> 12.0, 单价: 0, 单位: 件 -> 瓶 +2025-05-08 20:04:07,826 - app.core.excel.handlers.unit_converter_handlers - INFO - 赠品瓶单位处理: 保持原样 数量: 2.0, 单价: 0, 单位: 瓶 +2025-05-08 20:04:07,827 - app.core.excel.handlers.unit_converter_handlers - INFO - 赠品瓶单位处理: 保持原样 数量: 1.0, 单价: 0, 单位: 瓶 +2025-05-08 20:04:07,843 - app.core.excel.handlers.unit_converter_handlers - INFO - 赠品瓶单位处理: 保持原样 数量: 9.0, 单价: 0, 单位: 瓶 +2025-05-08 20:46:01,838 - app.core.excel.handlers.unit_converter_handlers - INFO - 件单位处理: 数量: 1.0 -> 15.0, 单价: 68.0 -> 4.533333333333333, 单位: 件 -> 瓶 +2025-05-08 20:46:01,839 - app.core.excel.handlers.unit_converter_handlers - INFO - 件单位处理: 数量: 1.0 -> 15.0, 单价: 68.0 -> 4.533333333333333, 单位: 件 -> 瓶 +2025-05-08 20:46:01,840 - app.core.excel.handlers.unit_converter_handlers - INFO - 件单位处理: 数量: 1.0 -> 15.0, 单价: 56.0 -> 3.7333333333333334, 单位: 件 -> 瓶 +2025-05-08 20:46:01,867 - app.core.excel.handlers.unit_converter_handlers - INFO - 件单位处理: 数量: 1.0 -> 15.0, 单价: 56.0 -> 3.7333333333333334, 单位: 件 -> 瓶 +2025-05-08 20:46:01,868 - app.core.excel.handlers.unit_converter_handlers - INFO - 件单位处理: 数量: 1.0 -> 15.0, 单价: 56.0 -> 3.7333333333333334, 单位: 件 -> 瓶 +2025-05-08 20:46:01,869 - app.core.excel.handlers.unit_converter_handlers - INFO - 件单位处理: 数量: 1.0 -> 15.0, 单价: 30.0 -> 2.0, 单位: 件 -> 瓶 +2025-05-08 20:46:01,870 - app.core.excel.handlers.unit_converter_handlers - INFO - 赠品件单位处理: 数量: 1.0 -> 12.0, 单价: 0, 单位: 件 -> 瓶 +2025-05-08 20:46:01,870 - app.core.excel.handlers.unit_converter_handlers - INFO - 赠品件单位处理: 数量: 1.0 -> 12.0, 单价: 0, 单位: 件 -> 瓶 +2025-05-08 20:46:01,871 - app.core.excel.handlers.unit_converter_handlers - INFO - 赠品瓶单位处理: 保持原样 数量: 2.0, 单价: 0, 单位: 瓶 +2025-05-08 20:46:01,872 - app.core.excel.handlers.unit_converter_handlers - INFO - 赠品瓶单位处理: 保持原样 数量: 1.0, 单价: 0, 单位: 瓶 +2025-05-08 20:46:03,157 - app.core.excel.handlers.unit_converter_handlers - INFO - 赠品瓶单位处理: 保持原样 数量: 9.0, 单价: 0, 单位: 瓶 diff --git a/logs/app.core.excel.merger.log b/logs/app.core.excel.merger.log index 1278319..bd77cdd 100644 --- a/logs/app.core.excel.merger.log +++ b/logs/app.core.excel.merger.log @@ -481,3 +481,15 @@ 2025-05-07 22:28:51,556 - app.core.excel.merger - INFO - 初始化完成,模板文件: templates\银豹-采购单模板.xls 2025-05-07 22:28:53,639 - app.core.excel.merger - INFO - 搜索目录 D:\My Documents\python\orc-order-v2\data\output 中的采购单Excel文件 2025-05-07 22:28:53,640 - app.core.excel.merger - INFO - 找到 1 个采购单Excel文件 +2025-05-08 19:45:41,034 - app.core.excel.merger - INFO - 初始化PurchaseOrderMerger +2025-05-08 19:45:41,035 - app.core.excel.merger - INFO - 初始化完成,模板文件: templates\银豹-采购单模板.xls +2025-05-08 19:45:56,357 - app.core.excel.merger - INFO - 搜索目录 D:\My Documents\python\orc-order-v2\data\output 中的采购单Excel文件 +2025-05-08 19:45:56,358 - app.core.excel.merger - INFO - 找到 1 个采购单Excel文件 +2025-05-08 19:47:28,885 - app.core.excel.merger - INFO - 初始化PurchaseOrderMerger +2025-05-08 19:47:28,886 - app.core.excel.merger - INFO - 初始化完成,模板文件: templates\银豹-采购单模板.xls +2025-05-08 19:51:23,189 - app.core.excel.merger - INFO - 初始化PurchaseOrderMerger +2025-05-08 19:51:23,189 - app.core.excel.merger - INFO - 初始化完成,模板文件: templates\银豹-采购单模板.xls +2025-05-08 20:04:06,909 - app.core.excel.merger - INFO - 初始化PurchaseOrderMerger +2025-05-08 20:04:06,909 - app.core.excel.merger - INFO - 初始化完成,模板文件: templates\银豹-采购单模板.xls +2025-05-08 20:46:00,700 - app.core.excel.merger - INFO - 初始化PurchaseOrderMerger +2025-05-08 20:46:00,701 - app.core.excel.merger - INFO - 初始化完成,模板文件: templates\银豹-采购单模板.xls diff --git a/logs/app.core.excel.processor.log b/logs/app.core.excel.processor.log index 549bbee..7137c3c 100644 --- a/logs/app.core.excel.processor.log +++ b/logs/app.core.excel.processor.log @@ -5374,3 +5374,382 @@ ValueError: could not convert string to float: '2\n96' 2025-05-07 22:28:53,634 - app.core.excel.processor - INFO - 条码 6902538007169 处理结果:正常商品数量15.0,单价3.6666666666666665,赠品数量0 2025-05-07 22:28:53,637 - app.core.excel.processor - INFO - 采购单已保存到: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250507222846.xls 2025-05-07 22:28:53,639 - app.core.excel.processor - INFO - 采购单已保存到: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250507222846.xls +2025-05-08 19:45:41,032 - app.core.excel.processor - INFO - 初始化ExcelProcessor +2025-05-08 19:45:41,034 - app.core.excel.processor - INFO - 初始化完成,模板文件: templates\银豹-采购单模板.xls +2025-05-08 19:45:48,795 - app.core.excel.processor - INFO - 搜索目录 D:\My Documents\python\orc-order-v2\data\output 中的Excel文件 +2025-05-08 19:45:48,797 - app.core.excel.processor - INFO - 找到最新的Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250508194532.xlsx +2025-05-08 19:45:48,798 - app.core.excel.processor - INFO - 开始处理Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250508194532.xlsx +2025-05-08 19:45:49,604 - app.core.excel.processor - INFO - 成功读取Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250508194532.xlsx, 共 13 行 +2025-05-08 19:45:49,606 - app.core.excel.processor - INFO - 找到可能的表头行: 第1行,评分: 60 +2025-05-08 19:45:49,606 - app.core.excel.processor - INFO - 识别到表头在第 1 行 +2025-05-08 19:45:49,623 - app.core.excel.processor - INFO - 使用表头行重新读取数据,共 12 行有效数据 +2025-05-08 19:45:49,623 - app.core.excel.processor - INFO - 找到精确匹配的条码列: 商品条码 +2025-05-08 19:45:49,623 - app.core.excel.processor - INFO - 使用条码列: 商品条码 +2025-05-08 19:45:49,623 - app.core.excel.processor - INFO - 找到name列: 商品名称 +2025-05-08 19:45:49,623 - app.core.excel.processor - INFO - 找到specification列: 规格型号 +2025-05-08 19:45:49,623 - app.core.excel.processor - INFO - 找到quantity列: 数量 +2025-05-08 19:45:49,624 - app.core.excel.processor - INFO - 找到unit列: 单位 +2025-05-08 19:45:49,624 - app.core.excel.processor - INFO - 找到price列: 单价 +2025-05-08 19:45:49,624 - app.core.excel.processor - INFO - 列名映射结果: {'barcode': '商品条码', 'name': '商品名称', 'specification': '规格型号', 'quantity': '数量', 'unit': '单位', 'price': '单价'} +2025-05-08 19:45:49,625 - app.core.excel.processor - INFO - 是否存在规格列: False +2025-05-08 19:45:49,625 - app.core.excel.processor - INFO - 第1行: 提取商品信息 条码=6937003700207, 名称=外星人PRO运动恢复类-原味, 规格=, 数量=0, 单位=件, 单价=68.0 +2025-05-08 19:45:49,628 - app.core.excel.processor - INFO - 第2行: 提取商品信息 条码=6937003706346, 名称=600ml外星人电解质西柚, 规格=, 数量=0, 单位=件, 单价=68.0 +2025-05-08 19:45:49,629 - app.core.excel.processor - INFO - 第3行: 提取商品信息 条码=6937003706568, 名称=外星人-维B水阳光青提500mL, 规格=, 数量=0, 单位=件, 单价=56.0 +2025-05-08 19:45:49,637 - app.core.excel.processor - INFO - 从商品名称推断规格: 外星人-维B水阳光青提500mL -> 500*None, 包装数量=None +2025-05-08 19:45:49,639 - app.core.excel.processor - INFO - 第4行: 提取商品信息 条码=6937003708173, 名称=天山爷爷新疆甜石榴复合果蔬汁饮品, 规格=, 数量=0, 单位=件, 单价=56.0 +2025-05-08 19:45:49,640 - app.core.excel.processor - INFO - 第5行: 提取商品信息 条码=6937003708166, 名称=天山爷爷新疆玉葡萄复合果汁饮品, 规格=, 数量=0, 单位=件, 单价=56.0 +2025-05-08 19:45:49,640 - app.core.excel.processor - INFO - 第6行: 提取商品信息 条码=6937003703086, 名称=外星人WAVE风味水饮品淡柠檬味, 规格=, 数量=0, 单位=件, 单价=30.0 +2025-05-08 19:45:49,641 - app.core.excel.processor - INFO - 第7行: 提取商品信息 条码=6975176784785, 名称=大柠檬冰茶, 规格=, 数量=0, 单位=件, 单价=0.0 +2025-05-08 19:45:49,641 - app.core.excel.processor - INFO - 第8行: 提取商品信息 条码=6937003703437, 名称=大葡萄柚冰绿茶, 规格=, 数量=0, 单位=件, 单价=0.0 +2025-05-08 19:45:49,879 - app.core.excel.processor - INFO - 第9行: 提取商品信息 条码=6937003708173, 名称=天山爷爷新疆甜石复合果蔬汁饮品, 规格=, 数量=2.0, 单位=瓶, 单价=0.0 +2025-05-08 19:45:49,880 - app.core.excel.processor - INFO - 第10行: 提取商品信息 条码=6975176781777, 名称=日式可乐味苏打气泡水, 规格=, 数量=0, 单位=瓶, 单价=0.0 +2025-05-08 19:45:49,882 - app.core.excel.processor - INFO - 第11行: 提取商品信息 条码=6975176782460, 名称=燃茶无糖茉莉花茶无糖饮料, 规格=, 数量=9.0, 单位=瓶, 单价=0.0 +2025-05-08 19:45:49,883 - app.core.excel.processor - INFO - 提取到 11 个商品信息 +2025-05-08 19:45:49,892 - app.core.excel.processor - INFO - 开始处理11 个产品信息 +2025-05-08 19:45:49,893 - app.core.excel.processor - INFO - 处理商品: 条码=6937003700207, 数量=0.0, 单价=68.0, 是否赠品=False +2025-05-08 19:45:49,893 - app.core.excel.processor - INFO - 发现正常商品:条码6937003700207, 数量=0.0, 单价=68.0 +2025-05-08 19:45:49,893 - app.core.excel.processor - INFO - 处理商品: 条码=6937003706346, 数量=0.0, 单价=68.0, 是否赠品=False +2025-05-08 19:45:49,893 - app.core.excel.processor - INFO - 发现正常商品:条码6937003706346, 数量=0.0, 单价=68.0 +2025-05-08 19:45:49,894 - app.core.excel.processor - INFO - 处理商品: 条码=6937003706568, 数量=0.0, 单价=56.0, 是否赠品=False +2025-05-08 19:45:49,894 - app.core.excel.processor - INFO - 发现正常商品:条码6937003706568, 数量=0.0, 单价=56.0 +2025-05-08 19:45:49,894 - app.core.excel.processor - INFO - 处理商品: 条码=6937003708173, 数量=0.0, 单价=56.0, 是否赠品=False +2025-05-08 19:45:49,894 - app.core.excel.processor - INFO - 发现正常商品:条码6937003708173, 数量=0.0, 单价=56.0 +2025-05-08 19:45:49,894 - app.core.excel.processor - INFO - 处理商品: 条码=6937003708166, 数量=0.0, 单价=56.0, 是否赠品=False +2025-05-08 19:45:49,894 - app.core.excel.processor - INFO - 发现正常商品:条码6937003708166, 数量=0.0, 单价=56.0 +2025-05-08 19:45:49,895 - app.core.excel.processor - INFO - 处理商品: 条码=6937003703086, 数量=0.0, 单价=30.0, 是否赠品=False +2025-05-08 19:45:49,895 - app.core.excel.processor - INFO - 发现正常商品:条码6937003703086, 数量=0.0, 单价=30.0 +2025-05-08 19:45:49,895 - app.core.excel.processor - INFO - 处理商品: 条码=6975176784785, 数量=0.0, 单价=0.0, 是否赠品=True +2025-05-08 19:45:49,895 - app.core.excel.processor - INFO - 发现赠品:条码6975176784785, 数量=0.0 +2025-05-08 19:45:49,895 - app.core.excel.processor - INFO - 处理商品: 条码=6937003703437, 数量=0.0, 单价=0.0, 是否赠品=True +2025-05-08 19:45:49,895 - app.core.excel.processor - INFO - 发现赠品:条码6937003703437, 数量=0.0 +2025-05-08 19:45:49,895 - app.core.excel.processor - INFO - 处理商品: 条码=6937003708173, 数量=2.0, 单价=0.0, 是否赠品=True +2025-05-08 19:45:49,896 - app.core.excel.processor - INFO - 发现赠品:条码6937003708173, 数量=2.0 +2025-05-08 19:45:49,896 - app.core.excel.processor - INFO - 处理商品: 条码=6975176781777, 数量=0.0, 单价=0.0, 是否赠品=True +2025-05-08 19:45:49,896 - app.core.excel.processor - INFO - 发现赠品:条码6975176781777, 数量=0.0 +2025-05-08 19:45:49,896 - app.core.excel.processor - INFO - 处理商品: 条码=6975176782460, 数量=9.0, 单价=0.0, 是否赠品=True +2025-05-08 19:45:49,897 - app.core.excel.processor - INFO - 发现赠品:条码6975176782460, 数量=9.0 +2025-05-08 19:45:49,897 - app.core.excel.processor - INFO - 分组后共10 个不同条码的商品 +2025-05-08 19:45:49,897 - app.core.excel.processor - INFO - 条码 6937003700207 处理结果:正常商品数量0.0,单价68.0,赠品数量0 +2025-05-08 19:45:49,897 - app.core.excel.processor - INFO - 条码 6937003706346 处理结果:正常商品数量0.0,单价68.0,赠品数量0 +2025-05-08 19:45:49,897 - app.core.excel.processor - INFO - 条码 6937003706568 处理结果:正常商品数量0.0,单价56.0,赠品数量0 +2025-05-08 19:45:49,897 - app.core.excel.processor - INFO - 条码 6937003708173 处理结果:正常商品数量0.0,单价56.0,赠品数量2.0 +2025-05-08 19:45:49,898 - app.core.excel.processor - INFO - 条码 6937003708166 处理结果:正常商品数量0.0,单价56.0,赠品数量0 +2025-05-08 19:45:56,348 - app.core.excel.processor - INFO - 条码 6937003703086 处理结果:正常商品数量0.0,单价30.0,赠品数量0 +2025-05-08 19:45:56,348 - app.core.excel.processor - INFO - 条码 6975176784785 处理结果:只有赠品,数量=0.0 +2025-05-08 19:45:56,348 - app.core.excel.processor - INFO - 条码 6937003703437 处理结果:只有赠品,数量=0.0 +2025-05-08 19:45:56,348 - app.core.excel.processor - INFO - 条码 6975176781777 处理结果:只有赠品,数量=0.0 +2025-05-08 19:45:56,348 - app.core.excel.processor - INFO - 条码 6975176782460 处理结果:只有赠品,数量=9.0 +2025-05-08 19:45:56,349 - app.core.excel.processor - INFO - 条码 6937003708173 填充:采购量=0.0,赠品数量2.0 +2025-05-08 19:45:56,349 - app.core.excel.processor - INFO - 条码 6975176784785 填充:仅有赠品,采购量=0,赠品数量=0.0 +2025-05-08 19:45:56,349 - app.core.excel.processor - INFO - 条码 6937003703437 填充:仅有赠品,采购量=0,赠品数量=0.0 +2025-05-08 19:45:56,349 - app.core.excel.processor - INFO - 条码 6975176781777 填充:仅有赠品,采购量=0,赠品数量=0.0 +2025-05-08 19:45:56,349 - app.core.excel.processor - INFO - 条码 6975176782460 填充:仅有赠品,采购量=0,赠品数量=9.0 +2025-05-08 19:45:56,354 - app.core.excel.processor - INFO - 采购单已保存到: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250508194532.xls +2025-05-08 19:45:56,356 - app.core.excel.processor - INFO - 采购单已保存到: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250508194532.xls +2025-05-08 19:47:28,884 - app.core.excel.processor - INFO - 初始化ExcelProcessor +2025-05-08 19:47:28,885 - app.core.excel.processor - INFO - 初始化完成,模板文件: templates\银豹-采购单模板.xls +2025-05-08 19:47:28,886 - app.core.excel.processor - INFO - 开始处理Excel文件: D:/My Documents/python/orc-order-v2/data/output/微信图片_20250508194532.xlsx +2025-05-08 19:47:29,546 - app.core.excel.processor - INFO - 成功读取Excel文件: D:/My Documents/python/orc-order-v2/data/output/微信图片_20250508194532.xlsx, 共 13 行 +2025-05-08 19:47:29,551 - app.core.excel.processor - INFO - 找到可能的表头行: 第1行,评分: 60 +2025-05-08 19:47:29,551 - app.core.excel.processor - INFO - 识别到表头在第 1 行 +2025-05-08 19:47:29,589 - app.core.excel.processor - INFO - 使用表头行重新读取数据,共 12 行有效数据 +2025-05-08 19:47:29,590 - app.core.excel.processor - INFO - 找到精确匹配的条码列: 商品条码 +2025-05-08 19:47:29,590 - app.core.excel.processor - INFO - 使用条码列: 商品条码 +2025-05-08 19:47:29,590 - app.core.excel.processor - INFO - 找到name列: 商品名称 +2025-05-08 19:47:29,590 - app.core.excel.processor - INFO - 找到specification列: 规格型号 +2025-05-08 19:47:29,590 - app.core.excel.processor - INFO - 找到quantity列: 数量 +2025-05-08 19:47:29,590 - app.core.excel.processor - INFO - 找到unit列: 单位 +2025-05-08 19:47:29,590 - app.core.excel.processor - INFO - 找到price列: 单价 +2025-05-08 19:47:29,590 - app.core.excel.processor - INFO - 列名映射结果: {'barcode': '商品条码', 'name': '商品名称', 'specification': '规格型号', 'quantity': '数量', 'unit': '单位', 'price': '单价'} +2025-05-08 19:47:29,590 - app.core.excel.processor - INFO - 是否存在规格列: False +2025-05-08 19:47:29,591 - app.core.excel.processor - INFO - 第1行: 提取商品信息 条码=6937003700207, 名称=外星人PRO运动恢复类-原味, 规格=, 数量=1.0, 单位=件, 单价=68.0 +2025-05-08 19:47:29,597 - app.core.excel.processor - INFO - 第2行: 提取商品信息 条码=6937003706346, 名称=600ml外星人电解质西柚, 规格=, 数量=1.0, 单位=件, 单价=68.0 +2025-05-08 19:47:29,598 - app.core.excel.processor - INFO - 第3行: 提取商品信息 条码=6937003706568, 名称=外星人-维B水阳光青提500mL, 规格=, 数量=1.0, 单位=件, 单价=56.0 +2025-05-08 19:47:29,600 - app.core.excel.processor - INFO - 从商品名称推断规格: 外星人-维B水阳光青提500mL -> 500*None, 包装数量=None +2025-05-08 19:47:29,602 - app.core.excel.processor - INFO - 第4行: 提取商品信息 条码=6937003708173, 名称=天山爷爷新疆甜石榴复合果蔬汁饮品, 规格=, 数量=1.0, 单位=件, 单价=56.0 +2025-05-08 19:47:29,602 - app.core.excel.processor - INFO - 第5行: 提取商品信息 条码=6937003708166, 名称=天山爷爷新疆玉葡萄复合果汁饮品, 规格=, 数量=1.0, 单位=件, 单价=56.0 +2025-05-08 19:47:29,602 - app.core.excel.processor - INFO - 第6行: 提取商品信息 条码=6937003703086, 名称=外星人WAVE风味水饮品淡柠檬味, 规格=, 数量=1.0, 单位=件, 单价=30.0 +2025-05-08 19:47:29,603 - app.core.excel.processor - INFO - 第7行: 提取商品信息 条码=6975176784785, 名称=大柠檬冰茶, 规格=, 数量=1.0, 单位=件, 单价=0.0 +2025-05-08 19:47:29,603 - app.core.excel.processor - INFO - 第8行: 提取商品信息 条码=6937003703437, 名称=大葡萄柚冰绿茶, 规格=, 数量=1.0, 单位=件, 单价=0.0 +2025-05-08 19:47:29,603 - app.core.excel.processor - INFO - 第9行: 提取商品信息 条码=6937003708173, 名称=天山爷爷新疆甜石复合果蔬汁饮品, 规格=, 数量=2.0, 单位=瓶, 单价=0.0 +2025-05-08 19:47:29,604 - app.core.excel.processor - INFO - 第10行: 提取商品信息 条码=6975176781777, 名称=日式可乐味苏打气泡水, 规格=, 数量=1.0, 单位=瓶, 单价=0.0 +2025-05-08 19:47:29,604 - app.core.excel.processor - INFO - 第11行: 提取商品信息 条码=6975176782460, 名称=燃茶无糖茉莉花茶无糖饮料, 规格=, 数量=9.0, 单位=瓶, 单价=0.0 +2025-05-08 19:47:29,604 - app.core.excel.processor - INFO - 提取到 11 个商品信息 +2025-05-08 19:47:29,616 - app.core.excel.processor - INFO - 开始处理11 个产品信息 +2025-05-08 19:47:29,616 - app.core.excel.processor - INFO - 处理商品: 条码=6937003700207, 数量=1.0, 单价=68.0, 是否赠品=False +2025-05-08 19:47:29,616 - app.core.excel.processor - INFO - 发现正常商品:条码6937003700207, 数量=1.0, 单价=68.0 +2025-05-08 19:47:29,617 - app.core.excel.processor - INFO - 处理商品: 条码=6937003706346, 数量=1.0, 单价=68.0, 是否赠品=False +2025-05-08 19:47:29,617 - app.core.excel.processor - INFO - 发现正常商品:条码6937003706346, 数量=1.0, 单价=68.0 +2025-05-08 19:47:29,617 - app.core.excel.processor - INFO - 处理商品: 条码=6937003706568, 数量=1.0, 单价=56.0, 是否赠品=False +2025-05-08 19:47:29,673 - app.core.excel.processor - INFO - 发现正常商品:条码6937003706568, 数量=1.0, 单价=56.0 +2025-05-08 19:47:29,673 - app.core.excel.processor - INFO - 处理商品: 条码=6937003708173, 数量=1.0, 单价=56.0, 是否赠品=False +2025-05-08 19:47:29,673 - app.core.excel.processor - INFO - 发现正常商品:条码6937003708173, 数量=1.0, 单价=56.0 +2025-05-08 19:47:29,673 - app.core.excel.processor - INFO - 处理商品: 条码=6937003708166, 数量=1.0, 单价=56.0, 是否赠品=False +2025-05-08 19:47:29,674 - app.core.excel.processor - INFO - 发现正常商品:条码6937003708166, 数量=1.0, 单价=56.0 +2025-05-08 19:47:29,674 - app.core.excel.processor - INFO - 处理商品: 条码=6937003703086, 数量=1.0, 单价=30.0, 是否赠品=False +2025-05-08 19:47:29,674 - app.core.excel.processor - INFO - 发现正常商品:条码6937003703086, 数量=1.0, 单价=30.0 +2025-05-08 19:47:29,674 - app.core.excel.processor - INFO - 处理商品: 条码=6975176784785, 数量=1.0, 单价=0.0, 是否赠品=True +2025-05-08 19:47:29,674 - app.core.excel.processor - INFO - 发现赠品:条码6975176784785, 数量=1.0 +2025-05-08 19:47:29,674 - app.core.excel.processor - INFO - 处理商品: 条码=6937003703437, 数量=1.0, 单价=0.0, 是否赠品=True +2025-05-08 19:47:29,674 - app.core.excel.processor - INFO - 发现赠品:条码6937003703437, 数量=1.0 +2025-05-08 19:47:29,674 - app.core.excel.processor - INFO - 处理商品: 条码=6937003708173, 数量=2.0, 单价=0.0, 是否赠品=True +2025-05-08 19:47:29,675 - app.core.excel.processor - INFO - 发现赠品:条码6937003708173, 数量=2.0 +2025-05-08 19:47:29,675 - app.core.excel.processor - INFO - 处理商品: 条码=6975176781777, 数量=1.0, 单价=0.0, 是否赠品=True +2025-05-08 19:47:29,675 - app.core.excel.processor - INFO - 发现赠品:条码6975176781777, 数量=1.0 +2025-05-08 19:47:29,675 - app.core.excel.processor - INFO - 处理商品: 条码=6975176782460, 数量=9.0, 单价=0.0, 是否赠品=True +2025-05-08 19:47:29,675 - app.core.excel.processor - INFO - 发现赠品:条码6975176782460, 数量=9.0 +2025-05-08 19:47:29,675 - app.core.excel.processor - INFO - 分组后共10 个不同条码的商品 +2025-05-08 19:47:29,675 - app.core.excel.processor - INFO - 条码 6937003700207 处理结果:正常商品数量1.0,单价68.0,赠品数量0 +2025-05-08 19:47:29,675 - app.core.excel.processor - INFO - 条码 6937003706346 处理结果:正常商品数量1.0,单价68.0,赠品数量0 +2025-05-08 19:47:29,675 - app.core.excel.processor - INFO - 条码 6937003706568 处理结果:正常商品数量1.0,单价56.0,赠品数量0 +2025-05-08 19:47:29,676 - app.core.excel.processor - INFO - 条码 6937003708173 处理结果:正常商品数量1.0,单价56.0,赠品数量2.0 +2025-05-08 19:47:29,676 - app.core.excel.processor - INFO - 条码 6937003708166 处理结果:正常商品数量1.0,单价56.0,赠品数量0 +2025-05-08 19:47:29,676 - app.core.excel.processor - INFO - 条码 6937003703086 处理结果:正常商品数量1.0,单价30.0,赠品数量0 +2025-05-08 19:47:29,676 - app.core.excel.processor - INFO - 条码 6975176784785 处理结果:只有赠品,数量=1.0 +2025-05-08 19:47:29,676 - app.core.excel.processor - INFO - 条码 6937003703437 处理结果:只有赠品,数量=1.0 +2025-05-08 19:47:29,676 - app.core.excel.processor - INFO - 条码 6975176781777 处理结果:只有赠品,数量=1.0 +2025-05-08 19:47:29,676 - app.core.excel.processor - INFO - 条码 6975176782460 处理结果:只有赠品,数量=9.0 +2025-05-08 19:47:29,677 - app.core.excel.processor - INFO - 条码 6937003708173 填充:采购量=1.0,赠品数量2.0 +2025-05-08 19:47:29,677 - app.core.excel.processor - INFO - 条码 6975176784785 填充:仅有赠品,采购量=0,赠品数量=1.0 +2025-05-08 19:47:29,677 - app.core.excel.processor - INFO - 条码 6937003703437 填充:仅有赠品,采购量=0,赠品数量=1.0 +2025-05-08 19:47:29,677 - app.core.excel.processor - INFO - 条码 6975176781777 填充:仅有赠品,采购量=0,赠品数量=1.0 +2025-05-08 19:47:29,677 - app.core.excel.processor - INFO - 条码 6975176782460 填充:仅有赠品,采购量=0,赠品数量=9.0 +2025-05-08 19:47:29,681 - app.core.excel.processor - INFO - 采购单已保存到: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250508194532.xls +2025-05-08 19:47:29,683 - app.core.excel.processor - INFO - 采购单已保存到: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250508194532.xls +2025-05-08 19:51:23,178 - app.core.excel.processor - INFO - 初始化ExcelProcessor +2025-05-08 19:51:23,188 - app.core.excel.processor - INFO - 初始化完成,模板文件: templates\银豹-采购单模板.xls +2025-05-08 19:51:23,190 - app.core.excel.processor - INFO - 开始处理Excel文件: D:/My Documents/python/orc-order-v2/data/output/微信图片_20250508194532.xlsx +2025-05-08 19:51:24,003 - app.core.excel.processor - INFO - 成功读取Excel文件: D:/My Documents/python/orc-order-v2/data/output/微信图片_20250508194532.xlsx, 共 13 行 +2025-05-08 19:51:24,008 - app.core.excel.processor - INFO - 找到可能的表头行: 第1行,评分: 60 +2025-05-08 19:51:24,008 - app.core.excel.processor - INFO - 识别到表头在第 1 行 +2025-05-08 19:51:24,060 - app.core.excel.processor - INFO - 使用表头行重新读取数据,共 12 行有效数据 +2025-05-08 19:51:24,060 - app.core.excel.processor - INFO - 找到精确匹配的条码列: 商品条码 +2025-05-08 19:51:24,061 - app.core.excel.processor - INFO - 使用条码列: 商品条码 +2025-05-08 19:51:24,061 - app.core.excel.processor - INFO - 找到name列: 商品名称 +2025-05-08 19:51:24,061 - app.core.excel.processor - INFO - 找到specification列: 规格型号 +2025-05-08 19:51:24,061 - app.core.excel.processor - INFO - 找到quantity列: 数量 +2025-05-08 19:51:24,061 - app.core.excel.processor - INFO - 找到unit列: 单位 +2025-05-08 19:51:24,061 - app.core.excel.processor - INFO - 找到price列: 单价 +2025-05-08 19:51:24,061 - app.core.excel.processor - INFO - 列名映射结果: {'barcode': '商品条码', 'name': '商品名称', 'specification': '规格型号', 'quantity': '数量', 'unit': '单位', 'price': '单价'} +2025-05-08 19:51:24,061 - app.core.excel.processor - INFO - 是否存在规格列: False +2025-05-08 19:51:24,075 - app.core.excel.processor - INFO - 第1行: 提取商品信息 条码=6937003700207, 名称=外星人PRO运动恢复类-原味, 规格=, 数量=1.0, 单位=件, 单价=68.0 +2025-05-08 19:51:24,076 - app.core.excel.processor - INFO - 从映射列解析规格: 500ml*15 -> 包装数量=15 +2025-05-08 19:51:24,077 - app.core.excel.processor - INFO - 第2行: 提取商品信息 条码=6937003706346, 名称=600ml外星人电解质西柚, 规格=, 数量=1.0, 单位=件, 单价=68.0 +2025-05-08 19:51:24,078 - app.core.excel.processor - INFO - 从映射列解析规格: 600ml*15 -> 包装数量=15 +2025-05-08 19:51:24,081 - app.core.excel.processor - INFO - 第3行: 提取商品信息 条码=6937003706568, 名称=外星人-维B水阳光青提500mL, 规格=, 数量=1.0, 单位=件, 单价=56.0 +2025-05-08 19:51:24,081 - app.core.excel.processor - INFO - 从映射列解析规格: 500ml*15 -> 包装数量=15 +2025-05-08 19:51:24,083 - app.core.excel.processor - INFO - 第4行: 提取商品信息 条码=6937003708173, 名称=天山爷爷新疆甜石榴复合果蔬汁饮品, 规格=, 数量=1.0, 单位=件, 单价=56.0 +2025-05-08 19:51:24,088 - app.core.excel.processor - INFO - 第5行: 提取商品信息 条码=6937003708166, 名称=天山爷爷新疆玉葡萄复合果汁饮品, 规格=, 数量=1.0, 单位=件, 单价=56.0 +2025-05-08 19:51:24,089 - app.core.excel.processor - INFO - 第6行: 提取商品信息 条码=6937003703086, 名称=外星人WAVE风味水饮品淡柠檬味, 规格=, 数量=1.0, 单位=件, 单价=30.0 +2025-05-08 19:51:24,089 - app.core.excel.processor - INFO - 从映射列解析规格: 600ml*15 -> 包装数量=15 +2025-05-08 19:51:24,090 - app.core.excel.processor - INFO - 第7行: 提取商品信息 条码=6975176784785, 名称=大柠檬冰茶, 规格=, 数量=1.0, 单位=件, 单价=0.0 +2025-05-08 19:51:24,104 - app.core.excel.processor - INFO - 从映射列解析规格: 900ml*12 -> 包装数量=12 +2025-05-08 19:51:24,105 - app.core.excel.processor - INFO - 第8行: 提取商品信息 条码=6937003703437, 名称=大葡萄柚冰绿茶, 规格=, 数量=1.0, 单位=件, 单价=0.0 +2025-05-08 19:51:24,105 - app.core.excel.processor - INFO - 从映射列解析规格: 900ml*12 -> 包装数量=12 +2025-05-08 19:51:24,106 - app.core.excel.processor - INFO - 第9行: 提取商品信息 条码=6937003708173, 名称=天山爷爷新疆甜石复合果蔬汁饮品, 规格=, 数量=2.0, 单位=瓶, 单价=0.0 +2025-05-08 19:51:24,107 - app.core.excel.processor - INFO - 第10行: 提取商品信息 条码=6975176781777, 名称=日式可乐味苏打气泡水, 规格=, 数量=1.0, 单位=瓶, 单价=0.0 +2025-05-08 19:51:24,107 - app.core.excel.processor - INFO - 从映射列解析规格: 480ml*15 -> 包装数量=15 +2025-05-08 19:51:24,108 - app.core.excel.processor - INFO - 第11行: 提取商品信息 条码=6975176782460, 名称=燃茶无糖茉莉花茶无糖饮料, 规格=, 数量=9.0, 单位=瓶, 单价=0.0 +2025-05-08 19:51:24,108 - app.core.excel.processor - INFO - 从映射列解析规格: 500ml*15 -> 包装数量=15 +2025-05-08 19:51:24,109 - app.core.excel.processor - INFO - 提取到 11 个商品信息 +2025-05-08 19:51:24,122 - app.core.excel.processor - INFO - 开始处理11 个产品信息 +2025-05-08 19:51:24,122 - app.core.excel.processor - INFO - 处理商品: 条码=6937003700207, 数量=15.0, 单价=4.533333333333333, 是否赠品=False +2025-05-08 19:51:24,122 - app.core.excel.processor - INFO - 发现正常商品:条码6937003700207, 数量=15.0, 单价=4.533333333333333 +2025-05-08 19:51:24,123 - app.core.excel.processor - INFO - 处理商品: 条码=6937003706346, 数量=15.0, 单价=4.533333333333333, 是否赠品=False +2025-05-08 19:51:24,123 - app.core.excel.processor - INFO - 发现正常商品:条码6937003706346, 数量=15.0, 单价=4.533333333333333 +2025-05-08 19:51:24,123 - app.core.excel.processor - INFO - 处理商品: 条码=6937003706568, 数量=15.0, 单价=3.7333333333333334, 是否赠品=False +2025-05-08 19:51:24,123 - app.core.excel.processor - INFO - 发现正常商品:条码6937003706568, 数量=15.0, 单价=3.7333333333333334 +2025-05-08 19:51:24,123 - app.core.excel.processor - INFO - 处理商品: 条码=6937003708173, 数量=15.0, 单价=3.7333333333333334, 是否赠品=False +2025-05-08 19:51:24,123 - app.core.excel.processor - INFO - 发现正常商品:条码6937003708173, 数量=15.0, 单价=3.7333333333333334 +2025-05-08 19:51:24,123 - app.core.excel.processor - INFO - 处理商品: 条码=6937003708166, 数量=15.0, 单价=3.7333333333333334, 是否赠品=False +2025-05-08 19:51:24,124 - app.core.excel.processor - INFO - 发现正常商品:条码6937003708166, 数量=15.0, 单价=3.7333333333333334 +2025-05-08 19:51:24,124 - app.core.excel.processor - INFO - 处理商品: 条码=6937003703086, 数量=15.0, 单价=2.0, 是否赠品=False +2025-05-08 19:51:24,124 - app.core.excel.processor - INFO - 发现正常商品:条码6937003703086, 数量=15.0, 单价=2.0 +2025-05-08 19:51:24,124 - app.core.excel.processor - INFO - 处理商品: 条码=6975176784785, 数量=12.0, 单价=0, 是否赠品=True +2025-05-08 19:51:27,449 - app.core.excel.processor - INFO - 发现赠品:条码6975176784785, 数量=12.0 +2025-05-08 19:51:27,450 - app.core.excel.processor - INFO - 处理商品: 条码=6937003703437, 数量=12.0, 单价=0, 是否赠品=True +2025-05-08 19:51:27,450 - app.core.excel.processor - INFO - 发现赠品:条码6937003703437, 数量=12.0 +2025-05-08 19:51:27,450 - app.core.excel.processor - INFO - 处理商品: 条码=6937003708173, 数量=2.0, 单价=0, 是否赠品=True +2025-05-08 19:51:27,450 - app.core.excel.processor - INFO - 发现赠品:条码6937003708173, 数量=2.0 +2025-05-08 19:51:27,450 - app.core.excel.processor - INFO - 处理商品: 条码=6975176781777, 数量=1.0, 单价=0, 是否赠品=True +2025-05-08 19:51:27,450 - app.core.excel.processor - INFO - 发现赠品:条码6975176781777, 数量=1.0 +2025-05-08 19:51:27,451 - app.core.excel.processor - INFO - 处理商品: 条码=6975176782460, 数量=9.0, 单价=0, 是否赠品=True +2025-05-08 19:51:27,451 - app.core.excel.processor - INFO - 发现赠品:条码6975176782460, 数量=9.0 +2025-05-08 19:51:27,451 - app.core.excel.processor - INFO - 分组后共10 个不同条码的商品 +2025-05-08 19:51:27,451 - app.core.excel.processor - INFO - 条码 6937003700207 处理结果:正常商品数量15.0,单价4.533333333333333,赠品数量0 +2025-05-08 19:51:27,451 - app.core.excel.processor - INFO - 条码 6937003706346 处理结果:正常商品数量15.0,单价4.533333333333333,赠品数量0 +2025-05-08 19:51:27,452 - app.core.excel.processor - INFO - 条码 6937003706568 处理结果:正常商品数量15.0,单价3.7333333333333334,赠品数量0 +2025-05-08 19:51:27,452 - app.core.excel.processor - INFO - 条码 6937003708173 处理结果:正常商品数量15.0,单价3.7333333333333334,赠品数量2.0 +2025-05-08 19:51:27,452 - app.core.excel.processor - INFO - 条码 6937003708166 处理结果:正常商品数量15.0,单价3.7333333333333334,赠品数量0 +2025-05-08 19:51:27,452 - app.core.excel.processor - INFO - 条码 6937003703086 处理结果:正常商品数量15.0,单价2.0,赠品数量0 +2025-05-08 19:51:27,452 - app.core.excel.processor - INFO - 条码 6975176784785 处理结果:只有赠品,数量=12.0 +2025-05-08 19:51:27,452 - app.core.excel.processor - INFO - 条码 6937003703437 处理结果:只有赠品,数量=12.0 +2025-05-08 19:51:27,452 - app.core.excel.processor - INFO - 条码 6975176781777 处理结果:只有赠品,数量=1.0 +2025-05-08 19:51:27,452 - app.core.excel.processor - INFO - 条码 6975176782460 处理结果:只有赠品,数量=9.0 +2025-05-08 19:51:27,453 - app.core.excel.processor - INFO - 条码 6937003708173 填充:采购量=15.0,赠品数量2.0 +2025-05-08 19:51:27,453 - app.core.excel.processor - INFO - 条码 6975176784785 填充:仅有赠品,采购量=0,赠品数量=12.0 +2025-05-08 19:51:27,453 - app.core.excel.processor - INFO - 条码 6937003703437 填充:仅有赠品,采购量=0,赠品数量=12.0 +2025-05-08 19:51:27,453 - app.core.excel.processor - INFO - 条码 6975176781777 填充:仅有赠品,采购量=0,赠品数量=1.0 +2025-05-08 19:51:27,453 - app.core.excel.processor - INFO - 条码 6975176782460 填充:仅有赠品,采购量=0,赠品数量=9.0 +2025-05-08 19:51:27,457 - app.core.excel.processor - INFO - 采购单已保存到: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250508194532.xls +2025-05-08 19:51:27,459 - app.core.excel.processor - INFO - 采购单已保存到: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250508194532.xls +2025-05-08 20:04:06,908 - app.core.excel.processor - INFO - 初始化ExcelProcessor +2025-05-08 20:04:06,908 - app.core.excel.processor - INFO - 初始化完成,模板文件: templates\银豹-采购单模板.xls +2025-05-08 20:04:06,910 - app.core.excel.processor - INFO - 开始处理Excel文件: D:/My Documents/python/orc-order-v2/data/output/微信图片_20250508194532.xlsx +2025-05-08 20:04:07,659 - app.core.excel.processor - INFO - 成功读取Excel文件: D:/My Documents/python/orc-order-v2/data/output/微信图片_20250508194532.xlsx, 共 13 行 +2025-05-08 20:04:07,674 - app.core.excel.processor - INFO - 找到可能的表头行: 第1行,评分: 60 +2025-05-08 20:04:07,674 - app.core.excel.processor - INFO - 识别到表头在第 1 行 +2025-05-08 20:04:07,785 - app.core.excel.processor - INFO - 使用表头行重新读取数据,共 12 行有效数据 +2025-05-08 20:04:07,786 - app.core.excel.processor - INFO - 找到精确匹配的条码列: 商品条码 +2025-05-08 20:04:07,786 - app.core.excel.processor - INFO - 使用条码列: 商品条码 +2025-05-08 20:04:07,786 - app.core.excel.processor - INFO - 找到name列: 商品名称 +2025-05-08 20:04:07,786 - app.core.excel.processor - INFO - 找到specification列: 规格型号 +2025-05-08 20:04:07,786 - app.core.excel.processor - INFO - 找到quantity列: 数量 +2025-05-08 20:04:07,787 - app.core.excel.processor - INFO - 找到unit列: 单位 +2025-05-08 20:04:07,787 - app.core.excel.processor - INFO - 找到price列: 单价 +2025-05-08 20:04:07,787 - app.core.excel.processor - INFO - 列名映射结果: {'barcode': '商品条码', 'name': '商品名称', 'specification': '规格型号', 'quantity': '数量', 'unit': '单位', 'price': '单价'} +2025-05-08 20:04:07,787 - app.core.excel.processor - INFO - 是否存在规格列: False +2025-05-08 20:04:07,788 - app.core.excel.processor - INFO - 第1行: 提取商品信息 条码=6937003700207, 名称=外星人PRO运动恢复类-原味, 规格=, 数量=1.0, 单位=件, 单价=68.0 +2025-05-08 20:04:07,789 - app.core.excel.processor - INFO - 从映射列解析规格: 500ml*15 -> 包装数量=15 +2025-05-08 20:04:07,792 - app.core.excel.processor - INFO - 第2行: 提取商品信息 条码=6937003706346, 名称=600ml外星人电解质西柚, 规格=, 数量=1.0, 单位=件, 单价=68.0 +2025-05-08 20:04:07,792 - app.core.excel.processor - INFO - 从映射列解析规格: 600ml*15 -> 包装数量=15 +2025-05-08 20:04:07,793 - app.core.excel.processor - INFO - 第3行: 提取商品信息 条码=6937003706568, 名称=外星人-维B水阳光青提500mL, 规格=, 数量=1.0, 单位=件, 单价=56.0 +2025-05-08 20:04:07,794 - app.core.excel.processor - INFO - 从映射列解析规格: 500ml*15 -> 包装数量=15 +2025-05-08 20:04:07,796 - app.core.excel.processor - INFO - 第4行: 提取商品信息 条码=6937003708173, 名称=天山爷爷新疆甜石榴复合果蔬汁饮品, 规格=, 数量=1.0, 单位=件, 单价=56.0 +2025-05-08 20:04:07,799 - app.core.excel.processor - INFO - 第5行: 提取商品信息 条码=6937003708166, 名称=天山爷爷新疆玉葡萄复合果汁饮品, 规格=, 数量=1.0, 单位=件, 单价=56.0 +2025-05-08 20:04:07,818 - app.core.excel.processor - INFO - 第6行: 提取商品信息 条码=6937003703086, 名称=外星人WAVE风味水饮品淡柠檬味, 规格=, 数量=1.0, 单位=件, 单价=30.0 +2025-05-08 20:04:07,821 - app.core.excel.processor - INFO - 从映射列解析规格: 600ml*15 -> 包装数量=15 +2025-05-08 20:04:07,822 - app.core.excel.processor - INFO - 第7行: 提取商品信息 条码=6975176784785, 名称=大柠檬冰茶, 规格=, 数量=1.0, 单位=件, 单价=0.0 +2025-05-08 20:04:07,823 - app.core.excel.processor - INFO - 从映射列解析规格: 900ml*12 -> 包装数量=12 +2025-05-08 20:04:07,824 - app.core.excel.processor - INFO - 第8行: 提取商品信息 条码=6937003703437, 名称=大葡萄柚冰绿茶, 规格=, 数量=1.0, 单位=件, 单价=0.0 +2025-05-08 20:04:07,824 - app.core.excel.processor - INFO - 从映射列解析规格: 900ml*12 -> 包装数量=12 +2025-05-08 20:04:07,825 - app.core.excel.processor - INFO - 第9行: 提取商品信息 条码=6937003708173, 名称=天山爷爷新疆甜石复合果蔬汁饮品, 规格=, 数量=2.0, 单位=瓶, 单价=0.0 +2025-05-08 20:04:07,826 - app.core.excel.processor - INFO - 第10行: 提取商品信息 条码=6975176781777, 名称=日式可乐味苏打气泡水, 规格=, 数量=1.0, 单位=瓶, 单价=0.0 +2025-05-08 20:04:07,826 - app.core.excel.processor - INFO - 从映射列解析规格: 480ml*15 -> 包装数量=15 +2025-05-08 20:04:07,827 - app.core.excel.processor - INFO - 第11行: 提取商品信息 条码=6975176782460, 名称=燃茶无糖茉莉花茶无糖饮料, 规格=, 数量=9.0, 单位=瓶, 单价=0.0 +2025-05-08 20:04:07,842 - app.core.excel.processor - INFO - 从映射列解析规格: 500ml*15 -> 包装数量=15 +2025-05-08 20:04:07,844 - app.core.excel.processor - INFO - 提取到 11 个商品信息 +2025-05-08 20:04:07,872 - app.core.excel.processor - INFO - 开始处理11 个产品信息 +2025-05-08 20:04:07,872 - app.core.excel.processor - INFO - 处理商品: 条码=6937003700207, 数量=15.0, 单价=4.533333333333333, 是否赠品=False +2025-05-08 20:04:07,873 - app.core.excel.processor - INFO - 发现正常商品:条码6937003700207, 数量=15.0, 单价=4.533333333333333 +2025-05-08 20:04:07,873 - app.core.excel.processor - INFO - 处理商品: 条码=6937003706346, 数量=15.0, 单价=4.533333333333333, 是否赠品=False +2025-05-08 20:04:07,873 - app.core.excel.processor - INFO - 发现正常商品:条码6937003706346, 数量=15.0, 单价=4.533333333333333 +2025-05-08 20:04:07,873 - app.core.excel.processor - INFO - 处理商品: 条码=6937003706568, 数量=15.0, 单价=3.7333333333333334, 是否赠品=False +2025-05-08 20:04:07,873 - app.core.excel.processor - INFO - 发现正常商品:条码6937003706568, 数量=15.0, 单价=3.7333333333333334 +2025-05-08 20:04:07,873 - app.core.excel.processor - INFO - 处理商品: 条码=6937003708173, 数量=15.0, 单价=3.7333333333333334, 是否赠品=False +2025-05-08 20:04:07,873 - app.core.excel.processor - INFO - 发现正常商品:条码6937003708173, 数量=15.0, 单价=3.7333333333333334 +2025-05-08 20:04:07,874 - app.core.excel.processor - INFO - 处理商品: 条码=6937003708166, 数量=15.0, 单价=3.7333333333333334, 是否赠品=False +2025-05-08 20:04:10,344 - app.core.excel.processor - INFO - 发现正常商品:条码6937003708166, 数量=15.0, 单价=3.7333333333333334 +2025-05-08 20:04:10,344 - app.core.excel.processor - INFO - 处理商品: 条码=6937003703086, 数量=15.0, 单价=2.0, 是否赠品=False +2025-05-08 20:04:10,344 - app.core.excel.processor - INFO - 发现正常商品:条码6937003703086, 数量=15.0, 单价=2.0 +2025-05-08 20:04:10,344 - app.core.excel.processor - INFO - 处理商品: 条码=6975176784785, 数量=12.0, 单价=0, 是否赠品=True +2025-05-08 20:04:10,345 - app.core.excel.processor - INFO - 发现赠品:条码6975176784785, 数量=12.0 +2025-05-08 20:04:10,345 - app.core.excel.processor - INFO - 处理商品: 条码=6937003703437, 数量=12.0, 单价=0, 是否赠品=True +2025-05-08 20:04:10,345 - app.core.excel.processor - INFO - 发现赠品:条码6937003703437, 数量=12.0 +2025-05-08 20:04:10,345 - app.core.excel.processor - INFO - 处理商品: 条码=6937003708173, 数量=2.0, 单价=0, 是否赠品=True +2025-05-08 20:04:10,345 - app.core.excel.processor - INFO - 发现赠品:条码6937003708173, 数量=2.0 +2025-05-08 20:04:10,345 - app.core.excel.processor - INFO - 处理商品: 条码=6975176781777, 数量=1.0, 单价=0, 是否赠品=True +2025-05-08 20:04:10,345 - app.core.excel.processor - INFO - 发现赠品:条码6975176781777, 数量=1.0 +2025-05-08 20:04:10,345 - app.core.excel.processor - INFO - 处理商品: 条码=6975176782460, 数量=9.0, 单价=0, 是否赠品=True +2025-05-08 20:04:10,345 - app.core.excel.processor - INFO - 发现赠品:条码6975176782460, 数量=9.0 +2025-05-08 20:04:10,345 - app.core.excel.processor - INFO - 分组后共10 个不同条码的商品 +2025-05-08 20:04:10,345 - app.core.excel.processor - INFO - 条码 6937003700207 处理结果:正常商品数量15.0,单价4.533333333333333,赠品数量0 +2025-05-08 20:04:10,345 - app.core.excel.processor - INFO - 条码 6937003706346 处理结果:正常商品数量15.0,单价4.533333333333333,赠品数量0 +2025-05-08 20:04:10,345 - app.core.excel.processor - INFO - 条码 6937003706568 处理结果:正常商品数量15.0,单价3.7333333333333334,赠品数量0 +2025-05-08 20:04:10,345 - app.core.excel.processor - INFO - 条码 6937003708173 处理结果:正常商品数量15.0,单价3.7333333333333334,赠品数量2.0 +2025-05-08 20:04:10,345 - app.core.excel.processor - INFO - 条码 6937003708166 处理结果:正常商品数量15.0,单价3.7333333333333334,赠品数量0 +2025-05-08 20:04:10,345 - app.core.excel.processor - INFO - 条码 6937003703086 处理结果:正常商品数量15.0,单价2.0,赠品数量0 +2025-05-08 20:04:10,346 - app.core.excel.processor - INFO - 条码 6975176784785 处理结果:只有赠品,数量=12.0 +2025-05-08 20:04:10,346 - app.core.excel.processor - INFO - 条码 6937003703437 处理结果:只有赠品,数量=12.0 +2025-05-08 20:04:10,346 - app.core.excel.processor - INFO - 条码 6975176781777 处理结果:只有赠品,数量=1.0 +2025-05-08 20:04:10,346 - app.core.excel.processor - INFO - 条码 6975176782460 处理结果:只有赠品,数量=9.0 +2025-05-08 20:04:10,346 - app.core.excel.processor - INFO - 条码 6937003700207 填充:采购量=15.0(原商品15.0+赠品0),单价=4.533333333333333 +2025-05-08 20:04:10,346 - app.core.excel.processor - INFO - 条码 6937003706346 填充:采购量=15.0(原商品15.0+赠品0),单价=4.533333333333333 +2025-05-08 20:04:10,346 - app.core.excel.processor - INFO - 条码 6937003706568 填充:采购量=15.0(原商品15.0+赠品0),单价=3.7333333333333334 +2025-05-08 20:04:10,346 - app.core.excel.processor - INFO - 条码 6937003708173 单价调整: 原价=3.7333333333333334, 新价=3.2941176470588234 (包含赠品后重新计算) +2025-05-08 20:04:10,346 - app.core.excel.processor - INFO - 条码 6937003708173 填充:采购量=17.0(原商品15.0+赠品2.0),单价=3.2941176470588234 +2025-05-08 20:04:10,346 - app.core.excel.processor - INFO - 条码 6937003708166 填充:采购量=15.0(原商品15.0+赠品0),单价=3.7333333333333334 +2025-05-08 20:04:10,346 - app.core.excel.processor - INFO - 条码 6937003703086 填充:采购量=15.0(原商品15.0+赠品0),单价=2.0 +2025-05-08 20:04:10,347 - app.core.excel.processor - INFO - 条码 6975176784785 填充:仅有赠品,采购量=12.0,单价=0 +2025-05-08 20:04:10,347 - app.core.excel.processor - INFO - 条码 6937003703437 填充:仅有赠品,采购量=12.0,单价=0 +2025-05-08 20:04:10,347 - app.core.excel.processor - INFO - 条码 6975176781777 填充:仅有赠品,采购量=1.0,单价=0 +2025-05-08 20:04:14,980 - app.core.excel.processor - INFO - 条码 6975176782460 填充:仅有赠品,采购量=9.0,单价=0 +2025-05-08 20:04:14,983 - app.core.excel.processor - INFO - 采购单已保存到: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250508194532.xls +2025-05-08 20:04:14,985 - app.core.excel.processor - INFO - 采购单已保存到: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250508194532.xls +2025-05-08 20:46:00,697 - app.core.excel.processor - INFO - 初始化ExcelProcessor +2025-05-08 20:46:00,700 - app.core.excel.processor - INFO - 初始化完成,模板文件: templates\银豹-采购单模板.xls +2025-05-08 20:46:00,750 - app.core.excel.processor - INFO - 开始处理Excel文件: D:/My Documents/python/orc-order-v2/data/output/微信图片_20250508194532.xlsx +2025-05-08 20:46:01,732 - app.core.excel.processor - INFO - 成功读取Excel文件: D:/My Documents/python/orc-order-v2/data/output/微信图片_20250508194532.xlsx, 共 13 行 +2025-05-08 20:46:01,737 - app.core.excel.processor - INFO - 找到可能的表头行: 第1行,评分: 60 +2025-05-08 20:46:01,737 - app.core.excel.processor - INFO - 识别到表头在第 1 行 +2025-05-08 20:46:01,833 - app.core.excel.processor - INFO - 使用表头行重新读取数据,共 12 行有效数据 +2025-05-08 20:46:01,833 - app.core.excel.processor - INFO - 找到精确匹配的条码列: 商品条码 +2025-05-08 20:46:01,834 - app.core.excel.processor - INFO - 使用条码列: 商品条码 +2025-05-08 20:46:01,834 - app.core.excel.processor - INFO - 找到name列: 商品名称 +2025-05-08 20:46:01,834 - app.core.excel.processor - INFO - 找到specification列: 规格型号 +2025-05-08 20:46:01,834 - app.core.excel.processor - INFO - 找到quantity列: 数量 +2025-05-08 20:46:01,834 - app.core.excel.processor - INFO - 找到unit列: 单位 +2025-05-08 20:46:01,834 - app.core.excel.processor - INFO - 找到price列: 单价 +2025-05-08 20:46:01,835 - app.core.excel.processor - INFO - 列名映射结果: {'barcode': '商品条码', 'name': '商品名称', 'specification': '规格型号', 'quantity': '数量', 'unit': '单位', 'price': '单价'} +2025-05-08 20:46:01,835 - app.core.excel.processor - INFO - 是否存在规格列: False +2025-05-08 20:46:01,836 - app.core.excel.processor - INFO - 第1行: 提取商品信息 条码=6937003700207, 名称=外星人PRO运动恢复类-原味, 规格=, 数量=1.0, 单位=件, 单价=68.0 +2025-05-08 20:46:01,836 - app.core.excel.processor - INFO - 从映射列解析规格: 500ml*15 -> 包装数量=15 +2025-05-08 20:46:01,838 - app.core.excel.processor - INFO - 第2行: 提取商品信息 条码=6937003706346, 名称=600ml外星人电解质西柚, 规格=, 数量=1.0, 单位=件, 单价=68.0 +2025-05-08 20:46:01,838 - app.core.excel.processor - INFO - 从映射列解析规格: 600ml*15 -> 包装数量=15 +2025-05-08 20:46:01,840 - app.core.excel.processor - INFO - 第3行: 提取商品信息 条码=6937003706568, 名称=外星人-维B水阳光青提500mL, 规格=, 数量=1.0, 单位=件, 单价=56.0 +2025-05-08 20:46:01,840 - app.core.excel.processor - INFO - 从映射列解析规格: 500ml*15 -> 包装数量=15 +2025-05-08 20:46:01,859 - app.core.excel.processor - INFO - 第4行: 提取商品信息 条码=6937003708173, 名称=天山爷爷新疆甜石榴复合果蔬汁饮品, 规格=, 数量=1.0, 单位=件, 单价=56.0 +2025-05-08 20:46:01,868 - app.core.excel.processor - INFO - 第5行: 提取商品信息 条码=6937003708166, 名称=天山爷爷新疆玉葡萄复合果汁饮品, 规格=, 数量=1.0, 单位=件, 单价=56.0 +2025-05-08 20:46:01,868 - app.core.excel.processor - INFO - 第6行: 提取商品信息 条码=6937003703086, 名称=外星人WAVE风味水饮品淡柠檬味, 规格=, 数量=1.0, 单位=件, 单价=30.0 +2025-05-08 20:46:01,868 - app.core.excel.processor - INFO - 从映射列解析规格: 600ml*15 -> 包装数量=15 +2025-05-08 20:46:01,869 - app.core.excel.processor - INFO - 第7行: 提取商品信息 条码=6975176784785, 名称=大柠檬冰茶, 规格=, 数量=1.0, 单位=件, 单价=0.0 +2025-05-08 20:46:01,869 - app.core.excel.processor - INFO - 从映射列解析规格: 900ml*12 -> 包装数量=12 +2025-05-08 20:46:01,870 - app.core.excel.processor - INFO - 第8行: 提取商品信息 条码=6937003703437, 名称=大葡萄柚冰绿茶, 规格=, 数量=1.0, 单位=件, 单价=0.0 +2025-05-08 20:46:01,870 - app.core.excel.processor - INFO - 从映射列解析规格: 900ml*12 -> 包装数量=12 +2025-05-08 20:46:01,871 - app.core.excel.processor - INFO - 第9行: 提取商品信息 条码=6937003708173, 名称=天山爷爷新疆甜石复合果蔬汁饮品, 规格=, 数量=2.0, 单位=瓶, 单价=0.0 +2025-05-08 20:46:01,872 - app.core.excel.processor - INFO - 第10行: 提取商品信息 条码=6975176781777, 名称=日式可乐味苏打气泡水, 规格=, 数量=1.0, 单位=瓶, 单价=0.0 +2025-05-08 20:46:01,872 - app.core.excel.processor - INFO - 从映射列解析规格: 480ml*15 -> 包装数量=15 +2025-05-08 20:46:01,872 - app.core.excel.processor - INFO - 第11行: 提取商品信息 条码=6975176782460, 名称=燃茶无糖茉莉花茶无糖饮料, 规格=, 数量=9.0, 单位=瓶, 单价=0.0 +2025-05-08 20:46:01,872 - app.core.excel.processor - INFO - 从映射列解析规格: 500ml*15 -> 包装数量=15 +2025-05-08 20:46:03,157 - app.core.excel.processor - INFO - 提取到 11 个商品信息 +2025-05-08 20:46:03,171 - app.core.excel.processor - INFO - 开始处理11 个产品信息 +2025-05-08 20:46:03,171 - app.core.excel.processor - INFO - 处理商品: 条码=6937003700207, 数量=15.0, 单价=4.533333333333333, 是否赠品=False +2025-05-08 20:46:03,172 - app.core.excel.processor - INFO - 发现正常商品:条码6937003700207, 数量=15.0, 单价=4.533333333333333 +2025-05-08 20:46:03,172 - app.core.excel.processor - INFO - 处理商品: 条码=6937003706346, 数量=15.0, 单价=4.533333333333333, 是否赠品=False +2025-05-08 20:46:03,172 - app.core.excel.processor - INFO - 发现正常商品:条码6937003706346, 数量=15.0, 单价=4.533333333333333 +2025-05-08 20:46:03,172 - app.core.excel.processor - INFO - 处理商品: 条码=6937003706568, 数量=15.0, 单价=3.7333333333333334, 是否赠品=False +2025-05-08 20:46:03,172 - app.core.excel.processor - INFO - 发现正常商品:条码6937003706568, 数量=15.0, 单价=3.7333333333333334 +2025-05-08 20:46:03,172 - app.core.excel.processor - INFO - 处理商品: 条码=6937003708173, 数量=15.0, 单价=3.7333333333333334, 是否赠品=False +2025-05-08 20:46:03,173 - app.core.excel.processor - INFO - 发现正常商品:条码6937003708173, 数量=15.0, 单价=3.7333333333333334 +2025-05-08 20:46:03,173 - app.core.excel.processor - INFO - 处理商品: 条码=6937003708166, 数量=15.0, 单价=3.7333333333333334, 是否赠品=False +2025-05-08 20:46:03,173 - app.core.excel.processor - INFO - 发现正常商品:条码6937003708166, 数量=15.0, 单价=3.7333333333333334 +2025-05-08 20:46:03,173 - app.core.excel.processor - INFO - 处理商品: 条码=6937003703086, 数量=15.0, 单价=2.0, 是否赠品=False +2025-05-08 20:46:03,174 - app.core.excel.processor - INFO - 发现正常商品:条码6937003703086, 数量=15.0, 单价=2.0 +2025-05-08 20:46:03,174 - app.core.excel.processor - INFO - 处理商品: 条码=6975176784785, 数量=12.0, 单价=0, 是否赠品=True +2025-05-08 20:46:03,174 - app.core.excel.processor - INFO - 发现赠品:条码6975176784785, 数量=12.0 +2025-05-08 20:46:03,175 - app.core.excel.processor - INFO - 处理商品: 条码=6937003703437, 数量=12.0, 单价=0, 是否赠品=True +2025-05-08 20:46:03,175 - app.core.excel.processor - INFO - 发现赠品:条码6937003703437, 数量=12.0 +2025-05-08 20:46:03,176 - app.core.excel.processor - INFO - 处理商品: 条码=6937003708173, 数量=2.0, 单价=0, 是否赠品=True +2025-05-08 20:46:03,176 - app.core.excel.processor - INFO - 发现赠品:条码6937003708173, 数量=2.0 +2025-05-08 20:46:03,176 - app.core.excel.processor - INFO - 处理商品: 条码=6975176781777, 数量=1.0, 单价=0, 是否赠品=True +2025-05-08 20:46:03,176 - app.core.excel.processor - INFO - 发现赠品:条码6975176781777, 数量=1.0 +2025-05-08 20:46:03,176 - app.core.excel.processor - INFO - 处理商品: 条码=6975176782460, 数量=9.0, 单价=0, 是否赠品=True +2025-05-08 20:46:03,177 - app.core.excel.processor - INFO - 发现赠品:条码6975176782460, 数量=9.0 +2025-05-08 20:46:03,177 - app.core.excel.processor - INFO - 分组后共10 个不同条码的商品 +2025-05-08 20:46:03,177 - app.core.excel.processor - INFO - 条码 6937003700207 处理结果:正常商品数量15.0,单价4.533333333333333,赠品数量0 +2025-05-08 20:46:03,177 - app.core.excel.processor - INFO - 条码 6937003706346 处理结果:正常商品数量15.0,单价4.533333333333333,赠品数量0 +2025-05-08 20:46:03,178 - app.core.excel.processor - INFO - 条码 6937003706568 处理结果:正常商品数量15.0,单价3.7333333333333334,赠品数量0 +2025-05-08 20:46:03,178 - app.core.excel.processor - INFO - 条码 6937003708173 处理结果:正常商品数量15.0,单价3.7333333333333334,赠品数量2.0 +2025-05-08 20:46:03,178 - app.core.excel.processor - INFO - 条码 6937003708166 处理结果:正常商品数量15.0,单价3.7333333333333334,赠品数量0 +2025-05-08 20:46:03,178 - app.core.excel.processor - INFO - 条码 6937003703086 处理结果:正常商品数量15.0,单价2.0,赠品数量0 +2025-05-08 20:46:03,178 - app.core.excel.processor - INFO - 条码 6975176784785 处理结果:只有赠品,数量=12.0 +2025-05-08 20:46:03,178 - app.core.excel.processor - INFO - 条码 6937003703437 处理结果:只有赠品,数量=12.0 +2025-05-08 20:46:07,558 - app.core.excel.processor - INFO - 条码 6975176781777 处理结果:只有赠品,数量=1.0 +2025-05-08 20:46:07,558 - app.core.excel.processor - INFO - 条码 6975176782460 处理结果:只有赠品,数量=9.0 +2025-05-08 20:46:07,558 - app.core.excel.processor - INFO - 条码 6937003708173 填充:采购量=15.0,赠品数量2.0 +2025-05-08 20:46:07,559 - app.core.excel.processor - INFO - 条码 6975176784785 填充:仅有赠品,采购量=0,赠品数量=12.0 +2025-05-08 20:46:07,559 - app.core.excel.processor - INFO - 条码 6937003703437 填充:仅有赠品,采购量=0,赠品数量=12.0 +2025-05-08 20:46:07,559 - app.core.excel.processor - INFO - 条码 6975176781777 填充:仅有赠品,采购量=0,赠品数量=1.0 +2025-05-08 20:46:07,559 - app.core.excel.processor - INFO - 条码 6975176782460 填充:仅有赠品,采购量=0,赠品数量=9.0 +2025-05-08 20:46:07,563 - app.core.excel.processor - INFO - 采购单已保存到: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250508194532.xls +2025-05-08 20:46:07,564 - app.core.excel.processor - INFO - 采购单已保存到: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250508194532.xls diff --git a/logs/app.core.excel.validators.log b/logs/app.core.excel.validators.log new file mode 100644 index 0000000..aa32354 --- /dev/null +++ b/logs/app.core.excel.validators.log @@ -0,0 +1,9 @@ +2025-05-08 19:45:49,628 - app.core.excel.validators - WARNING - 数量验证失败: 数量必须大于0,当前值: 0.0 +2025-05-08 19:45:49,629 - app.core.excel.validators - WARNING - 数量验证失败: 数量必须大于0,当前值: 0.0 +2025-05-08 19:45:49,638 - app.core.excel.validators - WARNING - 数量验证失败: 数量必须大于0,当前值: 0.0 +2025-05-08 19:45:49,639 - app.core.excel.validators - WARNING - 数量验证失败: 数量必须大于0,当前值: 0.0 +2025-05-08 19:45:49,640 - app.core.excel.validators - WARNING - 数量验证失败: 数量必须大于0,当前值: 0.0 +2025-05-08 19:45:49,641 - app.core.excel.validators - WARNING - 数量验证失败: 数量必须大于0,当前值: 0.0 +2025-05-08 19:45:49,641 - app.core.excel.validators - WARNING - 数量验证失败: 数量必须大于0,当前值: 0.0 +2025-05-08 19:45:49,878 - app.core.excel.validators - WARNING - 数量验证失败: 数量必须大于0,当前值: 0.0 +2025-05-08 19:45:49,881 - app.core.excel.validators - WARNING - 数量验证失败: 数量必须大于0,当前值: 0.0 diff --git a/logs/app.core.ocr.baidu_ocr.log b/logs/app.core.ocr.baidu_ocr.log index 6c0538e..e51cdd7 100644 --- a/logs/app.core.ocr.baidu_ocr.log +++ b/logs/app.core.ocr.baidu_ocr.log @@ -68,3 +68,4 @@ 2025-05-07 21:22:01,976 - app.core.ocr.baidu_ocr - INFO - 成功获取访问令牌 2025-05-07 21:54:55,605 - app.core.ocr.baidu_ocr - INFO - 成功获取访问令牌 2025-05-07 22:28:51,887 - app.core.ocr.baidu_ocr - INFO - 成功获取访问令牌 +2025-05-08 19:45:41,386 - app.core.ocr.baidu_ocr - INFO - 成功获取访问令牌 diff --git a/logs/app.core.ocr.table_ocr.log b/logs/app.core.ocr.table_ocr.log index 003cfd0..f6daaea 100644 --- a/logs/app.core.ocr.table_ocr.log +++ b/logs/app.core.ocr.table_ocr.log @@ -654,3 +654,29 @@ 2025-05-07 22:28:52,829 - app.core.ocr.table_ocr - INFO - 图片处理成功: D:\My Documents\python\orc-order-v2\data\input\微信图片_20250507222846.jpg, 输出文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250507222846.xlsx 2025-05-07 22:28:52,832 - app.core.ocr.table_ocr - INFO - 批次处理完成, 成功: 1/1 2025-05-07 22:28:52,832 - app.core.ocr.table_ocr - INFO - 所有图片处理完成, 总计: 1, 成功: 1 +2025-05-08 19:45:41,028 - app.core.ocr.table_ocr - INFO - 使用输入目录: D:\My Documents\python\orc-order-v2\data\input +2025-05-08 19:45:41,030 - app.core.ocr.table_ocr - INFO - 使用输出目录: D:\My Documents\python\orc-order-v2\data\output +2025-05-08 19:45:41,030 - app.core.ocr.table_ocr - INFO - 使用临时目录: D:\My Documents\python\orc-order-v2\data\temp +2025-05-08 19:45:41,030 - 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-08 19:45:41,036 - app.core.ocr.table_ocr - INFO - 找到 1 个图片文件,其中 1 个未处理 +2025-05-08 19:45:41,036 - app.core.ocr.table_ocr - INFO - 处理批次 1/1, 大小: 1 +2025-05-08 19:45:41,037 - app.core.ocr.table_ocr - INFO - 开始处理图片: D:\My Documents\python\orc-order-v2\data\input\微信图片_20250508194532.jpg +2025-05-08 19:45:48,792 - app.core.ocr.table_ocr - INFO - 图片处理成功: D:\My Documents\python\orc-order-v2\data\input\微信图片_20250508194532.jpg, 输出文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250508194532.xlsx +2025-05-08 19:45:48,794 - app.core.ocr.table_ocr - INFO - 批次处理完成, 成功: 1/1 +2025-05-08 19:45:48,795 - app.core.ocr.table_ocr - INFO - 所有图片处理完成, 总计: 1, 成功: 1 +2025-05-08 19:47:28,883 - app.core.ocr.table_ocr - INFO - 使用输入目录: D:\My Documents\python\orc-order-v2\data\input +2025-05-08 19:47:28,883 - app.core.ocr.table_ocr - INFO - 使用输出目录: D:\My Documents\python\orc-order-v2\data\output +2025-05-08 19:47:28,883 - app.core.ocr.table_ocr - INFO - 使用临时目录: D:\My Documents\python\orc-order-v2\data\temp +2025-05-08 19:47:28,884 - 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-08 19:51:23,177 - app.core.ocr.table_ocr - INFO - 使用输入目录: D:\My Documents\python\orc-order-v2\data\input +2025-05-08 19:51:23,177 - app.core.ocr.table_ocr - INFO - 使用输出目录: D:\My Documents\python\orc-order-v2\data\output +2025-05-08 19:51:23,177 - app.core.ocr.table_ocr - INFO - 使用临时目录: D:\My Documents\python\orc-order-v2\data\temp +2025-05-08 19:51:23,178 - 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-08 20:04:06,906 - app.core.ocr.table_ocr - INFO - 使用输入目录: D:\My Documents\python\orc-order-v2\data\input +2025-05-08 20:04:06,907 - app.core.ocr.table_ocr - INFO - 使用输出目录: D:\My Documents\python\orc-order-v2\data\output +2025-05-08 20:04:06,907 - app.core.ocr.table_ocr - INFO - 使用临时目录: D:\My Documents\python\orc-order-v2\data\temp +2025-05-08 20:04:06,907 - 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-08 20:46:00,693 - app.core.ocr.table_ocr - INFO - 使用输入目录: D:\My Documents\python\orc-order-v2\data\input +2025-05-08 20:46:00,694 - app.core.ocr.table_ocr - INFO - 使用输出目录: D:\My Documents\python\orc-order-v2\data\output +2025-05-08 20:46:00,694 - app.core.ocr.table_ocr - INFO - 使用临时目录: D:\My Documents\python\orc-order-v2\data\temp +2025-05-08 20:46:00,695 - 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.services.ocr_service.log b/logs/app.services.ocr_service.log index 37cc89a..e89077d 100644 --- a/logs/app.services.ocr_service.log +++ b/logs/app.services.ocr_service.log @@ -264,3 +264,14 @@ 2025-05-07 22:28:51,544 - app.services.ocr_service - INFO - 初始化OCRService 2025-05-07 22:28:51,548 - app.services.ocr_service - INFO - OCRService初始化完成 2025-05-07 22:28:51,557 - app.services.ocr_service - INFO - OCRService开始批量处理图片, batch_size=None, max_workers=None +2025-05-08 19:45:41,026 - app.services.ocr_service - INFO - 初始化OCRService +2025-05-08 19:45:41,030 - app.services.ocr_service - INFO - OCRService初始化完成 +2025-05-08 19:45:41,036 - app.services.ocr_service - INFO - OCRService开始批量处理图片, batch_size=None, max_workers=None +2025-05-08 19:47:28,878 - app.services.ocr_service - INFO - 初始化OCRService +2025-05-08 19:47:28,884 - app.services.ocr_service - INFO - OCRService初始化完成 +2025-05-08 19:51:23,176 - app.services.ocr_service - INFO - 初始化OCRService +2025-05-08 19:51:23,178 - app.services.ocr_service - INFO - OCRService初始化完成 +2025-05-08 20:04:06,905 - app.services.ocr_service - INFO - 初始化OCRService +2025-05-08 20:04:06,907 - app.services.ocr_service - INFO - OCRService初始化完成 +2025-05-08 20:46:00,690 - app.services.ocr_service - INFO - 初始化OCRService +2025-05-08 20:46:00,696 - app.services.ocr_service - INFO - OCRService初始化完成 diff --git a/logs/app.services.order_service.log b/logs/app.services.order_service.log index 68763d5..cf0cdb2 100644 --- a/logs/app.services.order_service.log +++ b/logs/app.services.order_service.log @@ -300,3 +300,18 @@ 2025-05-07 22:28:51,548 - app.services.order_service - INFO - 初始化OrderService 2025-05-07 22:28:51,556 - app.services.order_service - INFO - OrderService初始化完成 2025-05-07 22:28:52,834 - app.services.order_service - INFO - OrderService开始处理指定Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250507222846.xlsx +2025-05-08 19:45:41,030 - app.services.order_service - INFO - 初始化OrderService +2025-05-08 19:45:41,035 - app.services.order_service - INFO - OrderService初始化完成 +2025-05-08 19:45:48,798 - app.services.order_service - INFO - OrderService开始处理指定Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250508194532.xlsx +2025-05-08 19:47:28,884 - app.services.order_service - INFO - 初始化OrderService +2025-05-08 19:47:28,886 - app.services.order_service - INFO - OrderService初始化完成 +2025-05-08 19:47:28,886 - app.services.order_service - INFO - OrderService开始处理指定Excel文件: D:/My Documents/python/orc-order-v2/data/output/微信图片_20250508194532.xlsx +2025-05-08 19:51:23,178 - app.services.order_service - INFO - 初始化OrderService +2025-05-08 19:51:23,190 - app.services.order_service - INFO - OrderService初始化完成 +2025-05-08 19:51:23,190 - app.services.order_service - INFO - OrderService开始处理指定Excel文件: D:/My Documents/python/orc-order-v2/data/output/微信图片_20250508194532.xlsx +2025-05-08 20:04:06,907 - app.services.order_service - INFO - 初始化OrderService +2025-05-08 20:04:06,909 - app.services.order_service - INFO - OrderService初始化完成 +2025-05-08 20:04:06,910 - app.services.order_service - INFO - OrderService开始处理指定Excel文件: D:/My Documents/python/orc-order-v2/data/output/微信图片_20250508194532.xlsx +2025-05-08 20:46:00,697 - app.services.order_service - INFO - 初始化OrderService +2025-05-08 20:46:00,701 - app.services.order_service - INFO - OrderService初始化完成 +2025-05-08 20:46:00,750 - app.services.order_service - INFO - OrderService开始处理指定Excel文件: D:/My Documents/python/orc-order-v2/data/output/微信图片_20250508194532.xlsx diff --git a/v2-优化总结.md b/v2-优化总结.md index 0b3d436..2f0f175 100644 --- a/v2-优化总结.md +++ b/v2-优化总结.md @@ -69,4 +69,261 @@ 1. **配置外部化**:将配置参数提取到config.ini文件,便于调整 2. **模块间低耦合**:模块之间通过明确的接口交互,降低耦合度 3. **可扩展设计**:系统设计考虑未来扩展,如添加新的特殊条码处理规则 -4. **完整文档**:提供详细的README文档,说明系统功能和使用方法 \ No newline at end of file +4. **完整文档**:提供详细的README文档,说明系统功能和使用方法 + +# OCR订单处理系统 v2 优化建议 + +经过全面审查系统代码和架构,以下是对 OCR 订单处理系统的优化建议,旨在提高系统的性能、可维护性和用户体验。 + +## 1. 架构与结构优化 + +### 1.1 依赖注入与组件化 + +**当前情况**:系统主要组件在代码中直接实例化,造成模块间高耦合。 + +**优化建议**: +- 实现简单的依赖注入系统,降低模块间耦合度 +- 使用工厂模式创建核心组件,便于测试和替换 +- 示例代码: + ```python + class AppContainer: + def __init__(self, config): + self.config = config + self._services = {} + + def get_ocr_service(self): + if 'ocr_service' not in self._services: + self._services['ocr_service'] = OCRService(self.config) + return self._services['ocr_service'] + ``` + +### 1.2 配置系统增强 + +**当前情况**:配置存储在 `config.ini`,但部分硬编码的配置分散在代码中。 + +**优化建议**: +- 将所有配置项集中到配置文件,消除硬编码的配置 +- 添加环境变量支持,便于部署和CI/CD集成 +- 增加配置验证机制,防止错误配置 +- 支持不同环境(开发、测试、生产)的配置切换 + +### 1.3 模块化 UI 与核心逻辑分离 + +**当前情况**:`启动器.py` 文件过大 (1050行),同时包含 UI 和业务逻辑。 + +**优化建议**: +- 将 UI 逻辑与业务逻辑完全分离 +- 采用 MVC 或 MVVM 模式重构 UI 代码 +- 将 UI 组件模块化,每个页面/功能对应单独的类 + +## 2. 性能优化 + +### 2.1 数据处理性能 + +**当前情况**:处理大量数据时效率较低,特别是 Excel 数据处理部分。 + +**优化建议**: +- 使用 DataFrame 矢量化操作替代循环,提高数据处理速度 +- 对于大文件,实现分块读取和处理机制 +- 优化正则表达式,减少重复编译 +- 示例改进: + ```python + # 优化前 + for idx, row in df.iterrows(): + # 处理每一行... + + # 优化后 + # 使用 apply 或向量化操作 + df['barcode'] = df['barcode'].apply(format_barcode) + ``` + +### 2.2 并发处理增强 + +**当前情况**:已有初步的多线程支持,但未充分利用。 + +**优化建议**: +- 扩展并行处理能力,特别是在 OCR 识别部分 +- 实现任务队列系统,支持后台处理 +- 添加进度报告机制,提高用户体验 +- 考虑使用 asyncio 进行 I/O 密集型任务处理 + +### 2.3 缓存机制 + +**当前情况**:每次处理都重新加载和解析数据。 + +**优化建议**: +- 实现内存缓存机制,缓存常用数据和配置 +- 添加条码和商品信息的本地数据库,减少重复处理 +- 对规格解析结果进行缓存,提高处理速度 + +## 3. 代码质量改进 + +### 3.1 单元测试与代码覆盖率 + +**当前情况**:缺乏系统性的单元测试。 + +**优化建议**: +- 为核心功能编写单元测试,特别是单位转换和条码处理逻辑 +- 实现测试数据生成器,支持边界情况测试 +- 使用测试覆盖率工具,确保关键代码被测试覆盖 +- 集成持续测试到开发流程中 + +### 3.2 代码重构 + +**当前情况**:部分函数过长,职责不够单一。 + +**优化建议**: +- 对长函数进行拆分,特别是 `extract_product_info`(300+ 行) +- 使用 Strategy 模式重构条码处理和单位转换逻辑 +- 简化复杂的嵌套条件语句,提高代码可读性 +- 提取通用功能到辅助函数,减少代码重复 + +### 3.3 错误处理增强 + +**当前情况**:错误处理主要依靠日志记录。 + +**优化建议**: +- 设计更细粒度的异常类型,便于精确处理不同错误 +- 实现全局异常处理,防止程序崩溃 +- 添加用户友好的错误提示,而不只是记录日志 +- 增加错误恢复机制,允许在出错后继续处理其他项目 + +## 4. 功能增强 + +### 4.1 数据验证与清洗增强 + +**当前情况**:基本的数据验证和清洗逻辑。 + +**优化建议**: +- 增强数据验证规则,特别是对条码和数量的验证 +- 实现更智能的数据修复功能,处理常见错误格式 +- 添加数据异常检测算法,自动标记异常数据 +- 提供手动数据修正界面,允许用户修正识别错误 + +### 4.2 批量处理功能增强 + +**当前情况**:支持基本的批量处理。 + +**优化建议**: +- 支持拖放多个文件进行处理 +- 添加文件队列管理,显示待处理/已处理状态 +- 实现处理中断和恢复功能 +- 支持处理结果预览和批量修改 + +### 4.3 数据导出与集成 + +**当前情况**:生成固定格式的 Excel 文件。 + +**优化建议**: +- 支持多种导出格式(CSV、JSON、XML 等) +- 提供数据库存储选项,便于数据管理和查询 +- 添加 API 接口,支持与其他系统集成 +- 实现定制化报表生成功能 + +## 5. 用户体验改进 + +### 5.1 界面优化 + +**当前情况**:基本的功能界面。 + +**优化建议**: +- 重新设计 UI,采用现代化界面框架(如 PyQt6 或 wx.Python) +- 添加暗色主题支持 +- 实现响应式布局,适应不同屏幕尺寸 +- 增加操作引导和工具提示 + +### 5.2 用户反馈与报告 + +**当前情况**:主要通过日志记录处理结果。 + +**优化建议**: +- 设计直观的处理结果报告页面 +- 添加数据可视化功能,展示处理统计信息 +- 实现处理报告导出功能 +- 设计更友好的错误提示和建议 + +### 5.3 配置与偏好设置 + +**当前情况**:配置主要在 config.ini 中修改。 + +**优化建议**: +- 设计图形化配置界面,无需直接编辑配置文件 +- 支持用户偏好设置保存 +- 添加配置导入/导出功能 +- 实现配置模板,快速切换不同配置 + +## 6. 安全性改进 + +### 6.1 API 密钥管理 + +**当前情况**:API 密钥直接存储在配置文件中。 + +**优化建议**: +- 实现 API 密钥加密存储 +- 支持从环境变量或安全存储获取敏感信息 +- 添加 API 密钥轮换机制 +- 实现访问审计日志 + +### 6.2 数据安全 + +**当前情况**:数据以明文形式存储和处理。 + +**优化建议**: +- 添加敏感数据(如价格信息)的加密选项 +- 实现自动数据备份机制 +- 添加访问控制,限制对敏感数据的访问 +- 支持数据匿名化处理,用于测试和分析 + +## 7. 部署与维护改进 + +### 7.1 打包与分发 + +**当前情况**:依赖 Python 环境和手动安装依赖。 + +**优化建议**: +- 使用 PyInstaller 或 cx_Freeze 创建独立可执行文件 +- 提供自动安装脚本,简化部署过程 +- 支持自动更新机制 +- 创建详细的安装和部署文档 + +### 7.2 监控与日志 + +**当前情况**:基本的日志记录功能。 + +**优化建议**: +- 实现结构化日志系统,支持日志搜索和分析 +- 添加系统性能监控功能 +- 设计操作审计日志,记录关键操作 +- 支持日志远程存储和集中管理 + +### 7.3 文档完善 + +**当前情况**:有基本的 README 文档。 + +**优化建议**: +- 创建详细的开发者文档,包括架构说明和 API 参考 +- 编写用户手册和操作指南 +- 添加代码内文档字符串,支持自动文档生成 +- 提供常见问题解答和故障排除指南 + +## 8. 当前优化重点 + +基于系统现状,建议首先关注以下优化点: + +1. **重构单位转换逻辑**:将复杂的单位转换和条码映射逻辑模块化,提高可维护性 +2. **增强数据验证**:改进条码和规格提取逻辑,减少处理错误 +3. **UI 改进**:将大型启动器文件拆分为多个组件,采用 MVC 模式 +4. **添加单元测试**:为核心业务逻辑添加测试用例,确保功能正确性 +5. **实现缓存机制**:提高重复数据处理效率 + +## 9. 长期优化计划 + +长期来看,建议考虑以下方向: + +1. **迁移到 Web 应用**:考虑将系统转换为 Web 应用,提供更好的跨平台支持 +2. **数据智能分析**:增加智能分析功能,如采购趋势分析、异常检测等 +3. **与 ERP 系统集成**:提供与主流 ERP 系统的集成接口 +4. **移动端支持**:开发移动应用或响应式 Web 界面,支持手机操作 +5. **OCR 引擎替换选项**:支持多种 OCR 引擎,降低对单一 API 的依赖 + +通过以上优化,OCR 订单处理系统将更加健壮、高效、易用,能够更好地满足业务需求,并为未来功能扩展提供良好的基础。 \ No newline at end of file