v2.3.0: info 新增开发包/传输请求字段 (纯只读 E071)

- info 输出新增「开发包」(packageRef)、「传输请求」(纯只读查 E071)

- 优雅降级: 无请求/查询失败/非独立传输对象, 不中断 info

- bump 2.2.1 -> 2.3.0

- pack 从源码同步 (含 parser/transport 最新代码)
This commit is contained in:
WuRangyu
2026-08-06 11:57:36 +08:00
parent 607445b338
commit 02bc2c5165
7 changed files with 381 additions and 193 deletions
+39 -1
View File
@@ -4,6 +4,7 @@ from __future__ import annotations
import argparse
import logging
import os
import re
import xml.etree.ElementTree as ET
from sapcli.client import ADTClient
@@ -18,7 +19,7 @@ from sapcli.exceptions import (
SapCliError,
)
from sapcli.manifest import Manifest, ManifestEntry
from sapcli.types import get_type_config, parse_object_name
from sapcli.types import get_r3tr_object_code, get_type_config, parse_object_name
DDIC_TYPES = {"domain", "dataelement", "table", "structure", "tabletype"}
@@ -401,6 +402,30 @@ def _update_manifest_after_sync(
logger.warning("更新清单失败: %s", e)
def _query_transport_request(
client: ADTClient, obj_type: str, display_name: str
) -> str:
"""查对象当前所在(未释放)的传输请求号。纯只读查 E071;失败优雅降级。"""
r3tr_code = get_r3tr_object_code(obj_type)
if not r3tr_code:
return "—(非独立传输对象)"
obj_name_upper = display_name.upper().split("/")[-1]
if not re.fullmatch(r"[A-Za-z0-9_/$]+", obj_name_upper):
return "(对象名非法,跳过)"
try:
sql = (
f"SELECT trkorr FROM e071 WHERE pgmid='R3TR' "
f"AND object='{r3tr_code}' AND obj_name='{obj_name_upper}' "
f"UP TO 10 ROWS"
)
result = client.query_table_data(sql, max_rows=10)
trkorrs = sorted({row[0] for row in result.get("rows", []) if row and row[0]})
return ", ".join(trkorrs) if trkorrs else ""
except Exception:
logger.debug("查询 E071 传输请求失败", exc_info=True)
return "(无法获取)"
def cmd_info(args: argparse.Namespace, client: ADTClient) -> None:
"""查询对象元数据信息。"""
obj_type: str = args.type
@@ -469,6 +494,14 @@ def cmd_info(args: argparse.Namespace, client: ADTClient) -> None:
info_responsible = root.attrib.get(f"{{{ns['adtcore']}}}responsible", "")
info_language = root.attrib.get(f"{{{ns['adtcore']}}}masterLanguage", "")
# 开发包:解析对象 XML 自带的 packageRef 子元素
pkg_ref = root.find(f".//{{{ns['adtcore']}}}packageRef")
info_package = (
pkg_ref.attrib.get(f"{{{ns['adtcore']}}}name", "")
if pkg_ref is not None
else ""
)
if info_version == "active":
status_icon = "✓ 已激活"
elif info_version == "inactive":
@@ -476,6 +509,9 @@ def cmd_info(args: argparse.Namespace, client: ADTClient) -> None:
else:
status_icon = f"{info_version}"
# 传输请求号:纯只读查 E071(不在对象元数据 XML 中)
corr_display = _query_transport_request(client, obj_type, parsed.display_name)
print(f" ✓ 查询成功\n")
print(f" {'' * 50}")
print(f" 名称: {info_name}")
@@ -484,6 +520,8 @@ def cmd_info(args: argparse.Namespace, client: ADTClient) -> None:
print(f" 状态: {status_icon}")
print(f" 负责人: {info_responsible}")
print(f" 语言: {info_language}")
print(f" 开发包: {info_package or '(未指定)'}")
print(f" 传输请求: {corr_display}")
print(f" 创建者: {info_created_by}")
print(f" 修改者: {info_changed_by}")
print(f" 修改时间: {info_changed_at}")
+26 -1
View File
@@ -21,12 +21,15 @@ def cmd_transport(args: argparse.Namespace, client: ADTClient) -> None:
_transport_release(args, client)
elif action == "objects":
_transport_objects(args, client)
elif action == "create":
_transport_create(args, client)
else:
print(" 用法: sap-cli transport [list|info|release|objects]")
print(" 用法: sap-cli transport [list|info|release|objects|create]")
print(" list — 列出可修改的传输请求")
print(" info — 查看传输请求详情")
print(" release — 释放传输请求")
print(" objects — 列出传输请求中的对象")
print(" create — 新建传输请求 (--description)")
def _transport_list(args: argparse.Namespace, client: ADTClient) -> None:
@@ -107,6 +110,28 @@ def _transport_release(args: argparse.Namespace, client: ADTClient) -> None:
print(f" ✗ 释放失败,请检查传输请求状态")
def _transport_create(args: argparse.Namespace, client: ADTClient) -> None:
"""新建传输请求。"""
description: str = args.description
print("=" * 60)
print(" SAP 新建传输请求")
print("=" * 60)
print(f" 描述: {description}")
print(f"\n → 正在创建...")
try:
new_nr = client.create_transport_request(description)
except Exception as e:
print(f" ✗ 创建失败: {e}")
return
if new_nr:
print(f"\n ✓ 传输请求已创建: {new_nr}")
else:
print(f"\n ✗ 创建失败(未返回编号),请检查系统或描述合法性")
def _transport_objects(args: argparse.Namespace, client: ADTClient) -> None:
"""列出传输请求中的对象。"""
corr_nr: str = args.corr_nr