From 7b45d04a6b6a60c8462c906a35b2152a07759b04 Mon Sep 17 00:00:00 2001 From: BXY0414 <1079823339@qq.com> Date: Wed, 22 Apr 2026 17:12:37 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4=20=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E9=A1=BA=E5=BA=8F=E3=80=81=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .DS_Store | Bin 6148 -> 6148 bytes .gitignore | 2 ++ REFACTOR_PLAN.md | 20 +++++++-------- inventory_sync/feishu.py | 54 ++++++++++++++++----------------------- inventory_sync/specs.py | 25 +++++++++++++----- inventory_sync/sql.py | 5 ++-- 6 files changed, 56 insertions(+), 50 deletions(-) diff --git a/.DS_Store b/.DS_Store index c1709d2295b7cd9ed83e9cf3d827ed9894666cf3..4e5d78000abec618badcf31fa064fb7715bacf9f 100644 GIT binary patch delta 64 zcmZoMXfc=|#>CJ*u~2NHo+2aT!~knX#>qTP;hUc^E3$0B)qu~2NHo+2ab!~pA!9~u~ejGpj%ZiWJeB8Gg1EQVBuWQGz31qKZU zLk3OHoc!dZoctsP1_l8jz750{|G@yrVqn0bE2+G=0IF+8QbA5;afyM!HAW_87FITP z4t5T1j@aOg{PN(E#FEltr^KS@;EekG@{s)eoFp&@$WBZO%S&Q$=1uBUN&&*57FL%l>%}Xf;Y6cnwk>KFu;EWfLsIE3NHPBHoGPbPMQK+^w0kTCrJAIJuSjW5+%HnVf^a{%LK bool: + managed_names = {spec.feishu_name for spec in FIELD_SPECS} + managed_names.update(LEGACY_NAME_MAP.keys()) + managed_names.update(LEGACY_NAME_MAP.values()) + if field_name in managed_names: + return True + return bool(re.fullmatch(r"\d{1,2}月销售数量", field_name) or re.fullmatch(r"\d{6}销售数量", field_name)) + def batch_delete_records(self, access_token: str, app_token: str, table_id: str, record_ids: list[str]) -> int: deleted = 0 for start in range(0, len(record_ids), 500): @@ -217,40 +226,21 @@ class FeishuClient: self.update_field(access_token, app_token, table_id, primary_field["field_id"], payload) time.sleep(0.2) - for old_name, new_name in LEGACY_NAME_MAP.items(): - fields = self.list_fields(access_token, app_token, table_id) - field = next((item for item in fields if item.get("field_name") == old_name), None) - if field and old_name != new_name: - payload = {"field_name": new_name, "type": field.get("type")} - if field.get("property") is not None: - payload["property"] = field.get("property") - self.update_field(access_token, app_token, table_id, field["field_id"], payload) + fields = self.list_fields(access_token, app_token, table_id) + for field in fields: + field_name = field.get("field_name") + field_id = field.get("field_id") + if field.get("is_primary") or not field_name or not field_id: + continue + if self._is_managed_field(field_name): + self.delete_field(access_token, app_token, table_id, field_id) time.sleep(0.2) - fields = self.list_fields(access_token, app_token, table_id) - fields_by_name = {field.get("field_name"): field for field in fields if field.get("field_name")} for spec in FIELD_SPECS: - field = fields_by_name.get(spec.feishu_name) - if not field: - payload = {"field_name": spec.feishu_name, "type": spec.field_type} - if spec.formatter is not None: - payload["property"] = {"formatter": spec.formatter} - self.create_field(access_token, app_token, table_id, payload) - time.sleep(0.2) - fields = self.list_fields(access_token, app_token, table_id) - fields_by_name = {item.get("field_name"): item for item in fields if item.get("field_name")} - field = fields_by_name.get(spec.feishu_name) - if not field: - continue - current_formatter = (field.get("property") or {}).get("formatter") - if field.get("type") != spec.field_type or current_formatter != spec.formatter: - payload = {"field_name": spec.feishu_name, "type": spec.field_type} - if spec.formatter is not None: - payload["property"] = {"formatter": spec.formatter} - elif field.get("property") is not None: - payload["property"] = field.get("property") - self.update_field(access_token, app_token, table_id, field["field_id"], payload) - time.sleep(0.2) + payload = {"field_name": spec.feishu_name, "type": spec.field_type} + if spec.formatter is not None: + payload["property"] = {"formatter": spec.formatter} + self.create_field(access_token, app_token, table_id, payload) + time.sleep(0.2) return [field.get("field_name") for field in self.list_fields(access_token, app_token, table_id) if field.get("field_name")] - diff --git a/inventory_sync/specs.py b/inventory_sync/specs.py index 98fa691..8145344 100644 --- a/inventory_sync/specs.py +++ b/inventory_sync/specs.py @@ -1,6 +1,7 @@ from __future__ import annotations from dataclasses import dataclass +from datetime import date PRIMARY_FIELD_NAME = "货品编码" @@ -26,19 +27,28 @@ BRAND_SYNC_TARGETS = [ BrandSyncTarget(brand_name="羽心堂", table_name="羽心堂"), ] + +def month_sales_field_name(month_offset: int, today: date | None = None) -> str: + current = today or date.today() + month_index = current.year * 12 + current.month - 1 - month_offset + year = month_index // 12 + month = month_index % 12 + 1 + return f"{year}{month:02d}销售数量" + + FIELD_SPECS = [ - FieldSpec("品牌", "品牌名称", 1), - FieldSpec("分类", "一级分类", 1), - FieldSpec("产品类别", "货品简称", 1), FieldSpec("货品名称", "商品名称", 1), + FieldSpec("分类", "一级分类", 1), FieldSpec("条形码", "条形码", 1), FieldSpec("零售价", "零售价", 2, "0.00", 2), FieldSpec("店铺名称", "店铺名称", 1), FieldSpec("店铺可用库存", "店铺挂单量", 2, "0", 0), FieldSpec("总库存", "总库存", 2, "0", 0), - FieldSpec("本月销售数量", "本月销售数量", 2, "0", 0), - FieldSpec("上月销售数量", "上月销售数量", 2, "0", 0), - FieldSpec("上上月销售数量", "上上月销售数量", 2, "0", 0), + FieldSpec(month_sales_field_name(0), "本月销售数量", 2, "0", 0), + FieldSpec(month_sales_field_name(1), "上月销售数量", 2, "0", 0), + FieldSpec(month_sales_field_name(2), "上上月销售数量", 2, "0", 0), + FieldSpec("品牌", "品牌名称", 1), + FieldSpec("产品类别", "货品简称", 1), ] LEGACY_NAME_MAP = { @@ -47,5 +57,8 @@ LEGACY_NAME_MAP = { "货品简称": "产品类别", "商品名称": "货品名称", "店铺挂单量": "店铺可用库存", + "本月销售数量": month_sales_field_name(0), + "上月销售数量": month_sales_field_name(1), + "上上月销售数量": month_sales_field_name(2), "统一规格编码": PRIMARY_FIELD_NAME, } diff --git a/inventory_sync/sql.py b/inventory_sync/sql.py index bb1c0d0..909057f 100644 --- a/inventory_sync/sql.py +++ b/inventory_sync/sql.py @@ -54,6 +54,7 @@ shop_listing_latest AS ( sales_summary AS ( SELECT fs."统一规格编码", + fs."店铺主键", SUM( CASE WHEN fs."发货时间" >= date_trunc('month', now()) @@ -82,7 +83,7 @@ sales_summary AS ( WHERE NULLIF(TRIM(fs."统一规格编码"), '') IS NOT NULL AND fs."发货时间" >= date_trunc('month', now()) - INTERVAL '2 month' AND fs."发货时间" < date_trunc('month', now()) + INTERVAL '1 month' - GROUP BY fs."统一规格编码" + GROUP BY fs."统一规格编码", fs."店铺主键" ), product_dim AS ( @@ -120,8 +121,8 @@ LEFT JOIN analytics_layer.dim_shop ds ON sl."店铺主键" = ds."店铺主键" LEFT JOIN sales_summary ss ON sl."统一规格编码" = ss."统一规格编码" + AND sl."店铺主键" = ss."店铺主键" LEFT JOIN product_dim pd ON sl."统一规格编码" = pd."统一规格编码" ORDER BY sl."统一规格编码", sl."店铺主键" """ -