diff --git a/src/main/java/com/example/workutils/biz/apidoc/generator/ApiDocGenerator.java b/src/main/java/com/example/workutils/biz/apidoc/generator/ApiDocGenerator.java index 8e80c85..0df3231 100644 --- a/src/main/java/com/example/workutils/biz/apidoc/generator/ApiDocGenerator.java +++ b/src/main/java/com/example/workutils/biz/apidoc/generator/ApiDocGenerator.java @@ -72,6 +72,7 @@ public class ApiDocGenerator { .replace("{{GENERATE_TIME}}", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))) .replace("{{STATS_BAR}}", buildStatsBar(docs.size(), totalClasses, totalMethods)) .replace("{{SIDEBAR}}", buildSidebar(docs)) + .replace("{{SNAPSHOT_JS}}", resourceLoader.loadSnapshotJs()) .replace("{{CONTENT}}", buildContent(docs)); } diff --git a/src/main/java/com/example/workutils/biz/apidoc/generator/HtmlResourceLoader.java b/src/main/java/com/example/workutils/biz/apidoc/generator/HtmlResourceLoader.java index f5c2ee8..0f0ea7a 100644 --- a/src/main/java/com/example/workutils/biz/apidoc/generator/HtmlResourceLoader.java +++ b/src/main/java/com/example/workutils/biz/apidoc/generator/HtmlResourceLoader.java @@ -53,6 +53,14 @@ public class HtmlResourceLoader { return loadResource(RESOURCE_BASE + "html2canvas.min.js"); } + /** + * 加载快照导出 JS 脚本。 + * 对应资源文件:resources/templates/apidoc/api-doc-snapshot.js + */ + public String loadSnapshotJs() { + return loadResource(RESOURCE_BASE + "api-doc-snapshot.js"); + } + /** * 从 classpath 读取资源文件内容。 * 所有资源必须以 UTF-8 编码。 diff --git a/src/main/resources/templates/apidoc/api-doc-snapshot.js b/src/main/resources/templates/apidoc/api-doc-snapshot.js new file mode 100644 index 0000000..8351cab --- /dev/null +++ b/src/main/resources/templates/apidoc/api-doc-snapshot.js @@ -0,0 +1,137 @@ +/* ============================================================ + * 导出调试快照 — 生成独立 HTML,可离线打开继续调试 + * ============================================================ */ +function exportDebugSnapshot() { + var card = getActiveDubboCard(); + if (!card) { showToast('⚠ 未找到活动接口'); return; } + var iface = escHtml(card.getAttribute('data-dubbo-interface') || ''); + var method = escHtml(card.getAttribute('data-dubbo-method') || ''); + var paramTypes = card.getAttribute('data-dubbo-param-types') || '[]'; + var sampleArgs = card.getAttribute('data-dubbo-sample-args') || '[{}]'; + + var gwUrl = escHtml(((document.getElementById('dubboGwUrl') || {}).value || getGwUrl())); + var tntId = escHtml(((document.getElementById('dubboTntId') || {}).value || '0101')); + var env = escHtml(((document.getElementById('dubboEnv') || {}).value || 'DEV')); + var appId = escHtml(((document.getElementById('dubboAppId') || {}).value || '980010000114002')); + var evtEnv = escHtml(((document.getElementById('dubboEvtEnv') || {}).value || '')); + var args = (document.getElementById('dubboArgs') || {}).value || sampleArgs; + var respStatus = escHtml(((document.querySelector('.dubbo-response-status') || {}).textContent || '')); + var respTime = escHtml(((document.querySelector('.dubbo-response-content-time') || {}).textContent || '')); + var respBody = escHtml(((document.getElementById('dubboRespContent') || {}).textContent || '')); + + var proxy = (typeof DUBBO_PROXY_URL !== 'undefined') ? DUBBO_PROXY_URL : ''; + + var now = new Date(); + var ts = now.getFullYear() + pad2(now.getMonth()+1) + pad2(now.getDate()) + pad2(now.getHours()) + pad2(now.getMinutes()) + pad2(now.getSeconds()); + + var html = buildSnapshotHtml(iface, method, paramTypes, sampleArgs, gwUrl, tntId, env, appId, evtEnv, args, respStatus, respTime, respBody, proxy, ts); + + var blob = new Blob([html], { type: 'text/html;charset=utf-8' }); + var url = URL.createObjectURL(blob); + var a = document.createElement('a'); + a.href = url; + a.download = 'debug_snapshot_' + method + '_' + ts + '.html'; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + showToast('✅ 快照已导出 — ' + method); +} + +function buildSnapshotHtml(iface, method, paramTypes, sampleArgs, gwUrl, tntId, env, appId, evtEnv, args, respStatus, respTime, respBody, proxy, ts) { + // 收集所有 JS 代码,并转义闭合标签防止浏览器提前关闭 script 标签 + var snapJs = 'DUBBO_PROXY_URL = ' + JSON.stringify(proxy) + ';\n' + + '// 快照硬编码接口信息(替代原 getActiveDubboCard)\n' + + 'var SNAP_IFACE = ' + JSON.stringify(iface) + ';\n' + + 'var SNAP_METHOD = ' + JSON.stringify(method) + ';\n' + + 'var SNAP_PARAM_TYPES = ' + JSON.stringify(paramTypes) + ';\n' + + 'var SNAP_SAMPLE_ARGS = ' + JSON.stringify(sampleArgs) + ';\n' + + '\n' + SNAPSHOT_JS(); + // 转义内联 JS 中的 <\/script>、<\/style>、<\/title> 等闭合标签,防止 HTML 解析器提前截断 + snapJs = snapJs.replace(/<\/(s(?:cript|tyle)|title|head|body|html|div|span|pre|textarea|button|select|input|table|tr|td|th|thead|tbody|tfoot|label|form|a|p|h[1-6]|ul|ol|li|br|hr|img|meta|link|code)\b/gi, + function(m) { return '<\\/' + m.substring(2); }); + + return '\n\n\n\nDubbo 调试快照 — ' + method + '\n\n\n\n' + + '
\n' + + '
\n' + + ' 🔧 Dubbo 调试快照\n' + + ' 导出时间: ' + escHtml(ts) + '\n' + + '
\n' + + '
\n' + + '
\n' + + '
\n' + + '
接口信息
\n' + + ' \n' + + ' \n' + + ' \n' + + ' \n' + + '
接口' + iface + '
方法' + method + '
参数类型' + escHtml(paramTypes) + '
\n' + + '
\n' + + '
\n' + + '
调试参数
\n' + + '
\n' + + '
\n' + + ' \n' + + '
\n' + + '
\n' + + '
\n' + + '
\n' + + '
请求参数\n' + + ' \n' + + ' \n' + + ' \n' + + '
\n' + + ' \n' + + '
\n' + + '
\n' + + ' \n' + + ' \n' + + ' \n' + + '
\n' + + '
\n' + + '
\n' + + '
\n' + + '
📥 响应结果
\n' + + '
' + (respStatus||'等待发送...') + '
\n' + + (respTime ? '
' + respTime + '
\n' : '') + + '
' + respBody + '
\n' + + '
\n' + + '
\n' + + '
\n' + + '
\n' + + '<' + 'script>\n' + snapJs + '\n<' + '/script>\n\n'; +} + +function SNAPSHOT_CSS() { + return '*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-size:13px;color:#1d2129;background:#f2f3f5;min-height:100vh}.snap-page{max-width:1280px;margin:0 auto;padding:20px 24px}.snap-header{display:flex;align-items:center;justify-content:space-between;padding:14px 20px;background:#fff;border-radius:8px 8px 0 0;border:1px solid #e5e6eb;border-bottom:none}.snap-title{font-size:16px;font-weight:600}.snap-tag{font-size:12px;color:#86909c}.snap-body{display:flex;gap:0;border:1px solid #e5e6eb;border-top:none;background:#fff;border-radius:0 0 8px 8px;min-height:500px}.snap-left{flex:1;padding:16px 20px;min-width:0;border-right:1px solid #eee}.snap-right{flex:0 0 420px;padding:16px 20px;background:#fafbfc;display:flex;flex-direction:column}.snap-section{margin-bottom:16px}.snap-section-title{font-size:13px;font-weight:600;color:#4e5969;margin-bottom:10px;padding-bottom:6px;border-bottom:1px solid #f2f3f5;display:flex;align-items:center;gap:8px;flex-wrap:wrap}.snap-info-table{width:100%;border-collapse:collapse}.snap-info-table td{padding:4px 8px;border-bottom:1px solid #f7f8fa;font-size:12px}.snap-info-table .snap-label{width:80px;color:#86909c;white-space:nowrap}.snap-info-table .snap-val{word-break:break-all}.mono{font-family:Consolas,Monaco,"Courier New",monospace}.dubbo-field-row{display:flex;align-items:center;gap:8px;margin-bottom:8px;flex-wrap:wrap}.dubbo-field-row label{font-size:12px;color:#86909c;white-space:nowrap;min-width:60px}.dubbo-field-row input,.dubbo-field-row select{border:1px solid #d0d5dd;border-radius:4px;padding:4px 8px;font-size:12px;font-family:inherit;outline:none;background:#fff}.dubbo-field-row input:focus,.dubbo-field-row select:focus{border-color:#165DFF}.dubbo-btn{display:inline-flex;align-items:center;gap:4px;padding:5px 12px;font-size:12px;background:#f2f3f5;border:1px solid #e5e6eb;border-radius:4px;cursor:pointer;color:#4e5969;white-space:nowrap;transition:all .15s}.dubbo-btn:hover{background:#e5e6eb}.dubbo-btn.send{background:#165DFF;color:#fff;border-color:#165DFF;font-weight:500}.dubbo-btn.send:hover{background:#0e42d2}.dubbo-btn.send:disabled{background:#94b5ff;border-color:#94b5ff;cursor:not-allowed}.dubbo-btn.copied{background:#e6ffed!important;border-color:#b7eb8f!important;color:#389e0d!important}.dubbo-response-panel{border:1px solid #e5e6eb;border-radius:8px;overflow:hidden;flex:1;display:flex;flex-direction:column;min-height:0;background:#fff}.dubbo-response-panel-header{padding:8px 14px;font-size:12px;font-weight:600;color:#4e5969;background:#fafafa;border-bottom:1px solid #e5e6eb}.dubbo-response-status{padding:4px 14px;font-size:11px;color:#86909c;border-bottom:1px solid #eee}.dubbo-response-status.ok{color:#389e0d;font-weight:500}.dubbo-response-status.err{color:#cf1322;font-weight:500}.dubbo-response-content-time{padding:2px 14px;font-size:11px;color:#86909c}.dubbo-response-content{flex:1;padding:12px 14px;font-family:Consolas,Monaco,monospace;font-size:12px;line-height:1.6;white-space:pre-wrap;word-break:break-all;overflow:auto;margin:0;color:#1d2129}#dubboToast{position:fixed;bottom:32px;left:50%;transform:translateX(-50%);background:#1d2129;color:#fff;padding:8px 24px;border-radius:6px;font-size:13px;z-index:99999;pointer-events:none;transition:opacity .3s;opacity:0}\n' + + SNAPSHOT_OVERLAY_CSS(); +} + +function SNAPSHOT_OVERLAY_CSS() { + return '.snap-overlay{position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:100000;display:flex;align-items:center;justify-content:center}.snap-overlay-box{background:#fff;border-radius:10px;box-shadow:0 12px 60px rgba(0,0,0,.25);max-width:96vw;max-height:90vh;display:flex;flex-direction:column}.snap-overlay-hdr{padding:14px 20px;border-bottom:1px solid #e5e6eb;display:flex;align-items:center;justify-content:space-between;flex-shrink:0}.snap-overlay-hdr span{font-size:15px;font-weight:600}.snap-overlay-close{background:none;border:none;font-size:22px;cursor:pointer;color:#86909c;padding:0;line-height:1}.snap-overlay-body{padding:16px 20px;flex:1;display:flex;flex-direction:column;min-height:0}.snap-overlay-body .hint{color:#86909c;font-size:12px;margin-bottom:8px;line-height:1.6}.snap-overlay-body textarea{flex:1;min-height:200px;border:1px solid #d0d5dd;border-radius:6px;padding:10px 12px;font-family:Consolas,Monaco,monospace;font-size:12px;resize:vertical;outline:none;line-height:1.6}.snap-overlay-body textarea:focus{border-color:#165DFF}.snap-overlay-ft{padding:14px 20px;border-top:1px solid #e5e6eb;display:flex;gap:10px;flex-shrink:0}.snap-overlay-ft .btn-primary{background:#165DFF;color:#fff;border:none;border-radius:6px;padding:8px 22px;font-size:13px;cursor:pointer}.snap-overlay-ft .btn-secondary{background:#f2f3f5;border:1px solid #d0d5dd;border-radius:6px;padding:8px 22px;font-size:13px;cursor:pointer;color:#4e5969}.snap-overlay-ft .btn-ghost{background:#fff;border:1px solid #d0d5dd;border-radius:6px;padding:8px 22px;font-size:13px;cursor:pointer;color:#4e5969}.snap-compare-wrap{overflow:auto;min-height:0}.snap-compare-table{width:100%;border-collapse:collapse}.snap-compare-table th{text-align:left;padding:8px 10px;font-size:12px;color:#86909c;border-bottom:2px solid #e5e6eb;position:sticky;top:0;background:#fafafa;z-index:1}.snap-compare-table td{padding:5px 10px;font-size:12px;max-width:280px;word-break:break-all;border-bottom:1px solid #f7f8fa}.snap-badge{display:inline-block;padding:1px 8px;border-radius:3px;font-size:11px;font-weight:500}.snap-badge-changed{background:#fff7e6;color:#d46b08}.snap-badge-sample{background:#e6f7ff;color:#096dd9}.snap-badge-log{background:#f6ffed;color:#389e0d}.snap-badge-same{color:#bbb;font-size:11px}\n'; +} + +function SNAPSHOT_JS() { + // 返回完整的独立调试 JS,覆盖 getActiveDubboCard 以支持脱离原始页面运行 + return 'var DUBBO_GW_URL_KEY="dubbo_gw_url";function getGwUrl(){return localStorage.getItem(DUBBO_GW_URL_KEY)||"https://gw.test.bestpay.net/open/1.0/GenericFacade/genericInvoke"}function setGwUrl(u){localStorage.setItem(DUBBO_GW_URL_KEY,u)}function defaultHeaders(){return{"Content-Type":"application/json","User-Agent":"ApiDoc/1.0","Accept":"*/*"}}function buildEventContext(t,e,n){return JSON.stringify({tntId:t||"0101",env:n||e||"DEV",arNo:"",evtCd:"",pdCd:"",pdPath:""})}function formatLocalDateTime(){var e=new Date,t=pad2;return e.getFullYear()+"-"+t(e.getMonth()+1)+"-"+t(e.getDate())+" "+t(e.getHours())+":"+t(e.getMinutes())+":"+t(e.getSeconds())}function genTraceId(){return"xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx".replace(/[xy]/g,function(e){var t=16*Math.random()|0;return("x"===e?t:3&t|8).toString(16)})}function getActiveDubboCard(){return{getAttribute:function(e){var t={};t["data-dubbo-interface"]=SNAP_IFACE,t["data-dubbo-method"]=SNAP_METHOD,t["data-dubbo-param-types"]=SNAP_PARAM_TYPES,t["data-dubbo-sample-args"]=SNAP_SAMPLE_ARGS;return t[e]||""}}}function stripJsonComments(e){return e?e.replace(/\\\\"|"(?:\\\\"|[^"])*"|(\\/\\/.*|\\/\\*[\\s\\S]*?\\*\\/)/g,function(e,t){return t?"":e}).replace(/,\\s*\\}/g,"}").replace(/,\\s*\\]/g,"]"):""}function formatJsonPretty(e){try{var t=JSON.parse(stripJsonComments(e));return JSON.stringify(t,null,2)}catch(r){return e}}function formatDubboArgs(){var e=document.getElementById("dubboArgs");e&&(e.value=formatJsonPretty(e.value))}function resetDubboArgs(){var e=document.getElementById("dubboArgs");e&&(e.value=formatJsonPretty(SNAP_SAMPLE_ARGS))}function pad2(e){return e<10?"0"+e:""+e}function escHtml(e){return e?e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/\'/g,"'"):""}function copyToClip(e,t){navigator.clipboard?navigator.clipboard.writeText(e).then(function(){markCopied(t,"已复制")}).catch(function(){fallbackCopy(e,t)}):fallbackCopy(e,t)}function fallbackCopy(e,t){var n=document.createElement("textarea");n.value=e,n.style.position="fixed",n.style.left="-999px",document.body.appendChild(n),n.select();try{document.execCommand("copy"),markCopied(t,"已复制")}catch(r){markCopied(t,"失败")}document.body.removeChild(n)}function markCopied(e,t){if(!e)return;var n=e.textContent;e.textContent=t,e.classList.add("copied"),setTimeout(function(){e.textContent=n,e.classList.remove("copied")},1500)}function showToast(e){var t=document.getElementById("dubboToast");t||((t=document.createElement("div")).id="dubboToast",t.style.cssText="position:fixed;bottom:32px;left:50%;transform:translateX(-50%);background:#1d2129;color:#fff;padding:8px 24px;border-radius:6px;font-size:13px;z-index:99999;pointer-events:none;transition:opacity .3s;opacity:0",document.body.appendChild(t)),t.textContent=e,t.style.opacity="1",clearTimeout(t._tid),t._tid=setTimeout(function(){t.style.opacity="0"},1800)}function onFieldChange(){document.getElementById("dubboGwUrl")&&setGwUrl(document.getElementById("dubboGwUrl").value)}function buildDubboBody(){var e=document.getElementById("dubboArgs"),t=e?e.value:"{}",n=(document.getElementById("dubboTntId")||{}).value||"0101",r=(document.getElementById("dubboEnv")||{}).value||"DEV",o=(document.getElementById("dubboEvtEnv")||{}).value||"DEV",i=(document.getElementById("dubboAppId")||{}).value||"980010000114002",a=getActiveDubboCard(),d=a?a.getAttribute("data-dubbo-interface"):"",c=a?a.getAttribute("data-dubbo-method"):"",l=a?a.getAttribute("data-dubbo-param-types")||"[]":"[]",u;try{u=JSON.parse(stripJsonComments(t))}catch(p){return void alert("args JSON 格式错误: "+p.message)}var g=genTraceId();return{data:{interfacePath:d,method:c,parameterTypes:JSON.parse(l),args:u,traceId:g,zoneType:"G",eventContext:{tntId:n,ipTId:"",ipId:"null",ipRId:"",chlNo:genTraceId(),originalArNo:"",originalEvtCd:"",arNo:"",chlEvtCd:"",prePdCd:"null",preEvtCd:"null",pdCd:"",evtCd:"",pdPath:"",env:o,tagExtensionInfo:"{}",shadow:"0"}},tnt_id:n,app_id:i,timestamp:formatLocalDateTime(),env:r}}function sendDubboRequest(){var e=buildDubboBody();if(!e){showToast("构建请求体失败");return}var t=document.getElementById("dubboSendBtn");t&&(t.disabled=!0,t.textContent="发送中...");var n=document.getElementById("dubboStatus"),r=document.getElementById("dubboRespContent");if(!n||!r)return;n.textContent="发送中...",n.className="dubbo-response-status",r.textContent="";var o=document.querySelector(".dubbo-response-content-time");o&&o.remove();var i=document.getElementById("dubboGwUrl")?document.getElementById("dubboGwUrl").value:getGwUrl(),a=defaultHeaders(),d=(document.getElementById("dubboEvtEnv")||{}).value||"DEV";a["Event-Context"]=buildEventContext(e.tnt_id,e.env,d),a["X-Gateway-Url"]=i,fetch(DUBBO_PROXY_URL,{method:"POST",headers:a,body:JSON.stringify(e)}).then(function(t){var o=t.ok,i=t.status+" "+t.statusText;n.textContent=i,n.className="dubbo-response-status "+(o?"ok":"err");return t.text().then(function(t){var a;try{var d=JSON.parse(t);a=JSON.stringify(d,null,2)}catch(c){a=t}r.textContent=a;var u=document.querySelector(".dubbo-response-content-time");u&&u.remove();var g=\'
\'+formatLocalDateTime()+"
";r.insertAdjacentHTML("beforebegin",g),t&&(t.disabled=!1,t.textContent="\\u25b6 \\u53d1\\u9001\\u8bf7\\u6c42")})}).catch(function(e){var t=e.message||String(e);n.textContent="\\u7f51\\u7edc\\u9519\\u8bef",n.className="dubbo-response-status err",r.textContent=t;var o=document.getElementById("dubboSendBtn");o&&(o.disabled=!1,o.textContent="\\u25b6 \\u53d1\\u9001\\u8bf7\\u6c42")})}function copyDubboCurl(){var e=buildDubboBody();if(!e)return;var t=document.getElementById("dubboGwUrl")?document.getElementById("dubboGwUrl").value:getGwUrl(),n=defaultHeaders(),r=(document.getElementById("dubboEvtEnv")||{}).value||"DEV";n["Event-Context"]=buildEventContext(e.tnt_id,e.env,r);var o="curl -X POST \\\\\\n "+JSON.stringify(t)+" \\\\\\n";for(var i in n)o+=" -H "+JSON.stringify(i+": "+n[i])+" \\\\\\n";o+=" -d "+JSON.stringify(JSON.stringify(e,null,2)),copyToClip(o,null),showToast("cURL \\u5df2\\u590d\\u5236")}function copyDubboBody(){var e=buildDubboBody();if(!e)return;copyToClip(JSON.stringify(e,null,2),null),showToast("\\u8bf7\\u6c42\\u4f53\\u5df2\\u590d\\u5236")}\n' + + SNAPSHOT_LOGEXTRACT_JS() + '\n' + + SNAPSHOT_COMPARE_JS() + '\n' + + SNAPSHOT_ENV_JS() + '\n' + + 'document.addEventListener("DOMContentLoaded",function(){var e=document.getElementById("dubboEnv");if(e){var t=getDefaultEvtEnv(e.value);var n=document.getElementById("dubboEvtEnv");if(n&&!n.value)n.value=t}});\n'; +} + +function SNAPSHOT_LOGEXTRACT_JS() { + return 'function getDefaultEvtEnv(e){return"DEV"===e?"DEV_1156339215001361525":"SIT"===e?"SIT-"+new Date().getFullYear()+pad2(new Date().getMonth()+1)+pad2(new Date().getDate())+"01":""}function onEnvChange(){var e=document.getElementById("dubboEnv"),t=document.getElementById("dubboEvtEnv");e&&t&&(t.value=getDefaultEvtEnv(e.value))}function showLogExtractPopup(){var e=document.getElementById("logExtractOverlay");e&&e.remove();var t=document.createElement("div");t.id="logExtractOverlay",t.className="snap-overlay",t.innerHTML=\'
\'+\'
📋 从日志提取入参
\'+\'
粘贴日志内容,自动识别 接口入参:ClassName(field=value,...) 格式
\'+\'\'+\'
\',document.body.appendChild(t),t.addEventListener("click",function(e){e.target===t&&closeLogExtractPopup()}),setTimeout(function(){var e=document.getElementById("logExtractInput");e&&e.focus()},100)}function closeLogExtractPopup(){var e=document.getElementById("logExtractOverlay");e&&e.remove()}function doExtractLogArgs(){var e=document.getElementById("logExtractInput"),t=e&&e.value;if(!t||!t.trim())return void showToast("⚠ 请粘贴日志内容");t=t.trim();for(var n=t,r=["接口入参:","接口入参:","入参:","入参:"],o=0;o=0){n=t.substring(i+r[o].length).trim();break}}try{var a=flattenSuperChain(parseDtoExpr(n)),d={};try{var c=JSON.parse(SNAP_SAMPLE_ARGS);Array.isArray(c)&&c.length>0&&(d=c[0])}catch(u){}closeLogExtractPopup(),showParamComparePopup(d,a)}catch(u){showToast("❌ 解析失败:"+u.message)}}function parseDtoExpr(e){e=e.trim();var t=e.match(/^([\\w.]+)\\s*\\(([\\s\\S]*)\\)$/);if(!t){var n=findOuterParens(e);if(!n)throw new Error("未识别到 DTO 表达式");e=n,t=e.match(/^([\\w.]+)\\s*\\(([\\s\\S]*)\\)$/);if(!t)throw new Error("DTO 格式异常")}var r={},o=splitTopLevel(t[2],",");for(var i=0;i=0){var c=a.substring(0,d).trim(),u=a.substring(d+1).trim();r[c]=parseDtoValue(u)}}}return r}function parseDtoValue(e){e=e.trim();if(!e)return null;if("["===e.charAt(0)&&"]"===e.charAt(e.length-1)){var t=e.substring(1,e.length-1).trim();if(!t)return[];for(var n=splitTopLevel(t,","),r=[],o=0;o=0){var a=o.substring(0,i).trim(),d=o.substring(i+1).trim();t[a]=parseDtoValue(d)}}}return t}function flattenSuperChain(e){if(!e||"object"!=typeof e||Array.isArray(e))return e;var t={};for(var n in e)if(e.hasOwnProperty(n))if("super"===n&&e[n]&&"object"==typeof e[n]&&!Array.isArray(e[n])){var r=flattenSuperChain(e[n]);for(var o in r)r.hasOwnProperty(o)&&(t[o]=r[o])}else t[n]=e[n];return t}function findOuterParens(e){var t=e.match(/([\\w.]+)\\s*\\(/);if(!t)return null;for(var n=t.index,r=n+t[0].length-1,o=1,i=r+1;i变更\':"sample_only"===c.status?\'仅默认\':"log_only"===c.status?\'仅日志\':\'一致\',l="changed"===c.status?"background:#fffbe6":"sample_only"===c.status?"background:#f0f8ff":"log_only"===c.status?"background:#f6ffed":"";var g=void 0===c.sampleVal?\'—\':escHtml(fmtDiffVal(c.sampleVal)),p=void 0===c.logVal?\'—\':escHtml(fmtDiffVal(c.logVal));a+=\'\'+\'\'+u+""+\'\'+escHtml(c.path||"(根)")+""+\'\'+g+""+\'\'+p+""}var s=document.createElement("div");s.id="paramCompareOverlay",s.className="snap-overlay",s.innerHTML=\'
\'+\'
🔄 参数比对 — 默认值 vs 日志值\'+(o>0?\' \'+o+" 项变更":"")+(i>0?\' \'+i+" 项仅默认":"")+"
"+\'
\'+\'
\'+\'\'+\'\'+\"\"+a+\"
状态参数路径默认参考值日志提取值
\"+(0===r.length?\'
无差异
\':"")+\"
\"+\'
\'+\'\'+\'\'+\'\'+\"
\",document.body.appendChild(s),s.addEventListener("click",function(e){e.target===s&&closeParamComparePopup()}),s._sampleObj=e,s._logObj=t,s._diffCount=r.length}function closeParamComparePopup(){var e=document.getElementById("paramCompareOverlay");e&&e.remove()}function applyCompareResult(e){var t=document.getElementById("paramCompareOverlay"),n=t._sampleObj||{},r=t._logObj||{},o;o="merged"===e?deepMerge(n,r):r;var i=JSON.stringify([o],null,2),a=document.getElementById("dubboArgs");a&&(a.value=i,showToast("✅ 参数已填入(共 "+(t._diffCount||0)+" 个字段参与比对)")),closeParamComparePopup()}function deepMerge(e,t){if(!e||"object"!=typeof e)return t;if(!t||"object"!=typeof t)return e;if(Array.isArray(e)||Array.isArray(t))return t;var n={};for(var r in e)e.hasOwnProperty(r)&&(t.hasOwnProperty(r)?n[r]=deepMerge(e[r],t[r]):n[r]=e[r]);for(var o in t)t.hasOwnProperty(o)&&!e.hasOwnProperty(o)&&(n[o]=t[o]);return n}function diffObjects(e,t,n){var r=[],o=e&&"object"==typeof e&&!Array.isArray(e),i=t&&"object"==typeof t&&!Array.isArray(t);if(!o&&!i){var a=e,d=t;return void 0===a&&void 0!==d?r.push({path:n,sampleVal:void 0,logVal:d,status:"log_only"}):void 0===d&&void 0!==a?r.push({path:n,sampleVal:a,logVal:void 0,status:"sample_only"}):eqVal(a,d)||r.push({path:n,sampleVal:a,logVal:d,status:"changed"}),r}if(o!==i)return eqVal(e,t)||r.push({path:n,sampleVal:e,logVal:t,status:"changed"}),r;var c={};for(var u in e)if(e.hasOwnProperty(u)){c[u]=!0;var l=n?n+"."+u:u;r=r.concat(diffObjects(e[u],t?t[u]:void 0,l))}for(var u in t)t.hasOwnProperty(u)&&!c[u]&&(l=n?n+"."+u:u,r.push({path:l,sampleVal:void 0,logVal:t[u],status:"log_only"}));return r}function eqVal(e,t){return e===t||(void 0!==e&&void 0!==t&&("object"==typeof e||"object"==typeof t?JSON.stringify(e)===JSON.stringify(t):String(e)===String(t)))}function fmtDiffVal(e){return void 0===e||null===e?"null":"object"==typeof e?JSON.stringify(e):String(e)}\n'; +} + +function SNAPSHOT_ENV_JS() { + return ''; +} diff --git a/src/main/resources/templates/apidoc/api-doc.html b/src/main/resources/templates/apidoc/api-doc.html index 9b86dd0..805e177 100644 --- a/src/main/resources/templates/apidoc/api-doc.html +++ b/src/main/resources/templates/apidoc/api-doc.html @@ -39,6 +39,9 @@ + diff --git a/src/main/resources/templates/apidoc/api-doc.js b/src/main/resources/templates/apidoc/api-doc.js index 97983ec..8f79ba6 100644 --- a/src/main/resources/templates/apidoc/api-doc.js +++ b/src/main/resources/templates/apidoc/api-doc.js @@ -395,6 +395,7 @@ function openDubboDebug(card) { '' + '' + '' + + '' + '' + '';