Przeglądaj źródła

feat: add pinyin initial abbr helper for CRM names.

Co-authored-by: Cursor <cursoragent@cursor.com>
zlwu 2 miesięcy temu
rodzic
commit
fa9b3429c7

+ 58 - 0
backend/app/utils/pinyin_abbr.py

@@ -0,0 +1,58 @@
+"""名称拼音首字母缩写:库内存小写,检索时大小写不敏感。"""
+from __future__ import annotations
+
+import re
+from typing import Optional
+
+from pypinyin import Style, lazy_pinyin
+
+_MAX_LEN = 100
+_ALNUM = re.compile(r"[a-zA-Z0-9]")
+
+
+def normalize_name_pinyin_abbr(value: Optional[str]) -> str:
+    raw = (value or "").strip().lower()
+    if not raw:
+        return ""
+    return raw[:_MAX_LEN]
+
+
+def build_name_pinyin_abbr(name: Optional[str]) -> str:
+    text = (name or "").strip()
+    if not text:
+        return ""
+    parts: list[str] = []
+    for ch in text:
+        if "\u4e00" <= ch <= "\u9fff":
+            pys = lazy_pinyin(ch, style=Style.FIRST_LETTER)
+            if pys and pys[0]:
+                parts.append(str(pys[0]).lower()[0])
+        elif _ALNUM.match(ch):
+            parts.append(ch.lower())
+        # else: drop punctuation / spaces / % / brackets
+    return "".join(parts)[:_MAX_LEN]
+
+
+def resolve_name_pinyin_abbr(
+    *,
+    name: Optional[str],
+    submitted: Optional[str] = None,
+    existing: Optional[str] = None,
+    submitted_provided: bool = False,
+) -> Optional[str]:
+    """保存时解析拼音字段。
+
+    - submitted_provided=True:用提交值(空则按 name 生成)
+    - 否则:已有非空 existing 则保留;existing 空则按 name 生成
+    """
+    if submitted_provided:
+        normalized = normalize_name_pinyin_abbr(submitted)
+        if normalized:
+            return normalized
+        generated = build_name_pinyin_abbr(name)
+        return generated or None
+    existing_norm = normalize_name_pinyin_abbr(existing)
+    if existing_norm:
+        return existing_norm
+    generated = build_name_pinyin_abbr(name)
+    return generated or None

+ 1 - 0
backend/requirements.txt

@@ -18,3 +18,4 @@ Pillow>=10.0.0
 requests>=2.31.0
 openpyxl>=3.1.0
 alibabacloud_ocr_api20210707>=3.1.3
+pypinyin>=0.53.0

+ 74 - 0
backend/tests/test_pinyin_abbr.py

@@ -0,0 +1,74 @@
+from app.utils.pinyin_abbr import (
+    build_name_pinyin_abbr,
+    normalize_name_pinyin_abbr,
+    resolve_name_pinyin_abbr,
+)
+
+
+def test_empty_and_whitespace():
+    assert build_name_pinyin_abbr("") == ""
+    assert build_name_pinyin_abbr("   ") == ""
+    assert normalize_name_pinyin_abbr(None) == ""
+    assert normalize_name_pinyin_abbr("  AbC  ") == "abc"
+
+
+def test_chinese_and_alnum():
+    # pypinyin FIRST_LETTER: 工g业y硫l酸s(非设计稿中的 gylls)
+    assert build_name_pinyin_abbr("55% 工业硫酸") == "55gyls"
+    # pypinyin: 苯b骈p三s氮d唑z + BTA(非设计稿中的 bstnzbta)
+    assert build_name_pinyin_abbr("苯骈三氮唑BTA") == "bpsdzbta"
+    assert build_name_pinyin_abbr("PAC") == "pac"
+
+
+def test_truncate_to_100():
+    long_name = "测" * 120
+    out = build_name_pinyin_abbr(long_name)
+    assert len(out) == 100
+
+
+def test_resolve_submitted_provided_nonempty():
+    assert (
+        resolve_name_pinyin_abbr(
+            name="工业硫酸",
+            submitted="  CustomAbbr  ",
+            existing="old",
+            submitted_provided=True,
+        )
+        == "customabbr"
+    )
+
+
+def test_resolve_submitted_provided_empty_generates():
+    assert (
+        resolve_name_pinyin_abbr(
+            name="PAC",
+            submitted="  ",
+            existing="old",
+            submitted_provided=True,
+        )
+        == "pac"
+    )
+
+
+def test_resolve_not_submitted_keeps_existing():
+    assert (
+        resolve_name_pinyin_abbr(
+            name="工业硫酸",
+            submitted=None,
+            existing="  KeepMe  ",
+            submitted_provided=False,
+        )
+        == "keepme"
+    )
+
+
+def test_resolve_not_submitted_empty_existing_generates():
+    assert (
+        resolve_name_pinyin_abbr(
+            name="PAC",
+            submitted=None,
+            existing="",
+            submitted_provided=False,
+        )
+        == "pac"
+    )