新增市場情報 queue writer run package
All checks were successful
CD Pipeline / deploy (push) Successful in 1m4s
All checks were successful
CD Pipeline / deploy (push) Successful in 1m4s
This commit is contained in:
@@ -600,6 +600,9 @@
|
||||
<button class="market-intel-icon-button" type="button" title="產生 queue writer operator drill" data-market-intel-sample-candidate-queue-operator-drill>
|
||||
<i class="fas fa-list-check" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button class="market-intel-icon-button" type="button" title="產生 queue writer run package" data-market-intel-sample-candidate-queue-run-package>
|
||||
<i class="fas fa-box-archive" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -932,6 +935,7 @@
|
||||
const sampleCandidateQueuePreflight = sampleReviewRoot ? sampleReviewRoot.querySelector('[data-market-intel-sample-candidate-queue-preflight]') : null;
|
||||
const sampleCandidateQueuePostwriteSmoke = sampleReviewRoot ? sampleReviewRoot.querySelector('[data-market-intel-sample-candidate-queue-postwrite-smoke]') : null;
|
||||
const sampleCandidateQueueOperatorDrill = sampleReviewRoot ? sampleReviewRoot.querySelector('[data-market-intel-sample-candidate-queue-operator-drill]') : null;
|
||||
const sampleCandidateQueueRunPackage = sampleReviewRoot ? sampleReviewRoot.querySelector('[data-market-intel-sample-candidate-queue-run-package]') : null;
|
||||
const sampleReviewEndpoint = "{{ url_for('market_intel.market_intel_manual_sample_review') }}";
|
||||
const sampleReviewEvaluateEndpoint = "{{ url_for('market_intel.market_intel_manual_sample_review_evaluate') }}";
|
||||
const sampleCandidateHandoffEndpoint = "{{ url_for('market_intel.market_intel_manual_sample_candidate_handoff') }}";
|
||||
@@ -942,6 +946,7 @@
|
||||
const sampleCandidateQueuePreflightEndpoint = "{{ url_for('market_intel.market_intel_manual_sample_candidate_queue_writer_preflight') }}";
|
||||
const sampleCandidateQueuePostwriteSmokeEndpoint = "{{ url_for('market_intel.market_intel_manual_sample_candidate_queue_writer_postwrite_smoke') }}";
|
||||
const sampleCandidateQueueOperatorDrillEndpoint = "{{ url_for('market_intel.market_intel_manual_sample_candidate_queue_writer_operator_drill') }}";
|
||||
const sampleCandidateQueueRunPackageEndpoint = "{{ url_for('market_intel.market_intel_manual_sample_candidate_queue_writer_run_package') }}";
|
||||
const schedulerMeta = schedulerRoot ? schedulerRoot.querySelector('[data-market-intel-scheduler-meta]') : null;
|
||||
const schedulerBody = schedulerRoot ? schedulerRoot.querySelector('[data-market-intel-scheduler-body]') : null;
|
||||
const schedulerRefresh = schedulerRoot ? schedulerRoot.querySelector('[data-market-intel-scheduler-refresh]') : null;
|
||||
@@ -2528,6 +2533,97 @@
|
||||
}
|
||||
};
|
||||
|
||||
const renderCandidateQueueRunPackage = data => {
|
||||
const blockers = (data.blocked_reasons || []).join(' / ');
|
||||
const manifest = data.payload_manifest || {};
|
||||
const artifacts = data.required_artifacts || [];
|
||||
const commands = data.command_bundle || [];
|
||||
const signoff = data.operator_signoff || [];
|
||||
sampleReviewMeta.innerHTML = [
|
||||
`mode=${data.mode || 'unknown'}`,
|
||||
`ready=${data.package_ready ? 'yes' : 'no'}`,
|
||||
`payloads=${manifest.payload_count || 0}`,
|
||||
`api_file=${data.api_writes_file ? 'yes' : 'no'}`,
|
||||
`db_write=${data.database_write_executed ? 'yes' : 'no'}`
|
||||
].map(item => `<span class="market-intel-pill">${escapeHtml(item)}</span>`).join('');
|
||||
sampleReviewBody.innerHTML = `
|
||||
<div class="market-intel-empty mb-3">此卡只整理正式 CLI 小流量寫入前的證據包與命令包;API/UI 不產檔、不讀 token、不執行 CLI、不寫 DB。${blockers ? `阻擋:${escapeHtml(blockers)}` : ''}</div>
|
||||
<div class="market-intel-empty mb-3">manifest=${escapeHtml((manifest.manifest_hash || '').slice(0, 16) || 'none')} / dedupe=${escapeHtml((manifest.dedupe_keys || []).join(', ') || 'none')}</div>
|
||||
<div class="market-intel-deploy-grid">
|
||||
<div>
|
||||
<p class="market-intel-deploy-section-title">REQUIRED ARTIFACTS</p>
|
||||
<div class="market-intel-operation-list">${
|
||||
artifacts.map(item => `
|
||||
<article class="market-intel-operation">
|
||||
<strong>${escapeHtml(item.key)}</strong>
|
||||
<small>${escapeHtml(item.label)}</small>
|
||||
<small>${escapeHtml(item.path_shape)}</small>
|
||||
<small>created_by_api=${item.created_by_api ? 'yes' : 'no'}</small>
|
||||
</article>
|
||||
`).join('') || '<div class="market-intel-empty">尚未提供 artifacts。</div>'
|
||||
}</div>
|
||||
</div>
|
||||
<div>
|
||||
<p class="market-intel-deploy-section-title">COMMAND BUNDLE</p>
|
||||
<div class="market-intel-operation-list">${
|
||||
commands.map(item => `
|
||||
<article class="market-intel-operation">
|
||||
<strong>${escapeHtml(item.step)}. ${escapeHtml(item.key)}</strong>
|
||||
<small>${escapeHtml(item.command_shape)}</small>
|
||||
<small>db_write=${item.executes_database ? 'yes' : 'no'}</small>
|
||||
</article>
|
||||
`).join('') || '<div class="market-intel-empty">尚未提供 commands。</div>'
|
||||
}</div>
|
||||
</div>
|
||||
<div>
|
||||
<p class="market-intel-deploy-section-title">OPERATOR SIGNOFF</p>
|
||||
<div class="market-intel-check-list">${
|
||||
signoff.map((item, index) => `
|
||||
<div class="market-intel-check">
|
||||
<div>
|
||||
<strong>${escapeHtml(`signoff_${index + 1}`)}</strong>
|
||||
<small>${escapeHtml(item)}</small>
|
||||
</div>
|
||||
<span>REQUIRED</span>
|
||||
</div>
|
||||
`).join('') || '<div class="market-intel-empty">尚未提供 signoff。</div>'
|
||||
}</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
};
|
||||
|
||||
const loadCandidateQueueRunPackage = async () => {
|
||||
if (!sampleReviewMeta || !sampleReviewBody || !sampleReviewInput) return;
|
||||
let parsed;
|
||||
try {
|
||||
parsed = JSON.parse(sampleReviewInput.value || '{}');
|
||||
} catch (error) {
|
||||
sampleReviewMeta.innerHTML = '<span class="market-intel-pill">json_error</span>';
|
||||
sampleReviewBody.innerHTML = `<div class="market-intel-empty">JSON 格式錯誤:${escapeHtml(error.message)}</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
sampleReviewBody.innerHTML = '<div class="market-intel-empty">產生 queue writer run package 中...</div>';
|
||||
try {
|
||||
const response = await fetch(sampleCandidateQueueRunPackageEndpoint, {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': csrfToken
|
||||
},
|
||||
body: JSON.stringify({ sample_result: parsed })
|
||||
});
|
||||
const data = await response.json();
|
||||
if (!response.ok && !data.mode) throw new Error(`HTTP ${response.status}`);
|
||||
renderCandidateQueueRunPackage(data);
|
||||
} catch (error) {
|
||||
sampleReviewMeta.innerHTML = '<span class="market-intel-pill">error</span>';
|
||||
sampleReviewBody.innerHTML = `<div class="market-intel-empty">queue writer run package 產生失敗:${escapeHtml(error.message)}</div>`;
|
||||
}
|
||||
};
|
||||
|
||||
const renderSchedulerMeta = data => {
|
||||
schedulerMeta.innerHTML = [
|
||||
`mode=${data.mode || 'unknown'}`,
|
||||
@@ -4046,6 +4142,9 @@
|
||||
if (sampleCandidateQueueOperatorDrill) {
|
||||
sampleCandidateQueueOperatorDrill.addEventListener('click', loadCandidateQueueOperatorDrill);
|
||||
}
|
||||
if (sampleCandidateQueueRunPackage) {
|
||||
sampleCandidateQueueRunPackage.addEventListener('click', loadCandidateQueueRunPackage);
|
||||
}
|
||||
if (schedulerRefresh) {
|
||||
schedulerRefresh.addEventListener('click', loadScheduler);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user