feat: 益选 OCR 订单处理系统初始提交

- 智能供应商识别(蓉城易购/烟草/杨碧月/通用)
- 百度 OCR 表格识别集成
- 规则引擎(列映射/数据清洗/单位转换/规格推断)
- 条码映射管理与云端同步(Gitea REST API)
- 云端同步支持:条码映射、供应商配置、商品资料、采购模板
- 拖拽一键处理(图片→OCR→Excel→合并)
- 191 个单元测试
- 移除无用的模板管理功能
- 清理 IDE 产物目录

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-04 19:51:13 +08:00
commit e4d62df7e3
78 changed files with 15257 additions and 0 deletions
+187
View File
@@ -0,0 +1,187 @@
"""app.core.db.product_db 单元测试"""
import os
import tempfile
import pytest
import pandas as pd
from app.core.db.product_db import ProductDatabase
@pytest.fixture
def db_dir():
"""临时目录"""
with tempfile.TemporaryDirectory() as d:
yield d
@pytest.fixture
def sample_excel(db_dir):
"""创建测试用 Excel 文件"""
path = os.path.join(db_dir, '商品资料.xlsx')
df = pd.DataFrame({
'商品条码': ['6920584471055', '6901028001133', '6925303800013'],
'商品名称': ['农夫山泉550ml', '蒙牛纯牛奶', '可口可乐330ml'],
'进货价': [1.2, 3.5, 1.8],
'单位': ['', '', ''],
})
df.to_excel(path, index=False)
return path
@pytest.fixture
def db_with_data(db_dir, sample_excel):
"""已导入数据的数据库"""
db_path = os.path.join(db_dir, 'product_cache.db')
db = ProductDatabase(db_path, sample_excel)
return db
class TestProductDatabaseInit:
"""数据库初始化测试"""
def test_auto_import_on_first_run(self, db_dir, sample_excel):
"""首次运行自动从 Excel 导入"""
db_path = os.path.join(db_dir, 'product_cache.db')
assert not os.path.exists(db_path)
db = ProductDatabase(db_path, sample_excel)
assert os.path.exists(db_path)
assert db.count() == 3
def test_no_reimport_on_existing_db(self, db_dir, sample_excel):
"""数据库已存在时不重新导入"""
db_path = os.path.join(db_dir, 'product_cache.db')
db1 = ProductDatabase(db_path, sample_excel)
assert db1.count() == 3
# 删除 Excel 后仍能打开已有数据库
os.remove(sample_excel)
db2 = ProductDatabase(db_path, sample_excel)
assert db2.count() == 3
def test_missing_excel_creates_empty_db(self, db_dir):
"""Excel 不存在时创建空数据库"""
db_path = os.path.join(db_dir, 'product_cache.db')
fake_excel = os.path.join(db_dir, '不存在.xlsx')
db = ProductDatabase(db_path, fake_excel)
assert os.path.exists(db_path)
assert db.count() == 0
def test_missing_dir_created(self, db_dir, sample_excel):
"""数据库目录不存在时自动创建"""
db_path = os.path.join(db_dir, 'subdir', 'product_cache.db')
db = ProductDatabase(db_path, sample_excel)
assert os.path.exists(db_path)
assert db.count() == 3
class TestGetPrice:
"""单条查询测试"""
def test_existing_barcode(self, db_with_data):
price = db_with_data.get_price('6920584471055')
assert price == pytest.approx(1.2)
def test_nonexistent_barcode(self, db_with_data):
price = db_with_data.get_price('0000000000000')
assert price is None
def test_empty_barcode(self, db_with_data):
price = db_with_data.get_price('')
assert price is None
def test_barcode_with_spaces(self, db_with_data):
"""条码前后空格应能匹配"""
price = db_with_data.get_price(' 6920584471055 ')
assert price == pytest.approx(1.2)
class TestGetPrices:
"""批量查询测试"""
def test_multiple_barcodes(self, db_with_data):
result = db_with_data.get_prices(['6920584471055', '6901028001133'])
assert len(result) == 2
assert result['6920584471055'] == pytest.approx(1.2)
assert result['6901028001133'] == pytest.approx(3.5)
def test_partial_match(self, db_with_data):
"""部分条码存在,部分不存在"""
result = db_with_data.get_prices(['6920584471055', '0000000000000'])
assert len(result) == 1
assert '6920584471055' in result
def test_empty_list(self, db_with_data):
result = db_with_data.get_prices([])
assert result == {}
def test_all_nonexistent(self, db_with_data):
result = db_with_data.get_prices(['0000000000000', '1111111111111'])
assert result == {}
class TestReimport:
"""重新导入测试"""
def test_reimport_clears_and_reloads(self, db_dir, sample_excel):
db_path = os.path.join(db_dir, 'product_cache.db')
db = ProductDatabase(db_path, sample_excel)
assert db.count() == 3
# 修改 Excel,添加一行
df = pd.read_excel(sample_excel)
df = pd.concat([df, pd.DataFrame({
'商品条码': ['6954365200123'],
'商品名称': ['测试商品'],
'进货价': [5.0],
'单位': [''],
})])
df.to_excel(sample_excel, index=False)
count = db.reimport()
assert count == 4
assert db.count() == 4
assert db.get_price('6954365200123') == pytest.approx(5.0)
class TestEdgeCases:
"""边界条件测试"""
def test_excel_with_missing_price(self, db_dir):
"""Excel 中价格列为空的行"""
path = os.path.join(db_dir, '商品资料.xlsx')
df = pd.DataFrame({
'商品条码': ['6920584471055', '6901028001133'],
'商品名称': ['商品A', '商品B'],
'进货价': [1.5, None],
})
df.to_excel(path, index=False)
db_path = os.path.join(db_dir, 'product_cache.db')
db = ProductDatabase(db_path, path)
assert db.count() == 2
assert db.get_price('6920584471055') == pytest.approx(1.5)
assert db.get_price('6901028001133') == pytest.approx(0.0)
def test_excel_with_duplicate_barcodes(self, db_dir):
"""重复条码取最后一条 (INSERT OR REPLACE)"""
path = os.path.join(db_dir, '商品资料.xlsx')
df = pd.DataFrame({
'商品条码': ['6920584471055', '6920584471055'],
'商品名称': ['商品A', '商品A-新'],
'进货价': [1.0, 2.0],
})
df.to_excel(path, index=False)
db_path = os.path.join(db_dir, 'product_cache.db')
db = ProductDatabase(db_path, path)
assert db.count() == 1
assert db.get_price('6920584471055') == pytest.approx(2.0)