feat: sap-cli skill v2.1.0 — self-contained distributable package
- assets/: sap-cli source (v2.1.0, 26 commands, 16 object types) - references/: tool constraints + error handling (self-contained) - scripts/setup.py: one-click install/config/verify - SKILL.md: full command reference + dual-platform install guide - VERSION: 2.1.0 Built from D:/Codespace/sap-cli via scripts/pack_skill.py
This commit is contained in:
@@ -0,0 +1,251 @@
|
||||
"""项目模板命令:scaffold。"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
|
||||
from sapcli.client import ADTClient
|
||||
|
||||
logger = logging.getLogger("sapcli.commands.scaffold")
|
||||
|
||||
|
||||
def _alv_report_template(name: str, package: str) -> str:
|
||||
"""生成 ALV 报表模板。"""
|
||||
return (
|
||||
f"REPORT {name.upper()}.\n"
|
||||
f"\n"
|
||||
f"TYPES: BEGIN OF ty_data,\n"
|
||||
f" field1 TYPE char10,\n"
|
||||
f" field2 TYPE char20,\n"
|
||||
f" END OF ty_data.\n"
|
||||
f"\n"
|
||||
f"DATA: gt_data TYPE STANDARD TABLE OF ty_data,\n"
|
||||
f" gs_data TYPE ty_data,\n"
|
||||
f" go_alv TYPE REF TO cl_salv_table,\n"
|
||||
f" go_msg TYPE REF TO cx_salv_msg.\n"
|
||||
f"\n"
|
||||
f"START-OF-SELECTION.\n"
|
||||
f" PERFORM get_data.\n"
|
||||
f" PERFORM display_alv.\n"
|
||||
f"\n"
|
||||
f"FORM get_data.\n"
|
||||
f" \" TODO: 填充数据\n"
|
||||
f" gs_data-field1 = '示例'.\n"
|
||||
f" gs_data-field2 = '数据'.\n"
|
||||
f" APPEND gs_data TO gt_data.\n"
|
||||
f"ENDFORM.\n"
|
||||
f"\n"
|
||||
f"FORM display_alv.\n"
|
||||
f" TRY.\n"
|
||||
f" cl_salv_table=>factory(\n"
|
||||
f" IMPORTING\n"
|
||||
f" r_salv_table = go_alv\n"
|
||||
f" CHANGING\n"
|
||||
f" t_table = gt_data\n"
|
||||
f" ).\n"
|
||||
f" go_alv->display( ).\n"
|
||||
f" CATCH cx_salv_msg INTO go_msg.\n"
|
||||
f" MESSAGE go_msg->get_text( ) TYPE 'I'.\n"
|
||||
f" ENDTRY.\n"
|
||||
f"ENDFORM.\n"
|
||||
)
|
||||
|
||||
|
||||
def _bapi_wrapper_template(name: str, package: str) -> str:
|
||||
"""生成 BAPI 包装类模板。"""
|
||||
class_name = name.upper()
|
||||
return (
|
||||
f"CLASS {class_name} DEFINITION\n"
|
||||
f" PUBLIC\n"
|
||||
f" FINAL\n"
|
||||
f" CREATE PUBLIC.\n"
|
||||
f"\n"
|
||||
f" PUBLIC SECTION.\n"
|
||||
f" TYPES: BEGIN OF ty_result,\n"
|
||||
f" success TYPE abap_bool,\n"
|
||||
f" message TYPE string,\n"
|
||||
f" END OF ty_result.\n"
|
||||
f"\n"
|
||||
f" CLASS-METHODS:\n"
|
||||
f" call_bapi\n"
|
||||
f" IMPORTING\n"
|
||||
f" iv_param1 TYPE string OPTIONAL\n"
|
||||
f" RETURNING\n"
|
||||
f" VALUE(rs_result) TYPE ty_result.\n"
|
||||
f"\n"
|
||||
f" PRIVATE SECTION.\n"
|
||||
f" CLASS-METHODS:\n"
|
||||
f" _call_remote_bapi\n"
|
||||
f" IMPORTING\n"
|
||||
f" iv_param1 TYPE string\n"
|
||||
f" EXPORTING\n"
|
||||
f" ev_success TYPE abap_bool\n"
|
||||
f" ev_message TYPE string.\n"
|
||||
f"ENDCLASS.\n"
|
||||
f"\n"
|
||||
f"\n"
|
||||
f"CLASS {class_name} IMPLEMENTATION.\n"
|
||||
f"\n"
|
||||
f" METHOD call_bapi.\n"
|
||||
f" _call_remote_bapi(\n"
|
||||
f" EXPORTING\n"
|
||||
f" iv_param1 = iv_param1\n"
|
||||
f" IMPORTING\n"
|
||||
f" ev_success = rs_result-success\n"
|
||||
f" ev_message = rs_result-message\n"
|
||||
f" ).\n"
|
||||
f" ENDMETHOD.\n"
|
||||
f"\n"
|
||||
f" METHOD _call_remote_bapi.\n"
|
||||
f" \" TODO: 调用 BAPI 函数\n"
|
||||
f" ev_success = abap_true.\n"
|
||||
f" ev_message = '未实现'.\n"
|
||||
f" ENDMETHOD.\n"
|
||||
f"\n"
|
||||
f"ENDCLASS.\n"
|
||||
)
|
||||
|
||||
|
||||
def _interface_class_template(name: str, package: str) -> str:
|
||||
"""生成接口 + 实现类模板。"""
|
||||
name_upper = name.upper()
|
||||
if name_upper.startswith("Z"):
|
||||
if_name = f"ZIF_{name_upper[2:]}"
|
||||
else:
|
||||
if_name = f"ZIF_{name_upper}"
|
||||
cls_name = name_upper
|
||||
return (
|
||||
f"INTERFACE {if_name}\n"
|
||||
f" PUBLIC.\n"
|
||||
f" METHODS:\n"
|
||||
f" execute\n"
|
||||
f" RETURNING VALUE(rv_result) TYPE string.\n"
|
||||
f"ENDINTERFACE.\n"
|
||||
f"\n"
|
||||
f"\n"
|
||||
f"CLASS {cls_name} DEFINITION\n"
|
||||
f" PUBLIC\n"
|
||||
f" FINAL\n"
|
||||
f" CREATE PUBLIC.\n"
|
||||
f"\n"
|
||||
f" PUBLIC SECTION.\n"
|
||||
f" INTERFACES: {if_name}.\n"
|
||||
f"\n"
|
||||
f" PRIVATE SECTION.\n"
|
||||
f" DATA: mv_state TYPE string.\n"
|
||||
f"ENDCLASS.\n"
|
||||
f"\n"
|
||||
f"\n"
|
||||
f"CLASS {cls_name} IMPLEMENTATION.\n"
|
||||
f"\n"
|
||||
f" METHOD {if_name}~execute.\n"
|
||||
f" rv_result = 'Hello from {cls_name}'.\n"
|
||||
f" ENDMETHOD.\n"
|
||||
f"\n"
|
||||
f"ENDCLASS.\n"
|
||||
)
|
||||
|
||||
|
||||
def _data_model_template(name: str, package: str) -> str:
|
||||
"""生成数据模型模板(DDIC DDL)。"""
|
||||
name_lower = name.lower()
|
||||
return (
|
||||
f"@EndUserText.label: '{name.upper()} 数据模型'\n"
|
||||
f"@AbapCatalog.enhancementCategory: #NOT_CLASSIFIED\n"
|
||||
f"@AbapCatalog.tableCategory: #TRANSPARENT\n"
|
||||
f"@AbapCatalog.deliveryClass: #A\n"
|
||||
f"@AbapCatalog.dataMaintenance: #LIMITED\n"
|
||||
f"define table {name_lower} {{\n"
|
||||
f" key_client : abap.clnt not null;\n"
|
||||
f" key_id : abap.char10 not null;\n"
|
||||
f" name : abap.char40;\n"
|
||||
f" created_at : timestampl;\n"
|
||||
f" changed_at : timestampl;\n"
|
||||
f"}}\n"
|
||||
)
|
||||
|
||||
|
||||
# 模板注册表
|
||||
_TEMPLATES: dict[str, dict] = {
|
||||
"alv-report": {
|
||||
"desc": "ALV 报表模板",
|
||||
"file_name": "{name_lower}.abap",
|
||||
"dir": "reports",
|
||||
"generator": _alv_report_template,
|
||||
},
|
||||
"bapi-wrapper": {
|
||||
"desc": "BAPI 包装类模板",
|
||||
"file_name": "{name_lower}.abap",
|
||||
"dir": "classes",
|
||||
"generator": _bapi_wrapper_template,
|
||||
},
|
||||
"interface-class": {
|
||||
"desc": "接口 + 实现类模板",
|
||||
"file_name": "{name_lower}.abap",
|
||||
"dir": "classes",
|
||||
"generator": _interface_class_template,
|
||||
},
|
||||
"data-model": {
|
||||
"desc": "数据模型模板 (DDIC DDL)",
|
||||
"file_name": "{name_lower}.ddl",
|
||||
"dir": "tables",
|
||||
"generator": _data_model_template,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def cmd_scaffold(args: argparse.Namespace, client: ADTClient | None = None) -> None:
|
||||
"""项目模板创建。"""
|
||||
# 不指定模板时,列出可用模板
|
||||
template = getattr(args, "template", None)
|
||||
if not template:
|
||||
print("=" * 60)
|
||||
print(" sap-cli 可用模板")
|
||||
print("=" * 60)
|
||||
for key, info in _TEMPLATES.items():
|
||||
desc = info.get("description", "")
|
||||
print(f" {key:20s} {desc}")
|
||||
print()
|
||||
print(" 用法: sap-cli scaffold --name ZXXX --template <模板名>")
|
||||
return
|
||||
|
||||
name: str = args.name
|
||||
package: str = getattr(args, "package", "$TMP") or "$TMP"
|
||||
output_dir: str = getattr(args, "path", ".") or "."
|
||||
|
||||
template_info = _TEMPLATES.get(template)
|
||||
if not template_info:
|
||||
print(f" ✗ 不支持的模板类型: {template}")
|
||||
print(f" 可用模板: {', '.join(_TEMPLATES.keys())}")
|
||||
return
|
||||
|
||||
print("=" * 60)
|
||||
print(" sap-cli 项目模板创建")
|
||||
print("=" * 60)
|
||||
print(f" 对象名称: {name}")
|
||||
print(f" 模板: {template} ({template_info['desc']})")
|
||||
print(f" 包: {package}")
|
||||
print(f" 输出目录: {output_dir}")
|
||||
|
||||
# 生成源码
|
||||
name_lower = name.lower()
|
||||
source = template_info["generator"](name, package)
|
||||
|
||||
# 确定输出路径
|
||||
target_dir = os.path.join(output_dir, template_info["dir"])
|
||||
if not os.path.isdir(target_dir):
|
||||
os.makedirs(target_dir, exist_ok=True)
|
||||
|
||||
filename = template_info["file_name"].format(name_lower=name_lower)
|
||||
filepath = os.path.join(target_dir, filename)
|
||||
|
||||
with open(filepath, "w", encoding="utf-8") as f:
|
||||
f.write(source)
|
||||
|
||||
print(f"\n ✓ 模板已生成!")
|
||||
print(f" 文件: {filepath}")
|
||||
print(f" 大小: {len(source)} 字符")
|
||||
print(f"\n 下一步:")
|
||||
print(f" 1. 编辑 {filepath} 完善代码")
|
||||
print(f" 2. 运行 sap-cli sync --name {name} --type report --path {filepath}")
|
||||
Reference in New Issue
Block a user