一张配置表驱动所有接口参数转换——省掉几千行重复代码每接一个新接口就写一套 XML 拼装代码不如把 XML 的结构存成数据用递归自动生成。【toc]一、问题社保系统需要对接一堆外部接口人社部核心平台、省平台、Tuxedo 中间件。每个接口有自己的一套 XML 入参和出参格式。常规做法给每个接口写一段代码把业务参数拼成 XML。100 个接口100 段拼装代码。入参格式一调整所有相关代码都得改。写了几十个之后我发现这些代码长得都差不多-- 接口 Als_xml :headername||prm_name||/nameidno||prm_idno||/idno/header;-- 接口 Bls_xml :bodyxm||prm_name||/xmsfzh||prm_idno||/sfzh/body;-- 接口 C-- 又他妈写一遍就标签名不一样标签名不一样、嵌套层次不一样、取值位置不一样。但逻辑结构一模一样找值 → 包标签 → 拼起来。既然流程一样能不能把不一样的部分抽成数据一样的部分留给代码二、把 XML 结构存成一张表我把每个接口的 XML 结构描述成一张配置表createtableTABLE_YDPT_CONFIG(tag_id NUMBER(2),-- 标记ID排序用parent_id NUMBER(2),-- 父标记ID递归靠它serivce_name VARCHAR2(20),-- 接口名tag_name VARCHAR2(20),-- 标签名tag_type VARCHAR2(6),-- 1双标签 2单标签 3属性dataformat VARCHAR2(20),-- 目标日期格式值为code时做代码转化datapos NUMBER(3),-- 取值位置参数数组下标defaultvalue VARCHAR2(20),-- 默认值取不到时用sourceformat VARCHAR2(20),-- 源日期格式is_multi_node VARCHAR2(6),-- 是否支持多节点循环direction VARCHAR2(6),-- 1入参 2出参comments VARCHAR2(50),-- 注释ywbh VARCHAR2(10),-- 业务编号代码转化用iscode VARCHAR2(10),-- 是否需要二级代码转化haschildnode VARCHAR2(10),-- 是否包含子节点递归alternate_character VARCHAR2(6),-- 是否转义特殊字符max_length NUMBER(4)-- 最大长度超长截断);一张表16 个字段描述了 XML 的全部可变要素。剩下的交给存储过程。三、一个递归存储过程通用处理所有接口核心过程get_tuxedo_xml的逻辑查配置根据接口名 方向 父标记 ID 查出所有子节点三种标签类型tag_type1双标签tag_name值/tag_nametag_type2单标签tag_name/tag_type3属性tag_name值递归haschildnode1→ 递归调用自己处理子树循环is_multi_node1→ 数组数据遍历生成多组同构标签取值datapos→ 从参数数组的下标位置取数据加工默认值 → 日期格式转换 → 代码对照 → 特殊字符转义 → 长度截断简化版代码骨架PROCEDUREget_tuxedo_xml(prm_serivce_nameINVARCHAR2,prm_tag_idINNUMBER,prm_typeINNUMBER,prm_indexINNUMBER,prm_directionINVARCHAR2,prm_param_listINpkg_k_type.type_param_list,prm_appcodeOUTVARCHAR2,prm_result_xmlOUTVARCHAR2)ISCURSORcur_config(as_parent_id NUMBER,as_type NUMBER)ISSELECT*FROMtable_ydpt_configWHEREdirectionprm_directionANDserivce_nameprm_serivce_nameANDparent_idas_parent_idAND(as_type0OR(as_type1ANDtag_typeIN(1,2))OR(as_type2ANDtag_typeIN(3)))ORDERBYtag_idASC;BEGINOPENcur_config(prm_tag_id,prm_type);LOOPFETCHcur_configINTOrec_config;EXITWHENcur_config%NOTFOUND;-- 开标签IFrec_config.tag_type1THENls_xml :ls_xml||||rec_config.tag_name||;ELSIF rec_config.tag_type2THENls_xml :ls_xml||||rec_config.tag_name;ELSIF rec_config.tag_type3THENls_xml :ls_xml|| ||rec_config.tag_name;ENDIF;-- 有子节点 → 递归IFrec_config.haschildnode1THENget_tuxedo_xml(serivce_name,rec_config.tag_id,...);ls_xml :ls_xml||prm_result_xml;-- 叶子节点 → 取值 加工 拼接ELSEdatavalue :prm_param_list(prm_index).array(rec_config.datapos);-- 默认值IFdatavalueISNULLTHENdatavalue :rec_config.defaultvalue;ENDIF;-- 日期格式转换IFrec_config.dataformatISNOTNULLTHENdatavalue :to_char(to_date(datavalue,rec_config.sourceformat),rec_config.dataformat);ENDIF;-- 特殊字符转义IFrec_config.alternate_character1THENdatavalue :REPLACE(datavalue,,lt;);datavalue :REPLACE(datavalue,,gt;);ENDIF;-- 闭标签IFrec_config.tag_type1THENls_xml :ls_xml||datavalue||/||rec_config.tag_name||;ELSIF rec_config.tag_type2THENls_xml :ls_xml||/;ELSIF rec_config.tag_type3THENls_xml :ls_xml||||datavalue||;ENDIF;ENDIF;ENDLOOP;CLOSEcur_config;prm_result_xml :ls_xml;END;四、一个例子假设接口需要生成这样的 XMLrequestheadername张三idno320102199001011234/bodyitem1001/itemitem1002/item/body/request在配置表里配成这样就行tag_idparent_idtag_nametag_typedataposhaschildnodeis_multi_node10request1121header1132name3142idno3251body1165item131tag_id控制顺序parent_id控制嵌套is_multi_node控制循环。存储过程跑一遍自动递归生成完整的 XML。五、为什么比写代码好接新接口只加配置行。不写代码不编译不改存储过程。配置表可以做成管理界面业务人员自己配。格式变更不改代码。标签名变了、嵌套层次变了、多了一个字段——改配置行就行。代码不动。处理逻辑统一。默认值、日期转换、代码对照、特殊字符转义、长度截断——每个叶子节点自动经过这套流水线。手写的时候容易漏配置驱动不会漏。入参出参共用。direction1是入参业务参数 → XMLdirection2是出参XML → 业务参数同一张表、同一个过程只换方向。六、这个模式的本质回头看你前面写的那些文章——Activiti 视图替换、BPMN 分支条件自动注入、动态建表引擎——背后是同一种思维把可变的部分配成数据把不变的部分写成逻辑。它不是配置优于代码那种口号式的说法。它是在你接了几十个接口、写了一堆长得像孪生兄弟的拼装代码之后自然产生的冲动我能不能只写一次能。把 XML 的结构抽象成 tag_id→parent_id 的树、标签类型决定开闭方式、datapos 决定取值位置。这些规则覆盖了所有接口的 XML 结构变化剩下的就是一段递归程序。新增一个接口加几行配置。调通。下一个。这就是 20 年堆出来的直觉当你第三次写同样的代码时停下来。你要的不是写得更快是不写。