From 279eceaa9551b6b61911a456cb4bd95926732809 Mon Sep 17 00:00:00 2001 From: CHE LIANG ZHAO Date: Mon, 26 Jan 2026 14:12:05 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BA=AC=E4=B8=9C=E8=B4=A6=E5=8D=95?= =?UTF-8?q?=E4=B8=93=E5=B1=9E=E5=88=86=E7=B1=BB=E6=98=A0=E5=B0=84=E9=85=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- analyzer/cleaners/jd.py | 60 ++++++++++++++++++++++++++++---- analyzer/config/category.yaml | 46 ++++++++++++++++++++++++ analyzer/config/category_jd.yaml | 48 +++++++++++++++++++++++++ 3 files changed, 147 insertions(+), 7 deletions(-) create mode 100644 analyzer/config/category_jd.yaml diff --git a/analyzer/cleaners/jd.py b/analyzer/cleaners/jd.py index 9513e5b..a8632d1 100644 --- a/analyzer/cleaners/jd.py +++ b/analyzer/cleaners/jd.py @@ -4,6 +4,9 @@ import csv import re from decimal import Decimal +from pathlib import Path + +import yaml from .base import ( BaseCleaner, parse_amount, format_amount, @@ -12,6 +15,54 @@ from .base import ( from category import infer_category +# 加载京东专属分类配置 +JD_CONFIG_FILE = Path(__file__).parent.parent / "config" / "category_jd.yaml" + +def load_jd_config(): + """加载京东分类配置""" + with open(JD_CONFIG_FILE, "r", encoding="utf-8") as f: + return yaml.safe_load(f) + +_jd_config = load_jd_config() + + +def infer_jd_category(merchant: str, product: str, original_category: str) -> tuple[str, bool]: + """ + 根据京东账单的商户名称、商品说明和原分类推断统一分类 + + Args: + merchant: 商户名称(如"京东外卖"、"京东平台商户") + product: 交易说明/商品说明 + original_category: 京东原始分类(如"食品酒饮"、"数码电器") + + Returns: + (分类名称, 是否确定) - 如果无法确定分类,第二个值为 False + """ + # 1. 先检查商户名称直接映射(如"京东外卖" -> "餐饮美食") + merchant_mapping = _jd_config.get("商户映射", {}) + for merchant_key, category in merchant_mapping.items(): + if merchant_key in merchant: + return category, True + + # 2. 尝试直接映射京东原分类 + category_mapping = _jd_config.get("分类映射", {}) + + # 处理多分类情况(如"食品酒饮 其他网购") + original_cats = original_category.split() if original_category else [] + for orig_cat in original_cats: + mapped = category_mapping.get(orig_cat) + if mapped: # 非空映射 + return mapped, True + + # 3. 使用通用分类推断(已包含京东平台商户关键词) + category, is_certain = infer_category(merchant, product, "支出") + if is_certain: + return category, True + + # 4. 返回默认分类 + return _jd_config.get("默认分类", "其他支出"), False + + # 与支付宝/微信对齐的表头(包含"复核等级"字段) ALIGNED_HEADER = [ "交易时间", "交易分类", "交易对方", "对方账号", "商品说明", @@ -242,13 +293,8 @@ class JDCleaner(BaseCleaner): merchant_order_no = row[9] if len(row) > 9 else "" final_remark = remark if remark else (row[10] if len(row) > 10 else "/") - # 使用推断分类(京东原始分类相对准确,但仍可优化) - category, is_certain = infer_category(merchant, product, income_expense) - - # 如果推断失败但原分类非空,使用原分类 - if not is_certain and original_category and original_category not in ["其他", ""]: - category = original_category - is_certain = True # 信任京东原分类 + # 使用京东专属分类推断 + category, is_certain = infer_jd_category(merchant, product, original_category) # 复核等级: 空=无需复核, HIGH=无法判断 review_mark = "" if is_certain else "HIGH" diff --git a/analyzer/config/category.yaml b/analyzer/config/category.yaml index 292f872..ad7426d 100644 --- a/analyzer/config/category.yaml +++ b/analyzer/config/category.yaml @@ -84,6 +84,40 @@ - 供暖 - 暖气 + # 宠物用品 + 宠物用品: + - 宠物 + - 猫咪 + - 狗 + - 猫粮 + - 狗粮 + - 猫砂 + - 喂水 + - 猫零食 + - 犬猫 + + # 数码电器 + 数码电器: + - 饮水机 + - 净水 + - 制冰 + - nas + - 存储 + - 硬盘 + - 电脑 + - 手机 + - 平板 + - 电器 + - 小家电 + - 充电 + - 数据线 + - 路由器 + - 音箱 + - 耳机 + - 键盘 + - 鼠标 + - 显示器 + # 运动健身 运动健身: - 健身 @@ -113,6 +147,9 @@ - 电影 - 游戏 - 娱乐 + - 书 + - 图书 + - 文娱 - 旅游 - 景区 - 门票 @@ -157,6 +194,15 @@ - 妍丽 # AFIONA妍丽美妆店 - 屈臣氏 - 丝芙兰 + - 保鲜盒 + - 收纳 + - 厨房 + - 清洁 + - 洗衣 + - 纸巾 + - 毛巾 + - 床品 + - 家居 # 餐饮美食 餐饮美食: diff --git a/analyzer/config/category_jd.yaml b/analyzer/config/category_jd.yaml new file mode 100644 index 0000000..f7ab786 --- /dev/null +++ b/analyzer/config/category_jd.yaml @@ -0,0 +1,48 @@ +# ============================================================================= +# 京东账单分类映射配置 +# 将京东原始分类转换为统一分类 +# ============================================================================= + +# ============================================================================= +# 京东原始分类 -> 统一分类映射 +# 京东账单中的"交易分类"字段可能包含以下值: +# - 余额、小金库、白条:财务操作(已在清洗时过滤) +# - 其他、其他网购、网购:需要根据商品说明进一步判断 +# - 食品酒饮:餐饮美食 +# - 数码电器、电脑办公:数码电器 +# - 日用百货:日用百货 +# - 图书文娱:文化休闲 +# ============================================================================= + +分类映射: + # 直接映射(京东分类 -> 统一分类) + 食品酒饮: 餐饮美食 + 数码电器: 数码电器 + 电脑办公: 数码电器 + 日用百货: 日用百货 + 图书文娱: 文化休闲 + + # 需要进一步判断的分类(返回空字符串,由关键词推断) + 其他: "" + 其他网购: "" + 网购: "" + + # 财务类(通常已被过滤,但以防万一) + 余额: "" + 小金库: "" + 白条: "" + +# ============================================================================= +# 商户名称 -> 统一分类映射 +# 根据商户名称直接映射分类,无需关键词匹配 +# ============================================================================= + +商户映射: + 京东外卖: 餐饮美食 + +# ============================================================================= +# 默认分类 +# 当无法匹配任何规则时使用 +# ============================================================================= + +默认分类: 其他支出