feat(info): DDIC 类型补全描述与开发包(NW 7.40 ADT 不返回)
NW 7.40 的 */* 响应不含 description/packageRef,info 对 structure/table/tabletype/dataelement/domain 在 ADT 返回值为空时回退查文本表补描述(DD02T/DD40T/DD04T/DD01T)、查 TADIR 补开发包。语言按「登录语言 -> E -> 1 -> 首行」回退;补查异常静默降级不打断输出;TADIR 无行时保持 (未指定)。
This commit is contained in:
@@ -23,6 +23,35 @@ from sapcli.types import get_r3tr_object_code, get_type_config, parse_object_nam
|
||||
|
||||
DDIC_TYPES = {"domain", "dataelement", "table", "structure", "tabletype"}
|
||||
|
||||
# NW 7.40 ADT 对 DDIC 对象(structure/table/tabletype/dataelement/domain)的 */* 响应
|
||||
# 不含 description / packageRef,需回退查文本表补描述、查 TADIR 补开发包。
|
||||
# 描述表的语言列固定为 DDLANGUAGE、文本列固定为 DDTEXT。
|
||||
_DDIC_DESC_TABLE: dict[str, tuple[str, str]] = {
|
||||
"structure": ("DD02T", "TABNAME"),
|
||||
"table": ("DD02T", "TABNAME"),
|
||||
"tabletype": ("DD40T", "TYPENAME"),
|
||||
"dataelement": ("DD04T", "ROLLNAME"),
|
||||
"domain": ("DD01T", "DOMNAME"),
|
||||
}
|
||||
|
||||
# DDIC 对象 → TADIR.OBJECT 代码(用于补开发包 DEVCLASS)。
|
||||
_DDIC_PACKAGE_OBJECT: dict[str, str] = {
|
||||
"structure": "TABL",
|
||||
"table": "TABL",
|
||||
"tabletype": "TTYP",
|
||||
"dataelement": "DTEL",
|
||||
"domain": "DOMA",
|
||||
}
|
||||
|
||||
# ADT masterLanguage 返回 ISO 码(如 'ZH'/'EN'),文本表 DDLANGUAGE 用 SAP 单字符键。
|
||||
_ISO_TO_SAP_LANGUAGE: dict[str, str] = {
|
||||
"ZH": "1",
|
||||
"EN": "E",
|
||||
"DE": "D",
|
||||
"JA": "J",
|
||||
"KO": "3",
|
||||
}
|
||||
|
||||
logger = logging.getLogger("sapcli.commands.crud")
|
||||
|
||||
_PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
@@ -426,6 +455,101 @@ def _query_transport_request(
|
||||
return "(无法获取)"
|
||||
|
||||
|
||||
def _sap_language_key(master_language: str) -> str:
|
||||
"""把 ADT masterLanguage(ISO 码,如 'ZH'/'EN')转成 SAP 单字符语言键。
|
||||
|
||||
已是单字符时原样返回;无法映射时原样返回(回退顺序会自然跳过不匹配项)。
|
||||
"""
|
||||
lang = (master_language or "").strip().upper()
|
||||
if not lang:
|
||||
return ""
|
||||
if len(lang) == 1:
|
||||
return lang
|
||||
return _ISO_TO_SAP_LANGUAGE.get(lang, lang)
|
||||
|
||||
|
||||
def _pick_description_text(rows, preferred_lang: str = "") -> str:
|
||||
"""按「登录语言 -> 'E' -> '1' -> 首行」回退,从 (语言, 文本) 行中挑描述文本。"""
|
||||
if not rows:
|
||||
return ""
|
||||
by_lang: dict[str, str] = {}
|
||||
order: list[str] = []
|
||||
for lang, text in rows:
|
||||
lang = (lang or "").strip().upper()
|
||||
if lang not in by_lang:
|
||||
by_lang[lang] = (text or "").strip()
|
||||
order.append(lang)
|
||||
priority = [p for p in (preferred_lang, "E", "1") if p]
|
||||
for p in priority:
|
||||
p = p.strip().upper()
|
||||
for lang in order:
|
||||
if lang == p and by_lang[lang]:
|
||||
return by_lang[lang]
|
||||
for lang in order:
|
||||
if by_lang[lang]:
|
||||
return by_lang[lang]
|
||||
return ""
|
||||
|
||||
|
||||
def _query_ddic_description(
|
||||
client: ADTClient, obj_type: str, obj_name: str, preferred_lang: str
|
||||
) -> str:
|
||||
"""ADT 返回空描述时,查描述表补 DDIC 对象描述;失败返回 '' 不抛。"""
|
||||
if obj_type not in _DDIC_DESC_TABLE:
|
||||
return ""
|
||||
table, key_col = _DDIC_DESC_TABLE[obj_type]
|
||||
name_upper = obj_name.upper().split("/")[-1]
|
||||
if not re.fullmatch(r"[A-Za-z0-9_/$]+", name_upper):
|
||||
return ""
|
||||
try:
|
||||
sql = (
|
||||
f"SELECT DDLANGUAGE, DDTEXT FROM {table} "
|
||||
f"WHERE {key_col} IN ('{name_upper}')"
|
||||
)
|
||||
result = client.query_table_data(sql, max_rows=20)
|
||||
columns = result.get("columns", [])
|
||||
rows = result.get("rows", [])
|
||||
lang_idx = columns.index("DDLANGUAGE") if "DDLANGUAGE" in columns else 0
|
||||
text_idx = columns.index("DDTEXT") if "DDTEXT" in columns else 1
|
||||
pairs = []
|
||||
for row in rows:
|
||||
if row is None:
|
||||
continue
|
||||
lang = row[lang_idx] if lang_idx < len(row) else ""
|
||||
text = row[text_idx] if text_idx < len(row) else ""
|
||||
pairs.append((lang, text))
|
||||
return _pick_description_text(pairs, preferred_lang)
|
||||
except Exception:
|
||||
logger.debug("查询 %s 描述失败", table, exc_info=True)
|
||||
return ""
|
||||
|
||||
|
||||
def _query_ddic_package(client: ADTClient, obj_type: str, obj_name: str) -> str:
|
||||
"""ADT 返回空开发包时,查 TADIR 补 DEVCLASS;失败返回 '' 不抛。"""
|
||||
if obj_type not in _DDIC_PACKAGE_OBJECT:
|
||||
return ""
|
||||
obj_code = _DDIC_PACKAGE_OBJECT[obj_type]
|
||||
name_upper = obj_name.upper().split("/")[-1]
|
||||
if not re.fullmatch(r"[A-Za-z0-9_/$]+", name_upper):
|
||||
return ""
|
||||
try:
|
||||
sql = (
|
||||
f"SELECT DEVCLASS FROM TADIR "
|
||||
f"WHERE OBJECT IN ('{obj_code}') AND OBJ_NAME IN ('{name_upper}')"
|
||||
)
|
||||
result = client.query_table_data(sql, max_rows=10)
|
||||
columns = result.get("columns", [])
|
||||
rows = result.get("rows", [])
|
||||
dev_idx = columns.index("DEVCLASS") if "DEVCLASS" in columns else 0
|
||||
for row in rows:
|
||||
if row and dev_idx < len(row) and row[dev_idx]:
|
||||
return str(row[dev_idx]).strip()
|
||||
return ""
|
||||
except Exception:
|
||||
logger.debug("查询 TADIR 开发包失败", exc_info=True)
|
||||
return ""
|
||||
|
||||
|
||||
def cmd_info(args: argparse.Namespace, client: ADTClient) -> None:
|
||||
"""查询对象元数据信息。"""
|
||||
obj_type: str = args.type
|
||||
@@ -505,6 +629,15 @@ def cmd_info(args: argparse.Namespace, client: ADTClient) -> None:
|
||||
else ""
|
||||
)
|
||||
|
||||
# NW 7.40 DDIC 的 */* 响应不含 description/packageRef:仅在 ADT 返回值为空时补查,
|
||||
# 补查失败静默降级(保持原样),不打断 info 输出。
|
||||
if not info_desc:
|
||||
info_desc = _query_ddic_description(
|
||||
client, obj_type, parsed.display_name, _sap_language_key(info_language)
|
||||
)
|
||||
if not info_package:
|
||||
info_package = _query_ddic_package(client, obj_type, parsed.display_name)
|
||||
|
||||
if info_version == "active":
|
||||
status_icon = "✓ 已激活"
|
||||
elif info_version == "inactive":
|
||||
|
||||
Reference in New Issue
Block a user