diff --git a/app/.env b/app/.env index 517a315..63f4056 100644 --- a/app/.env +++ b/app/.env @@ -13,8 +13,8 @@ client_name=ShuFuAI # java_api_base=http://47.111.163.154:18080 -# java_api_base=http://127.0.0.1:18080 -java_api_base=http://8.136.19.173:18080 +java_api_base=http://127.0.0.1:18080 +# java_api_base=http://8.136.19.173:18080 # java_api_base=http://121.196.149.225:18080 diff --git a/app/amazon/__pycache__/approve.cpython-312.pyc b/app/amazon/__pycache__/approve.cpython-312.pyc index 859dd8c..d4bc9b6 100644 Binary files a/app/amazon/__pycache__/approve.cpython-312.pyc and b/app/amazon/__pycache__/approve.cpython-312.pyc differ diff --git a/app/amazon/__pycache__/asin_status.cpython-312.pyc b/app/amazon/__pycache__/asin_status.cpython-312.pyc new file mode 100644 index 0000000..1224c5c Binary files /dev/null and b/app/amazon/__pycache__/asin_status.cpython-312.pyc differ diff --git a/app/amazon/__pycache__/detail_spider.cpython-312.pyc b/app/amazon/__pycache__/detail_spider.cpython-312.pyc new file mode 100644 index 0000000..a646c09 Binary files /dev/null and b/app/amazon/__pycache__/detail_spider.cpython-312.pyc differ diff --git a/app/amazon/__pycache__/main.cpython-312.pyc b/app/amazon/__pycache__/main.cpython-312.pyc index a92e3b5..d1560d0 100644 Binary files a/app/amazon/__pycache__/main.cpython-312.pyc and b/app/amazon/__pycache__/main.cpython-312.pyc differ diff --git a/app/amazon/__pycache__/match_action.cpython-312.pyc b/app/amazon/__pycache__/match_action.cpython-312.pyc index b049682..da45664 100644 Binary files a/app/amazon/__pycache__/match_action.cpython-312.pyc and b/app/amazon/__pycache__/match_action.cpython-312.pyc differ diff --git a/app/amazon/__pycache__/price_match.cpython-312.pyc b/app/amazon/__pycache__/price_match.cpython-312.pyc index b3bad90..ec269dd 100644 Binary files a/app/amazon/__pycache__/price_match.cpython-312.pyc and b/app/amazon/__pycache__/price_match.cpython-312.pyc differ diff --git a/app/amazon/detail_spider.py b/app/amazon/detail_spider.py index 0558bbb..276c1fe 100644 --- a/app/amazon/detail_spider.py +++ b/app/amazon/detail_spider.py @@ -390,19 +390,20 @@ class SpiderTask: runing_task[task_id]["status"] = "failed" runing_task[task_id]["error"] = str(e) - def post_result(self, task_id: int, chunkIndex:int,chunkTotal: int, asin: str, error:str="", - item_data:dict={}, - is_done: bool = False): + def post_result(self, task_id: int, chunkIndex:int,chunkTotal: int, asin: str, error:str="", + item_data:dict={}, + is_done: bool = False): """回传处理结果到API """ url = f"{DELETE_BRAND_API_BASE}/api/appearance-patent/tasks/{task_id}/result" - payload ={ - "submissionId": f"{int(time.time())}", - "chunkIndex": chunkIndex, - "chunkTotal": chunkTotal, - "error": error, + submission_id = f"appearance-patent:{task_id}" + payload ={ + "submissionId": submission_id, + "chunkIndex": chunkIndex, + "chunkTotal": chunkTotal, + "error": error, "items": [ item_data ], @@ -410,8 +411,9 @@ class SpiderTask: } max_retries = 3 - for retry in range(max_retries): - try: + for retry in range(max_retries): + try: + request_timeout = 300 if is_done else 30 print("================【详情采集】=====================") self.log(f"尝试回传结果 (第 {retry + 1}/{max_retries} 次)") self.log(f"回传URL: {url}") @@ -420,7 +422,7 @@ class SpiderTask: url, json=payload, headers={"Content-Type": "application/json"}, - timeout=30, + timeout=request_timeout, verify=False ) self.log(f"回传结果: {response.text}") @@ -6904,4 +6906,4 @@ if __name__ == '__main__': }, "code": None } - spide.process_task(task_data) \ No newline at end of file + spide.process_task(task_data) diff --git a/app/assets/convert.js b/app/assets/convert.js index c820697..bec17e0 100644 --- a/app/assets/convert.js +++ b/app/assets/convert.js @@ -1 +1 @@ -import { d as A, o as H, a as l, c as n, b as z, B as K, e as s, F as w, r as F, t as i, f as _, n as P, w as M, u as j, x as q, g as d, h as E, y as J, i as k, E as r, z as L, A as O, C as G, D as Q, _ as W, q as X, s as Y } from "./pywebview-C66x_2Dh.js"; import { e as Z } from "./brand-BZije8D7.js"; const ee = { class: "page-shell module-page" }, se = { class: "main-content" }, te = { class: "left-panel" }, ae = { class: "upload-zone" }, le = { class: "selected-files clean-placeholder" }, ne = { key: 0, class: "more-line" }, oe = { key: 1 }, re = { class: "option-group" }, ie = ["value"], ce = { class: "template-item-content" }, ue = { class: "label" }, de = { key: 0, class: "template-tag" }, ve = { key: 1, class: "template-tag muted" }, pe = { key: 2, class: "template-tag custom" }, _e = { class: "desc" }, fe = { key: 0, class: "empty-option" }, me = { class: "option-group" }, he = { class: "radio-item active" }, ye = { class: "desc" }, ge = { class: "run-row" }, be = ["disabled"], we = { class: "loading-msg" }, ke = { class: "right-panel" }, Ce = { class: "task-list-wrap" }, xe = { class: "clean-result-summary" }, Fe = { class: "summary-card" }, Ie = { class: "summary-card" }, Pe = { class: "summary-card" }, Ee = { class: "result-list-wrap" }, Te = { key: 0, class: "empty-tasks" }, Be = { key: 1, class: "task-list clean-result-list" }, De = { class: "left" }, Ne = ["title"], Re = { key: 0, class: "files" }, $e = { key: 1, class: "files" }, Se = { class: "task-right" }, Ue = ["onClick"], Ve = ["onClick"], Ae = A({ __name: "BrandConvertTab", setup(ze) { const p = d([]), g = d(""), f = d([]), m = d(!1), b = d([]), h = d({ total: 0, successCount: 0, failedCount: 0, items: [] }), v = d([]), u = d(""), C = E(() => p.value.slice(0, 8)), T = E(() => v.value.find(a => a.id === u.value) || null); async function I(a) { const e = k(); if (!e?.upload_file_to_java) throw new Error("当前桌面端未提供文件上传桥接能力"); const t = []; for (const o of a) { const c = typeof o == "string" ? o : o.absolutePath, V = typeof o == "string" ? void 0 : o.relativePath, y = await e.upload_file_to_java(c, V); if (!y?.success || !y.data) throw new Error(y?.error || y?.message || `上传失败:${c}`); t.push(y.data) } return t } async function B(a) { const e = await J(); v.value = e || []; const t = v.value.find(c => c.id === u.value); if (t) { u.value = t.id; return } const o = v.value.find(c => c.isDefault); if (o) { u.value = o.id; return } u.value = v.value[0]?.id || "" } async function D(a, e) { p.value = a, g.value = "", f.value = await I(a), r.success(e) } async function N() { const a = k(); if (!a?.select_brand_xlsx_files) { r.warning("当前环境不支持文件选择,请在本机客户端中打开"); return } try { const e = await a.select_brand_xlsx_files(); if (!e?.length) return; await D(e, `已选择 ${e.length} 个待转换文件`) } catch (e) { r.error(e instanceof Error ? e.message : "选择失败") } } async function R() { const a = k(); if (!a?.select_brand_folder) { r.warning("当前环境不支持文件夹选择,请在本机客户端中打开"); return } try { const e = await a.select_brand_folder(); if (!e) return; g.value = e.split(/[/\\]/).filter(Boolean).pop() || ""; const t = await Z(e); if (t.success && t.items?.length) { p.value = t.items.map(o => o.relativePath), f.value = await I(t.items), r.success(`已选择文件夹内 ${t.items.length} 个 xlsx 文件`); return } r.warning(t.error || "该文件夹下没有 xlsx 文件") } catch (e) { r.error(e instanceof Error ? e.message : "选择失败") } } async function $() { if (!f.value.length) { r.warning("请先选择待转换 Excel 文件或文件夹"); return } if (!u.value) { r.warning("请先选择模板"); return } try { m.value = !0; const a = await L({ files: f.value.map(e => ({ fileKey: e.fileKey, originalFilename: e.originalFilename, relativePath: e.relativePath })), templateId: u.value, archiveName: f.value.some(e => !!e.relativePath) && g.value ? g.value : void 0 }); h.value = a, b.value = a.items || [], await x(), r.success("格式转换完成") } catch (a) { r.error(a instanceof Error ? a.message : "格式转换失败") } finally { m.value = !1 } } async function x() { try { const a = await O(); b.value = a.items || [], h.value = { total: a.items?.length || 0, successCount: a.items?.filter(e => e.success).length || 0, failedCount: a.items?.filter(e => !e.success).length || 0, items: a.items || [] } } catch { } } async function S(a) { try { await G(a), await x(), r.success("已删除") } catch (e) { r.error(e instanceof Error ? e.message : "删除失败") } } async function U(a) { const e = k(), t = a.downloadUrl || (a.resultId ? Q(a.resultId) : ""); if (!t) { r.warning("当前结果没有下载地址"); return } const o = a.outputFilename || `${new Date().toISOString().slice(0, 10).replace(/-/g, "")}.txt`; if (e?.save_file_from_url_new) { const c = await e.save_file_from_url_new(t, o); c.success ? r.success(`已保存:${c.path || o}`) : c.error && c.error !== "用户取消" && r.error(c.error); return } } return H(() => { B().catch(() => { }), x().catch(() => { }) }), (a, e) => (l(), n("div", ee, [z(K, { active: "convert" }), s("div", se, [s("aside", te, [e[5] || (e[5] = s("div", { class: "section-title" }, "选择文件", -1)), s("div", ae, [e[1] || (e[1] = s("div", { class: "hint" }, "选择需要转换的 Excel 文件或文件夹,将按所选模板生成 txt 文件。", -1)), s("div", { class: "btns" }, [s("button", { type: "button", class: "opt-btn", onClick: N }, "选择 Excel 文件"), s("button", { type: "button", class: "opt-btn", onClick: R }, "选择文件夹")]), s("div", le, [p.value.length ? (l(), n(w, { key: 0 }, [(l(!0), n(w, null, F(C.value, t => (l(), n("span", { key: t }, i(t), 1))), 128)), p.value.length > C.value.length ? (l(), n("span", ne, " 还有 " + i(p.value.length - C.value.length) + " 个文件未展开显示 ", 1)) : _("", !0)], 64)) : (l(), n("span", oe, "暂未选择待转换文件"))])]), e[6] || (e[6] = s("div", { class: "section-title" }, "模板选择", -1)), s("div", re, [(l(!0), n(w, null, F(v.value, t => (l(), n("label", { key: t.id, class: P(["radio-item", { active: u.value === t.id }]) }, [M(s("input", { "onUpdate:modelValue": e[0] || (e[0] = o => u.value = o), type: "radio", value: t.id, name: "convert-template" }, null, 8, ie), [[j, u.value]]), s("div", ce, [s("div", null, [s("span", ue, [q(i(t.templateName) + " ", 1), t.isDefault ? (l(), n("span", de, "默认")) : t.builtIn ? (l(), n("span", ve, "内置")) : (l(), n("span", pe, "自定义"))]), s("div", _e, "输出文件:" + i(t.outputFilename), 1)])])], 2))), 128)), v.value.length ? _("", !0) : (l(), n("div", fe, "暂无可用模板"))]), e[7] || (e[7] = s("div", { class: "section-title" }, "转换说明", -1)), s("div", me, [s("label", he, [e[3] || (e[3] = s("input", { type: "checkbox", checked: "", disabled: "" }, null, -1)), s("div", null, [e[2] || (e[2] = s("span", { class: "label" }, "按当前模板输出", -1)), s("div", ye, "当前模板:" + i(T.value?.templateName || "未加载模板"), 1)])]), e[4] || (e[4] = s("label", { class: "radio-item active" }, [s("input", { type: "checkbox", checked: "", disabled: "" }), s("div", null, [s("span", { class: "label" }, "SKU 使用雪花ID-序号"), s("div", { class: "desc" }, "例如 1773816146796-1")])], -1))]), s("div", ge, [s("button", { type: "button", class: "btn-run", disabled: m.value, onClick: $ }, i(m.value ? "转换中..." : "开始转换"), 9, be), s("span", we, i(m.value ? "正在生成并上传结果,请稍候…" : "选择文件后即可执行格式转换,结果会显示在右侧供下载"), 1)])]), s("section", ke, [e[12] || (e[12] = s("div", { class: "panel-header" }, "转换结果", -1)), s("div", Ce, [s("div", xe, [s("div", Fe, [e[8] || (e[8] = s("span", { class: "summary-label" }, "已处理文件", -1)), s("strong", null, i(h.value.total), 1)]), s("div", Ie, [e[9] || (e[9] = s("span", { class: "summary-label" }, "成功结果", -1)), s("strong", null, i(h.value.successCount), 1)]), s("div", Pe, [e[10] || (e[10] = s("span", { class: "summary-label" }, "失败文件", -1)), s("strong", null, i(h.value.failedCount), 1)])]), s("div", Ee, [e[11] || (e[11] = s("div", { class: "result-list-header" }, [s("span", null, "生成的结果压缩包列表")], -1)), b.value.length === 0 ? (l(), n("div", Te, " 暂无转换结果,完成格式转换后会在这里展示结果压缩包 ")) : (l(), n("ul", Be, [(l(!0), n(w, null, F(b.value, t => (l(), n("li", { key: `${t.resultId || t.outputFilename || t.sourceFilename}`, class: "task-item" }, [s("div", De, [s("span", { class: "id", title: t.sourceFilename }, i(t.sourceFilename || "-"), 9, Ne), t.outputFilename ? (l(), n("div", Re, "压缩包文件:" + i(t.outputFilename), 1)) : _("", !0), t.error ? (l(), n("div", $e, "错误信息:" + i(t.error), 1)) : _("", !0)]), s("div", Se, [s("span", { class: P(["status", t.success ? "success" : "failed"]) }, i(t.success ? "已完成" : "失败"), 3), t.success && (t.downloadUrl || t.resultId) ? (l(), n("button", { key: 0, type: "button", class: "download", onClick: o => U(t) }, " 下载压缩包 ", 8, Ue)) : _("", !0), t.resultId ? (l(), n("button", { key: 1, type: "button", class: "btn-delete", onClick: o => S(t.resultId) }, " 删除 ", 8, Ve)) : _("", !0)])]))), 128))]))])])])])])) } }), He = W(Ae, [["__scopeId", "data-v-0e58908e"]]); X(He).use(Y).mount("#app"); +import{d as A,o as H,a as l,c as n,b as z,B as K,e as s,F as w,r as F,t as i,f as _,n as P,w as M,u as j,x as q,g as d,h as E,y as J,i as k,E as r,z as L,A as O,C as G,D as Q,_ as W,q as X,s as Y}from"./pywebview-C66x_2Dh.js";import{e as Z}from"./brand-BZije8D7.js";const ee={class:"page-shell module-page"},se={class:"main-content"},te={class:"left-panel"},ae={class:"upload-zone"},le={class:"selected-files clean-placeholder"},ne={key:0,class:"more-line"},oe={key:1},re={class:"option-group"},ie=["value"],ce={class:"template-item-content"},ue={class:"label"},de={key:0,class:"template-tag"},ve={key:1,class:"template-tag muted"},pe={key:2,class:"template-tag custom"},_e={class:"desc"},fe={key:0,class:"empty-option"},me={class:"option-group"},he={class:"radio-item active"},ye={class:"desc"},ge={class:"run-row"},be=["disabled"],we={class:"loading-msg"},ke={class:"right-panel"},Ce={class:"task-list-wrap"},xe={class:"clean-result-summary"},Fe={class:"summary-card"},Ie={class:"summary-card"},Pe={class:"summary-card"},Ee={class:"result-list-wrap"},Te={key:0,class:"empty-tasks"},Be={key:1,class:"task-list clean-result-list"},De={class:"left"},Ne=["title"],Re={key:0,class:"files"},$e={key:1,class:"files"},Se={class:"task-right"},Ue=["onClick"],Ve=["onClick"],Ae=A({__name:"BrandConvertTab",setup(ze){const p=d([]),g=d(""),f=d([]),m=d(!1),b=d([]),h=d({total:0,successCount:0,failedCount:0,items:[]}),v=d([]),u=d(""),C=E(()=>p.value.slice(0,8)),T=E(()=>v.value.find(a=>a.id===u.value)||null);async function I(a){const e=k();if(!e?.upload_file_to_java)throw new Error("当前桌面端未提供文件上传桥接能力");const t=[];for(const o of a){const c=typeof o=="string"?o:o.absolutePath,V=typeof o=="string"?void 0:o.relativePath,y=await e.upload_file_to_java(c,V);if(!y?.success||!y.data)throw new Error(y?.error||y?.message||`上传失败:${c}`);t.push(y.data)}return t}async function B(a){const e=await J();v.value=e||[];const t=v.value.find(c=>c.id===u.value);if(t){u.value=t.id;return}const o=v.value.find(c=>c.isDefault);if(o){u.value=o.id;return}u.value=v.value[0]?.id||""}async function D(a,e){p.value=a,g.value="",f.value=await I(a),r.success(e)}async function N(){const a=k();if(!a?.select_brand_xlsx_files){r.warning("当前环境不支持文件选择,请在本机客户端中打开");return}try{const e=await a.select_brand_xlsx_files();if(!e?.length)return;await D(e,`已选择 ${e.length} 个待转换文件`)}catch(e){r.error(e instanceof Error?e.message:"选择失败")}}async function R(){const a=k();if(!a?.select_brand_folder){r.warning("当前环境不支持文件夹选择,请在本机客户端中打开");return}try{const e=await a.select_brand_folder();if(!e)return;g.value=e.split(/[/\\]/).filter(Boolean).pop()||"";const t=await Z(e);if(t.success&&t.items?.length){p.value=t.items.map(o=>o.relativePath),f.value=await I(t.items),r.success(`已选择文件夹内 ${t.items.length} 个 xlsx 文件`);return}r.warning(t.error||"该文件夹下没有 xlsx 文件")}catch(e){r.error(e instanceof Error?e.message:"选择失败")}}async function $(){if(!f.value.length){r.warning("请先选择待转换 Excel 文件或文件夹");return}if(!u.value){r.warning("请先选择模板");return}try{m.value=!0;const a=await L({files:f.value.map(e=>({fileKey:e.fileKey,originalFilename:e.originalFilename,relativePath:e.relativePath})),templateId:u.value,archiveName:f.value.some(e=>!!e.relativePath)&&g.value?g.value:void 0});h.value=a,b.value=a.items||[],await x(),r.success("格式转换完成")}catch(a){r.error(a instanceof Error?a.message:"格式转换失败")}finally{m.value=!1}}async function x(){try{const a=await O();b.value=a.items||[],h.value={total:a.items?.length||0,successCount:a.items?.filter(e=>e.success).length||0,failedCount:a.items?.filter(e=>!e.success).length||0,items:a.items||[]}}catch{}}async function S(a){try{await G(a),await x(),r.success("已删除")}catch(e){r.error(e instanceof Error?e.message:"删除失败")}}async function U(a){const e=k(),t=a.downloadUrl||(a.resultId?Q(a.resultId):"");if(!t){r.warning("当前结果没有下载地址");return}const o=a.outputFilename||`${new Date().toISOString().slice(0,10).replace(/-/g,"")}.txt`;if(e?.save_file_from_url_new){const c=await e.save_file_from_url_new(t,o);c.success?r.success(`已保存:${c.path||o}`):c.error&&c.error!=="用户取消"&&r.error(c.error);return}}return H(()=>{B().catch(()=>{}),x().catch(()=>{})}),(a,e)=>(l(),n("div",ee,[z(K,{active:"convert"}),s("div",se,[s("aside",te,[e[5]||(e[5]=s("div",{class:"section-title"},"选择文件",-1)),s("div",ae,[e[1]||(e[1]=s("div",{class:"hint"},"选择需要转换的 Excel 文件或文件夹,将按所选模板生成 txt 文件。",-1)),s("div",{class:"btns"},[s("button",{type:"button",class:"opt-btn",onClick:N},"选择 Excel 文件"),s("button",{type:"button",class:"opt-btn",onClick:R},"选择文件夹")]),s("div",le,[p.value.length?(l(),n(w,{key:0},[(l(!0),n(w,null,F(C.value,t=>(l(),n("span",{key:t},i(t),1))),128)),p.value.length>C.value.length?(l(),n("span",ne," 还有 "+i(p.value.length-C.value.length)+" 个文件未展开显示 ",1)):_("",!0)],64)):(l(),n("span",oe,"暂未选择待转换文件"))])]),e[6]||(e[6]=s("div",{class:"section-title"},"模板选择",-1)),s("div",re,[(l(!0),n(w,null,F(v.value,t=>(l(),n("label",{key:t.id,class:P(["radio-item",{active:u.value===t.id}])},[M(s("input",{"onUpdate:modelValue":e[0]||(e[0]=o=>u.value=o),type:"radio",value:t.id,name:"convert-template"},null,8,ie),[[j,u.value]]),s("div",ce,[s("div",null,[s("span",ue,[q(i(t.templateName)+" ",1),t.isDefault?(l(),n("span",de,"默认")):t.builtIn?(l(),n("span",ve,"内置")):(l(),n("span",pe,"自定义"))]),s("div",_e,"输出文件:"+i(t.outputFilename),1)])])],2))),128)),v.value.length?_("",!0):(l(),n("div",fe,"暂无可用模板"))]),e[7]||(e[7]=s("div",{class:"section-title"},"转换说明",-1)),s("div",me,[s("label",he,[e[3]||(e[3]=s("input",{type:"checkbox",checked:"",disabled:""},null,-1)),s("div",null,[e[2]||(e[2]=s("span",{class:"label"},"按当前模板输出",-1)),s("div",ye,"当前模板:"+i(T.value?.templateName||"未加载模板"),1)])]),e[4]||(e[4]=s("label",{class:"radio-item active"},[s("input",{type:"checkbox",checked:"",disabled:""}),s("div",null,[s("span",{class:"label"},"SKU 使用雪花ID-序号"),s("div",{class:"desc"},"例如 1773816146796-1")])],-1))]),s("div",ge,[s("button",{type:"button",class:"btn-run",disabled:m.value,onClick:$},i(m.value?"转换中...":"开始转换"),9,be),s("span",we,i(m.value?"正在生成并上传结果,请稍候…":"选择文件后即可执行格式转换,结果会显示在右侧供下载"),1)])]),s("section",ke,[e[12]||(e[12]=s("div",{class:"panel-header"},"转换结果",-1)),s("div",Ce,[s("div",xe,[s("div",Fe,[e[8]||(e[8]=s("span",{class:"summary-label"},"已处理文件",-1)),s("strong",null,i(h.value.total),1)]),s("div",Ie,[e[9]||(e[9]=s("span",{class:"summary-label"},"成功结果",-1)),s("strong",null,i(h.value.successCount),1)]),s("div",Pe,[e[10]||(e[10]=s("span",{class:"summary-label"},"失败文件",-1)),s("strong",null,i(h.value.failedCount),1)])]),s("div",Ee,[e[11]||(e[11]=s("div",{class:"result-list-header"},[s("span",null,"生成的结果压缩包列表")],-1)),b.value.length===0?(l(),n("div",Te," 暂无转换结果,完成格式转换后会在这里展示结果压缩包 ")):(l(),n("ul",Be,[(l(!0),n(w,null,F(b.value,t=>(l(),n("li",{key:`${t.resultId||t.outputFilename||t.sourceFilename}`,class:"task-item"},[s("div",De,[s("span",{class:"id",title:t.sourceFilename},i(t.sourceFilename||"-"),9,Ne),t.outputFilename?(l(),n("div",Re,"压缩包文件:"+i(t.outputFilename),1)):_("",!0),t.error?(l(),n("div",$e,"错误信息:"+i(t.error),1)):_("",!0)]),s("div",Se,[s("span",{class:P(["status",t.success?"success":"failed"])},i(t.success?"已完成":"失败"),3),t.success&&(t.downloadUrl||t.resultId)?(l(),n("button",{key:0,type:"button",class:"download",onClick:o=>U(t)}," 下载压缩包 ",8,Ue)):_("",!0),t.resultId?(l(),n("button",{key:1,type:"button",class:"btn-delete",onClick:o=>S(t.resultId)}," 删除 ",8,Ve)):_("",!0)])]))),128))]))])])])])]))}}),He=W(Ae,[["__scopeId","data-v-0e58908e"]]);X(He).use(Y).mount("#app"); diff --git a/app/assets/dedupe.js b/app/assets/dedupe.js index 92667fd..4aebb8b 100644 --- a/app/assets/dedupe.js +++ b/app/assets/dedupe.js @@ -1 +1 @@ -import { d as z, o as W, a, c as n, b as q, B as J, e as s, F as C, r as F, t as u, f as b, n as I, w as P, v as B, g as c, h as L, i as x, E as o, j as O, k as G, l as Q, m as X, p as Y, _ as Z, q as ee, s as se } from "./pywebview-C66x_2Dh.js"; import { e as te } from "./brand-BZije8D7.js"; const le = { class: "page-shell module-page" }, ae = { class: "main-content" }, ne = { class: "left-panel" }, oe = { class: "upload-zone" }, re = { class: "selected-files clean-placeholder" }, ie = { key: 0, class: "more-line" }, ue = { key: 1 }, ce = { class: "option-group column-option-group" }, de = { class: "column-toolbar" }, ve = { class: "column-count" }, pe = { key: 0, class: "column-grid" }, fe = ["value"], _e = { key: 1, class: "empty-column-state" }, me = { class: "option-group" }, he = { class: "run-row" }, ge = ["disabled"], ye = { class: "loading-msg" }, be = { class: "right-panel" }, we = { class: "task-list-wrap" }, ke = { class: "clean-result-summary" }, Ce = { class: "summary-card" }, Ie = { class: "summary-card" }, xe = { class: "summary-card" }, De = { class: "result-list-wrap" }, Ee = { key: 0, class: "empty-tasks" }, Fe = { key: 1, class: "task-list clean-result-list" }, Pe = { class: "left" }, Be = ["title"], Se = { key: 0, class: "files" }, Ue = { key: 1, class: "files" }, Ne = { class: "task-right" }, $e = ["onClick"], Ae = ["onClick"], He = z({ __name: "BrandDedupeTab", setup(Re) { const p = c([]), d = c([]), f = c([]), w = c(""), v = c([]), S = c(!1), _ = c(!0), m = c(!0), h = c(!1), k = c([]), g = c({ total: 0, successCount: 0, failedCount: 0, items: [] }), D = L(() => f.value.slice(0, 8)); function $() { d.value = [...p.value] } function A() { d.value = [] } async function U(l) { const e = x(); if (!e?.upload_file_to_java) throw new Error("当前桌面端未提供文件上传桥接能力"); const t = []; for (const r of l) { const i = typeof r == "string" ? r : r.absolutePath, j = typeof r == "string" ? void 0 : r.relativePath, y = await e.upload_file_to_java(i, j); if (!y?.success || !y.data) throw new Error(y?.error || y?.message || `上传失败:${i}`); t.push(y.data) } return t } async function N(l) { const e = await Y(l); if (!e.headers?.length) { o.error("读取 Excel 表头失败"), p.value = [], d.value = []; return } const t = ["id", "ASIN", "国家", "价格", "卖家名称"], r = [...t.filter(i => e.headers?.includes(i)), ...(e.headers || []).filter(i => !t.includes(i))]; p.value = r, d.value = t.filter(i => r.includes(i)) } async function H(l, e) { f.value = l, w.value = "", v.value = await U(l), v.value.length > 0 && await N(v.value[0].fileKey), o.success(e) } async function K() { const l = x(); if (!l?.select_brand_xlsx_files) { o.warning("当前环境不支持文件选择,请在本机客户端中打开"); return } try { const e = await l.select_brand_xlsx_files(); if (!e?.length) return; await H(e, `已选择 ${e.length} 个待清洗文件`) } catch (e) { o.error(e instanceof Error ? e.message : "选择失败") } } async function R() { const l = x(); if (!l?.select_brand_folder) { o.warning("当前环境不支持文件夹选择,请在本机客户端中打开"); return } try { const e = await l.select_brand_folder(); if (!e) return; w.value = e.split(/[/\\]/).filter(Boolean).pop() || ""; const t = await te(e); if (t.success && t.items?.length) { f.value = t.items.map(r => r.relativePath), v.value = await U(t.items), v.value.length > 0 && await N(v.value[0].fileKey), o.success(`已选择文件夹内 ${t.items.length} 个 xlsx 文件`); return } o.warning(t.error || "该文件夹下没有 xlsx 文件") } catch (e) { o.error(e instanceof Error ? e.message : "选择失败") } } async function V() { if (!v.value.length) { o.warning("请先选择待清洗 Excel 文件或文件夹"); return } if (!d.value.length) { o.warning("请至少选择一列保留列"); return } if (!S.value && !_.value && !m.value) { o.warning("请至少选择一种 ID 保留规则"); return } try { h.value = !0; const l = await O({ files: v.value.map(e => ({ fileKey: e.fileKey, originalFilename: e.originalFilename, relativePath: e.relativePath })), selectedColumns: d.value, keepIntegerIds: S.value, keepUnderscoreIds: _.value, keepIntegerMainIdsWhenNoSubIds: m.value, archiveName: v.value.some(e => !!e.relativePath) && w.value ? w.value : void 0 }); g.value = l, k.value = l.items || [], await E(), o.success("数据去重完成") } catch (l) { o.error(l instanceof Error ? l.message : "去重失败") } finally { h.value = !1 } } async function E() { try { const l = await G(); k.value = l.items || [], g.value = { total: l.items?.length || 0, successCount: l.items?.filter(e => e.success).length || 0, failedCount: l.items?.filter(e => !e.success).length || 0, items: l.items || [] } } catch { } } async function M(l) { try { await Q(l), await E(), o.success("已删除") } catch (e) { o.error(e instanceof Error ? e.message : "删除失败") } } async function T(l) { const e = x(), t = l.downloadUrl || (l.resultId ? X(l.resultId) : ""); if (!t) { o.warning("当前结果没有下载地址"); return } const r = l.outputFilename || `${new Date().toISOString().slice(0, 10).replace(/-/g, "")}_cleaned.xlsx`; if (e?.save_file_from_url_new) { const i = await e.save_file_from_url_new(t, r); i.success ? o.success(`已保存:${i.path || r}`) : i.error && i.error !== "用户取消" && o.error(i.error); return } } return W(() => { E().catch(() => { }) }), (l, e) => (a(), n("div", le, [q(J, { active: "dedupe" }), s("div", ae, [s("aside", ne, [e[7] || (e[7] = s("div", { class: "section-title" }, "选择文件", -1)), s("div", oe, [e[3] || (e[3] = s("div", { class: "hint" }, "选择需要清洗的 Excel 文件或文件夹,后续将按所选规则输出处理结果。", -1)), s("div", { class: "btns" }, [s("button", { type: "button", class: "opt-btn", onClick: K }, "选择 Excel 文件"), s("button", { type: "button", class: "opt-btn", onClick: R }, "选择文件夹")]), s("div", re, [f.value.length ? (a(), n(C, { key: 0 }, [(a(!0), n(C, null, F(D.value, t => (a(), n("span", { key: t }, u(t), 1))), 128)), f.value.length > D.value.length ? (a(), n("span", ie, " 还有 " + u(f.value.length - D.value.length) + " 个文件未展开显示 ", 1)) : b("", !0)], 64)) : (a(), n("span", ue, "暂未选择待清洗文件"))])]), e[8] || (e[8] = s("div", { class: "section-title" }, "保留列设置", -1)), s("div", ce, [s("div", de, [s("span", ve, "已选择 " + u(d.value.length) + " / " + u(p.value.length) + " 列", 1), s("div", { class: "column-actions" }, [s("button", { type: "button", class: "opt-btn small", onClick: $ }, "全选"), s("button", { type: "button", class: "opt-btn small", onClick: A }, "清空")])]), p.value.length ? (a(), n("div", pe, [(a(!0), n(C, null, F(p.value, t => (a(), n("label", { key: t, class: I(["column-item", { active: d.value.includes(t) }]) }, [P(s("input", { "onUpdate:modelValue": e[0] || (e[0] = r => d.value = r), type: "checkbox", value: t }, null, 8, fe), [[B, d.value]]), s("span", null, u(t), 1)], 2))), 128))])) : (a(), n("div", _e, "请选择 Excel 文件后读取表头并填充可保留列")), e[4] || (e[4] = s("div", { class: "desc" }, "读取首个 Excel 文件的第一行作为表头,用于动态选择需要保留的列。", -1))]), e[9] || (e[9] = s("div", { class: "section-title" }, "ID 保留规则", -1)), s("div", me, [s("label", { class: I(["radio-item", { active: _.value }]) }, [P(s("input", { "onUpdate:modelValue": e[1] || (e[1] = t => _.value = t), type: "checkbox" }, null, 512), [[B, _.value]]), e[5] || (e[5] = s("div", null, [s("span", { class: "label" }, "保留子链接"), s("div", { class: "desc" }, "例如 1_1、2_3 这种带下划线的子项 ID")], -1))], 2), s("label", { class: I(["radio-item", { active: m.value }]) }, [P(s("input", { "onUpdate:modelValue": e[2] || (e[2] = t => m.value = t), type: "checkbox" }, null, 512), [[B, m.value]]), e[6] || (e[6] = s("div", null, [s("span", { class: "label" }, "仅剩主链接无子链接"), s("div", { class: "desc" }, "当某个主链接只有 13 这类主 ID、没有 13_1 这类对应子 ID 时,自动保留该主 ID")], -1))], 2)]), s("div", he, [s("button", { type: "button", class: "btn-run", disabled: h.value, onClick: V }, u(h.value ? "清洗中..." : "开始清洗"), 9, ge), s("span", ye, u(h.value ? "正在处理文件并上传结果,请稍候…" : "选择文件、列和 ID 规则后即可执行清洗,结果会显示在右侧供下载"), 1)])]), s("section", be, [e[14] || (e[14] = s("div", { class: "panel-header" }, "去重结果", -1)), s("div", we, [s("div", ke, [s("div", Ce, [e[10] || (e[10] = s("span", { class: "summary-label" }, "已处理文件", -1)), s("strong", null, u(g.value.total), 1)]), s("div", Ie, [e[11] || (e[11] = s("span", { class: "summary-label" }, "成功结果", -1)), s("strong", null, u(g.value.successCount), 1)]), s("div", xe, [e[12] || (e[12] = s("span", { class: "summary-label" }, "失败文件", -1)), s("strong", null, u(g.value.failedCount), 1)])]), s("div", De, [e[13] || (e[13] = s("div", { class: "result-list-header" }, [s("span", null, "处理后的 Excel 列表")], -1)), k.value.length === 0 ? (a(), n("div", Ee, " 暂无去重结果,完成数据去重后会在这里展示输出文件 ")) : (a(), n("ul", Fe, [(a(!0), n(C, null, F(k.value, t => (a(), n("li", { key: `${t.resultId || t.outputFilename || t.sourceFilename}`, class: "task-item" }, [s("div", Pe, [s("span", { class: "id", title: t.sourceFilename }, u(t.sourceFilename || "-"), 9, Be), t.outputFilename ? (a(), n("div", Se, "输出文件:" + u(t.outputFilename), 1)) : b("", !0), t.error ? (a(), n("div", Ue, "错误信息:" + u(t.error), 1)) : b("", !0)]), s("div", Ne, [s("span", { class: I(["status", t.success ? "success" : "failed"]) }, u(t.success ? "已完成" : "失败"), 3), t.success && (t.downloadUrl || t.resultId) ? (a(), n("button", { key: 0, type: "button", class: "download", onClick: r => T(t) }, " 下载文件 ", 8, $e)) : b("", !0), t.resultId ? (a(), n("button", { key: 1, type: "button", class: "btn-delete", onClick: r => M(t.resultId) }, " 删除 ", 8, Ae)) : b("", !0)])]))), 128))]))])])])])])) } }), Ke = Z(He, [["__scopeId", "data-v-c7e8afd4"]]); ee(Ke).use(se).mount("#app"); +import{d as z,o as W,a,c as n,b as q,B as J,e as s,F as C,r as F,t as u,f as b,n as I,w as P,v as B,g as c,h as L,i as x,E as o,j as O,k as G,l as Q,m as X,p as Y,_ as Z,q as ee,s as se}from"./pywebview-C66x_2Dh.js";import{e as te}from"./brand-BZije8D7.js";const le={class:"page-shell module-page"},ae={class:"main-content"},ne={class:"left-panel"},oe={class:"upload-zone"},re={class:"selected-files clean-placeholder"},ie={key:0,class:"more-line"},ue={key:1},ce={class:"option-group column-option-group"},de={class:"column-toolbar"},ve={class:"column-count"},pe={key:0,class:"column-grid"},fe=["value"],_e={key:1,class:"empty-column-state"},me={class:"option-group"},he={class:"run-row"},ge=["disabled"],ye={class:"loading-msg"},be={class:"right-panel"},we={class:"task-list-wrap"},ke={class:"clean-result-summary"},Ce={class:"summary-card"},Ie={class:"summary-card"},xe={class:"summary-card"},De={class:"result-list-wrap"},Ee={key:0,class:"empty-tasks"},Fe={key:1,class:"task-list clean-result-list"},Pe={class:"left"},Be=["title"],Se={key:0,class:"files"},Ue={key:1,class:"files"},Ne={class:"task-right"},$e=["onClick"],Ae=["onClick"],He=z({__name:"BrandDedupeTab",setup(Re){const p=c([]),d=c([]),f=c([]),w=c(""),v=c([]),S=c(!1),_=c(!0),m=c(!0),h=c(!1),k=c([]),g=c({total:0,successCount:0,failedCount:0,items:[]}),D=L(()=>f.value.slice(0,8));function $(){d.value=[...p.value]}function A(){d.value=[]}async function U(l){const e=x();if(!e?.upload_file_to_java)throw new Error("当前桌面端未提供文件上传桥接能力");const t=[];for(const r of l){const i=typeof r=="string"?r:r.absolutePath,j=typeof r=="string"?void 0:r.relativePath,y=await e.upload_file_to_java(i,j);if(!y?.success||!y.data)throw new Error(y?.error||y?.message||`上传失败:${i}`);t.push(y.data)}return t}async function N(l){const e=await Y(l);if(!e.headers?.length){o.error("读取 Excel 表头失败"),p.value=[],d.value=[];return}const t=["id","ASIN","国家","价格","卖家名称"],r=[...t.filter(i=>e.headers?.includes(i)),...(e.headers||[]).filter(i=>!t.includes(i))];p.value=r,d.value=t.filter(i=>r.includes(i))}async function H(l,e){f.value=l,w.value="",v.value=await U(l),v.value.length>0&&await N(v.value[0].fileKey),o.success(e)}async function K(){const l=x();if(!l?.select_brand_xlsx_files){o.warning("当前环境不支持文件选择,请在本机客户端中打开");return}try{const e=await l.select_brand_xlsx_files();if(!e?.length)return;await H(e,`已选择 ${e.length} 个待清洗文件`)}catch(e){o.error(e instanceof Error?e.message:"选择失败")}}async function R(){const l=x();if(!l?.select_brand_folder){o.warning("当前环境不支持文件夹选择,请在本机客户端中打开");return}try{const e=await l.select_brand_folder();if(!e)return;w.value=e.split(/[/\\]/).filter(Boolean).pop()||"";const t=await te(e);if(t.success&&t.items?.length){f.value=t.items.map(r=>r.relativePath),v.value=await U(t.items),v.value.length>0&&await N(v.value[0].fileKey),o.success(`已选择文件夹内 ${t.items.length} 个 xlsx 文件`);return}o.warning(t.error||"该文件夹下没有 xlsx 文件")}catch(e){o.error(e instanceof Error?e.message:"选择失败")}}async function V(){if(!v.value.length){o.warning("请先选择待清洗 Excel 文件或文件夹");return}if(!d.value.length){o.warning("请至少选择一列保留列");return}if(!S.value&&!_.value&&!m.value){o.warning("请至少选择一种 ID 保留规则");return}try{h.value=!0;const l=await O({files:v.value.map(e=>({fileKey:e.fileKey,originalFilename:e.originalFilename,relativePath:e.relativePath})),selectedColumns:d.value,keepIntegerIds:S.value,keepUnderscoreIds:_.value,keepIntegerMainIdsWhenNoSubIds:m.value,archiveName:v.value.some(e=>!!e.relativePath)&&w.value?w.value:void 0});g.value=l,k.value=l.items||[],await E(),o.success("数据去重完成")}catch(l){o.error(l instanceof Error?l.message:"去重失败")}finally{h.value=!1}}async function E(){try{const l=await G();k.value=l.items||[],g.value={total:l.items?.length||0,successCount:l.items?.filter(e=>e.success).length||0,failedCount:l.items?.filter(e=>!e.success).length||0,items:l.items||[]}}catch{}}async function M(l){try{await Q(l),await E(),o.success("已删除")}catch(e){o.error(e instanceof Error?e.message:"删除失败")}}async function T(l){const e=x(),t=l.downloadUrl||(l.resultId?X(l.resultId):"");if(!t){o.warning("当前结果没有下载地址");return}const r=l.outputFilename||`${new Date().toISOString().slice(0,10).replace(/-/g,"")}_cleaned.xlsx`;if(e?.save_file_from_url_new){const i=await e.save_file_from_url_new(t,r);i.success?o.success(`已保存:${i.path||r}`):i.error&&i.error!=="用户取消"&&o.error(i.error);return}}return W(()=>{E().catch(()=>{})}),(l,e)=>(a(),n("div",le,[q(J,{active:"dedupe"}),s("div",ae,[s("aside",ne,[e[7]||(e[7]=s("div",{class:"section-title"},"选择文件",-1)),s("div",oe,[e[3]||(e[3]=s("div",{class:"hint"},"选择需要清洗的 Excel 文件或文件夹,后续将按所选规则输出处理结果。",-1)),s("div",{class:"btns"},[s("button",{type:"button",class:"opt-btn",onClick:K},"选择 Excel 文件"),s("button",{type:"button",class:"opt-btn",onClick:R},"选择文件夹")]),s("div",re,[f.value.length?(a(),n(C,{key:0},[(a(!0),n(C,null,F(D.value,t=>(a(),n("span",{key:t},u(t),1))),128)),f.value.length>D.value.length?(a(),n("span",ie," 还有 "+u(f.value.length-D.value.length)+" 个文件未展开显示 ",1)):b("",!0)],64)):(a(),n("span",ue,"暂未选择待清洗文件"))])]),e[8]||(e[8]=s("div",{class:"section-title"},"保留列设置",-1)),s("div",ce,[s("div",de,[s("span",ve,"已选择 "+u(d.value.length)+" / "+u(p.value.length)+" 列",1),s("div",{class:"column-actions"},[s("button",{type:"button",class:"opt-btn small",onClick:$},"全选"),s("button",{type:"button",class:"opt-btn small",onClick:A},"清空")])]),p.value.length?(a(),n("div",pe,[(a(!0),n(C,null,F(p.value,t=>(a(),n("label",{key:t,class:I(["column-item",{active:d.value.includes(t)}])},[P(s("input",{"onUpdate:modelValue":e[0]||(e[0]=r=>d.value=r),type:"checkbox",value:t},null,8,fe),[[B,d.value]]),s("span",null,u(t),1)],2))),128))])):(a(),n("div",_e,"请选择 Excel 文件后读取表头并填充可保留列")),e[4]||(e[4]=s("div",{class:"desc"},"读取首个 Excel 文件的第一行作为表头,用于动态选择需要保留的列。",-1))]),e[9]||(e[9]=s("div",{class:"section-title"},"ID 保留规则",-1)),s("div",me,[s("label",{class:I(["radio-item",{active:_.value}])},[P(s("input",{"onUpdate:modelValue":e[1]||(e[1]=t=>_.value=t),type:"checkbox"},null,512),[[B,_.value]]),e[5]||(e[5]=s("div",null,[s("span",{class:"label"},"保留子链接"),s("div",{class:"desc"},"例如 1_1、2_3 这种带下划线的子项 ID")],-1))],2),s("label",{class:I(["radio-item",{active:m.value}])},[P(s("input",{"onUpdate:modelValue":e[2]||(e[2]=t=>m.value=t),type:"checkbox"},null,512),[[B,m.value]]),e[6]||(e[6]=s("div",null,[s("span",{class:"label"},"仅剩主链接无子链接"),s("div",{class:"desc"},"当某个主链接只有 13 这类主 ID、没有 13_1 这类对应子 ID 时,自动保留该主 ID")],-1))],2)]),s("div",he,[s("button",{type:"button",class:"btn-run",disabled:h.value,onClick:V},u(h.value?"清洗中...":"开始清洗"),9,ge),s("span",ye,u(h.value?"正在处理文件并上传结果,请稍候…":"选择文件、列和 ID 规则后即可执行清洗,结果会显示在右侧供下载"),1)])]),s("section",be,[e[14]||(e[14]=s("div",{class:"panel-header"},"去重结果",-1)),s("div",we,[s("div",ke,[s("div",Ce,[e[10]||(e[10]=s("span",{class:"summary-label"},"已处理文件",-1)),s("strong",null,u(g.value.total),1)]),s("div",Ie,[e[11]||(e[11]=s("span",{class:"summary-label"},"成功结果",-1)),s("strong",null,u(g.value.successCount),1)]),s("div",xe,[e[12]||(e[12]=s("span",{class:"summary-label"},"失败文件",-1)),s("strong",null,u(g.value.failedCount),1)])]),s("div",De,[e[13]||(e[13]=s("div",{class:"result-list-header"},[s("span",null,"处理后的 Excel 列表")],-1)),k.value.length===0?(a(),n("div",Ee," 暂无去重结果,完成数据去重后会在这里展示输出文件 ")):(a(),n("ul",Fe,[(a(!0),n(C,null,F(k.value,t=>(a(),n("li",{key:`${t.resultId||t.outputFilename||t.sourceFilename}`,class:"task-item"},[s("div",Pe,[s("span",{class:"id",title:t.sourceFilename},u(t.sourceFilename||"-"),9,Be),t.outputFilename?(a(),n("div",Se,"输出文件:"+u(t.outputFilename),1)):b("",!0),t.error?(a(),n("div",Ue,"错误信息:"+u(t.error),1)):b("",!0)]),s("div",Ne,[s("span",{class:I(["status",t.success?"success":"failed"])},u(t.success?"已完成":"失败"),3),t.success&&(t.downloadUrl||t.resultId)?(a(),n("button",{key:0,type:"button",class:"download",onClick:r=>T(t)}," 下载文件 ",8,$e)):b("",!0),t.resultId?(a(),n("button",{key:1,type:"button",class:"btn-delete",onClick:r=>M(t.resultId)}," 删除 ",8,Ae)):b("",!0)])]))),128))]))])])])])]))}}),Ke=Z(He,[["__scopeId","data-v-c7e8afd4"]]);ee(Ke).use(se).mount("#app"); diff --git a/app/assets/delete-brand.js b/app/assets/delete-brand.js index 63783c6..d0ceee6 100644 --- a/app/assets/delete-brand.js +++ b/app/assets/delete-brand.js @@ -1,5 +1 @@ -<<<<<<< HEAD import{d as Ye,o as Ze,L as es,a as d,c as f,b as ss,B as ts,e as r,F as H,r as fe,t as v,f as h,M as as,N as De,n as Me,g as y,h as G,O as ns,i as J,E as p,P as ls,Q as rs,R as os,S as us,T as is,_ as cs,q as ds,s as fs}from"./pywebview-C66x_2Dh.js";import{e as vs}from"./brand-BZije8D7.js";const hs={class:"page-shell module-page"},ps={class:"main-content"},_s={class:"left-panel"},ys={class:"upload-zone"},gs={class:"selected-files clean-placeholder split-selected-files"},ms={key:0,class:"more-line"},ks={key:1},ws={class:"run-row"},Is=["disabled"],bs=["disabled"],Ss={class:"loading-msg"},Ts={key:0,class:"queue-debug-card"},Es={key:0,class:"queue-debug-line"},Cs={key:1,class:"queue-debug-payload"},Fs={class:"right-panel"},Rs={class:"task-list-wrap"},Ns={class:"clean-result-summary"},Ps={class:"summary-card"},xs={class:"summary-card"},$s={class:"summary-card"},As={class:"result-list-wrap"},Ds={key:0,class:"empty-tasks"},Ms={key:0,class:"result-subsection"},Ls={class:"task-list clean-result-list"},Bs={class:"left split-result-main"},qs=["title"],Us={class:"files"},Os={class:"files"},Ks={key:0,class:"files"},Hs={key:1,class:"files"},Gs={key:2,class:"files"},Js={key:3,class:"time"},js={key:4,class:"delete-brand-progress-block"},zs={class:"delete-brand-progress-header"},Vs={class:"delete-brand-progress-bar"},Xs={class:"files delete-brand-progress"},Qs={key:5,class:"files split-entry-list delete-brand-preview"},Ws={key:6,class:"files"},Ys={class:"task-right split-result-actions"},Zs=["onClick"],et=["onClick"],st={key:1,class:"result-subsection"},tt={class:"task-list clean-result-list"},at={class:"left split-result-main"},nt=["title"],lt={class:"files"},rt={class:"files"},ot={key:0,class:"files"},ut={key:1,class:"files"},it={key:2,class:"time"},ct={key:3,class:"delete-brand-progress-block"},dt={class:"delete-brand-progress-header"},ft={class:"delete-brand-progress-bar"},vt={class:"files delete-brand-progress"},ht={key:4,class:"files split-entry-list delete-brand-preview"},pt={key:5,class:"files"},_t={class:"task-right split-result-actions"},yt=["onClick"],gt=["onClick"],mt=Ye({__name:"BrandDeleteBrandTab",setup(wt){function $(){return`delete-brand:current-tasks:${typeof window<"u"&&window.localStorage.getItem("uid")||"0"}`}const A=y([]),j=y([]),L=y(!1),ve=y([]),i=y([]),B=y([]),z=y({total:0,successCount:0,failedCount:0,items:[]}),b=y(""),S=y(""),m=y({}),T=y(null),ee=y(!1),V=y(!1),C=y(!1),D=y(""),X=y(!1),q=y(null),se=y(!1),te=G(()=>A.value.slice(0,10)),F=G(()=>{const e=[];return i.value.forEach(s=>{e.push(...s.items)}),e});function Le(){try{const e=typeof window<"u"?window.localStorage.getItem($()):null;if(e){if(i.value=JSON.parse(e),i.value.length>0){const s=i.value[i.value.length-1];b.value=s.queuePushResult||"",S.value=s.queuePayloadText||""}}else i.value=[]}catch{i.value=[]}}function ae(e,s,t,a){if(!s?.length)return;const l=i.value.findIndex(c=>c.taskId===e),n=l>=0?i.value[l]:null,o=s.map(c=>{const _=n?.items.find(We=>M(We,c)),k=Array.isArray(c.countries)?c.countries:void 0,g=Array.isArray(_?.countries)?_?.countries:void 0,w=k&&k.length>0?k:g&&g.length>0?g:k??g,I=Array.isArray(c.previewRows)?c.previewRows:void 0,E=Array.isArray(_?.previewRows)?_?.previewRows:void 0,de=I&&I.length>0?I:E&&E.length>0?E:I??E;return{...c,countries:w,previewRows:de,_pushed:_?._pushed??c._pushed??!1,_completed:_?._completed??c._completed??!1}}),u={taskId:e,items:o,createdAt:n?.createdAt||Date.now(),queuePushResult:t??n?.queuePushResult,queuePayloadText:a??n?.queuePayloadText};l>=0?i.value[l]=u:i.value.push(u),typeof window<"u"&&window.localStorage.setItem($(),JSON.stringify(i.value))}function he(e){const s=i.value.length;i.value=i.value.filter(t=>t.taskId!==e),i.value.length!==s&&typeof window<"u"&&window.localStorage.setItem($(),JSON.stringify(i.value))}function pe(e){return e==="SUCCESS"||e==="FAILED"||e==="COMPLETED"}function Be(e){if(!e?.length)return!1;const s=new Set;if(e.forEach(a=>{a.taskId&&pe(a.taskStatus)&&s.add(a.taskId)}),!s.size)return!1;const t=i.value.length;return i.value=i.value.filter(a=>!s.has(a.taskId)),i.value.length!==t?(ne(),!0):!1}const Q=G(()=>B.value.filter(e=>!F.value.some(s=>M(s,e)))),qe=G(()=>F.value.length>0||Q.value.length>0),Ue=G(()=>F.value.filter(e=>Pe(e)&&R(e)==="未入队"));function ne(){typeof window<"u"&&(i.value.length===0?window.localStorage.removeItem($()):window.localStorage.setItem($(),JSON.stringify(i.value)))}function N(e){return e?(i.value.find(t=>t.taskId===e)?.items.length||0)>1:!1}function le(e){return e&&i.value.find(s=>s.taskId===e)||null}function M(e,s){return!e||!s?!1:e.resultId!=null&&s.resultId!=null?e.resultId===s.resultId:e.fileKey&&s.fileKey?e.fileKey===s.fileKey:e.sourceFilename===s.sourceFilename}function P(e,s){const t=le(e);return t&&t.items.find(a=>M(a,s))||null}function _e(e){if(!e)return!1;const s=le(e);if(!s)return!1;let t=!1;const a=m.value[e]?.line_progress?.info,l=N(e);if(a?.file_name&&a.current_line!=null&&a.total_lines!=null&&a.total_lines>0&&a.current_line>=a.total_lines){const n=s.items.find(o=>o.sourceFilename===a.file_name);n&&!n._completed&&(n._completed=!0,t=!0)}if(!l){const n=m.value[e]?.task?.status;if((n==="SUCCESS"||n==="FAILED")&&s.items.length===1){const o=s.items[0];o&&!o._completed&&(o._completed=!0,t=!0)}}return t&&ne(),t}function ye(e){return e&&m.value[e]?.task?.status||""}function W(e){if(e.error)return e.error;if(e.matchStatus&&e.matchStatus!=="MATCHED")return e.matchMessage||"";const s=e.taskId;return s&&m.value[s]?.task?.errorMessage||""}function ge(e){const s=e.taskId;if(!s)return!1;const t=m.value[s]?.line_progress,a=ye(s),l=N(s),n=R(e);if(l){const o=P(s,e),u=K(e)===D.value,c=!!(t?.has_progress&&t?.info?.file_name===e.sourceFilename);return u||c||o?._pushed||(a==="RUNNING"||a==="PENDING"||!a)&&o?._completed?!0:{success:!1,retryable:!0}}return n==="本文件解析完毕"||n==="已被全局判定为终态"?!1:t?.has_progress||t?.info?!0:n==="已入队"||n==="处理中"}function me(e){return e.map(s=>s.matched||s.matchStatus==="MATCHED"||s.matchStatus==="INDEX_STALE"?{...s,_pushed:!1,_completed:!1}:{...s,matched:!1,openStoreUrl:void 0,error:s.error||void 0,_pushed:!1,_completed:!1})}function ke(e){return e.matched&&e.matchStatus==="INDEX_STALE"?e.matchMessage||`已匹配 ${e.shopId||""}(索引过期)`.trim():e.matchStatus==="MATCHED"?`已匹配 ${e.shopId||""}`.trim():e.matchMessage?e.matchMessage:e.matchStatus==="CONFLICT"?"存在多个同名店铺,请人工确认":e.matchStatus==="PENDING"?"店铺索引暂未就绪":e.matchStatus?e.matchStatus:e.matched?`已匹配 ${e.shopId||""}`.trim():"待匹配"}function Oe(){ve.value=[...F.value,...Q.value]}function x(){Oe(),Ke(ve.value)}function Ke(e){z.value={total:e.length,successCount:e.filter(s=>s.success).length,failedCount:e.filter(s=>!s.success).length,items:e}}function re(e){const s=e.taskId;return s&&(m.value[s]?.fileProgress||[]).find(l=>l.fileKey&&e.fileKey?l.fileKey===e.fileKey:l.sourceFilename&&e.sourceFilename?l.sourceFilename===e.sourceFilename:!1)||null}function we(e){const s=e.taskId;if(!s)return"";const t=m.value[s],a=t?.line_progress?.info,l=re(e);if(N(s)){const u=P(s,e),c=t?.task?.status,_=!!(a?.file_name&&a.file_name===e.sourceFilename);let k=u?._completed?"COMPLETED":(u?._pushed,"PENDING"),g=0,w=Math.max(0,e.totalRows??l?.totalRows??0);c==="SUCCESS"||c==="FAILED"&&u?._completed?(k="COMPLETED",g=w):_?(k="RUNNING",g=Math.max(0,Math.min(w||Number.MAX_SAFE_INTEGER,a?.current_line??0)),!w&&a?.total_lines&&a.total_lines>0&&(w=a.total_lines)):u?._completed?(k="COMPLETED",g=w):u?._pushed&&(k="PENDING",g=0);const I=[];if(I.push(`文件状态:${k}`),I.push(`文件进度:${g}/${w}`),a?.finished_files!=null){const E=a.file_total&&a.file_total>0?`/${a.file_total}`:"";I.push(`已完成文件:${a.finished_files}${E}`)}return a?.phase&&I.push(`阶段:${a.phase}`),I.join("|")}if(l){const u=[],c=l.processedRows??0,_=l.totalRows??0;if(l.status&&u.push(`文件状态:${l.status}`),u.push(`文件进度:${c}/${_}`),a?.finished_files!=null){const k=a.file_total&&a.file_total>0?`/${a.file_total}`:"";u.push(`已完成文件:${a.finished_files}${k}`)}return a?.phase&&u.push(`阶段:${a.phase}`),u.join("|")}if(!t?.line_progress?.has_progress||!a){const u=t?.task?.status;return u?`任务状态:${u}`:""}const o=[];if(a.phase&&o.push(`阶段:${a.phase}`),a.file_index&&a.file_total&&o.push(`文件:${a.file_index}/${a.file_total}`),a.file_name&&o.push(`当前文件:${a.file_name}`),a.current_line!=null&&a.total_lines!=null&&o.push(`${N(s)?"当前文件进度":"进度"}:${a.current_line}/${a.total_lines}`),a.current_country&&o.push(`国家:${a.current_country}`),a.current_asin&&o.push(`ASIN:${a.current_asin}`),a.finished_files!=null){const u=a.file_total&&a.file_total>0?`/${a.file_total}`:"";o.push(`已完成文件:${a.finished_files}${u}`)}return o.join("|")}function Y(e){const s=e.taskId;if(!s)return 0;const t=m.value[s]?.line_progress?.info;if(N(s)){const o=P(s,e);if(m.value[s]?.task?.status==="SUCCESS"||o?._completed)return 100;const c=Math.max(0,e.totalRows??re(e)?.totalRows??0);if(!!(t?.file_name&&t.file_name===e.sourceFilename)){if(t?.current_line!=null&&t?.total_lines&&t.total_lines>0)return Math.max(0,Math.min(100,Math.round(t.current_line/t.total_lines*100)));if(t?.current_line!=null&&c>0)return Math.max(0,Math.min(100,Math.round(t.current_line/c*100)))}return 0}const l=re(e);return l?Math.max(0,Math.min(100,l.percent??0)):t?t.current_line!=null&&t.total_lines&&t.total_lines>0?Math.max(0,Math.min(100,Math.round(t.current_line/t.total_lines*100))):t.finished_files!=null&&t.file_total&&t.file_total>0?Math.max(0,Math.min(100,Math.round(t.finished_files/t.file_total*100))):m.value[s]?.task?.status==="SUCCESS"?100:0:0}function Ie(e){const s=m.value[e]?.task?.status;return pe(s)}function U(){const e=new Set;return i.value.forEach(s=>{s.items.some(t=>t._pushed)&&e.add(s.taskId)}),Array.from(e)}function R(e){const s=e.taskId;if(!s||!le(s))return"";const a=P(s,e),l=ye(s),n=m.value[s]?.line_progress,o=N(s);if(!o&&(l==="SUCCESS"||l==="FAILED"||e.taskStatus==="SUCCESS"||e.taskStatus==="FAILED"))return"已被全局判定为终态";if(a?._completed)return"本文件解析完毕";if(l==="RUNNING"&&n?.has_progress&&n?.info?.file_name===e.sourceFilename)return"处理中";if(o&&!a?._pushed&&!a?._completed)return"未入队";if(o&&l==="SUCCESS")return"本文件解析完毕";if(!o){const u=m.value[s];if(u?.items&&u.items.find(_=>M(_,e))&&l==="SUCCESS")return"本文件解析完毕"}return a?._pushed?"已入队":"未入队"}function be(e){return!!(e.downloadUrl||e.fileReady)}function He(e){const s=e?.task?.id;if(!s)return!1;const t=i.value.find(o=>o.taskId===s);if(!t)return{success:!1,retryable:!1};const a=e?.items||[];let l=!1;const n=t.items.map(o=>{const u=a.find(de=>M(de,o));if(!u)return o;const c=Array.isArray(u.countries)?u.countries:void 0,_=Array.isArray(o.countries)?o.countries:void 0,k=c&&c.length>0?c:_&&_.length>0?_:c??_,g=Array.isArray(u.previewRows)?u.previewRows:void 0,w=Array.isArray(o.previewRows)?o.previewRows:void 0,I=g&&g.length>0?g:w&&w.length>0?w:g??w,E={...o,...u,countries:k,previewRows:I,_pushed:o._pushed,_completed:o._completed};return JSON.stringify(E)!==JSON.stringify(o)&&(l=!0),E});for(const o of a)n.some(u=>M(u,o))||(n.push({...o,_pushed:!1,_completed:!1}),l=!0);return JSON.stringify(n)!==JSON.stringify(t.items)&&(t.items=n,l=!0),l&&ne(),l}async function oe(e){const s=e||U();if(s.length)try{const t=await ns(s);se.value&&C.value&&(b.value="后端已恢复,继续执行当前任务并自动衔接后续任务..."),se.value=!1;let a=!1;for(const l of t.items||[]){const n=l?.task?.id;typeof n=="number"&&n>0&&(m.value[n]=l,He(l)&&(a=!0),_e(n)&&(a=!0))}a&&x()}catch(t){const a=(t instanceof Error?t.message:String(t||"")).toLowerCase();["无法连接到后端服务","bad gateway","gateway timeout","network error","timeout","502","503","504"].some(n=>a.includes(n.toLowerCase()))&&C.value&&(se.value=!0,b.value="当前任务运行中,正在等待后端服务恢复...")}}function Ge(){return is()}function ue(e=!1){if(T.value){if(!e)return;clearTimeout(T.value),T.value=null}const s=async()=>{if(T.value=null,ee.value){ue();return}const t=U();if(!t.length)return;ee.value=!0;let a=!1,l=!1;try{await oe(t),await Ae();for(const n of t)Ie(n)&&(he(n),a=!0,l=!0)}finally{ee.value=!1}l?await O():a&&x(),U().length>0&&ue()};e?s():T.value=window.setTimeout(s,Ge())}function ie(e=!1){T.value&&!e||ue(e)}function Z(e){const s=e.taskId?m.value[e.taskId]?.task?.status:null;return s==="SUCCESS"?{className:"success",text:"已完成"}:s==="FAILED"?{className:"failed",text:"失败"}:s==="RUNNING"?{className:"running",text:"执行中"}:e.taskStatus==="SUCCESS"?{className:"success",text:"已完成"}:e.taskStatus==="FAILED"?{className:"failed",text:"失败"}:e.taskStatus==="RUNNING"?{className:"running",text:"执行中"}:e.success?{className:"success",text:"已完成"}:{className:"failed",text:"失败"}}function Se(){T.value&&(window.clearTimeout(T.value),T.value=null),ce()}async function Te(e){const s=J();if(!s?.upload_file_to_java)throw new Error("当前桌面端未提供文件上传桥接能力");const t=[];for(const a of e){const l=typeof a=="string"?a:a.absolutePath,n=typeof a=="string"?void 0:a.relativePath,o=await s.upload_file_to_java(l,n);if(!o?.success||!o.data)throw new Error(o?.error||o?.message||`上传失败:${l}`);t.push(o.data)}return t}async function Je(){const e=J();if(!e?.select_brand_xlsx_files){p.warning("当前环境不支持文件选择,请在本机客户端中打开");return}try{const s=await e.select_brand_xlsx_files();if(!s?.length)return;A.value=s,j.value=await Te(s),p.success(`已选择 ${s.length} 个删除品牌文件`)}catch(s){p.error(s instanceof Error?s.message:"选择失败")}}async function je(){const e=J();if(!e?.select_brand_folder){p.warning("当前环境不支持文件夹选择,请在本机客户端中打开");return}try{const s=await e.select_brand_folder();if(!s)return;const t=await vs(s);if(!t.success||!t.items?.length){p.warning(t.error||"该文件夹下没有 xlsx 文件");return}A.value=t.items.map(a=>a.relativePath||a.absolutePath),j.value=await Te(t.items),p.success(`已选择文件夹内 ${t.items.length} 个 xlsx 文件`)}catch(s){p.error(s instanceof Error?s.message:"选择失败")}}async function ze(){if(!j.value.length){p.warning("请先选择待处理文件");return}try{L.value=!0;const e=await ls({files:j.value.map(n=>({fileKey:n.fileKey,originalFilename:n.originalFilename,relativePath:n.relativePath}))}),s=me(e.items||[]),t=s.some(n=>n.matchStatus==="MATCHED"||n.matchStatus==="INDEX_STALE"||n.matched),a=s.some(n=>n.matchStatus==="INDEX_STALE"&&n.matched),l=s.some(n=>!n.matched&&n.matchStatus!=="MATCHED"&&n.matchStatus!=="INDEX_STALE");b.value=t?"解析完成,可在左侧点击“推送到 Python 队列”开始串行处理":"解析完成,当前没有可推送的文件",S.value="",s.length&&s[0].taskId&&ae(s[0].taskId,s,b.value,S.value),x(),l?p.warning("部分文件尚未命中可用店铺索引,已保留状态信息,请等待后台刷新后重试。"):a?p.warning("部分文件命中的是过期索引,仍可推送到 Python 队列;后台刷新后会自动重新匹配。"):t?p.success("删除品牌解析完成,请在左侧推送到 Python 队列"):p.warning("当前没有可推送的文件,请先等待店铺索引刷新。"),await O()}catch(e){p.error(e instanceof Error?e.message:"执行失败")}finally{L.value=!1}}async function Ee(e){const s=e.taskId;if(!s){p.warning("未找到任务ID");return}const t=J(),a=rs(s),l=e.outputFilename||e.sourceFilename||`delete-brand_${s}.xlsx`;if(t?.save_file_from_url_new){const n=await t.save_file_from_url_new(a,l);n.success?p.success(`已保存:${n.path||l}`):n.error&&n.error!=="用户取消"&&p.error(n.error);return}}async function O(){try{const e=await os();B.value=me(e.items||[]),Be(B.value),x(),await oe(),ie(!0)}catch(e){const s=(e instanceof Error?e.message:String(e||"")).toLowerCase();["无法连接到后端服务","bad gateway","gateway timeout","network error","timeout","502","503","504"].some(a=>s.includes(a.toLowerCase()))||console.error("loadHistory error:",e)}}function Ce(e){i.value.forEach(t=>{t.items=t.items.filter(a=>a.resultId!==e)});const s=i.value.length;i.value=i.value.filter(t=>t.items.length>0),i.value.length!==s&&typeof window<"u"&&window.localStorage.setItem($(),JSON.stringify(i.value)),B.value=B.value.filter(t=>t.resultId!==e),x(),U().length===0&&Se()}async function Fe(e){try{await us(e),Ce(e),await O(),p.success("已删除")}catch(s){const t=s instanceof Error?s.message:"删除失败";if(typeof t=="string"&&t.includes("记录不存在")){Ce(e),p.success("已删除");return}p.error(t)}}function Re(e){const s=e.slice(0,5).map(a=>`${a.country}:${a.asin}${a.status?`(${a.status})`:""}`);return e.length-s.length>0?`${s.join("、")} 等 ${e.length} 条`:s.join("、")}function It(e,s){}function ce(){q.value&&(window.clearTimeout(q.value),q.value=null)}function Ve(e=3e3){ce(),q.value=window.setTimeout(()=>{q.value=null,Ae().catch(()=>{})},e)}function Xe(e){return e.includes("当前店铺正在执行中")}function Ne(){for(const e of i.value)for(const s of e.items){const t=R(s);if(t==="已入队"||t==="处理中")return!0}return!1}function K(e){return e.resultId!=null?`result:${e.resultId}`:`task:${e.taskId||0}:${e.sourceFilename||""}`}function Pe(e){return!!(e.taskId&&(e.matchStatus==="MATCHED"||e.matchStatus==="INDEX_STALE"||e.matched))}function xe(){return F.value.find(e=>!Pe(e)||K(e)===D.value||P(e.taskId,e)?._completed?!1:R(e)==="未入队")}async function $e(e,s){if(s?.auto&&Ne())return e.taskId,e.sourceFilename,{success:!1,retryable:!0};const t=J(),a=e.taskId;if(!a)return{success:!1,retryable:!1};const l=i.value.find(c=>c.taskId===a);if(!l)return{success:!1,retryable:!1};if(!t?.enqueue_json)return p.error("当前环境未启用 pywebview enqueue_json(浏览器环境不会推送)"),{success:!1,retryable:!1};const n={type:"delete-brand-run",ts:Date.now(),data:{taskId:a,items:[e]}};S.value=JSON.stringify(n,null,2);const o=await t.enqueue_json(n);let u="";if(o?.success){u=`已推送文件 ${e.sourceFilename},当前队列长度:${o.queue_size??"-"}`;const c=P(a,e);return c&&(c._pushed=!0,c._completed=!1),D.value=K(e),ae(a,l.items,u,S.value),ie(!0),b.value=u,{success:!0,retryable:!1}}else{const c=o?.error||"未知错误";if(u=`推送到 Python 队列失败:${c}`,b.value=u,ae(a,l.items,u,S.value),s?.auto&&Xe(c))return{success:!1,retryable:!0}}return b.value=u,{success:!1,retryable:!1}}async function Qe(){const e=xe();if(!e){p.warning("当前没有可推送的文件");return}V.value=!0,C.value=!0,ce();const s=await $e(e);if(V.value=!1,!s.success){C.value=!1,D.value="";return}p.success("已开始按顺序推送到 Python 队列")}async function Ae(){if(!C.value||X.value){C.value,X.value;return}if(Ne())return;const e=D.value;if(e){const t=F.value.find(a=>K(a)===e);if(t){const a=t.taskId,l=N(a);l&&(_e(a),R(t),void 0);const n=R(t);if(n==="已入队"||n==="处理中"){t.sourceFilename;return}if(l){const o=P(a,t);if(o&&!o._completed){t.sourceFilename,o._pushed,o._completed;return}}}}const s=xe();if(!s){D.value="",C.value=!1;return}X.value=!0;try{s.taskId,s.sourceFilename,K(s);const t=await $e(s,{auto:!0});if(!t.success){if(t.retryable){s.taskId,s.sourceFilename,Ve();return}s.taskId,s.sourceFilename,C.value=!1}}finally{X.value=!1}}return Ze(()=>{Le();const e=U();e.length>0?oe(e).then(async()=>{let s=!1,t=!1;for(const a of e)Ie(a)&&(he(a),s=!0,t=!0);t?await O():s&&x(),ie(!0)}):x(),O().catch(()=>{})}),es(()=>{Se()}),(e,s)=>(d(),f("div",hs,[ss(ts,{active:"delete-brand"}),r("div",ps,[r("aside",_s,[s[2]||(s[2]=r("div",{class:"section-title"},"选择文件",-1)),r("div",ys,[s[0]||(s[0]=r("div",{class:"hint"},"上传删除品牌 Excel;无论是直接上传文件还是上传文件夹批量导入,都会按每个 Excel 文件名作为店铺名。",-1)),r("div",{class:"btns"},[r("button",{type:"button",class:"opt-btn",onClick:Je},"选择 Excel 文件"),r("button",{type:"button",class:"opt-btn",onClick:je},"选择文件夹")]),r("div",gs,[A.value.length?(d(),f(H,{key:0},[(d(!0),f(H,null,fe(te.value,t=>(d(),f("span",{key:t},v(t),1))),128)),A.value.length>te.value.length?(d(),f("span",ms," 还有 "+v(A.value.length-te.value.length)+" 个文件未展开显示 ",1)):h("",!0)],64)):(d(),f("span",ks,"暂未选择删除品牌文件"))])]),s[3]||(s[3]=as('
执行说明
',2)),r("div",ws,[r("button",{type:"button",class:"btn-run",disabled:L.value,onClick:ze},v(L.value?"解析中...":"开始解析"),9,Is),r("button",{type:"button",class:"btn-run btn-queue",disabled:V.value||!Ue.value.length,onClick:Qe},v(V.value?"推送中...":"推送到 Python 队列"),9,bs),r("span",Ss,v(L.value?"正在读取删除品牌数据,请稍候…":"解析后会在右侧展示按国家分组的去重结果"),1)]),b.value||S.value?(d(),f("div",Ts,[s[1]||(s[1]=r("div",{class:"section-title queue-debug-title"},"Python 队列推送结果",-1)),b.value?(d(),f("div",Es,v(b.value),1)):h("",!0),S.value?(d(),f("pre",Cs,v(S.value),1)):h("",!0)])):h("",!0)]),r("section",Fs,[s[12]||(s[12]=r("div",{class:"panel-header"},"删除品牌结果",-1)),r("div",Rs,[r("div",Ns,[r("div",Ps,[s[4]||(s[4]=r("span",{class:"summary-label"},"已处理文件",-1)),r("strong",null,v(z.value.total),1)]),r("div",xs,[s[5]||(s[5]=r("span",{class:"summary-label"},"成功结果",-1)),r("strong",null,v(z.value.successCount),1)]),r("div",$s,[s[6]||(s[6]=r("span",{class:"summary-label"},"失败文件",-1)),r("strong",null,v(z.value.failedCount),1)])]),r("div",As,[s[11]||(s[11]=r("div",{class:"result-list-header"},[r("span",null,"删除品牌结果列表")],-1)),qe.value?(d(),f(H,{key:1},[F.value.length?(d(),f("div",Ms,[s[8]||(s[8]=r("div",{class:"result-subsection-title"},"当前任务",-1)),r("ul",Ls,[(d(!0),f(H,null,fe(F.value,t=>(d(),f("li",{key:`current-${t.taskId||t.resultId||t.sourceFilename}`,class:"task-item split-result-item"},[r("div",Bs,[r("span",{class:"id",title:t.sourceFilename},v(t.sourceFilename||"-"),9,qs),r("div",Us,"店铺名:"+v(t.shopName||"-"),1),r("div",Os,"匹配结果:"+v(ke(t)),1),t.platform?(d(),f("div",Ks,"平台:"+v(t.platform),1)):h("",!0),R(t)?(d(),f("div",Hs,"队列状态:"+v(R(t)),1)):h("",!0),t.countryCount!==void 0&&t.matched?(d(),f("div",Gs,"国家数:"+v(t.countryCount),1)):h("",!0),t.totalRows!==void 0&&t.matched?(d(),f("div",Js,"去重后 "+v(t.totalRows)+" 条 ",1)):h("",!0),ge(t)?(d(),f("div",js,[r("div",zs,[s[7]||(s[7]=r("span",null,"任务进度",-1)),r("span",null,v(Y(t))+"%",1)]),r("div",Vs,[r("div",{class:"delete-brand-progress-bar-fill",style:De({width:`${Y(t)}%`})},null,4)]),r("div",Xs,v(we(t)),1)])):h("",!0),t.previewRows?.length?(d(),f("div",Qs,v(Re(t.previewRows)),1)):h("",!0),W(t)?(d(),f("div",Ws,"错误信息:"+v(W(t)),1)):h("",!0)]),r("div",Ys,[r("span",{class:Me(["status",Z(t).className])},v(Z(t).text),3),be(t)?(d(),f("button",{key:0,type:"button",class:"download",onClick:a=>Ee(t)}," 下载 ",8,Zs)):h("",!0),t.resultId?(d(),f("button",{key:1,type:"button",class:"btn-delete",onClick:a=>Fe(t.resultId)}," 删除 ",8,et)):h("",!0)])]))),128))])])):h("",!0),Q.value.length?(d(),f("div",st,[s[10]||(s[10]=r("div",{class:"result-subsection-title"},"历史记录",-1)),r("ul",tt,[(d(!0),f(H,null,fe(Q.value,t=>(d(),f("li",{key:`history-${t.taskId||t.resultId||t.sourceFilename}`,class:"task-item split-result-item"},[r("div",at,[r("span",{class:"id",title:t.sourceFilename},v(t.sourceFilename||"-"),9,nt),r("div",lt,"店铺名:"+v(t.shopName||"-"),1),r("div",rt,"匹配结果:"+v(ke(t)),1),t.platform?(d(),f("div",ot,"平台:"+v(t.platform),1)):h("",!0),t.countryCount!==void 0&&t.matched?(d(),f("div",ut,"国家数:"+v(t.countryCount),1)):h("",!0),t.totalRows!==void 0&&t.matched?(d(),f("div",it,"去重后 "+v(t.totalRows)+" 条 ",1)):h("",!0),ge(t)?(d(),f("div",ct,[r("div",dt,[s[9]||(s[9]=r("span",null,"任务进度",-1)),r("span",null,v(Y(t))+"%",1)]),r("div",ft,[r("div",{class:"delete-brand-progress-bar-fill",style:De({width:`${Y(t)}%`})},null,4)]),r("div",vt,v(we(t)),1)])):h("",!0),t.previewRows?.length?(d(),f("div",ht,v(Re(t.previewRows)),1)):h("",!0),W(t)?(d(),f("div",pt,"错误信息:"+v(W(t)),1)):h("",!0)]),r("div",_t,[r("span",{class:Me(["status",Z(t).className])},v(Z(t).text),3),be(t)?(d(),f("button",{key:0,type:"button",class:"download",onClick:a=>Ee(t)}," 下载 ",8,yt)):h("",!0),t.resultId?(d(),f("button",{key:1,type:"button",class:"btn-delete",onClick:a=>Fe(t.resultId)}," 删除 ",8,gt)):h("",!0)])]))),128))])])):h("",!0)],64)):(d(),f("div",Ds," 暂无删除品牌结果,完成解析后会在这里展示按国家分组的去重结果 "))])])])])]))}}),kt=cs(mt,[["__scopeId","data-v-248c8db1"]]);ds(kt).use(fs).mount("#app"); -======= -import{d as Ye,o as Ze,I as es,a as d,c as f,b as ss,B as ts,e as l,F as H,r as fe,t as v,f as h,J as ns,K as De,n as Me,g as y,h as J,L as as,i as G,E as p,M as ls,N as rs,O as os,P as us,Q as is,_ as cs,p as ds,q as fs}from"./pywebview-Bt854mYs.js";import{e as vs}from"./brand-5-W0Wonr.js";const hs={class:"page-shell module-page"},ps={class:"main-content"},_s={class:"left-panel"},ys={class:"upload-zone"},gs={class:"selected-files clean-placeholder split-selected-files"},ms={key:0,class:"more-line"},ks={key:1},ws={class:"run-row"},Is=["disabled"],bs=["disabled"],Ss={class:"loading-msg"},Ts={key:0,class:"queue-debug-card"},Cs={key:0,class:"queue-debug-line"},Es={key:1,class:"queue-debug-payload"},Fs={class:"right-panel"},Ps={class:"task-list-wrap"},Rs={class:"clean-result-summary"},xs={class:"summary-card"},Ns={class:"summary-card"},$s={class:"summary-card"},As={class:"result-list-wrap"},Ds={key:0,class:"empty-tasks"},Ms={key:0,class:"result-subsection"},Bs={class:"task-list clean-result-list"},qs={class:"left split-result-main"},Ls=["title"],Us={class:"files"},Os={class:"files"},Ks={key:0,class:"files"},Hs={key:1,class:"files"},Js={key:2,class:"files"},Gs={key:3,class:"time"},js={key:4,class:"delete-brand-progress-block"},zs={class:"delete-brand-progress-header"},Vs={class:"delete-brand-progress-bar"},Qs={class:"files delete-brand-progress"},Xs={key:5,class:"files split-entry-list delete-brand-preview"},Ws={key:6,class:"files"},Ys={class:"task-right split-result-actions"},Zs=["onClick"],et=["onClick"],st={key:1,class:"result-subsection"},tt={class:"task-list clean-result-list"},nt={class:"left split-result-main"},at=["title"],lt={class:"files"},rt={class:"files"},ot={key:0,class:"files"},ut={key:1,class:"files"},it={key:2,class:"time"},ct={key:3,class:"delete-brand-progress-block"},dt={class:"delete-brand-progress-header"},ft={class:"delete-brand-progress-bar"},vt={class:"files delete-brand-progress"},ht={key:4,class:"files split-entry-list delete-brand-preview"},pt={key:5,class:"files"},_t={class:"task-right split-result-actions"},yt=["onClick"],gt=["onClick"],mt=Ye({__name:"BrandDeleteBrandTab",setup(wt){function $(){return`delete-brand:current-tasks:${typeof window<"u"&&window.localStorage.getItem("uid")||"0"}`}const A=y([]),j=y([]),B=y(!1),ve=y([]),i=y([]),q=y([]),z=y({total:0,successCount:0,failedCount:0,items:[]}),b=y(""),S=y(""),g=y({}),T=y(null),ee=y(!1),V=y(!1),E=y(!1),D=y(""),Q=y(!1),L=y(null),se=y(!1),te=J(()=>A.value.slice(0,10)),F=J(()=>{const e=[];return i.value.forEach(s=>{e.push(...s.items)}),e});function Be(){try{const e=typeof window<"u"?window.localStorage.getItem($()):null;if(e){if(i.value=JSON.parse(e),i.value.length>0){const s=i.value[i.value.length-1];b.value=s.queuePushResult||"",S.value=s.queuePayloadText||""}}else i.value=[]}catch{i.value=[]}}function ne(e,s,t,n){if(!s?.length)return;const a=i.value.findIndex(c=>c.taskId===e),o=a>=0?i.value[a]:null,r=s.map(c=>{const _=o?.items.find(We=>M(We,c)),k=Array.isArray(c.countries)?c.countries:void 0,m=Array.isArray(_?.countries)?_?.countries:void 0,w=k&&k.length>0?k:m&&m.length>0?m:k??m,I=Array.isArray(c.previewRows)?c.previewRows:void 0,C=Array.isArray(_?.previewRows)?_?.previewRows:void 0,de=I&&I.length>0?I:C&&C.length>0?C:I??C;return{...c,countries:w,previewRows:de,_pushed:_?._pushed??c._pushed??!1,_completed:_?._completed??c._completed??!1}}),u={taskId:e,items:r,createdAt:o?.createdAt||Date.now(),queuePushResult:t??o?.queuePushResult,queuePayloadText:n??o?.queuePayloadText};a>=0?i.value[a]=u:i.value.push(u),typeof window<"u"&&window.localStorage.setItem($(),JSON.stringify(i.value))}function he(e){const s=i.value.length;i.value=i.value.filter(t=>t.taskId!==e),i.value.length!==s&&typeof window<"u"&&window.localStorage.setItem($(),JSON.stringify(i.value))}function pe(e){return e==="SUCCESS"||e==="FAILED"||e==="COMPLETED"}function qe(e){if(!e?.length)return!1;const s=new Set;if(e.forEach(n=>{n.taskId&&pe(n.taskStatus)&&s.add(n.taskId)}),!s.size)return!1;const t=i.value.length;return i.value=i.value.filter(n=>!s.has(n.taskId)),i.value.length!==t?(ae(),!0):!1}const X=J(()=>q.value.filter(e=>!F.value.some(s=>M(s,e)))),Le=J(()=>F.value.length>0||X.value.length>0),Ue=J(()=>F.value.filter(e=>xe(e)&&P(e)==="未入队"));function ae(){typeof window<"u"&&(i.value.length===0?window.localStorage.removeItem($()):window.localStorage.setItem($(),JSON.stringify(i.value)))}function R(e){return e?(i.value.find(t=>t.taskId===e)?.items.length||0)>1:!1}function le(e){return e&&i.value.find(s=>s.taskId===e)||null}function M(e,s){return!e||!s?!1:e.resultId!=null&&s.resultId!=null?e.resultId===s.resultId:e.fileKey&&s.fileKey?e.fileKey===s.fileKey:e.sourceFilename===s.sourceFilename}function x(e,s){const t=le(e);return t&&t.items.find(n=>M(n,s))||null}function _e(e){if(!e)return!1;const s=le(e);if(!s)return!1;let t=!1;const n=g.value[e]?.line_progress?.info,a=R(e);if(n?.file_name&&n.current_line!=null&&n.total_lines!=null&&n.total_lines>0&&n.current_line>=n.total_lines){const o=s.items.find(r=>r.sourceFilename===n.file_name);o&&!o._completed&&(o._completed=!0,t=!0)}if(!a){const o=g.value[e]?.task?.status;if((o==="SUCCESS"||o==="FAILED")&&s.items.length===1){const r=s.items[0];r&&!r._completed&&(r._completed=!0,t=!0)}}return t&&ae(),t}function ye(e){return e&&g.value[e]?.task?.status||""}function W(e){if(e.error)return e.error;if(e.matchStatus&&e.matchStatus!=="MATCHED")return e.matchMessage||"";const s=e.taskId;return s&&g.value[s]?.task?.errorMessage||""}function ge(e){const s=e.taskId;if(!s)return!1;const t=g.value[s]?.line_progress,n=ye(s),a=R(s),o=P(e);if(a){const r=x(s,e),u=K(e)===D.value,c=!!(t?.has_progress&&t?.info?.file_name===e.sourceFilename);return u||c||r?._pushed||(n==="RUNNING"||n==="PENDING"||!n)&&r?._completed?!0:{success:!1,retryable:!0}}return o==="本文件解析完毕"||o==="已被全局判定为终态"?!1:t?.has_progress||t?.info?!0:o==="已入队"||o==="处理中"}function me(e){return e.map(s=>s.matchStatus==="MATCHED"?{...s,_pushed:!1,_completed:!1}:{...s,matched:!1,openStoreUrl:void 0,error:s.error||void 0,_pushed:!1,_completed:!1})}function ke(e){return e.matchStatus==="MATCHED"?`已匹配 ${e.shopId||""}`.trim():e.matchMessage?e.matchMessage:e.matchStatus==="CONFLICT"?"存在多个同名店铺,请人工确认":e.matchStatus==="PENDING"||e.matchStatus==="INDEX_STALE"?"店铺索引暂未就绪":e.matchStatus?e.matchStatus:e.matched?`已匹配 ${e.shopId||""}`.trim():"待匹配"}function Oe(){ve.value=[...F.value,...X.value]}function N(){Oe(),Ke(ve.value)}function Ke(e){z.value={total:e.length,successCount:e.filter(s=>s.success).length,failedCount:e.filter(s=>!s.success).length,items:e}}function re(e){const s=e.taskId;return s&&(g.value[s]?.fileProgress||[]).find(a=>a.fileKey&&e.fileKey?a.fileKey===e.fileKey:a.sourceFilename&&e.sourceFilename?a.sourceFilename===e.sourceFilename:!1)||null}function we(e){const s=e.taskId;if(!s)return"";const t=g.value[s],n=t?.line_progress?.info,a=re(e);if(R(s)){const u=x(s,e),c=t?.task?.status,_=!!(n?.file_name&&n.file_name===e.sourceFilename);let k=u?._completed?"COMPLETED":(u?._pushed,"PENDING"),m=0,w=Math.max(0,e.totalRows??a?.totalRows??0);c==="SUCCESS"||c==="FAILED"&&u?._completed?(k="COMPLETED",m=w):_?(k="RUNNING",m=Math.max(0,Math.min(w||Number.MAX_SAFE_INTEGER,n?.current_line??0)),!w&&n?.total_lines&&n.total_lines>0&&(w=n.total_lines)):u?._completed?(k="COMPLETED",m=w):u?._pushed&&(k="PENDING",m=0);const I=[];if(I.push(`文件状态:${k}`),I.push(`文件进度:${m}/${w}`),n?.finished_files!=null){const C=n.file_total&&n.file_total>0?`/${n.file_total}`:"";I.push(`已完成文件:${n.finished_files}${C}`)}return n?.phase&&I.push(`阶段:${n.phase}`),I.join("|")}if(a){const u=[],c=a.processedRows??0,_=a.totalRows??0;if(a.status&&u.push(`文件状态:${a.status}`),u.push(`文件进度:${c}/${_}`),n?.finished_files!=null){const k=n.file_total&&n.file_total>0?`/${n.file_total}`:"";u.push(`已完成文件:${n.finished_files}${k}`)}return n?.phase&&u.push(`阶段:${n.phase}`),u.join("|")}if(!t?.line_progress?.has_progress||!n){const u=t?.task?.status;return u?`任务状态:${u}`:""}const r=[];if(n.phase&&r.push(`阶段:${n.phase}`),n.file_index&&n.file_total&&r.push(`文件:${n.file_index}/${n.file_total}`),n.file_name&&r.push(`当前文件:${n.file_name}`),n.current_line!=null&&n.total_lines!=null&&r.push(`${R(s)?"当前文件进度":"进度"}:${n.current_line}/${n.total_lines}`),n.current_country&&r.push(`国家:${n.current_country}`),n.current_asin&&r.push(`ASIN:${n.current_asin}`),n.finished_files!=null){const u=n.file_total&&n.file_total>0?`/${n.file_total}`:"";r.push(`已完成文件:${n.finished_files}${u}`)}return r.join("|")}function Y(e){const s=e.taskId;if(!s)return 0;const t=g.value[s]?.line_progress?.info;if(R(s)){const r=x(s,e);if(g.value[s]?.task?.status==="SUCCESS"||r?._completed)return 100;const c=Math.max(0,e.totalRows??re(e)?.totalRows??0);if(!!(t?.file_name&&t.file_name===e.sourceFilename)){if(t?.current_line!=null&&t?.total_lines&&t.total_lines>0)return Math.max(0,Math.min(100,Math.round(t.current_line/t.total_lines*100)));if(t?.current_line!=null&&c>0)return Math.max(0,Math.min(100,Math.round(t.current_line/c*100)))}return 0}const a=re(e);return a?Math.max(0,Math.min(100,a.percent??0)):t?t.current_line!=null&&t.total_lines&&t.total_lines>0?Math.max(0,Math.min(100,Math.round(t.current_line/t.total_lines*100))):t.finished_files!=null&&t.file_total&&t.file_total>0?Math.max(0,Math.min(100,Math.round(t.finished_files/t.file_total*100))):g.value[s]?.task?.status==="SUCCESS"?100:0:0}function Ie(e){const s=g.value[e]?.task?.status;return pe(s)}function U(){const e=new Set;return i.value.forEach(s=>{s.items.some(t=>t._pushed)&&e.add(s.taskId)}),Array.from(e)}function P(e){const s=e.taskId;if(!s||!le(s))return"";const n=x(s,e),a=ye(s),o=g.value[s]?.line_progress,r=R(s);if(!r&&(a==="SUCCESS"||a==="FAILED"||e.taskStatus==="SUCCESS"||e.taskStatus==="FAILED"))return"已被全局判定为终态";if(n?._completed)return"本文件解析完毕";if(a==="RUNNING"&&o?.has_progress&&o?.info?.file_name===e.sourceFilename)return"处理中";if(r&&!n?._pushed&&!n?._completed)return"未入队";if(r&&a==="SUCCESS")return"本文件解析完毕";if(!r){const u=g.value[s];if(u?.items&&u.items.find(_=>M(_,e))&&a==="SUCCESS")return"本文件解析完毕"}return n?._pushed?"已入队":"未入队"}function be(e){if(e.downloadUrl)return!0;const s=e.taskId;return s?g.value[s]?.task?.status==="SUCCESS":{success:!1,retryable:!1}}function He(e){const s=e?.task?.id;if(!s)return!1;const t=i.value.find(r=>r.taskId===s);if(!t)return{success:!1,retryable:!1};const n=e?.items||[];let a=!1;const o=t.items.map(r=>{const u=n.find(de=>M(de,r));if(!u)return r;const c=Array.isArray(u.countries)?u.countries:void 0,_=Array.isArray(r.countries)?r.countries:void 0,k=c&&c.length>0?c:_&&_.length>0?_:c??_,m=Array.isArray(u.previewRows)?u.previewRows:void 0,w=Array.isArray(r.previewRows)?r.previewRows:void 0,I=m&&m.length>0?m:w&&w.length>0?w:m??w,C={...r,...u,countries:k,previewRows:I,_pushed:r._pushed,_completed:r._completed};return JSON.stringify(C)!==JSON.stringify(r)&&(a=!0),C});for(const r of n)o.some(u=>M(u,r))||(o.push({...r,_pushed:!1,_completed:!1}),a=!0);return JSON.stringify(o)!==JSON.stringify(t.items)&&(t.items=o,a=!0),a&&ae(),a}async function oe(e){const s=e||U();if(s.length)try{const t=await as(s);se.value&&E.value&&(b.value="后端已恢复,继续执行当前任务并自动衔接后续任务..."),se.value=!1;let n=!1;for(const a of t.items||[]){const o=a?.task?.id;typeof o=="number"&&o>0&&(g.value[o]=a,He(a)&&(n=!0),_e(o)&&(n=!0))}n&&N()}catch(t){const n=(t instanceof Error?t.message:String(t||"")).toLowerCase();["无法连接到后端服务","bad gateway","gateway timeout","network error","timeout","502","503","504"].some(o=>n.includes(o.toLowerCase()))&&E.value&&(se.value=!0,b.value="当前任务运行中,正在等待后端服务恢复...")}}function Je(){return is()}function ue(e=!1){if(T.value){if(!e)return;clearTimeout(T.value),T.value=null}const s=async()=>{if(T.value=null,ee.value){ue();return}const t=U();if(!t.length)return;ee.value=!0;let n=!1,a=!1;try{await oe(t),await Ae();for(const o of t)Ie(o)&&(he(o),n=!0,a=!0)}finally{ee.value=!1}a?await O():n&&N(),U().length>0&&ue()};e?s():T.value=window.setTimeout(s,Je())}function ie(e=!1){T.value&&!e||ue(e)}function Z(e){const s=e.taskId?g.value[e.taskId]?.task?.status:null;return s==="SUCCESS"?{className:"success",text:"已完成"}:s==="FAILED"?{className:"failed",text:"失败"}:s==="RUNNING"?{className:"running",text:"执行中"}:e.taskStatus==="SUCCESS"?{className:"success",text:"已完成"}:e.taskStatus==="FAILED"?{className:"failed",text:"失败"}:e.taskStatus==="RUNNING"?{className:"running",text:"执行中"}:e.success?{className:"success",text:"已完成"}:{className:"failed",text:"失败"}}function Se(){T.value&&(window.clearTimeout(T.value),T.value=null),ce()}async function Te(e){const s=G();if(!s?.upload_file_to_java)throw new Error("当前桌面端未提供文件上传桥接能力");const t=[];for(const n of e){const a=typeof n=="string"?n:n.absolutePath,o=typeof n=="string"?void 0:n.relativePath,r=await s.upload_file_to_java(a,o);if(!r?.success||!r.data)throw new Error(r?.error||r?.message||`上传失败:${a}`);t.push(r.data)}return t}async function Ge(){const e=G();if(!e?.select_brand_xlsx_files){p.warning("当前环境不支持文件选择,请在本机客户端中打开");return}try{const s=await e.select_brand_xlsx_files();if(!s?.length)return;A.value=s,j.value=await Te(s),p.success(`已选择 ${s.length} 个删除品牌文件`)}catch(s){p.error(s instanceof Error?s.message:"选择失败")}}async function je(){const e=G();if(!e?.select_brand_folder){p.warning("当前环境不支持文件夹选择,请在本机客户端中打开");return}try{const s=await e.select_brand_folder();if(!s)return;const t=await vs(s);if(!t.success||!t.items?.length){p.warning(t.error||"该文件夹下没有 xlsx 文件");return}A.value=t.items.map(n=>n.relativePath||n.absolutePath),j.value=await Te(t.items),p.success(`已选择文件夹内 ${t.items.length} 个 xlsx 文件`)}catch(s){p.error(s instanceof Error?s.message:"选择失败")}}async function ze(){if(!j.value.length){p.warning("请先选择待处理文件");return}try{B.value=!0;const e=await ls({files:j.value.map(a=>({fileKey:a.fileKey,originalFilename:a.originalFilename,relativePath:a.relativePath}))}),s=me(e.items||[]),t=s.some(a=>a.matchStatus==="MATCHED"),n=s.some(a=>a.matchStatus&&a.matchStatus!=="MATCHED");b.value=t?"解析完成,可在左侧点击“推送到 Python 队列”开始串行处理":"解析完成,当前没有可推送的已匹配文件",S.value="",s.length&&s[0].taskId&&ne(s[0].taskId,s,b.value,S.value),N(),n?p.warning("部分文件尚未命中可用店铺索引,已保留状态信息,请等待后台刷新后重试。"):t?p.success("删除品牌解析完成,请在左侧推送到 Python 队列"):p.warning("当前没有可推送的已匹配文件,请先等待店铺索引刷新。"),await O()}catch(e){p.error(e instanceof Error?e.message:"执行失败")}finally{B.value=!1}}async function Ce(e){const s=e.taskId;if(!s){p.warning("未找到任务ID");return}const t=G(),n=rs(s),a=e.outputFilename||e.sourceFilename||`delete-brand_${s}.xlsx`;if(t?.save_file_from_url_new){const o=await t.save_file_from_url_new(n,a);o.success?p.success(`已保存:${o.path||a}`):o.error&&o.error!=="用户取消"&&p.error(o.error);return}}async function O(){try{const e=await os();q.value=me(e.items||[]),qe(q.value),N(),await oe(),ie(!0)}catch(e){const s=(e instanceof Error?e.message:String(e||"")).toLowerCase();["无法连接到后端服务","bad gateway","gateway timeout","network error","timeout","502","503","504"].some(n=>s.includes(n.toLowerCase()))||console.error("loadHistory error:",e)}}function Ee(e){i.value.forEach(t=>{t.items=t.items.filter(n=>n.resultId!==e)});const s=i.value.length;i.value=i.value.filter(t=>t.items.length>0),i.value.length!==s&&typeof window<"u"&&window.localStorage.setItem($(),JSON.stringify(i.value)),q.value=q.value.filter(t=>t.resultId!==e),N(),U().length===0&&Se()}async function Fe(e){try{await us(e),Ee(e),await O(),p.success("已删除")}catch(s){const t=s instanceof Error?s.message:"删除失败";if(typeof t=="string"&&t.includes("记录不存在")){Ee(e),p.success("已删除");return}p.error(t)}}function Pe(e){const s=e.slice(0,5).map(n=>`${n.country}:${n.asin}${n.status?`(${n.status})`:""}`);return e.length-s.length>0?`${s.join("、")} 等 ${e.length} 条`:s.join("、")}function It(e,s){}function ce(){L.value&&(window.clearTimeout(L.value),L.value=null)}function Ve(e=3e3){ce(),L.value=window.setTimeout(()=>{L.value=null,Ae().catch(()=>{})},e)}function Qe(e){return e.includes("当前店铺正在执行中")}function Re(){for(const e of i.value)for(const s of e.items){const t=P(s);if(t==="已入队"||t==="处理中")return!0}return!1}function K(e){return e.resultId!=null?`result:${e.resultId}`:`task:${e.taskId||0}:${e.sourceFilename||""}`}function xe(e){return!!(e.taskId&&(e.matchStatus==="MATCHED"||e.matched))}function Ne(){return F.value.find(e=>!xe(e)||K(e)===D.value||x(e.taskId,e)?._completed?!1:P(e)==="未入队")}async function $e(e,s){if(s?.auto&&Re())return e.taskId,e.sourceFilename,{success:!1,retryable:!0};const t=G(),n=e.taskId;if(!n)return{success:!1,retryable:!1};const a=i.value.find(c=>c.taskId===n);if(!a)return{success:!1,retryable:!1};if(!t?.enqueue_json)return p.error("当前环境未启用 pywebview enqueue_json(浏览器环境不会推送)"),{success:!1,retryable:!1};const o={type:"delete-brand-run",ts:Date.now(),data:{taskId:n,items:[e]}};S.value=JSON.stringify(o,null,2);const r=await t.enqueue_json(o);let u="";if(r?.success){u=`已推送文件 ${e.sourceFilename},当前队列长度:${r.queue_size??"-"}`;const c=x(n,e);return c&&(c._pushed=!0,c._completed=!1),D.value=K(e),ne(n,a.items,u,S.value),ie(!0),b.value=u,{success:!0,retryable:!1}}else{const c=r?.error||"未知错误";if(u=`推送到 Python 队列失败:${c}`,b.value=u,ne(n,a.items,u,S.value),s?.auto&&Qe(c))return{success:!1,retryable:!0}}return b.value=u,{success:!1,retryable:!1}}async function Xe(){const e=Ne();if(!e){p.warning("当前没有可推送的已匹配文件");return}V.value=!0,E.value=!0,ce();const s=await $e(e);if(V.value=!1,!s.success){E.value=!1,D.value="";return}p.success("已开始按顺序推送到 Python 队列")}async function Ae(){if(!E.value||Q.value){E.value,Q.value;return}if(Re())return;const e=D.value;if(e){const t=F.value.find(n=>K(n)===e);if(t){const n=t.taskId,a=R(n);a&&(_e(n),P(t),void 0);const o=P(t);if(o==="已入队"||o==="处理中"){t.sourceFilename;return}if(a){const r=x(n,t);if(r&&!r._completed){t.sourceFilename,r._pushed,r._completed;return}}}}const s=Ne();if(!s){D.value="",E.value=!1;return}Q.value=!0;try{s.taskId,s.sourceFilename,K(s);const t=await $e(s,{auto:!0});if(!t.success){if(t.retryable){s.taskId,s.sourceFilename,Ve();return}s.taskId,s.sourceFilename,E.value=!1}}finally{Q.value=!1}}return Ze(()=>{Be();const e=U();e.length>0?oe(e).then(async()=>{let s=!1,t=!1;for(const n of e)Ie(n)&&(he(n),s=!0,t=!0);t?await O():s&&N(),ie(!0)}):N(),O().catch(()=>{})}),es(()=>{Se()}),(e,s)=>(d(),f("div",hs,[ss(ts,{active:"delete-brand"}),l("div",ps,[l("aside",_s,[s[2]||(s[2]=l("div",{class:"section-title"},"选择文件",-1)),l("div",ys,[s[0]||(s[0]=l("div",{class:"hint"},"上传删除品牌 Excel;无论是直接上传文件还是上传文件夹批量导入,都会按每个 Excel 文件名作为店铺名。",-1)),l("div",{class:"btns"},[l("button",{type:"button",class:"opt-btn",onClick:Ge},"选择 Excel 文件"),l("button",{type:"button",class:"opt-btn",onClick:je},"选择文件夹")]),l("div",gs,[A.value.length?(d(),f(H,{key:0},[(d(!0),f(H,null,fe(te.value,t=>(d(),f("span",{key:t},v(t),1))),128)),A.value.length>te.value.length?(d(),f("span",ms," 还有 "+v(A.value.length-te.value.length)+" 个文件未展开显示 ",1)):h("",!0)],64)):(d(),f("span",ks,"暂未选择删除品牌文件"))])]),s[3]||(s[3]=ns('
执行说明
',2)),l("div",ws,[l("button",{type:"button",class:"btn-run",disabled:B.value,onClick:ze},v(B.value?"解析中...":"开始解析"),9,Is),l("button",{type:"button",class:"btn-run btn-queue",disabled:V.value||!Ue.value.length,onClick:Xe},v(V.value?"推送中...":"推送到 Python 队列"),9,bs),l("span",Ss,v(B.value?"正在读取删除品牌数据,请稍候…":"解析后会在右侧展示按国家分组的去重结果"),1)]),b.value||S.value?(d(),f("div",Ts,[s[1]||(s[1]=l("div",{class:"section-title queue-debug-title"},"Python 队列推送结果",-1)),b.value?(d(),f("div",Cs,v(b.value),1)):h("",!0),S.value?(d(),f("pre",Es,v(S.value),1)):h("",!0)])):h("",!0)]),l("section",Fs,[s[12]||(s[12]=l("div",{class:"panel-header"},"删除品牌结果",-1)),l("div",Ps,[l("div",Rs,[l("div",xs,[s[4]||(s[4]=l("span",{class:"summary-label"},"已处理文件",-1)),l("strong",null,v(z.value.total),1)]),l("div",Ns,[s[5]||(s[5]=l("span",{class:"summary-label"},"成功结果",-1)),l("strong",null,v(z.value.successCount),1)]),l("div",$s,[s[6]||(s[6]=l("span",{class:"summary-label"},"失败文件",-1)),l("strong",null,v(z.value.failedCount),1)])]),l("div",As,[s[11]||(s[11]=l("div",{class:"result-list-header"},[l("span",null,"删除品牌结果列表")],-1)),Le.value?(d(),f(H,{key:1},[F.value.length?(d(),f("div",Ms,[s[8]||(s[8]=l("div",{class:"result-subsection-title"},"当前任务",-1)),l("ul",Bs,[(d(!0),f(H,null,fe(F.value,t=>(d(),f("li",{key:`current-${t.taskId||t.resultId||t.sourceFilename}`,class:"task-item split-result-item"},[l("div",qs,[l("span",{class:"id",title:t.sourceFilename},v(t.sourceFilename||"-"),9,Ls),l("div",Us,"店铺名:"+v(t.shopName||"-"),1),l("div",Os,"匹配结果:"+v(ke(t)),1),t.platform?(d(),f("div",Ks,"平台:"+v(t.platform),1)):h("",!0),P(t)?(d(),f("div",Hs,"队列状态:"+v(P(t)),1)):h("",!0),t.countryCount!==void 0&&t.matched?(d(),f("div",Js,"国家数:"+v(t.countryCount),1)):h("",!0),t.totalRows!==void 0&&t.matched?(d(),f("div",Gs,"去重后 "+v(t.totalRows)+" 条 ",1)):h("",!0),ge(t)?(d(),f("div",js,[l("div",zs,[s[7]||(s[7]=l("span",null,"任务进度",-1)),l("span",null,v(Y(t))+"%",1)]),l("div",Vs,[l("div",{class:"delete-brand-progress-bar-fill",style:De({width:`${Y(t)}%`})},null,4)]),l("div",Qs,v(we(t)),1)])):h("",!0),t.previewRows?.length?(d(),f("div",Xs,v(Pe(t.previewRows)),1)):h("",!0),W(t)?(d(),f("div",Ws,"错误信息:"+v(W(t)),1)):h("",!0)]),l("div",Ys,[l("span",{class:Me(["status",Z(t).className])},v(Z(t).text),3),be(t)?(d(),f("button",{key:0,type:"button",class:"download",onClick:n=>Ce(t)}," 下载 ",8,Zs)):h("",!0),t.resultId?(d(),f("button",{key:1,type:"button",class:"btn-delete",onClick:n=>Fe(t.resultId)}," 删除 ",8,et)):h("",!0)])]))),128))])])):h("",!0),X.value.length?(d(),f("div",st,[s[10]||(s[10]=l("div",{class:"result-subsection-title"},"历史记录",-1)),l("ul",tt,[(d(!0),f(H,null,fe(X.value,t=>(d(),f("li",{key:`history-${t.taskId||t.resultId||t.sourceFilename}`,class:"task-item split-result-item"},[l("div",nt,[l("span",{class:"id",title:t.sourceFilename},v(t.sourceFilename||"-"),9,at),l("div",lt,"店铺名:"+v(t.shopName||"-"),1),l("div",rt,"匹配结果:"+v(ke(t)),1),t.platform?(d(),f("div",ot,"平台:"+v(t.platform),1)):h("",!0),t.countryCount!==void 0&&t.matched?(d(),f("div",ut,"国家数:"+v(t.countryCount),1)):h("",!0),t.totalRows!==void 0&&t.matched?(d(),f("div",it,"去重后 "+v(t.totalRows)+" 条 ",1)):h("",!0),ge(t)?(d(),f("div",ct,[l("div",dt,[s[9]||(s[9]=l("span",null,"任务进度",-1)),l("span",null,v(Y(t))+"%",1)]),l("div",ft,[l("div",{class:"delete-brand-progress-bar-fill",style:De({width:`${Y(t)}%`})},null,4)]),l("div",vt,v(we(t)),1)])):h("",!0),t.previewRows?.length?(d(),f("div",ht,v(Pe(t.previewRows)),1)):h("",!0),W(t)?(d(),f("div",pt,"错误信息:"+v(W(t)),1)):h("",!0)]),l("div",_t,[l("span",{class:Me(["status",Z(t).className])},v(Z(t).text),3),be(t)?(d(),f("button",{key:0,type:"button",class:"download",onClick:n=>Ce(t)}," 下载 ",8,yt)):h("",!0),t.resultId?(d(),f("button",{key:1,type:"button",class:"btn-delete",onClick:n=>Fe(t.resultId)}," 删除 ",8,gt)):h("",!0)])]))),128))])])):h("",!0)],64)):(d(),f("div",Ds," 暂无删除品牌结果,完成解析后会在这里展示按国家分组的去重结果 "))])])])])]))}}),kt=cs(mt,[["__scopeId","data-v-64ee024e"]]);ds(kt).use(fs).mount("#app"); ->>>>>>> 0b57aea5f697f563ecfed0f37b11115762201764 diff --git a/app/assets/split.js b/app/assets/split.js index eca30a4..f86d567 100644 --- a/app/assets/split.js +++ b/app/assets/split.js @@ -1 +1 @@ -import { d as q, o as G, a, c as n, b as L, B as O, e, F as x, r as I, t as i, f, n as F, w as C, u as V, G as M, g as d, h as Q, i as E, E as r, H as W, I as X, p as Y, v as Z, J as ss, K as es, _ as ts, q as ls, s as as } from "./pywebview-C66x_2Dh.js"; import { e as ns } from "./brand-BZije8D7.js"; const os = { class: "page-shell module-page" }, is = { class: "main-content" }, rs = { class: "left-panel" }, us = { class: "upload-zone" }, ds = { class: "selected-files clean-placeholder split-selected-files" }, cs = { key: 0, class: "more-line" }, ps = { key: 1 }, vs = { class: "option-group column-option-group" }, fs = { class: "column-toolbar" }, _s = { class: "column-count" }, ms = { class: "column-actions" }, ys = { key: 0, class: "column-grid" }, gs = ["value"], hs = { key: 1, class: "empty-column-state" }, bs = { class: "option-group" }, ws = { class: "radio-item active" }, ks = { class: "desc" }, Cs = { class: "option-group" }, Ss = { class: "option-group" }, xs = { key: 0, class: "split-input-card" }, Fs = { key: 1, class: "split-input-card" }, Es = { class: "run-row" }, Ps = ["disabled"], $s = { class: "loading-msg" }, Is = { class: "right-panel" }, Rs = { class: "task-list-wrap" }, Bs = { class: "clean-result-summary" }, Us = { class: "summary-card" }, Vs = { class: "summary-card" }, Ms = { class: "summary-card" }, Ts = { class: "result-list-wrap" }, Ds = { key: 0, class: "empty-tasks" }, Hs = { key: 1, class: "task-list clean-result-list" }, Ks = { class: "left split-result-main" }, Ns = ["title"], js = { key: 0, class: "files" }, As = { key: 1, class: "time" }, zs = { key: 2, class: "files" }, Js = { key: 3, class: "files split-entry-list" }, qs = { key: 4, class: "files" }, Gs = { class: "task-right split-result-actions" }, Ls = ["onClick"], Os = ["onClick"], Qs = q({ __name: "BrandSplitTab", setup(Xs) { const _ = d([]), S = d(""), c = d([]), m = d([]), p = d([]), R = d(0), u = d("rows_per_file"), y = d(2e3), g = d(5), h = d(!1), b = d([]), w = d({ total: 0, successCount: 0, failedCount: 0, items: [] }), P = Q(() => _.value.slice(0, 8)); function T(l) { w.value = { total: l.length, successCount: l.filter(s => s.success).length, failedCount: l.filter(s => !s.success).length, items: l } } async function B(l) { const s = E(); if (!s?.upload_file_to_java) throw new Error("当前桌面端未提供文件上传桥接能力"); const t = []; for (const o of l) { const v = typeof o == "string" ? o : o.absolutePath, J = typeof o == "string" ? void 0 : o.relativePath, k = await s.upload_file_to_java(v, J); if (!k?.success || !k.data) throw new Error(k?.error || k?.message || `上传失败:${v}`); t.push(k.data) } return t } async function U(l) { const s = await Y(l); m.value = s.headers || [], p.value = [...s.headers || []], R.value = s.totalRows || 0 } async function D(l, s) { _.value = l, S.value = "", c.value = await B(l), c.value.length > 0 && await U(c.value[0].fileKey), r.success(s) } async function H() { const l = E(); if (!l?.select_brand_xlsx_files) { r.warning("当前环境不支持文件选择,请在本机客户端中打开"); return } try { const s = await l.select_brand_xlsx_files(); if (!s?.length) return; await D(s, `已选择 ${s.length} 个待拆分文件`) } catch (s) { r.error(s instanceof Error ? s.message : "选择失败") } } async function K() { const l = E(); if (!l?.select_brand_folder) { r.warning("当前环境不支持文件夹选择,请在本机客户端中打开"); return } try { const s = await l.select_brand_folder(); if (!s) return; S.value = s.split(/[/\\]/).filter(Boolean).pop() || ""; const t = await ns(s); if (t.success && t.items?.length) { _.value = t.items.map(o => o.relativePath), c.value = await B(t.items), c.value.length > 0 && await U(c.value[0].fileKey), r.success(`已选择文件夹内 ${t.items.length} 个 xlsx 文件`); return } r.warning(t.error || "该文件夹下没有 xlsx 文件") } catch (s) { r.error(s instanceof Error ? s.message : "选择失败") } } async function N() { if (!c.value.length) { r.warning("请先选择待拆分 Excel 文件或文件夹"); return } if (!p.value.length) { r.warning("请至少选择一列保留列"); return } if (u.value === "rows_per_file" && (!y.value || y.value <= 0)) { r.warning("请输入有效的每份条数"); return } if (u.value === "parts" && (!g.value || g.value <= 0)) { r.warning("请输入有效的拆分份数"); return } try { h.value = !0; const l = await W({ files: c.value.map(s => ({ fileKey: s.fileKey, originalFilename: s.originalFilename, relativePath: s.relativePath })), selectedColumns: p.value, splitMode: u.value, rowsPerFile: u.value === "rows_per_file" && y.value || void 0, parts: u.value === "parts" && g.value || void 0, archiveName: c.value.some(s => !!s.relativePath) && S.value ? S.value : void 0 }); w.value = l, b.value = l.items || [], await $(), r.success("数据拆分完成") } catch (l) { r.error(l instanceof Error ? l.message : "数据拆分失败") } finally { h.value = !1 } } async function $() { try { const l = await X(); b.value = l.items || [], T(b.value) } catch { } } async function j(l) { try { await ss(l), await $(), r.success("已删除") } catch (s) { r.error(s instanceof Error ? s.message : "删除失败") } } function A(l) { const s = l.slice(0, 4).map(o => o.rowCount !== void 0 ? `${o.filename}(${o.rowCount}条)` : o.filename); return l.length - s.length > 0 ? `${s.join("、")} 等 ${l.length} 个文件` : s.join("、") } async function z(l) { const s = E(), t = l.downloadUrl || (l.resultId ? es(l.resultId) : ""); if (!t) { r.warning("当前结果没有下载地址"); return } const o = l.outputFilename || `${new Date().toISOString().slice(0, 10).replace(/-/g, "")}.zip`; if (s?.save_file_from_url_new) { const v = await s.save_file_from_url_new(t, o); v.success ? r.success(`已保存:${v.path || o}`) : v.error && v.error !== "用户取消" && r.error(v.error); return } } return G(() => { $().catch(() => { }) }), (l, s) => (a(), n("div", os, [L(O, { active: "split" }), e("div", is, [e("aside", rs, [s[14] || (s[14] = e("div", { class: "section-title" }, "选择文件", -1)), e("div", us, [s[7] || (s[7] = e("div", { class: "hint" }, "选择需要拆分的 Excel 文件或文件夹,按规则拆成多个 Excel 文件。", -1)), e("div", { class: "btns" }, [e("button", { type: "button", class: "opt-btn", onClick: H }, "选择 Excel 文件"), e("button", { type: "button", class: "opt-btn", onClick: K }, "选择文件夹")]), e("div", ds, [_.value.length ? (a(), n(x, { key: 0 }, [(a(!0), n(x, null, I(P.value, t => (a(), n("span", { key: t }, i(t), 1))), 128)), _.value.length > P.value.length ? (a(), n("span", cs, " 还有 " + i(_.value.length - P.value.length) + " 个文件未展开显示 ", 1)) : f("", !0)], 64)) : (a(), n("span", ps, "暂未选择待拆分文件"))])]), s[15] || (s[15] = e("div", { class: "section-title" }, "保留列设置", -1)), e("div", vs, [e("div", fs, [e("span", _s, "已选择 " + i(p.value.length) + " / " + i(m.value.length) + " 列", 1), e("div", ms, [e("button", { type: "button", class: "opt-btn small", onClick: s[0] || (s[0] = t => p.value = [...m.value]) }, "全选"), e("button", { type: "button", class: "opt-btn small", onClick: s[1] || (s[1] = t => p.value = []) }, "清空")])]), m.value.length ? (a(), n("div", ys, [(a(!0), n(x, null, I(m.value, t => (a(), n("label", { key: t, class: F(["column-item", { active: p.value.includes(t) }]) }, [C(e("input", { "onUpdate:modelValue": s[2] || (s[2] = o => p.value = o), type: "checkbox", value: t }, null, 8, gs), [[Z, p.value]]), e("span", null, i(t), 1)], 2))), 128))])) : (a(), n("div", hs, "请选择 Excel 文件后读取表头并填充可保留列"))]), s[16] || (s[16] = e("div", { class: "section-title" }, "总条数", -1)), e("div", bs, [e("label", ws, [s[9] || (s[9] = e("input", { type: "checkbox", checked: "", disabled: "" }, null, -1)), e("div", null, [s[8] || (s[8] = e("span", { class: "label" }, "当前数据总条数", -1)), e("div", ks, i(R.value) + " 条(不含表头)", 1)])])]), s[17] || (s[17] = e("div", { class: "section-title" }, "拆分模式", -1)), e("div", Cs, [e("label", { class: F(["radio-item", { active: u.value === "rows_per_file" }]) }, [C(e("input", { "onUpdate:modelValue": s[3] || (s[3] = t => u.value = t), type: "radio", value: "rows_per_file", name: "split-mode" }, null, 512), [[V, u.value]]), s[10] || (s[10] = e("div", null, [e("span", { class: "label" }, "按每份条数"), e("div", { class: "desc" }, "例如每份 2000 条,最后一份放剩余数据")], -1))], 2), e("label", { class: F(["radio-item", { active: u.value === "parts" }]) }, [C(e("input", { "onUpdate:modelValue": s[4] || (s[4] = t => u.value = t), type: "radio", value: "parts", name: "split-mode" }, null, 512), [[V, u.value]]), s[11] || (s[11] = e("div", null, [e("span", { class: "label" }, "按份数平均拆分"), e("div", { class: "desc" }, "例如拆 5 份,按总条数尽量平均分配")], -1))], 2)]), s[18] || (s[18] = e("div", { class: "section-title" }, "拆分参数", -1)), e("div", Ss, [u.value === "rows_per_file" ? (a(), n("label", xs, [s[12] || (s[12] = e("span", { class: "label" }, "每份条数", -1)), C(e("input", { "onUpdate:modelValue": s[5] || (s[5] = t => y.value = t), class: "split-number-input", type: "number", min: "1", placeholder: "例如 2000" }, null, 512), [[M, y.value, void 0, { number: !0 }]])])) : (a(), n("label", Fs, [s[13] || (s[13] = e("span", { class: "label" }, "拆分份数", -1)), C(e("input", { "onUpdate:modelValue": s[6] || (s[6] = t => g.value = t), class: "split-number-input", type: "number", min: "1", placeholder: "例如 5" }, null, 512), [[M, g.value, void 0, { number: !0 }]])]))]), e("div", Es, [e("button", { type: "button", class: "btn-run", disabled: h.value, onClick: N }, i(h.value ? "拆分中..." : "开始拆分"), 9, Ps), e("span", $s, i(h.value ? "正在拆分并上传结果,请稍候…" : "选择文件、保留列和拆分规则后即可执行拆分,结果会显示在右侧供下载"), 1)])]), e("section", Is, [s[23] || (s[23] = e("div", { class: "panel-header" }, "拆分结果", -1)), e("div", Rs, [e("div", Bs, [e("div", Us, [s[19] || (s[19] = e("span", { class: "summary-label" }, "已处理文件", -1)), e("strong", null, i(w.value.total), 1)]), e("div", Vs, [s[20] || (s[20] = e("span", { class: "summary-label" }, "成功结果", -1)), e("strong", null, i(w.value.successCount), 1)]), e("div", Ms, [s[21] || (s[21] = e("span", { class: "summary-label" }, "失败文件", -1)), e("strong", null, i(w.value.failedCount), 1)])]), e("div", Ts, [s[22] || (s[22] = e("div", { class: "result-list-header" }, [e("span", null, "拆分结果压缩包列表")], -1)), b.value.length === 0 ? (a(), n("div", Ds, " 暂无拆分结果,完成数据拆分后会在这里展示压缩包文件 ")) : (a(), n("ul", Hs, [(a(!0), n(x, null, I(b.value, t => (a(), n("li", { key: `${t.resultId || t.outputFilename || t.sourceFilename}-${t.rowCount || 0}`, class: "task-item split-result-item" }, [e("div", Ks, [e("span", { class: "id", title: t.sourceFilename }, i(t.sourceFilename || "-"), 9, Ns), t.outputFilename ? (a(), n("div", js, "压缩包文件:" + i(t.outputFilename), 1)) : f("", !0), t.rowCount !== void 0 ? (a(), n("div", As, "压缩包共包含 " + i(t.rowCount) + " 条数据", 1)) : f("", !0), t.entryCount && t.entryCount > 0 ? (a(), n("div", zs, "压缩包内共 " + i(t.entryCount) + " 个拆分文件 ", 1)) : f("", !0), t.entries && t.entries.length ? (a(), n("div", Js, " 压缩包内容:" + i(A(t.entries)), 1)) : f("", !0), t.error ? (a(), n("div", qs, "错误信息:" + i(t.error), 1)) : f("", !0)]), e("div", Gs, [e("span", { class: F(["status", t.success ? "success" : "failed"]) }, i(t.success ? "已完成" : "失败"), 3), t.success && (t.downloadUrl || t.resultId) ? (a(), n("button", { key: 0, type: "button", class: "download", onClick: o => z(t) }, " 下载压缩包 ", 8, Ls)) : f("", !0), t.resultId ? (a(), n("button", { key: 1, type: "button", class: "btn-delete", onClick: o => j(t.resultId) }, " 删除 ", 8, Os)) : f("", !0)])]))), 128))]))])])])])])) } }), Ws = ts(Qs, [["__scopeId", "data-v-f3b42aa3"]]); ls(Ws).use(as).mount("#app"); +import{d as q,o as G,a,c as n,b as L,B as O,e,F as x,r as I,t as i,f,n as F,w as C,u as V,G as M,g as d,h as Q,i as E,E as r,H as W,I as X,p as Y,v as Z,J as ss,K as es,_ as ts,q as ls,s as as}from"./pywebview-C66x_2Dh.js";import{e as ns}from"./brand-BZije8D7.js";const os={class:"page-shell module-page"},is={class:"main-content"},rs={class:"left-panel"},us={class:"upload-zone"},ds={class:"selected-files clean-placeholder split-selected-files"},cs={key:0,class:"more-line"},ps={key:1},vs={class:"option-group column-option-group"},fs={class:"column-toolbar"},_s={class:"column-count"},ms={class:"column-actions"},ys={key:0,class:"column-grid"},gs=["value"],hs={key:1,class:"empty-column-state"},bs={class:"option-group"},ws={class:"radio-item active"},ks={class:"desc"},Cs={class:"option-group"},Ss={class:"option-group"},xs={key:0,class:"split-input-card"},Fs={key:1,class:"split-input-card"},Es={class:"run-row"},Ps=["disabled"],$s={class:"loading-msg"},Is={class:"right-panel"},Rs={class:"task-list-wrap"},Bs={class:"clean-result-summary"},Us={class:"summary-card"},Vs={class:"summary-card"},Ms={class:"summary-card"},Ts={class:"result-list-wrap"},Ds={key:0,class:"empty-tasks"},Hs={key:1,class:"task-list clean-result-list"},Ks={class:"left split-result-main"},Ns=["title"],js={key:0,class:"files"},As={key:1,class:"time"},zs={key:2,class:"files"},Js={key:3,class:"files split-entry-list"},qs={key:4,class:"files"},Gs={class:"task-right split-result-actions"},Ls=["onClick"],Os=["onClick"],Qs=q({__name:"BrandSplitTab",setup(Xs){const _=d([]),S=d(""),c=d([]),m=d([]),p=d([]),R=d(0),u=d("rows_per_file"),y=d(2e3),g=d(5),h=d(!1),b=d([]),w=d({total:0,successCount:0,failedCount:0,items:[]}),P=Q(()=>_.value.slice(0,8));function T(l){w.value={total:l.length,successCount:l.filter(s=>s.success).length,failedCount:l.filter(s=>!s.success).length,items:l}}async function B(l){const s=E();if(!s?.upload_file_to_java)throw new Error("当前桌面端未提供文件上传桥接能力");const t=[];for(const o of l){const v=typeof o=="string"?o:o.absolutePath,J=typeof o=="string"?void 0:o.relativePath,k=await s.upload_file_to_java(v,J);if(!k?.success||!k.data)throw new Error(k?.error||k?.message||`上传失败:${v}`);t.push(k.data)}return t}async function U(l){const s=await Y(l);m.value=s.headers||[],p.value=[...s.headers||[]],R.value=s.totalRows||0}async function D(l,s){_.value=l,S.value="",c.value=await B(l),c.value.length>0&&await U(c.value[0].fileKey),r.success(s)}async function H(){const l=E();if(!l?.select_brand_xlsx_files){r.warning("当前环境不支持文件选择,请在本机客户端中打开");return}try{const s=await l.select_brand_xlsx_files();if(!s?.length)return;await D(s,`已选择 ${s.length} 个待拆分文件`)}catch(s){r.error(s instanceof Error?s.message:"选择失败")}}async function K(){const l=E();if(!l?.select_brand_folder){r.warning("当前环境不支持文件夹选择,请在本机客户端中打开");return}try{const s=await l.select_brand_folder();if(!s)return;S.value=s.split(/[/\\]/).filter(Boolean).pop()||"";const t=await ns(s);if(t.success&&t.items?.length){_.value=t.items.map(o=>o.relativePath),c.value=await B(t.items),c.value.length>0&&await U(c.value[0].fileKey),r.success(`已选择文件夹内 ${t.items.length} 个 xlsx 文件`);return}r.warning(t.error||"该文件夹下没有 xlsx 文件")}catch(s){r.error(s instanceof Error?s.message:"选择失败")}}async function N(){if(!c.value.length){r.warning("请先选择待拆分 Excel 文件或文件夹");return}if(!p.value.length){r.warning("请至少选择一列保留列");return}if(u.value==="rows_per_file"&&(!y.value||y.value<=0)){r.warning("请输入有效的每份条数");return}if(u.value==="parts"&&(!g.value||g.value<=0)){r.warning("请输入有效的拆分份数");return}try{h.value=!0;const l=await W({files:c.value.map(s=>({fileKey:s.fileKey,originalFilename:s.originalFilename,relativePath:s.relativePath})),selectedColumns:p.value,splitMode:u.value,rowsPerFile:u.value==="rows_per_file"&&y.value||void 0,parts:u.value==="parts"&&g.value||void 0,archiveName:c.value.some(s=>!!s.relativePath)&&S.value?S.value:void 0});w.value=l,b.value=l.items||[],await $(),r.success("数据拆分完成")}catch(l){r.error(l instanceof Error?l.message:"数据拆分失败")}finally{h.value=!1}}async function $(){try{const l=await X();b.value=l.items||[],T(b.value)}catch{}}async function j(l){try{await ss(l),await $(),r.success("已删除")}catch(s){r.error(s instanceof Error?s.message:"删除失败")}}function A(l){const s=l.slice(0,4).map(o=>o.rowCount!==void 0?`${o.filename}(${o.rowCount}条)`:o.filename);return l.length-s.length>0?`${s.join("、")} 等 ${l.length} 个文件`:s.join("、")}async function z(l){const s=E(),t=l.downloadUrl||(l.resultId?es(l.resultId):"");if(!t){r.warning("当前结果没有下载地址");return}const o=l.outputFilename||`${new Date().toISOString().slice(0,10).replace(/-/g,"")}.zip`;if(s?.save_file_from_url_new){const v=await s.save_file_from_url_new(t,o);v.success?r.success(`已保存:${v.path||o}`):v.error&&v.error!=="用户取消"&&r.error(v.error);return}}return G(()=>{$().catch(()=>{})}),(l,s)=>(a(),n("div",os,[L(O,{active:"split"}),e("div",is,[e("aside",rs,[s[14]||(s[14]=e("div",{class:"section-title"},"选择文件",-1)),e("div",us,[s[7]||(s[7]=e("div",{class:"hint"},"选择需要拆分的 Excel 文件或文件夹,按规则拆成多个 Excel 文件。",-1)),e("div",{class:"btns"},[e("button",{type:"button",class:"opt-btn",onClick:H},"选择 Excel 文件"),e("button",{type:"button",class:"opt-btn",onClick:K},"选择文件夹")]),e("div",ds,[_.value.length?(a(),n(x,{key:0},[(a(!0),n(x,null,I(P.value,t=>(a(),n("span",{key:t},i(t),1))),128)),_.value.length>P.value.length?(a(),n("span",cs," 还有 "+i(_.value.length-P.value.length)+" 个文件未展开显示 ",1)):f("",!0)],64)):(a(),n("span",ps,"暂未选择待拆分文件"))])]),s[15]||(s[15]=e("div",{class:"section-title"},"保留列设置",-1)),e("div",vs,[e("div",fs,[e("span",_s,"已选择 "+i(p.value.length)+" / "+i(m.value.length)+" 列",1),e("div",ms,[e("button",{type:"button",class:"opt-btn small",onClick:s[0]||(s[0]=t=>p.value=[...m.value])},"全选"),e("button",{type:"button",class:"opt-btn small",onClick:s[1]||(s[1]=t=>p.value=[])},"清空")])]),m.value.length?(a(),n("div",ys,[(a(!0),n(x,null,I(m.value,t=>(a(),n("label",{key:t,class:F(["column-item",{active:p.value.includes(t)}])},[C(e("input",{"onUpdate:modelValue":s[2]||(s[2]=o=>p.value=o),type:"checkbox",value:t},null,8,gs),[[Z,p.value]]),e("span",null,i(t),1)],2))),128))])):(a(),n("div",hs,"请选择 Excel 文件后读取表头并填充可保留列"))]),s[16]||(s[16]=e("div",{class:"section-title"},"总条数",-1)),e("div",bs,[e("label",ws,[s[9]||(s[9]=e("input",{type:"checkbox",checked:"",disabled:""},null,-1)),e("div",null,[s[8]||(s[8]=e("span",{class:"label"},"当前数据总条数",-1)),e("div",ks,i(R.value)+" 条(不含表头)",1)])])]),s[17]||(s[17]=e("div",{class:"section-title"},"拆分模式",-1)),e("div",Cs,[e("label",{class:F(["radio-item",{active:u.value==="rows_per_file"}])},[C(e("input",{"onUpdate:modelValue":s[3]||(s[3]=t=>u.value=t),type:"radio",value:"rows_per_file",name:"split-mode"},null,512),[[V,u.value]]),s[10]||(s[10]=e("div",null,[e("span",{class:"label"},"按每份条数"),e("div",{class:"desc"},"例如每份 2000 条,最后一份放剩余数据")],-1))],2),e("label",{class:F(["radio-item",{active:u.value==="parts"}])},[C(e("input",{"onUpdate:modelValue":s[4]||(s[4]=t=>u.value=t),type:"radio",value:"parts",name:"split-mode"},null,512),[[V,u.value]]),s[11]||(s[11]=e("div",null,[e("span",{class:"label"},"按份数平均拆分"),e("div",{class:"desc"},"例如拆 5 份,按总条数尽量平均分配")],-1))],2)]),s[18]||(s[18]=e("div",{class:"section-title"},"拆分参数",-1)),e("div",Ss,[u.value==="rows_per_file"?(a(),n("label",xs,[s[12]||(s[12]=e("span",{class:"label"},"每份条数",-1)),C(e("input",{"onUpdate:modelValue":s[5]||(s[5]=t=>y.value=t),class:"split-number-input",type:"number",min:"1",placeholder:"例如 2000"},null,512),[[M,y.value,void 0,{number:!0}]])])):(a(),n("label",Fs,[s[13]||(s[13]=e("span",{class:"label"},"拆分份数",-1)),C(e("input",{"onUpdate:modelValue":s[6]||(s[6]=t=>g.value=t),class:"split-number-input",type:"number",min:"1",placeholder:"例如 5"},null,512),[[M,g.value,void 0,{number:!0}]])]))]),e("div",Es,[e("button",{type:"button",class:"btn-run",disabled:h.value,onClick:N},i(h.value?"拆分中...":"开始拆分"),9,Ps),e("span",$s,i(h.value?"正在拆分并上传结果,请稍候…":"选择文件、保留列和拆分规则后即可执行拆分,结果会显示在右侧供下载"),1)])]),e("section",Is,[s[23]||(s[23]=e("div",{class:"panel-header"},"拆分结果",-1)),e("div",Rs,[e("div",Bs,[e("div",Us,[s[19]||(s[19]=e("span",{class:"summary-label"},"已处理文件",-1)),e("strong",null,i(w.value.total),1)]),e("div",Vs,[s[20]||(s[20]=e("span",{class:"summary-label"},"成功结果",-1)),e("strong",null,i(w.value.successCount),1)]),e("div",Ms,[s[21]||(s[21]=e("span",{class:"summary-label"},"失败文件",-1)),e("strong",null,i(w.value.failedCount),1)])]),e("div",Ts,[s[22]||(s[22]=e("div",{class:"result-list-header"},[e("span",null,"拆分结果压缩包列表")],-1)),b.value.length===0?(a(),n("div",Ds," 暂无拆分结果,完成数据拆分后会在这里展示压缩包文件 ")):(a(),n("ul",Hs,[(a(!0),n(x,null,I(b.value,t=>(a(),n("li",{key:`${t.resultId||t.outputFilename||t.sourceFilename}-${t.rowCount||0}`,class:"task-item split-result-item"},[e("div",Ks,[e("span",{class:"id",title:t.sourceFilename},i(t.sourceFilename||"-"),9,Ns),t.outputFilename?(a(),n("div",js,"压缩包文件:"+i(t.outputFilename),1)):f("",!0),t.rowCount!==void 0?(a(),n("div",As,"压缩包共包含 "+i(t.rowCount)+" 条数据",1)):f("",!0),t.entryCount&&t.entryCount>0?(a(),n("div",zs,"压缩包内共 "+i(t.entryCount)+" 个拆分文件 ",1)):f("",!0),t.entries&&t.entries.length?(a(),n("div",Js," 压缩包内容:"+i(A(t.entries)),1)):f("",!0),t.error?(a(),n("div",qs,"错误信息:"+i(t.error),1)):f("",!0)]),e("div",Gs,[e("span",{class:F(["status",t.success?"success":"failed"])},i(t.success?"已完成":"失败"),3),t.success&&(t.downloadUrl||t.resultId)?(a(),n("button",{key:0,type:"button",class:"download",onClick:o=>z(t)}," 下载压缩包 ",8,Ls)):f("",!0),t.resultId?(a(),n("button",{key:1,type:"button",class:"btn-delete",onClick:o=>j(t.resultId)}," 删除 ",8,Os)):f("",!0)])]))),128))]))])])])])]))}}),Ws=ts(Qs,[["__scopeId","data-v-f3b42aa3"]]);ls(Ws).use(as).mount("#app"); diff --git a/app/new_web_source/convert.html b/app/new_web_source/convert.html index 38281d9..ed6e58f 100644 --- a/app/new_web_source/convert.html +++ b/app/new_web_source/convert.html @@ -1,19 +1,16 @@ - - - - - 格式转换 - 数富AI - - - - - - - - -
- - - \ No newline at end of file + + + + 格式转换 - 数富AI + + + + + + + +
+ + diff --git a/app/new_web_source/dedupe.html b/app/new_web_source/dedupe.html index 26dce21..2f621af 100644 --- a/app/new_web_source/dedupe.html +++ b/app/new_web_source/dedupe.html @@ -1,19 +1,16 @@ - - - - - 数据去重 - 数富AI - - - - - - - - -
- - - \ No newline at end of file + + + + 数据去重 - 数富AI + + + + + + + +
+ + diff --git a/app/new_web_source/delete-brand.html b/app/new_web_source/delete-brand.html index 5c69a19..ff84f95 100644 --- a/app/new_web_source/delete-brand.html +++ b/app/new_web_source/delete-brand.html @@ -1,19 +1,16 @@ - - - - - 删除品牌 - 数富AI - - - - - - - - -
- - - \ No newline at end of file + + + + 删除品牌 - 数富AI + + + + + + + +
+ + diff --git a/app/new_web_source/split.html b/app/new_web_source/split.html index 9552361..3ef460a 100644 --- a/app/new_web_source/split.html +++ b/app/new_web_source/split.html @@ -1,19 +1,16 @@ - - - - - 数据拆分 - 数富AI - - - - - - - - -
- - - \ No newline at end of file + + + + 数据拆分 - 数富AI + + + + + + + +
+ + diff --git a/backend-java/jstack-6488.txt b/backend-java/jstack-6488.txt new file mode 100644 index 0000000..f64239b --- /dev/null +++ b/backend-java/jstack-6488.txt @@ -0,0 +1,1056 @@ +2026-04-27 09:31:53 +Full thread dump Java HotSpot(TM) 64-Bit Server VM (21.0.9+7-LTS-338 mixed mode, sharing): + +Threads class SMR info: +_java_thread_list=0x00000206f5796f90, length=60, elements={ +0x00000206ef866420, 0x00000206ef868e90, 0x00000206ef86cf00, 0x00000206ef86ddf0, +0x00000206ef870990, 0x00000206ef87b540, 0x00000206ef881950, 0x00000206ef883800, +0x00000206f424c120, 0x00000206f4256ee0, 0x00000206f8610410, 0x00000206f54b87d0, +0x00000206f5302900, 0x00000206fae021d0, 0x00000206fa48d240, 0x00000206fa48a450, +0x00000206fa48cbb0, 0x00000206fa48c520, 0x00000206fb996340, 0x00000206fb997d80, +0x00000206fb998410, 0x00000206fb9976f0, 0x00000206fb994f90, 0x00000206fb9969d0, +0x00000206fb998aa0, 0x00000206fb995620, 0x00000206fb997060, 0x00000206fb999130, +0x00000206fb995cb0, 0x00000206fb99b890, 0x00000206fb9997c0, 0x00000206fb99c5b0, +0x00000206fb99bf20, 0x00000206fb99b200, 0x00000206fb999e50, 0x00000206fb99a4e0, +0x00000206fb99ab70, 0x00000206f613bc10, 0x00000206f613aef0, 0x00000206f613dce0, +0x00000206f613b580, 0x00000206f613f090, 0x00000206f613ea00, 0x00000206f613c2a0, +0x00000206f613c930, 0x00000206f6140440, 0x00000206f613fdb0, 0x00000206f6140ad0, +0x00000206f6141e80, 0x00000206f6141160, 0x00000206fbd56be0, 0x00000206fbd56550, +0x00000206fbd54b10, 0x00000206fbd58620, 0x00000206fbd59340, 0x00000206ff0af860, +0x00000206ff0b2650, 0x00000206ff0b1fc0, 0x00000206ff0b2ce0, 0x00000206ff0b3370 +} + +"Reference Handler" #9 [27888] daemon prio=10 os_prio=2 cpu=0.00ms elapsed=2043.37s tid=0x00000206ef866420 nid=27888 waiting on condition [0x000000a9555ff000] + java.lang.Thread.State: RUNNABLE + at java.lang.ref.Reference.waitForReferencePendingList(java.base@21.0.9/Native Method) + at java.lang.ref.Reference.processPendingReferences(java.base@21.0.9/Reference.java:246) + at java.lang.ref.Reference$ReferenceHandler.run(java.base@21.0.9/Reference.java:208) + +"Finalizer" #10 [3824] daemon prio=8 os_prio=1 cpu=0.00ms elapsed=2043.37s tid=0x00000206ef868e90 nid=3824 in Object.wait() [0x000000a9556ff000] + java.lang.Thread.State: WAITING (on object monitor) + at java.lang.Object.wait0(java.base@21.0.9/Native Method) + - waiting on <0x000000068c286d88> (a java.lang.ref.NativeReferenceQueue$Lock) + at java.lang.Object.wait(java.base@21.0.9/Object.java:366) + at java.lang.Object.wait(java.base@21.0.9/Object.java:339) + at java.lang.ref.NativeReferenceQueue.await(java.base@21.0.9/NativeReferenceQueue.java:48) + at java.lang.ref.ReferenceQueue.remove0(java.base@21.0.9/ReferenceQueue.java:158) + at java.lang.ref.NativeReferenceQueue.remove(java.base@21.0.9/NativeReferenceQueue.java:89) + - locked <0x000000068c286d88> (a java.lang.ref.NativeReferenceQueue$Lock) + at java.lang.ref.Finalizer$FinalizerThread.run(java.base@21.0.9/Finalizer.java:173) + +"Signal Dispatcher" #11 [36016] daemon prio=9 os_prio=2 cpu=0.00ms elapsed=2043.37s tid=0x00000206ef86cf00 nid=36016 waiting on condition [0x0000000000000000] + java.lang.Thread.State: RUNNABLE + +"Attach Listener" #12 [32968] daemon prio=5 os_prio=2 cpu=0.00ms elapsed=2043.37s tid=0x00000206ef86ddf0 nid=32968 waiting on condition [0x0000000000000000] + java.lang.Thread.State: RUNNABLE + +"Service Thread" #13 [31112] daemon prio=9 os_prio=0 cpu=31.25ms elapsed=2043.37s tid=0x00000206ef870990 nid=31112 runnable [0x0000000000000000] + java.lang.Thread.State: RUNNABLE + +"Monitor Deflation Thread" #14 [34356] daemon prio=9 os_prio=0 cpu=109.38ms elapsed=2043.37s tid=0x00000206ef87b540 nid=34356 runnable [0x0000000000000000] + java.lang.Thread.State: RUNNABLE + +"C2 CompilerThread0" #15 [33528] daemon prio=9 os_prio=2 cpu=9781.25ms elapsed=2043.37s tid=0x00000206ef881950 nid=33528 waiting on condition [0x0000000000000000] + java.lang.Thread.State: RUNNABLE + No compile task + +"C1 CompilerThread0" #23 [34520] daemon prio=9 os_prio=2 cpu=2015.62ms elapsed=2043.37s tid=0x00000206ef883800 nid=34520 waiting on condition [0x0000000000000000] + java.lang.Thread.State: RUNNABLE + No compile task + +"Notification Thread" #27 [30388] daemon prio=9 os_prio=0 cpu=15.62ms elapsed=2043.32s tid=0x00000206f424c120 nid=30388 runnable [0x0000000000000000] + java.lang.Thread.State: RUNNABLE + +"Common-Cleaner" #28 [13868] daemon prio=8 os_prio=1 cpu=0.00ms elapsed=2043.31s tid=0x00000206f4256ee0 nid=13868 waiting on condition [0x000000a955fff000] + java.lang.Thread.State: TIMED_WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068c2871c8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.parkNanos(java.base@21.0.9/LockSupport.java:269) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(java.base@21.0.9/AbstractQueuedSynchronizer.java:1852) + at java.lang.ref.ReferenceQueue.await(java.base@21.0.9/ReferenceQueue.java:71) + at java.lang.ref.ReferenceQueue.remove0(java.base@21.0.9/ReferenceQueue.java:143) + at java.lang.ref.ReferenceQueue.remove(java.base@21.0.9/ReferenceQueue.java:218) + at jdk.internal.ref.CleanerImpl.run(java.base@21.0.9/CleanerImpl.java:140) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + at jdk.internal.misc.InnocuousThread.run(java.base@21.0.9/InnocuousThread.java:186) + +"AsyncAppender-Worker-DefaultAppender" #48 [29512] daemon prio=5 os_prio=0 cpu=0.00ms elapsed=2038.73s tid=0x00000206f8610410 nid=29512 waiting on condition [0x000000a956eff000] + java.lang.Thread.State: WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068db9c8a0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(java.base@21.0.9/LockSupport.java:371) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block(java.base@21.0.9/AbstractQueuedSynchronizer.java:519) + at java.util.concurrent.ForkJoinPool.unmanagedBlock(java.base@21.0.9/ForkJoinPool.java:3778) + at java.util.concurrent.ForkJoinPool.managedBlock(java.base@21.0.9/ForkJoinPool.java:3723) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(java.base@21.0.9/AbstractQueuedSynchronizer.java:1712) + at java.util.concurrent.ArrayBlockingQueue.take(java.base@21.0.9/ArrayBlockingQueue.java:420) + at org.apache.rocketmq.logging.ch.qos.logback.core.AsyncAppenderBase$Worker.run(AsyncAppenderBase.java:298) + +"Catalina-utility-1" #49 [34028] prio=1 os_prio=-2 cpu=640.62ms elapsed=2037.71s tid=0x00000206f54b87d0 nid=34028 waiting on condition [0x000000a9578fe000] + java.lang.Thread.State: WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068dbb6988> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(java.base@21.0.9/LockSupport.java:371) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block(java.base@21.0.9/AbstractQueuedSynchronizer.java:519) + at java.util.concurrent.ForkJoinPool.unmanagedBlock(java.base@21.0.9/ForkJoinPool.java:3778) + at java.util.concurrent.ForkJoinPool.managedBlock(java.base@21.0.9/ForkJoinPool.java:3723) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(java.base@21.0.9/AbstractQueuedSynchronizer.java:1712) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@21.0.9/ScheduledThreadPoolExecutor.java:1177) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@21.0.9/ScheduledThreadPoolExecutor.java:899) + at java.util.concurrent.ThreadPoolExecutor.getTask(java.base@21.0.9/ThreadPoolExecutor.java:1070) + at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@21.0.9/ThreadPoolExecutor.java:1130) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@21.0.9/ThreadPoolExecutor.java:642) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"Catalina-utility-2" #50 [32756] prio=1 os_prio=-2 cpu=625.00ms elapsed=2037.71s tid=0x00000206f5302900 nid=32756 waiting on condition [0x000000a9579ff000] + java.lang.Thread.State: TIMED_WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068dbb6988> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.parkNanos(java.base@21.0.9/LockSupport.java:269) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(java.base@21.0.9/AbstractQueuedSynchronizer.java:1763) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@21.0.9/ScheduledThreadPoolExecutor.java:1182) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@21.0.9/ScheduledThreadPoolExecutor.java:899) + at java.util.concurrent.ThreadPoolExecutor.getTask(java.base@21.0.9/ThreadPoolExecutor.java:1070) + at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@21.0.9/ThreadPoolExecutor.java:1130) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@21.0.9/ThreadPoolExecutor.java:642) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"container-0" #51 [20256] prio=5 os_prio=0 cpu=0.00ms elapsed=2037.70s tid=0x00000206fae021d0 nid=20256 waiting on condition [0x000000a957aff000] + java.lang.Thread.State: TIMED_WAITING (sleeping) + at java.lang.Thread.sleep0(java.base@21.0.9/Native Method) + at java.lang.Thread.sleep(java.base@21.0.9/Thread.java:509) + at org.apache.catalina.core.StandardServer.await(StandardServer.java:520) + at org.springframework.boot.web.embedded.tomcat.TomcatWebServer$1.run(TomcatWebServer.java:219) + +"Jndi-Dns-address-change-listener" #52 [7160] daemon prio=5 os_prio=0 cpu=0.00ms elapsed=2037.51s tid=0x00000206fa48d240 nid=7160 runnable [0x000000a957bff000] + java.lang.Thread.State: RUNNABLE + at sun.net.dns.ResolverConfigurationImpl.notifyAddrChange0(java.base@21.0.9/Native Method) + at sun.net.dns.ResolverConfigurationImpl$AddressChangeListener.run(java.base@21.0.9/ResolverConfigurationImpl.java:179) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + at jdk.internal.misc.InnocuousThread.run(java.base@21.0.9/InnocuousThread.java:186) + +"lettuce-timer-3-1" #53 [31324] daemon prio=5 os_prio=0 cpu=578.12ms elapsed=2037.40s tid=0x00000206fa48a450 nid=31324 waiting on condition [0x000000a957cfe000] + java.lang.Thread.State: TIMED_WAITING (sleeping) + at java.lang.Thread.sleep0(java.base@21.0.9/Native Method) + at java.lang.Thread.sleep(java.base@21.0.9/Thread.java:509) + at io.netty.util.HashedWheelTimer$Worker.waitForNextTick(HashedWheelTimer.java:598) + at io.netty.util.HashedWheelTimer$Worker.run(HashedWheelTimer.java:494) + at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"mysql-cj-abandoned-connection-cleanup" #54 [6776] daemon prio=5 os_prio=0 cpu=46.88ms elapsed=2036.99s tid=0x00000206fa48cbb0 nid=6776 waiting on condition [0x000000a957dff000] + java.lang.Thread.State: TIMED_WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068d7eebc8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.parkNanos(java.base@21.0.9/LockSupport.java:269) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(java.base@21.0.9/AbstractQueuedSynchronizer.java:1852) + at java.lang.ref.ReferenceQueue.await(java.base@21.0.9/ReferenceQueue.java:71) + at java.lang.ref.ReferenceQueue.remove0(java.base@21.0.9/ReferenceQueue.java:143) + at java.lang.ref.ReferenceQueue.remove(java.base@21.0.9/ReferenceQueue.java:218) + at com.mysql.cj.jdbc.AbandonedConnectionCleanupThread.run(AbandonedConnectionCleanupThread.java:93) + at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@21.0.9/ThreadPoolExecutor.java:1144) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@21.0.9/ThreadPoolExecutor.java:642) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"MQClientFactoryScheduledThread" #56 [27804] prio=5 os_prio=0 cpu=62.50ms elapsed=2034.19s tid=0x00000206fa48c520 nid=27804 waiting on condition [0x000000a957eff000] + java.lang.Thread.State: TIMED_WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068eb00c98> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.parkNanos(java.base@21.0.9/LockSupport.java:269) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(java.base@21.0.9/AbstractQueuedSynchronizer.java:1763) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@21.0.9/ScheduledThreadPoolExecutor.java:1182) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@21.0.9/ScheduledThreadPoolExecutor.java:899) + at java.util.concurrent.ThreadPoolExecutor.getTask(java.base@21.0.9/ThreadPoolExecutor.java:1070) + at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@21.0.9/ThreadPoolExecutor.java:1130) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@21.0.9/ThreadPoolExecutor.java:642) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"NettyEventExecutor" #57 [28596] prio=5 os_prio=0 cpu=31.25ms elapsed=2034.19s tid=0x00000206fb996340 nid=28596 waiting on condition [0x000000a957fff000] + java.lang.Thread.State: TIMED_WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068eafa238> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.parkNanos(java.base@21.0.9/LockSupport.java:269) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(java.base@21.0.9/AbstractQueuedSynchronizer.java:1763) + at java.util.concurrent.LinkedBlockingQueue.poll(java.base@21.0.9/LinkedBlockingQueue.java:460) + at org.apache.rocketmq.remoting.netty.NettyRemotingAbstract$NettyEventExecutor.run(NettyRemotingAbstract.java:701) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"ClientHouseKeepingService" #55 [13476] prio=5 os_prio=0 cpu=796.88ms elapsed=2034.19s tid=0x00000206fb997d80 nid=13476 waiting on condition [0x000000a9580ff000] + java.lang.Thread.State: TIMED_WAITING (sleeping) + at java.lang.Thread.sleep0(java.base@21.0.9/Native Method) + at java.lang.Thread.sleep(java.base@21.0.9/Thread.java:509) + at io.netty.util.HashedWheelTimer$Worker.waitForNextTick(HashedWheelTimer.java:598) + at io.netty.util.HashedWheelTimer$Worker.run(HashedWheelTimer.java:494) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"PullMessageService" #58 [37592] prio=5 os_prio=0 cpu=0.00ms elapsed=2034.18s tid=0x00000206fb998410 nid=37592 waiting on condition [0x000000a9581fe000] + java.lang.Thread.State: WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068eb14bd8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(java.base@21.0.9/LockSupport.java:371) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block(java.base@21.0.9/AbstractQueuedSynchronizer.java:519) + at java.util.concurrent.ForkJoinPool.unmanagedBlock(java.base@21.0.9/ForkJoinPool.java:3778) + at java.util.concurrent.ForkJoinPool.managedBlock(java.base@21.0.9/ForkJoinPool.java:3723) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(java.base@21.0.9/AbstractQueuedSynchronizer.java:1712) + at java.util.concurrent.LinkedBlockingQueue.take(java.base@21.0.9/LinkedBlockingQueue.java:435) + at org.apache.rocketmq.client.impl.consumer.PullMessageService.run(PullMessageService.java:131) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"RebalanceService" #59 [5336] prio=5 os_prio=0 cpu=0.00ms elapsed=2034.18s tid=0x00000206fb9976f0 nid=5336 waiting on condition [0x000000a9582ff000] + java.lang.Thread.State: TIMED_WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068eb1b618> (a org.apache.rocketmq.common.CountDownLatch2$Sync) + at java.util.concurrent.locks.LockSupport.parkNanos(java.base@21.0.9/LockSupport.java:269) + at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(java.base@21.0.9/AbstractQueuedSynchronizer.java:756) + at java.util.concurrent.locks.AbstractQueuedSynchronizer.tryAcquireSharedNanos(java.base@21.0.9/AbstractQueuedSynchronizer.java:1126) + at org.apache.rocketmq.common.CountDownLatch2.await(CountDownLatch2.java:114) + at org.apache.rocketmq.common.ServiceThread.waitForRunning(ServiceThread.java:117) + at org.apache.rocketmq.client.impl.consumer.RebalanceService.run(RebalanceService.java:45) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"LatencyFaultToleranceScheduledThread" #60 [34044] prio=5 os_prio=0 cpu=343.75ms elapsed=2034.18s tid=0x00000206fb994f90 nid=34044 waiting on condition [0x000000a9583ff000] + java.lang.Thread.State: TIMED_WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068eb22078> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.parkNanos(java.base@21.0.9/LockSupport.java:269) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(java.base@21.0.9/AbstractQueuedSynchronizer.java:1763) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@21.0.9/ScheduledThreadPoolExecutor.java:1182) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@21.0.9/ScheduledThreadPoolExecutor.java:899) + at java.util.concurrent.ThreadPoolExecutor.getTask(java.base@21.0.9/ThreadPoolExecutor.java:1070) + at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@21.0.9/ThreadPoolExecutor.java:1130) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@21.0.9/ThreadPoolExecutor.java:642) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"RequestHouseKeepingService1" #61 [36360] prio=5 os_prio=0 cpu=15.62ms elapsed=2034.18s tid=0x00000206fb9969d0 nid=36360 waiting on condition [0x000000a9584fe000] + java.lang.Thread.State: TIMED_WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068eb28ad8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.parkNanos(java.base@21.0.9/LockSupport.java:269) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(java.base@21.0.9/AbstractQueuedSynchronizer.java:1763) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@21.0.9/ScheduledThreadPoolExecutor.java:1182) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@21.0.9/ScheduledThreadPoolExecutor.java:899) + at java.util.concurrent.ThreadPoolExecutor.getTask(java.base@21.0.9/ThreadPoolExecutor.java:1070) + at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@21.0.9/ThreadPoolExecutor.java:1130) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@21.0.9/ThreadPoolExecutor.java:642) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"LatencyFaultToleranceScheduledThread" #62 [36976] prio=5 os_prio=0 cpu=15.62ms elapsed=2034.18s tid=0x00000206fb998aa0 nid=36976 waiting on condition [0x000000a9585ff000] + java.lang.Thread.State: TIMED_WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068e266628> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.parkNanos(java.base@21.0.9/LockSupport.java:269) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(java.base@21.0.9/AbstractQueuedSynchronizer.java:1763) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@21.0.9/ScheduledThreadPoolExecutor.java:1182) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@21.0.9/ScheduledThreadPoolExecutor.java:899) + at java.util.concurrent.ThreadPoolExecutor.getTask(java.base@21.0.9/ThreadPoolExecutor.java:1070) + at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@21.0.9/ThreadPoolExecutor.java:1130) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@21.0.9/ThreadPoolExecutor.java:642) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"Client_192.168.9.14@6488#167351209475700_GuardForSyncSend" #63 [33984] prio=5 os_prio=0 cpu=4125.00ms elapsed=2034.18s tid=0x00000206fb995620 nid=33984 waiting on condition [0x000000a9586ff000] + java.lang.Thread.State: TIMED_WAITING (sleeping) + at java.lang.Thread.sleep0(java.base@21.0.9/Native Method) + at java.lang.Thread.sleep(java.base@21.0.9/Thread.java:509) + at org.apache.rocketmq.client.producer.ProduceAccumulator$GuardForSyncSendService.doWork(ProduceAccumulator.java:109) + at org.apache.rocketmq.client.producer.ProduceAccumulator$GuardForSyncSendService.run(ProduceAccumulator.java:84) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"Client_192.168.9.14@6488#167351209475700_GuardForAsyncSend" #64 [29332] prio=5 os_prio=0 cpu=4062.50ms elapsed=2034.18s tid=0x00000206fb997060 nid=29332 waiting on condition [0x000000a9587ff000] + java.lang.Thread.State: TIMED_WAITING (sleeping) + at java.lang.Thread.sleep0(java.base@21.0.9/Native Method) + at java.lang.Thread.sleep(java.base@21.0.9/Thread.java:509) + at org.apache.rocketmq.client.producer.ProduceAccumulator$GuardForAsyncSendService.doWork(ProduceAccumulator.java:154) + at org.apache.rocketmq.client.producer.ProduceAccumulator$GuardForAsyncSendService.run(ProduceAccumulator.java:131) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"http-nio-18080-exec-1" #65 [20552] daemon prio=5 os_prio=0 cpu=203.12ms elapsed=2033.58s tid=0x00000206fb999130 nid=20552 waiting on condition [0x000000a9588fe000] + java.lang.Thread.State: WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068eb49ee0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(java.base@21.0.9/LockSupport.java:371) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block(java.base@21.0.9/AbstractQueuedSynchronizer.java:519) + at java.util.concurrent.ForkJoinPool.unmanagedBlock(java.base@21.0.9/ForkJoinPool.java:3778) + at java.util.concurrent.ForkJoinPool.managedBlock(java.base@21.0.9/ForkJoinPool.java:3723) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(java.base@21.0.9/AbstractQueuedSynchronizer.java:1712) + at java.util.concurrent.LinkedBlockingQueue.take(java.base@21.0.9/LinkedBlockingQueue.java:435) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:117) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1114) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1175) + at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"http-nio-18080-exec-2" #66 [32148] daemon prio=5 os_prio=0 cpu=390.62ms elapsed=2033.58s tid=0x00000206fb995cb0 nid=32148 waiting on condition [0x000000a9589fe000] + java.lang.Thread.State: WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068eb49ee0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(java.base@21.0.9/LockSupport.java:371) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block(java.base@21.0.9/AbstractQueuedSynchronizer.java:519) + at java.util.concurrent.ForkJoinPool.unmanagedBlock(java.base@21.0.9/ForkJoinPool.java:3778) + at java.util.concurrent.ForkJoinPool.managedBlock(java.base@21.0.9/ForkJoinPool.java:3723) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(java.base@21.0.9/AbstractQueuedSynchronizer.java:1712) + at java.util.concurrent.LinkedBlockingQueue.take(java.base@21.0.9/LinkedBlockingQueue.java:435) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:117) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1114) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1175) + at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"http-nio-18080-exec-3" #67 [26732] daemon prio=5 os_prio=0 cpu=125.00ms elapsed=2033.58s tid=0x00000206fb99b890 nid=26732 waiting on condition [0x000000a958aff000] + java.lang.Thread.State: WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068eb49ee0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(java.base@21.0.9/LockSupport.java:371) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block(java.base@21.0.9/AbstractQueuedSynchronizer.java:519) + at java.util.concurrent.ForkJoinPool.unmanagedBlock(java.base@21.0.9/ForkJoinPool.java:3778) + at java.util.concurrent.ForkJoinPool.managedBlock(java.base@21.0.9/ForkJoinPool.java:3723) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(java.base@21.0.9/AbstractQueuedSynchronizer.java:1712) + at java.util.concurrent.LinkedBlockingQueue.take(java.base@21.0.9/LinkedBlockingQueue.java:435) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:117) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1114) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1175) + at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"http-nio-18080-exec-4" #68 [25444] daemon prio=5 os_prio=0 cpu=500.00ms elapsed=2033.58s tid=0x00000206fb9997c0 nid=25444 waiting on condition [0x000000a958bff000] + java.lang.Thread.State: WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068eb49ee0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(java.base@21.0.9/LockSupport.java:371) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block(java.base@21.0.9/AbstractQueuedSynchronizer.java:519) + at java.util.concurrent.ForkJoinPool.unmanagedBlock(java.base@21.0.9/ForkJoinPool.java:3778) + at java.util.concurrent.ForkJoinPool.managedBlock(java.base@21.0.9/ForkJoinPool.java:3723) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(java.base@21.0.9/AbstractQueuedSynchronizer.java:1712) + at java.util.concurrent.LinkedBlockingQueue.take(java.base@21.0.9/LinkedBlockingQueue.java:435) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:117) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1114) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1175) + at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"http-nio-18080-exec-5" #69 [9372] daemon prio=5 os_prio=0 cpu=125.00ms elapsed=2033.58s tid=0x00000206fb99c5b0 nid=9372 runnable [0x000000a958cf8000] + java.lang.Thread.State: RUNNABLE + at sun.nio.ch.Net.poll(java.base@21.0.9/Native Method) + at sun.nio.ch.NioSocketImpl.park(java.base@21.0.9/NioSocketImpl.java:191) + at sun.nio.ch.NioSocketImpl.park(java.base@21.0.9/NioSocketImpl.java:201) + at sun.nio.ch.NioSocketImpl.implRead(java.base@21.0.9/NioSocketImpl.java:309) + at sun.nio.ch.NioSocketImpl.read(java.base@21.0.9/NioSocketImpl.java:346) + at sun.nio.ch.NioSocketImpl$1.read(java.base@21.0.9/NioSocketImpl.java:796) + at java.net.Socket$SocketInputStream.read(java.base@21.0.9/Socket.java:1099) + at com.mysql.cj.protocol.ReadAheadInputStream.fill(ReadAheadInputStream.java:107) + at com.mysql.cj.protocol.ReadAheadInputStream.readFromUnderlyingStreamIfNecessary(ReadAheadInputStream.java:150) + at com.mysql.cj.protocol.ReadAheadInputStream.read(ReadAheadInputStream.java:180) + - locked <0x000000068ee69208> (a com.mysql.cj.protocol.ReadAheadInputStream) + at java.io.FilterInputStream.read(java.base@21.0.9/FilterInputStream.java:119) + at com.mysql.cj.protocol.FullReadInputStream.readFully(FullReadInputStream.java:64) + at com.mysql.cj.protocol.a.SimplePacketReader.readHeaderLocal(SimplePacketReader.java:81) + at com.mysql.cj.protocol.a.SimplePacketReader.readHeader(SimplePacketReader.java:63) + at com.mysql.cj.protocol.a.SimplePacketReader.readHeader(SimplePacketReader.java:45) + at com.mysql.cj.protocol.a.TimeTrackingPacketReader.readHeader(TimeTrackingPacketReader.java:52) + at com.mysql.cj.protocol.a.TimeTrackingPacketReader.readHeader(TimeTrackingPacketReader.java:41) + at com.mysql.cj.protocol.a.MultiPacketReader.readHeader(MultiPacketReader.java:54) + at com.mysql.cj.protocol.a.MultiPacketReader.readHeader(MultiPacketReader.java:44) + at com.mysql.cj.protocol.a.NativeProtocol.readMessage(NativeProtocol.java:576) + at com.mysql.cj.protocol.a.NativeProtocol.checkErrorMessage(NativeProtocol.java:762) + at com.mysql.cj.protocol.a.NativeProtocol.sendCommand(NativeProtocol.java:701) + at com.mysql.cj.protocol.a.NativeProtocol.sendQueryPacket(NativeProtocol.java:1050) + at com.mysql.cj.NativeSession.execSQL(NativeSession.java:660) + at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:889) + - locked <0x000000068ee66c78> (a com.mysql.cj.jdbc.ConnectionImpl) + at com.mysql.cj.jdbc.ClientPreparedStatement.execute(ClientPreparedStatement.java:354) + - locked <0x000000068ee66c78> (a com.mysql.cj.jdbc.ConnectionImpl) + at com.zaxxer.hikari.pool.ProxyPreparedStatement.execute(ProxyPreparedStatement.java:44) + at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.execute(HikariProxyPreparedStatement.java) + at java.lang.invoke.LambdaForm$DMH/0x00000206810d2c00.invokeInterface(java.base@21.0.9/LambdaForm$DMH) + at java.lang.invoke.LambdaForm$MH/0x00000206811f8400.invoke(java.base@21.0.9/LambdaForm$MH) + at java.lang.invoke.Invokers$Holder.invokeExact_MT(java.base@21.0.9/Invokers$Holder) + at jdk.internal.reflect.DirectMethodHandleAccessor.invokeImpl(java.base@21.0.9/DirectMethodHandleAccessor.java:153) + at jdk.internal.reflect.DirectMethodHandleAccessor.invoke(java.base@21.0.9/DirectMethodHandleAccessor.java:103) + at java.lang.reflect.Method.invoke(java.base@21.0.9/Method.java:580) + at org.apache.ibatis.logging.jdbc.PreparedStatementLogger.invoke(PreparedStatementLogger.java:58) + at jdk.proxy3.$Proxy198.execute(jdk.proxy3/Unknown Source) + at org.apache.ibatis.executor.statement.PreparedStatementHandler.update(PreparedStatementHandler.java:48) + at org.apache.ibatis.executor.statement.RoutingStatementHandler.update(RoutingStatementHandler.java:75) + at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:50) + at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:117) + at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:76) + at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:197) + at org.apache.ibatis.session.defaults.DefaultSqlSession.insert(DefaultSqlSession.java:184) + at java.lang.invoke.LambdaForm$DMH/0x0000020681588800.invokeInterface(java.base@21.0.9/LambdaForm$DMH) + at java.lang.invoke.LambdaForm$MH/0x0000020681c38c00.invoke(java.base@21.0.9/LambdaForm$MH) + at java.lang.invoke.LambdaForm$MH/0x00000206810c8000.invokeExact_MT(java.base@21.0.9/LambdaForm$MH) + at jdk.internal.reflect.DirectMethodHandleAccessor.invokeImpl(java.base@21.0.9/DirectMethodHandleAccessor.java:155) + at jdk.internal.reflect.DirectMethodHandleAccessor.invoke(java.base@21.0.9/DirectMethodHandleAccessor.java:103) + at java.lang.reflect.Method.invoke(java.base@21.0.9/Method.java:580) + at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:425) + at jdk.proxy2.$Proxy119.insert(jdk.proxy2/Unknown Source) + at org.mybatis.spring.SqlSessionTemplate.insert(SqlSessionTemplate.java:272) + at com.baomidou.mybatisplus.core.override.MybatisMapperMethod.execute(MybatisMapperMethod.java:59) + at com.baomidou.mybatisplus.core.override.MybatisMapperProxy$PlainMethodInvoker.invoke(MybatisMapperProxy.java:152) + at com.baomidou.mybatisplus.core.override.MybatisMapperProxy.invoke(MybatisMapperProxy.java:89) + at jdk.proxy2.$Proxy122.insert(jdk.proxy2/Unknown Source) + at com.nanri.aiimage.modules.deletebrand.service.DeleteBrandRunService.run(DeleteBrandRunService.java:103) + at java.lang.invoke.LambdaForm$DMH/0x0000020681120c00.invokeVirtual(java.base@21.0.9/LambdaForm$DMH) + at java.lang.invoke.LambdaForm$MH/0x00000206810b0400.invoke(java.base@21.0.9/LambdaForm$MH) + at java.lang.invoke.Invokers$Holder.invokeExact_MT(java.base@21.0.9/Invokers$Holder) + at jdk.internal.reflect.DirectMethodHandleAccessor.invokeImpl(java.base@21.0.9/DirectMethodHandleAccessor.java:154) + at jdk.internal.reflect.DirectMethodHandleAccessor.invoke(java.base@21.0.9/DirectMethodHandleAccessor.java:103) + at java.lang.reflect.Method.invoke(java.base@21.0.9/Method.java:580) + at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:355) + at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:716) + at com.nanri.aiimage.modules.deletebrand.service.DeleteBrandRunService$$SpringCGLIB$$0.run() + at com.nanri.aiimage.modules.deletebrand.controller.DeleteBrandRunController.run(DeleteBrandRunController.java:54) + at java.lang.invoke.LambdaForm$DMH/0x0000020681120c00.invokeVirtual(java.base@21.0.9/LambdaForm$DMH) + at java.lang.invoke.LambdaForm$MH/0x00000206810b0400.invoke(java.base@21.0.9/LambdaForm$MH) + at java.lang.invoke.Invokers$Holder.invokeExact_MT(java.base@21.0.9/Invokers$Holder) + at jdk.internal.reflect.DirectMethodHandleAccessor.invokeImpl(java.base@21.0.9/DirectMethodHandleAccessor.java:154) + at jdk.internal.reflect.DirectMethodHandleAccessor.invoke(java.base@21.0.9/DirectMethodHandleAccessor.java:103) + at java.lang.reflect.Method.invoke(java.base@21.0.9/Method.java:580) + at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:255) + at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:188) + at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) + at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:926) + at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:831) + at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) + at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) + at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) + at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) + at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914) + at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:590) + at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) + at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at com.nanri.aiimage.config.RequestTraceFilter.doFilterInternal(RequestTraceFilter.java:48) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) + at org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) + at org.springframework.security.web.FilterChainProxy$$Lambda/0x0000020681ac1a68.doFilter(Unknown Source) + at org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:479) + at org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation$$Lambda/0x0000020681aca0a8.doFilter(Unknown Source) + at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:340) + at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation$$Lambda/0x0000020681aca2c0.doFilter(Unknown Source) + at org.springframework.security.web.ObservationFilterChainDecorator.lambda$wrapSecured$0(ObservationFilterChainDecorator.java:82) + at org.springframework.security.web.ObservationFilterChainDecorator$$Lambda/0x0000020681ac1e98.doFilter(Unknown Source) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:128) + at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:100) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:126) + at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:120) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilterInternal(BasicAuthenticationFilter.java:181) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) + at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) + at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) + at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter$$Lambda/0x0000020681ac3c50.doFilter(Unknown Source) + at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323) + at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation$$Lambda/0x0000020681ac3e88.doFilter(Unknown Source) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) + at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$3(HandlerMappingIntrospector.java:195) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda/0x0000020681914658.doFilter(Unknown Source) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) + at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:230) + at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:362) + at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:278) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:113) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) + at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) + at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483) + at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115) + at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) + at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) + at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) + at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:397) + at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) + at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:905) + at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1743) + at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1190) + at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"http-nio-18080-exec-6" #70 [35160] daemon prio=5 os_prio=0 cpu=31.25ms elapsed=2033.58s tid=0x00000206fb99bf20 nid=35160 waiting on condition [0x000000a958dfe000] + java.lang.Thread.State: WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068eb49ee0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(java.base@21.0.9/LockSupport.java:371) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block(java.base@21.0.9/AbstractQueuedSynchronizer.java:519) + at java.util.concurrent.ForkJoinPool.unmanagedBlock(java.base@21.0.9/ForkJoinPool.java:3778) + at java.util.concurrent.ForkJoinPool.managedBlock(java.base@21.0.9/ForkJoinPool.java:3723) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(java.base@21.0.9/AbstractQueuedSynchronizer.java:1712) + at java.util.concurrent.LinkedBlockingQueue.take(java.base@21.0.9/LinkedBlockingQueue.java:435) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:117) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1114) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1175) + at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"http-nio-18080-exec-7" #71 [6692] daemon prio=5 os_prio=0 cpu=62.50ms elapsed=2033.58s tid=0x00000206fb99b200 nid=6692 waiting on condition [0x000000a958efe000] + java.lang.Thread.State: WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068eb49ee0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(java.base@21.0.9/LockSupport.java:371) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block(java.base@21.0.9/AbstractQueuedSynchronizer.java:519) + at java.util.concurrent.ForkJoinPool.unmanagedBlock(java.base@21.0.9/ForkJoinPool.java:3778) + at java.util.concurrent.ForkJoinPool.managedBlock(java.base@21.0.9/ForkJoinPool.java:3723) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(java.base@21.0.9/AbstractQueuedSynchronizer.java:1712) + at java.util.concurrent.LinkedBlockingQueue.take(java.base@21.0.9/LinkedBlockingQueue.java:435) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:117) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1114) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1175) + at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"http-nio-18080-exec-8" #72 [12476] daemon prio=5 os_prio=0 cpu=31.25ms elapsed=2033.58s tid=0x00000206fb999e50 nid=12476 waiting on condition [0x000000a958ffe000] + java.lang.Thread.State: WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068eb49ee0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(java.base@21.0.9/LockSupport.java:371) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block(java.base@21.0.9/AbstractQueuedSynchronizer.java:519) + at java.util.concurrent.ForkJoinPool.unmanagedBlock(java.base@21.0.9/ForkJoinPool.java:3778) + at java.util.concurrent.ForkJoinPool.managedBlock(java.base@21.0.9/ForkJoinPool.java:3723) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(java.base@21.0.9/AbstractQueuedSynchronizer.java:1712) + at java.util.concurrent.LinkedBlockingQueue.take(java.base@21.0.9/LinkedBlockingQueue.java:435) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:117) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1114) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1175) + at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"http-nio-18080-exec-9" #73 [26972] daemon prio=5 os_prio=0 cpu=109.38ms elapsed=2033.58s tid=0x00000206fb99a4e0 nid=26972 waiting on condition [0x000000a9590ff000] + java.lang.Thread.State: WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068eb49ee0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(java.base@21.0.9/LockSupport.java:371) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block(java.base@21.0.9/AbstractQueuedSynchronizer.java:519) + at java.util.concurrent.ForkJoinPool.unmanagedBlock(java.base@21.0.9/ForkJoinPool.java:3778) + at java.util.concurrent.ForkJoinPool.managedBlock(java.base@21.0.9/ForkJoinPool.java:3723) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(java.base@21.0.9/AbstractQueuedSynchronizer.java:1712) + at java.util.concurrent.LinkedBlockingQueue.take(java.base@21.0.9/LinkedBlockingQueue.java:435) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:117) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1114) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1175) + at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"http-nio-18080-exec-10" #74 [35860] daemon prio=5 os_prio=0 cpu=62.50ms elapsed=2033.58s tid=0x00000206fb99ab70 nid=35860 waiting on condition [0x000000a9591ff000] + java.lang.Thread.State: WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068eb49ee0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(java.base@21.0.9/LockSupport.java:371) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block(java.base@21.0.9/AbstractQueuedSynchronizer.java:519) + at java.util.concurrent.ForkJoinPool.unmanagedBlock(java.base@21.0.9/ForkJoinPool.java:3778) + at java.util.concurrent.ForkJoinPool.managedBlock(java.base@21.0.9/ForkJoinPool.java:3723) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(java.base@21.0.9/AbstractQueuedSynchronizer.java:1712) + at java.util.concurrent.LinkedBlockingQueue.take(java.base@21.0.9/LinkedBlockingQueue.java:435) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:117) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1114) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1175) + at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"http-nio-18080-Poller" #75 [28396] daemon prio=5 os_prio=0 cpu=109.38ms elapsed=2033.58s tid=0x00000206f613bc10 nid=28396 runnable [0x000000a9592ff000] + java.lang.Thread.State: RUNNABLE + at sun.nio.ch.WEPoll.wait(java.base@21.0.9/Native Method) + at sun.nio.ch.WEPollSelectorImpl.doSelect(java.base@21.0.9/WEPollSelectorImpl.java:114) + at sun.nio.ch.SelectorImpl.lockAndDoSelect(java.base@21.0.9/SelectorImpl.java:130) + - locked <0x000000068eb0e360> (a sun.nio.ch.Util$2) + - locked <0x000000068eb0e300> (a sun.nio.ch.WEPollSelectorImpl) + at sun.nio.ch.SelectorImpl.select(java.base@21.0.9/SelectorImpl.java:142) + at org.apache.tomcat.util.net.NioEndpoint$Poller.run(NioEndpoint.java:757) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"http-nio-18080-Acceptor" #76 [1948] daemon prio=5 os_prio=0 cpu=15.62ms elapsed=2033.58s tid=0x00000206f613aef0 nid=1948 runnable [0x000000a9593ff000] + java.lang.Thread.State: RUNNABLE + at sun.nio.ch.Net.accept(java.base@21.0.9/Native Method) + at sun.nio.ch.ServerSocketChannelImpl.implAccept(java.base@21.0.9/ServerSocketChannelImpl.java:433) + at sun.nio.ch.ServerSocketChannelImpl.accept(java.base@21.0.9/ServerSocketChannelImpl.java:399) + at org.apache.tomcat.util.net.NioEndpoint.serverSocketAccept(NioEndpoint.java:518) + at org.apache.tomcat.util.net.NioEndpoint.serverSocketAccept(NioEndpoint.java:71) + at org.apache.tomcat.util.net.Acceptor.run(Acceptor.java:128) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"aiimage-scheduling-1" #77 [14256] prio=5 os_prio=0 cpu=546.88ms elapsed=2033.55s tid=0x00000206f613dce0 nid=14256 waiting on condition [0x000000a9594fe000] + java.lang.Thread.State: WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068e2666c0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(java.base@21.0.9/LockSupport.java:371) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block(java.base@21.0.9/AbstractQueuedSynchronizer.java:519) + at java.util.concurrent.ForkJoinPool.unmanagedBlock(java.base@21.0.9/ForkJoinPool.java:3778) + at java.util.concurrent.ForkJoinPool.managedBlock(java.base@21.0.9/ForkJoinPool.java:3723) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(java.base@21.0.9/AbstractQueuedSynchronizer.java:1712) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@21.0.9/ScheduledThreadPoolExecutor.java:1177) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@21.0.9/ScheduledThreadPoolExecutor.java:899) + at java.util.concurrent.ThreadPoolExecutor.getTask(java.base@21.0.9/ThreadPoolExecutor.java:1070) + at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@21.0.9/ThreadPoolExecutor.java:1130) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@21.0.9/ThreadPoolExecutor.java:642) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"aiimage-scheduling-2" #78 [31968] prio=5 os_prio=0 cpu=328.12ms elapsed=2033.55s tid=0x00000206f613b580 nid=31968 waiting on condition [0x000000a9595ff000] + java.lang.Thread.State: TIMED_WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068e2666c0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.parkNanos(java.base@21.0.9/LockSupport.java:269) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(java.base@21.0.9/AbstractQueuedSynchronizer.java:1763) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@21.0.9/ScheduledThreadPoolExecutor.java:1182) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@21.0.9/ScheduledThreadPoolExecutor.java:899) + at java.util.concurrent.ThreadPoolExecutor.getTask(java.base@21.0.9/ThreadPoolExecutor.java:1070) + at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@21.0.9/ThreadPoolExecutor.java:1130) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@21.0.9/ThreadPoolExecutor.java:642) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"aiimage-scheduling-3" #79 [13816] prio=5 os_prio=0 cpu=1671.88ms elapsed=2033.55s tid=0x00000206f613f090 nid=13816 waiting on condition [0x000000a9596ff000] + java.lang.Thread.State: TIMED_WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068e2666c0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.parkNanos(java.base@21.0.9/LockSupport.java:269) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(java.base@21.0.9/AbstractQueuedSynchronizer.java:1763) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@21.0.9/ScheduledThreadPoolExecutor.java:1182) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@21.0.9/ScheduledThreadPoolExecutor.java:899) + at java.util.concurrent.ThreadPoolExecutor.getTask(java.base@21.0.9/ThreadPoolExecutor.java:1070) + at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@21.0.9/ThreadPoolExecutor.java:1130) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@21.0.9/ThreadPoolExecutor.java:642) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"aiimage-scheduling-4" #80 [28896] prio=5 os_prio=0 cpu=343.75ms elapsed=2033.55s tid=0x00000206f613ea00 nid=28896 waiting on condition [0x000000a9597fe000] + java.lang.Thread.State: WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068e2666c0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(java.base@21.0.9/LockSupport.java:371) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block(java.base@21.0.9/AbstractQueuedSynchronizer.java:519) + at java.util.concurrent.ForkJoinPool.unmanagedBlock(java.base@21.0.9/ForkJoinPool.java:3778) + at java.util.concurrent.ForkJoinPool.managedBlock(java.base@21.0.9/ForkJoinPool.java:3723) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(java.base@21.0.9/AbstractQueuedSynchronizer.java:1712) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@21.0.9/ScheduledThreadPoolExecutor.java:1177) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@21.0.9/ScheduledThreadPoolExecutor.java:899) + at java.util.concurrent.ThreadPoolExecutor.getTask(java.base@21.0.9/ThreadPoolExecutor.java:1070) + at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@21.0.9/ThreadPoolExecutor.java:1130) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@21.0.9/ThreadPoolExecutor.java:642) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"lettuce-nioEventLoop-4-1" #81 [32980] daemon prio=5 os_prio=0 cpu=109.38ms elapsed=2033.11s tid=0x00000206f613c2a0 nid=32980 runnable [0x000000a9599fe000] + java.lang.Thread.State: RUNNABLE + at sun.nio.ch.WEPoll.wait(java.base@21.0.9/Native Method) + at sun.nio.ch.WEPollSelectorImpl.doSelect(java.base@21.0.9/WEPollSelectorImpl.java:114) + at sun.nio.ch.SelectorImpl.lockAndDoSelect(java.base@21.0.9/SelectorImpl.java:130) + - locked <0x000000068eb00fa0> (a io.netty.channel.nio.SelectedSelectionKeySet) + - locked <0x000000068eb00f40> (a sun.nio.ch.WEPollSelectorImpl) + at sun.nio.ch.SelectorImpl.select(java.base@21.0.9/SelectorImpl.java:147) + at io.netty.channel.nio.SelectedSelectionKeySetSelector.select(SelectedSelectionKeySetSelector.java:68) + at io.netty.channel.nio.NioEventLoop.select(NioEventLoop.java:887) + at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:526) + at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:998) + at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) + at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"HikariPool-1 housekeeper" #82 [29912] daemon prio=5 os_prio=0 cpu=0.00ms elapsed=2032.86s tid=0x00000206f613c930 nid=29912 waiting on condition [0x000000a959aff000] + java.lang.Thread.State: TIMED_WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068eb28cf8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.parkNanos(java.base@21.0.9/LockSupport.java:269) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(java.base@21.0.9/AbstractQueuedSynchronizer.java:1763) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@21.0.9/ScheduledThreadPoolExecutor.java:1182) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@21.0.9/ScheduledThreadPoolExecutor.java:899) + at java.util.concurrent.ThreadPoolExecutor.getTask(java.base@21.0.9/ThreadPoolExecutor.java:1070) + at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@21.0.9/ThreadPoolExecutor.java:1130) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@21.0.9/ThreadPoolExecutor.java:642) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"DestroyJavaVM" #84 [15708] prio=5 os_prio=0 cpu=6906.25ms elapsed=2032.85s tid=0x00000206f6140440 nid=15708 waiting on condition [0x0000000000000000] + java.lang.Thread.State: RUNNABLE + +"boundedElastic-evictor-1" #85 [18992] daemon prio=5 os_prio=0 cpu=15.62ms elapsed=2010.51s tid=0x00000206f613fdb0 nid=18992 waiting on condition [0x000000a955dfe000] + java.lang.Thread.State: TIMED_WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068f347428> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.parkNanos(java.base@21.0.9/LockSupport.java:269) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(java.base@21.0.9/AbstractQueuedSynchronizer.java:1763) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@21.0.9/ScheduledThreadPoolExecutor.java:1182) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@21.0.9/ScheduledThreadPoolExecutor.java:899) + at java.util.concurrent.ThreadPoolExecutor.getTask(java.base@21.0.9/ThreadPoolExecutor.java:1070) + at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@21.0.9/ThreadPoolExecutor.java:1130) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@21.0.9/ThreadPoolExecutor.java:642) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"lettuce-nioEventLoop-4-2" #87 [29928] daemon prio=5 os_prio=0 cpu=15.62ms elapsed=2010.51s tid=0x00000206f6140ad0 nid=29928 runnable [0x000000a9561fe000] + java.lang.Thread.State: RUNNABLE + at sun.nio.ch.WEPoll.wait(java.base@21.0.9/Native Method) + at sun.nio.ch.WEPollSelectorImpl.doSelect(java.base@21.0.9/WEPollSelectorImpl.java:114) + at sun.nio.ch.SelectorImpl.lockAndDoSelect(java.base@21.0.9/SelectorImpl.java:130) + - locked <0x000000068eb22280> (a io.netty.channel.nio.SelectedSelectionKeySet) + - locked <0x000000068eb22220> (a sun.nio.ch.WEPollSelectorImpl) + at sun.nio.ch.SelectorImpl.select(java.base@21.0.9/SelectorImpl.java:147) + at io.netty.channel.nio.SelectedSelectionKeySetSelector.select(SelectedSelectionKeySetSelector.java:68) + at io.netty.channel.nio.NioEventLoop.select(NioEventLoop.java:887) + at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:526) + at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:998) + at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) + at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"lettuce-eventExecutorLoop-1-1" #112 [34372] daemon prio=5 os_prio=0 cpu=0.00ms elapsed=1024.00s tid=0x00000206f6141e80 nid=34372 waiting on condition [0x000000a954bfe000] + java.lang.Thread.State: WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068dc62420> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(java.base@21.0.9/LockSupport.java:371) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block(java.base@21.0.9/AbstractQueuedSynchronizer.java:519) + at java.util.concurrent.ForkJoinPool.unmanagedBlock(java.base@21.0.9/ForkJoinPool.java:3778) + at java.util.concurrent.ForkJoinPool.managedBlock(java.base@21.0.9/ForkJoinPool.java:3723) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(java.base@21.0.9/AbstractQueuedSynchronizer.java:1712) + at java.util.concurrent.LinkedBlockingQueue.take(java.base@21.0.9/LinkedBlockingQueue.java:435) + at io.netty.util.concurrent.SingleThreadEventExecutor.takeTask(SingleThreadEventExecutor.java:243) + at io.netty.util.concurrent.DefaultEventExecutor.run(DefaultEventExecutor.java:64) + at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:998) + at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) + at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"lettuce-nioEventLoop-4-3" #113 [14056] daemon prio=5 os_prio=0 cpu=78.12ms elapsed=1023.99s tid=0x00000206f6141160 nid=14056 runnable [0x000000a954cfe000] + java.lang.Thread.State: RUNNABLE + at sun.nio.ch.WEPoll.wait(java.base@21.0.9/Native Method) + at sun.nio.ch.WEPollSelectorImpl.doSelect(java.base@21.0.9/WEPollSelectorImpl.java:114) + at sun.nio.ch.SelectorImpl.lockAndDoSelect(java.base@21.0.9/SelectorImpl.java:130) + - locked <0x000000068ec204a0> (a io.netty.channel.nio.SelectedSelectionKeySet) + - locked <0x000000068ec214c8> (a sun.nio.ch.WEPollSelectorImpl) + at sun.nio.ch.SelectorImpl.select(java.base@21.0.9/SelectorImpl.java:147) + at io.netty.channel.nio.SelectedSelectionKeySetSelector.select(SelectedSelectionKeySetSelector.java:68) + at io.netty.channel.nio.NioEventLoop.select(NioEventLoop.java:887) + at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:526) + at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:998) + at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) + at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"OkHttp TaskRunner" #121 [37016] daemon prio=5 os_prio=0 cpu=0.00ms elapsed=337.15s tid=0x00000206fbd56be0 nid=37016 waiting on condition [0x000000a9598ff000] + java.lang.Thread.State: TIMED_WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068fff7390> (a java.util.concurrent.SynchronousQueue$Transferer) + at java.util.concurrent.locks.LockSupport.parkNanos(java.base@21.0.9/LockSupport.java:410) + at java.util.concurrent.LinkedTransferQueue$DualNode.await(java.base@21.0.9/LinkedTransferQueue.java:452) + at java.util.concurrent.SynchronousQueue$Transferer.xferLifo(java.base@21.0.9/SynchronousQueue.java:194) + at java.util.concurrent.SynchronousQueue.xfer(java.base@21.0.9/SynchronousQueue.java:235) + at java.util.concurrent.SynchronousQueue.poll(java.base@21.0.9/SynchronousQueue.java:338) + at java.util.concurrent.ThreadPoolExecutor.getTask(java.base@21.0.9/ThreadPoolExecutor.java:1069) + at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@21.0.9/ThreadPoolExecutor.java:1130) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@21.0.9/ThreadPoolExecutor.java:642) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"Okio Watchdog" #122 [18768] daemon prio=5 os_prio=0 cpu=15.62ms elapsed=337.15s tid=0x00000206fbd56550 nid=18768 waiting on condition [0x000000a9572ff000] + java.lang.Thread.State: TIMED_WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068ffece98> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.parkNanos(java.base@21.0.9/LockSupport.java:269) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(java.base@21.0.9/AbstractQueuedSynchronizer.java:1852) + at okio.AsyncTimeout$Companion.awaitTimeout$okio(AsyncTimeout.kt:320) + at okio.AsyncTimeout$Watchdog.run(AsyncTimeout.kt:186) + +"task-file-job-dispatch-1" #125 [37848] prio=5 os_prio=0 cpu=1515.62ms elapsed=335.26s tid=0x00000206fbd54b10 nid=37848 waiting on condition [0x000000a956cff000] + java.lang.Thread.State: WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068e362240> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(java.base@21.0.9/LockSupport.java:371) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block(java.base@21.0.9/AbstractQueuedSynchronizer.java:519) + at java.util.concurrent.ForkJoinPool.unmanagedBlock(java.base@21.0.9/ForkJoinPool.java:3778) + at java.util.concurrent.ForkJoinPool.managedBlock(java.base@21.0.9/ForkJoinPool.java:3723) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(java.base@21.0.9/AbstractQueuedSynchronizer.java:1712) + at java.util.concurrent.LinkedBlockingQueue.take(java.base@21.0.9/LinkedBlockingQueue.java:435) + at java.util.concurrent.ThreadPoolExecutor.getTask(java.base@21.0.9/ThreadPoolExecutor.java:1070) + at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@21.0.9/ThreadPoolExecutor.java:1130) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@21.0.9/ThreadPoolExecutor.java:642) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"Java2D Disposer" #129 [13036] daemon prio=10 os_prio=2 cpu=0.00ms elapsed=334.37s tid=0x00000206fbd58620 nid=13036 waiting on condition [0x000000a959efe000] + java.lang.Thread.State: WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068fdafd50> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(java.base@21.0.9/LockSupport.java:371) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block(java.base@21.0.9/AbstractQueuedSynchronizer.java:519) + at java.util.concurrent.ForkJoinPool.unmanagedBlock(java.base@21.0.9/ForkJoinPool.java:3778) + at java.util.concurrent.ForkJoinPool.managedBlock(java.base@21.0.9/ForkJoinPool.java:3723) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(java.base@21.0.9/AbstractQueuedSynchronizer.java:1712) + at java.lang.ref.ReferenceQueue.await(java.base@21.0.9/ReferenceQueue.java:67) + at java.lang.ref.ReferenceQueue.remove0(java.base@21.0.9/ReferenceQueue.java:158) + at java.lang.ref.ReferenceQueue.remove(java.base@21.0.9/ReferenceQueue.java:234) + at sun.java2d.Disposer.run(java.desktop@21.0.9/Disposer.java:145) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"task-file-job-dispatch-2" #131 [33976] prio=5 os_prio=0 cpu=546.88ms elapsed=333.03s tid=0x00000206fbd59340 nid=33976 waiting on condition [0x000000a959fff000] + java.lang.Thread.State: WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068e362240> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(java.base@21.0.9/LockSupport.java:371) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block(java.base@21.0.9/AbstractQueuedSynchronizer.java:519) + at java.util.concurrent.ForkJoinPool.unmanagedBlock(java.base@21.0.9/ForkJoinPool.java:3778) + at java.util.concurrent.ForkJoinPool.managedBlock(java.base@21.0.9/ForkJoinPool.java:3723) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(java.base@21.0.9/AbstractQueuedSynchronizer.java:1712) + at java.util.concurrent.LinkedBlockingQueue.take(java.base@21.0.9/LinkedBlockingQueue.java:435) + at java.util.concurrent.ThreadPoolExecutor.getTask(java.base@21.0.9/ThreadPoolExecutor.java:1070) + at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@21.0.9/ThreadPoolExecutor.java:1130) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@21.0.9/ThreadPoolExecutor.java:642) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"boundedElastic-2" #208 [22740] daemon prio=5 os_prio=0 cpu=0.00ms elapsed=44.42s tid=0x00000206ff0af860 nid=22740 waiting on condition [0x000000a954afe000] + java.lang.Thread.State: WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x00000007ffd58b90> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(java.base@21.0.9/LockSupport.java:371) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block(java.base@21.0.9/AbstractQueuedSynchronizer.java:519) + at java.util.concurrent.ForkJoinPool.unmanagedBlock(java.base@21.0.9/ForkJoinPool.java:3778) + at java.util.concurrent.ForkJoinPool.managedBlock(java.base@21.0.9/ForkJoinPool.java:3723) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(java.base@21.0.9/AbstractQueuedSynchronizer.java:1712) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@21.0.9/ScheduledThreadPoolExecutor.java:1170) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@21.0.9/ScheduledThreadPoolExecutor.java:899) + at java.util.concurrent.ThreadPoolExecutor.getTask(java.base@21.0.9/ThreadPoolExecutor.java:1070) + at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@21.0.9/ThreadPoolExecutor.java:1130) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@21.0.9/ThreadPoolExecutor.java:642) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"lettuce-eventExecutorLoop-1-2" #209 [2160] daemon prio=5 os_prio=0 cpu=0.00ms elapsed=39.39s tid=0x00000206ff0b2650 nid=2160 waiting on condition [0x000000a9562ff000] + java.lang.Thread.State: WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068dc62200> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(java.base@21.0.9/LockSupport.java:371) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block(java.base@21.0.9/AbstractQueuedSynchronizer.java:519) + at java.util.concurrent.ForkJoinPool.unmanagedBlock(java.base@21.0.9/ForkJoinPool.java:3778) + at java.util.concurrent.ForkJoinPool.managedBlock(java.base@21.0.9/ForkJoinPool.java:3723) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(java.base@21.0.9/AbstractQueuedSynchronizer.java:1712) + at java.util.concurrent.LinkedBlockingQueue.take(java.base@21.0.9/LinkedBlockingQueue.java:435) + at io.netty.util.concurrent.SingleThreadEventExecutor.takeTask(SingleThreadEventExecutor.java:243) + at io.netty.util.concurrent.DefaultEventExecutor.run(DefaultEventExecutor.java:64) + at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:998) + at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) + at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"OkHttp TaskRunner" #210 [37308] daemon prio=5 os_prio=0 cpu=0.00ms elapsed=36.98s tid=0x00000206ff0b1fc0 nid=37308 in Object.wait() [0x000000a956bff000] + java.lang.Thread.State: TIMED_WAITING (on object monitor) + at java.lang.Object.wait0(java.base@21.0.9/Native Method) + - waiting on + at java.lang.Object.wait(java.base@21.0.9/Object.java:366) + at java.lang.Object.wait(java.base@21.0.9/Object.java:488) + at okhttp3.internal.concurrent.TaskRunner$RealBackend.coordinatorWait(TaskRunner.kt:294) + at okhttp3.internal.concurrent.TaskRunner.awaitTaskToRun(TaskRunner.kt:218) + at okhttp3.internal.concurrent.TaskRunner$runnable$1.run(TaskRunner.kt:59) + - locked <0x000000068ffeefb0> (a okhttp3.internal.concurrent.TaskRunner) + at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@21.0.9/ThreadPoolExecutor.java:1144) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@21.0.9/ThreadPoolExecutor.java:642) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"lettuce-eventExecutorLoop-1-3" #211 [35152] daemon prio=5 os_prio=0 cpu=0.00ms elapsed=25.18s tid=0x00000206ff0b2ce0 nid=35152 waiting on condition [0x000000a9573ff000] + java.lang.Thread.State: WAITING (parking) + at jdk.internal.misc.Unsafe.park(java.base@21.0.9/Native Method) + - parking to wait for <0x000000068dc61fe0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(java.base@21.0.9/LockSupport.java:371) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block(java.base@21.0.9/AbstractQueuedSynchronizer.java:519) + at java.util.concurrent.ForkJoinPool.unmanagedBlock(java.base@21.0.9/ForkJoinPool.java:3778) + at java.util.concurrent.ForkJoinPool.managedBlock(java.base@21.0.9/ForkJoinPool.java:3723) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(java.base@21.0.9/AbstractQueuedSynchronizer.java:1712) + at java.util.concurrent.LinkedBlockingQueue.take(java.base@21.0.9/LinkedBlockingQueue.java:435) + at io.netty.util.concurrent.SingleThreadEventExecutor.takeTask(SingleThreadEventExecutor.java:243) + at io.netty.util.concurrent.DefaultEventExecutor.run(DefaultEventExecutor.java:64) + at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:998) + at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) + at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"lettuce-nioEventLoop-4-4" #212 [32884] daemon prio=5 os_prio=0 cpu=0.00ms elapsed=25.18s tid=0x00000206ff0b3370 nid=32884 runnable [0x000000a9574fe000] + java.lang.Thread.State: RUNNABLE + at sun.nio.ch.WEPoll.wait(java.base@21.0.9/Native Method) + at sun.nio.ch.WEPollSelectorImpl.doSelect(java.base@21.0.9/WEPollSelectorImpl.java:114) + at sun.nio.ch.SelectorImpl.lockAndDoSelect(java.base@21.0.9/SelectorImpl.java:130) + - locked <0x000000068ec066d0> (a io.netty.channel.nio.SelectedSelectionKeySet) + - locked <0x000000068ec066e8> (a sun.nio.ch.WEPollSelectorImpl) + at sun.nio.ch.SelectorImpl.select(java.base@21.0.9/SelectorImpl.java:147) + at io.netty.channel.nio.SelectedSelectionKeySetSelector.select(SelectedSelectionKeySetSelector.java:68) + at io.netty.channel.nio.NioEventLoop.select(NioEventLoop.java:887) + at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:526) + at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:998) + at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) + at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) + at java.lang.Thread.runWith(java.base@21.0.9/Thread.java:1596) + at java.lang.Thread.run(java.base@21.0.9/Thread.java:1583) + +"G1 Conc#2" os_prio=2 cpu=218.75ms elapsed=2041.95s tid=0x00000206f735ee50 nid=18900 runnable + +"G1 Conc#1" os_prio=2 cpu=203.12ms elapsed=2041.96s tid=0x00000206f735eaa0 nid=33484 runnable + +"GC Thread#12" os_prio=2 cpu=62.50ms elapsed=2042.22s tid=0x00000206f785f210 nid=31868 runnable + +"GC Thread#11" os_prio=2 cpu=62.50ms elapsed=2042.22s tid=0x00000206f6581a70 nid=4496 runnable + +"GC Thread#10" os_prio=2 cpu=109.38ms elapsed=2042.22s tid=0x00000206f6253e90 nid=33260 runnable + +"GC Thread#9" os_prio=2 cpu=62.50ms elapsed=2042.22s tid=0x00000206f64433f0 nid=37096 runnable + +"GC Thread#8" os_prio=2 cpu=93.75ms elapsed=2043.16s tid=0x00000206f4bfe950 nid=33308 runnable + +"GC Thread#7" os_prio=2 cpu=93.75ms elapsed=2043.16s tid=0x00000206f4bfe5a0 nid=29340 runnable + +"GC Thread#6" os_prio=2 cpu=109.38ms elapsed=2043.16s tid=0x00000206f4bfd5e0 nid=30004 runnable + +"GC Thread#5" os_prio=2 cpu=109.38ms elapsed=2043.16s tid=0x00000206f4bfd230 nid=37836 runnable + +"GC Thread#4" os_prio=2 cpu=78.12ms elapsed=2043.16s tid=0x00000206f535ee00 nid=28080 runnable + +"GC Thread#1" os_prio=2 cpu=93.75ms elapsed=2043.16s tid=0x00000206f4dddd20 nid=7744 runnable + +"GC Thread#3" os_prio=2 cpu=62.50ms elapsed=2043.16s tid=0x00000206f535ea50 nid=32244 runnable + +"GC Thread#2" os_prio=2 cpu=78.12ms elapsed=2043.16s tid=0x00000206f4ef5900 nid=36548 runnable + +"VM Thread" os_prio=2 cpu=109.38ms elapsed=2043.38s tid=0x00000206ef850220 nid=36908 runnable + +"VM Periodic Task Thread" os_prio=2 cpu=828.12ms elapsed=2043.38s tid=0x00000206ef839e80 nid=26176 waiting on condition + +"G1 Service" os_prio=2 cpu=78.12ms elapsed=2043.39s tid=0x00000206ef708f10 nid=29916 runnable + +"G1 Refine#0" os_prio=2 cpu=875.00ms elapsed=2043.39s tid=0x00000206ef7076d0 nid=32744 runnable + +"G1 Conc#0" os_prio=2 cpu=203.12ms elapsed=2043.39s tid=0x00000206cdf35680 nid=10956 runnable + +"G1 Main Marker" os_prio=2 cpu=0.00ms elapsed=2043.39s tid=0x00000206cdf33740 nid=17824 runnable + +"GC Thread#0" os_prio=2 cpu=93.75ms elapsed=2043.39s tid=0x00000206cdf26720 nid=36252 runnable + +JNI global refs: 52, weak refs: 1 + diff --git a/backend-java/src/main/java/com/nanri/aiimage/config/AppearancePatentProperties.java b/backend-java/src/main/java/com/nanri/aiimage/config/AppearancePatentProperties.java index b75b930..29e672e 100644 --- a/backend-java/src/main/java/com/nanri/aiimage/config/AppearancePatentProperties.java +++ b/backend-java/src/main/java/com/nanri/aiimage/config/AppearancePatentProperties.java @@ -11,6 +11,8 @@ public class AppearancePatentProperties { private String cozeWorkflowId = "7632683471312355338"; private String cozeToken = ""; private int cozeBatchSize = 10; + private int cozeConnectTimeoutMillis = 10000; + private int cozeReadTimeoutMillis = 60000; private int staleTimeoutMinutes = 20; private String staleFinalizeCron = "0 */2 * * * *"; } diff --git a/backend-java/src/main/java/com/nanri/aiimage/config/DeleteBrandProgressProperties.java b/backend-java/src/main/java/com/nanri/aiimage/config/DeleteBrandProgressProperties.java index 3e17430..7c79101 100644 --- a/backend-java/src/main/java/com/nanri/aiimage/config/DeleteBrandProgressProperties.java +++ b/backend-java/src/main/java/com/nanri/aiimage/config/DeleteBrandProgressProperties.java @@ -7,6 +7,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = "aiimage.delete-brand-progress") public class DeleteBrandProgressProperties { private long heartbeatTimeoutMinutes = 15; + private long deleteBrandInitialTimeoutMinutes = 20; private String staleCheckCron = "*/30 * * * * *"; /** diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/appearancepatent/client/AppearancePatentCozeClient.java b/backend-java/src/main/java/com/nanri/aiimage/modules/appearancepatent/client/AppearancePatentCozeClient.java index e1de001..6518717 100644 --- a/backend-java/src/main/java/com/nanri/aiimage/modules/appearancepatent/client/AppearancePatentCozeClient.java +++ b/backend-java/src/main/java/com/nanri/aiimage/modules/appearancepatent/client/AppearancePatentCozeClient.java @@ -9,11 +9,14 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.http.MediaType; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.stereotype.Component; +import org.springframework.util.StreamUtils; import org.springframework.web.client.RestClient; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; +import java.util.Locale; import java.util.Map; @Component @@ -33,29 +36,72 @@ public class AppearancePatentCozeClient { return rows.stream().map(this::copy).toList(); } try { - String raw = postWorkflow(rows, prompt); - List results = parseResults(raw); - List merged = new ArrayList<>(); - for (int i = 0; i < rows.size(); i++) { - AppearancePatentResultRowDto row = copy(rows.get(i)); - if (i < results.size()) { - CozeResult result = results.get(i); - row.setTitleRisk(result.title()); - row.setAppearanceRisk(result.appearance()); - row.setPatentRisk(result.patent()); - row.setConclusion(result.result()); - } - merged.add(row); - } - return merged; + return inspectWithFallback(rows, prompt); } catch (Exception ex) { - log.warn("[appearance-patent] coze batch failed size={} err={}", rows.size(), ex.getMessage()); - return rows.stream().map(this::copy).toList(); + String failureMessage = failureMessage(ex); + log.warn("[appearance-patent] coze batch failed size={} err={}", rows.size(), failureMessage); + return rows.stream().map(this::copy).map(row -> markFailed(row, failureMessage)).toList(); } } + private List inspectWithFallback(List rows, String prompt) { + try { + if (rows.size() == 1) { + return inspectSingleRowWithRetry(rows, prompt); + } + InspectAttempt attempt = inspectOnce(rows, prompt); + if (attempt.resolvedCount() < rows.size()) { + throw new PartialCozeResultException(attempt.resolvedCount(), rows.size(), attempt.rawResultCount()); + } + return attempt.mergedRows(); + } catch (Exception ex) { + if (shouldSplitBatch(rows, ex)) { + int middle = rows.size() / 2; + log.warn("[appearance-patent] coze batch fallback split size={} left={} right={} err={}", + rows.size(), middle, rows.size() - middle, failureMessage(ex)); + List merged = new ArrayList<>(rows.size()); + merged.addAll(inspectWithFallback(rows.subList(0, middle), prompt)); + merged.addAll(inspectWithFallback(rows.subList(middle, rows.size()), prompt)); + return merged; + } + throw propagate(ex); + } + } + + private List inspectSingleRowWithRetry(List rows, String prompt) throws Exception { + AppearancePatentResultRowDto row = rows.getFirst(); + PartialCozeResultException lastFailure = null; + for (int attemptIndex = 1; attemptIndex <= 3; attemptIndex++) { + InspectAttempt attempt = inspectOnce(rows, prompt); + if (attempt.resolvedCount() == rows.size()) { + return attempt.mergedRows(); + } + log.warn("[appearance-patent] coze single unresolved attempt={} rowId={} asin={} country={} title={} url={} raw={}", + attemptIndex, + row.getId(), + row.getAsin(), + row.getCountry(), + abbreviate(row.getTitle(), 120), + abbreviate(row.getUrl(), 120), + abbreviate(attempt.raw(), 500)); + lastFailure = new PartialCozeResultException(attempt.resolvedCount(), rows.size(), attempt.rawResultCount()); + } + throw lastFailure == null ? new PartialCozeResultException(0, rows.size(), 0) : lastFailure; + } + + private InspectAttempt inspectOnce(List rows, String prompt) throws Exception { + String raw = postWorkflow(rows, prompt); + List results = parseResults(raw); + List merged = mergeRows(rows, results); + return new InspectAttempt(raw, merged, resolvedCount(merged), results.size()); + } + private String postWorkflow(List rows, String prompt) { Map parameters = new LinkedHashMap<>(); + parameters.put("row_id_list", rows.stream().map(row -> nonBlank(row.getId(), "")).toList()); + parameters.put("asin_list", rows.stream().map(row -> nonBlank(row.getAsin(), "")).toList()); + parameters.put("country_list", rows.stream().map(row -> nonBlank(row.getCountry(), "")).toList()); + parameters.put("row_key_list", rows.stream().map(this::rowKey).toList()); parameters.put("title_list", rows.stream().map(row -> nonBlank(row.getTitle(), row.getAsin())).toList()); parameters.put("url_list", rows.stream().map(row -> nonBlank(row.getUrl(), "")).toList()); parameters.put("prompt", prompt == null ? "" : prompt); @@ -71,7 +117,10 @@ public class AppearancePatentCozeClient { headers.setContentType(MediaType.APPLICATION_JSON); }); request.body(body); - return request.retrieve().body(String.class); + return request.exchange((clientRequest, clientResponse) -> { + byte[] responseBytes = StreamUtils.copyToByteArray(clientResponse.getBody()); + return responseBytes.length == 0 ? "" : new String(responseBytes, StandardCharsets.UTF_8); + }); } private List parseResults(String raw) throws Exception { @@ -89,6 +138,9 @@ public class AppearancePatentCozeClient { if (array.isArray()) { for (JsonNode node : array) { results.add(new CozeResult( + text(firstNonNull(node.get("row_id"), firstNonNull(node.get("rowId"), node.get("id")))), + text(node.get("asin")), + text(firstNonNull(node.get("country"), node.get("site"))), text(node.get("title")), text(node.get("appearance")), text(firstNonNull(node.get("patent"), node.get("patent "))), @@ -99,10 +151,51 @@ public class AppearancePatentCozeClient { return results; } + private List mergeRows(List rows, List results) { + Map resultByCompositeKey = new LinkedHashMap<>(); + Map resultByRowId = new LinkedHashMap<>(); + for (CozeResult result : results) { + String compositeKey = rowKey(result.rowId(), result.asin(), result.country()); + if (!compositeKey.isBlank()) { + resultByCompositeKey.putIfAbsent(compositeKey, result); + } + String rowIdKey = normalize(result.rowId()); + if (!rowIdKey.isBlank()) { + resultByRowId.putIfAbsent(rowIdKey, result); + } + } + + List merged = new ArrayList<>(rows.size()); + boolean allowIndexFallback = results.size() == rows.size(); + for (int i = 0; i < rows.size(); i++) { + AppearancePatentResultRowDto row = copy(rows.get(i)); + CozeResult result = resultByCompositeKey.get(rowKey(row.getId(), row.getAsin(), row.getCountry())); + if (result == null) { + result = resultByRowId.get(normalize(row.getId())); + } + if (result == null && allowIndexFallback && i < results.size()) { + result = results.get(i); + } + applyResult(row, result); + merged.add(row); + } + return merged; + } + + private void applyResult(AppearancePatentResultRowDto row, CozeResult result) { + if (row == null || result == null) { + return; + } + row.setTitleRisk(result.title()); + row.setAppearanceRisk(result.appearance()); + row.setPatentRisk(result.patent()); + row.setConclusion(result.result()); + } + private RestClient restClient() { SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); - requestFactory.setConnectTimeout(10000); - requestFactory.setReadTimeout(60000); + requestFactory.setConnectTimeout(properties.getCozeConnectTimeoutMillis()); + requestFactory.setReadTimeout(properties.getCozeReadTimeoutMillis()); return RestClient.builder().requestFactory(requestFactory).build(); } @@ -122,6 +215,67 @@ public class AppearancePatentCozeClient { return row; } + private AppearancePatentResultRowDto markFailed(AppearancePatentResultRowDto row, String failureMessage) { + if (row.getError() == null || row.getError().isBlank()) { + row.setError(failureMessage); + } + if (row.getConclusion() == null || row.getConclusion().isBlank()) { + row.setConclusion(failureMessage); + } + return row; + } + + private boolean shouldSplitBatch(List rows, Exception ex) { + return rows != null && rows.size() > 1 && isRetryableBatchFailure(ex); + } + + private boolean isRetryableBatchFailure(Exception ex) { + if (ex instanceof PartialCozeResultException) { + return true; + } + String message = ex == null ? "" : nonBlank(ex.getMessage(), ""); + return message.contains("Workflow node execution limit exceeded") + || message.contains("Read timed out") + || message.contains("Connection reset") + || message.contains("I/O error on POST request") + || message.toLowerCase(Locale.ROOT).contains("timeout"); + } + + private int resolvedCount(List rows) { + int resolved = 0; + for (AppearancePatentResultRowDto row : rows) { + if (hasResolvedCozeFields(row)) { + resolved++; + } + } + return resolved; + } + + private boolean hasResolvedCozeFields(AppearancePatentResultRowDto row) { + if (row == null) { + return false; + } + return !normalize(row.getTitleRisk()).isBlank() + || !normalize(row.getAppearanceRisk()).isBlank() + || !normalize(row.getPatentRisk()).isBlank() + || !normalize(row.getConclusion()).isBlank(); + } + + private String abbreviate(String value, int maxLength) { + String normalized = value == null ? "" : value.trim(); + if (normalized.length() <= maxLength) { + return normalized; + } + return normalized.substring(0, Math.max(0, maxLength - 3)) + "..."; + } + + private RuntimeException propagate(Exception ex) { + if (ex instanceof RuntimeException runtimeException) { + return runtimeException; + } + return new IllegalStateException(nonBlank(ex.getMessage(), "Coze call failed"), ex); + } + private JsonNode firstNonNull(JsonNode left, JsonNode right) { return left == null || left.isNull() ? right : left; } @@ -134,6 +288,38 @@ public class AppearancePatentCozeClient { return value == null || value.isBlank() ? fallback : value; } + private String normalize(String value) { + return value == null ? "" : value.replace("\ufeff", "").replace("\u3000", " ").trim(); + } + + private String rowKey(AppearancePatentResultRowDto row) { + if (row == null) { + return ""; + } + return rowKey(row.getId(), row.getAsin(), row.getCountry()); + } + + private String rowKey(String rowId, String asin, String country) { + return normalize(rowId) + "::" + normalize(asin).toUpperCase(Locale.ROOT) + "::" + normalize(country); + } + + private String failureMessage(Exception ex) { + if (ex instanceof PartialCozeResultException partial) { + return "Coze返回结果不完整(" + partial.resolvedCount() + "/" + partial.expectedCount() + ")"; + } + String message = ex == null ? null : ex.getMessage(); + if (message == null || message.isBlank()) { + return "Coze调用失败"; + } + if (message.contains("Workflow node execution limit exceeded")) { + return "Coze工作流节点执行超限,请检查工作流配置"; + } + if (message.contains("Read timed out")) { + return "Coze调用超时,请稍后重试"; + } + return "Coze调用失败: " + message; + } + private String stripBearer(String token) { String normalized = token == null ? "" : token.trim(); return normalized.regionMatches(true, 0, "Bearer ", 0, 7) ? normalized.substring(7).trim() : normalized; @@ -151,6 +337,49 @@ public class AppearancePatentCozeClient { return base + suffix; } - private record CozeResult(String title, String appearance, String patent, String result) { + private record CozeResult( + String rowId, + String asin, + String country, + String title, + String appearance, + String patent, + String result + ) { + } + + private record InspectAttempt( + String raw, + List mergedRows, + int resolvedCount, + int rawResultCount + ) { + } + + private static final class PartialCozeResultException extends RuntimeException { + + private final int resolvedCount; + private final int expectedCount; + private final int rawResultCount; + + private PartialCozeResultException(int resolvedCount, int expectedCount, int rawResultCount) { + super("partial-result resolved=" + resolvedCount + "/" + expectedCount + " raw=" + rawResultCount); + this.resolvedCount = resolvedCount; + this.expectedCount = expectedCount; + this.rawResultCount = rawResultCount; + } + + private int resolvedCount() { + return resolvedCount; + } + + private int expectedCount() { + return expectedCount; + } + + @SuppressWarnings("unused") + private int rawResultCount() { + return rawResultCount; + } } } diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/appearancepatent/model/vo/AppearancePatentParsedRowVo.java b/backend-java/src/main/java/com/nanri/aiimage/modules/appearancepatent/model/vo/AppearancePatentParsedRowVo.java index a286e4f..420e9b4 100644 --- a/backend-java/src/main/java/com/nanri/aiimage/modules/appearancepatent/model/vo/AppearancePatentParsedRowVo.java +++ b/backend-java/src/main/java/com/nanri/aiimage/modules/appearancepatent/model/vo/AppearancePatentParsedRowVo.java @@ -16,6 +16,7 @@ public class AppearancePatentParsedRowVo { @Schema(description = "前端展示和 Python 回传使用的 id。整数 id 原样保留,子数据第一条如 2_1 原样保留。", example = "2_1") private String displayId; @Schema(description = "亚马逊 ASIN。", example = "B0CJ8SNXXV") + private String groupKey; private String asin; @Schema(description = "国家或站点。", example = "英国") private String country; diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/appearancepatent/service/AppearancePatentTaskCacheService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/appearancepatent/service/AppearancePatentTaskCacheService.java index 31e71db..cded051 100644 --- a/backend-java/src/main/java/com/nanri/aiimage/modules/appearancepatent/service/AppearancePatentTaskCacheService.java +++ b/backend-java/src/main/java/com/nanri/aiimage/modules/appearancepatent/service/AppearancePatentTaskCacheService.java @@ -22,14 +22,14 @@ public class AppearancePatentTaskCacheService { private final StringRedisTemplate stringRedisTemplate; private final ObjectMapper objectMapper; - public void appendPendingRow(Long taskId, Integer chunkIndex, AppearancePatentResultRowDto row) { - if (taskId == null || taskId <= 0 || chunkIndex == null || row == null) { + public void appendPendingRow(Long taskId, String scopeHash, Integer chunkIndex, AppearancePatentResultRowDto row) { + if (taskId == null || taskId <= 0 || scopeHash == null || scopeHash.isBlank() || chunkIndex == null || row == null) { return; } try { stringRedisTemplate.opsForList().rightPush( pendingRowsKey(taskId), - objectMapper.writeValueAsString(new PendingRow(chunkIndex, row)) + objectMapper.writeValueAsString(new PendingRow(scopeHash, chunkIndex, row)) ); stringRedisTemplate.expire(pendingRowsKey(taskId), Duration.ofHours(TTL_HOURS)); touchTaskHeartbeat(taskId); @@ -139,6 +139,6 @@ public class AppearancePatentTaskCacheService { return "appearance-patent:task:heartbeat:" + taskId; } - public record PendingRow(Integer chunkIndex, AppearancePatentResultRowDto row) { + public record PendingRow(String scopeHash, Integer chunkIndex, AppearancePatentResultRowDto row) { } } diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/appearancepatent/service/AppearancePatentTaskService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/appearancepatent/service/AppearancePatentTaskService.java index d64f984..4e129b5 100644 --- a/backend-java/src/main/java/com/nanri/aiimage/modules/appearancepatent/service/AppearancePatentTaskService.java +++ b/backend-java/src/main/java/com/nanri/aiimage/modules/appearancepatent/service/AppearancePatentTaskService.java @@ -49,7 +49,10 @@ import org.apache.poi.xssf.streaming.SXSSFWorkbook; import org.springframework.dao.DuplicateKeyException; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; +import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.support.TransactionTemplate; import java.io.File; import java.io.FileInputStream; @@ -65,6 +68,7 @@ import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.function.Supplier; @Service @RequiredArgsConstructor @@ -101,6 +105,7 @@ public class AppearancePatentTaskService { private final AppearancePatentProperties properties; private final TaskFileJobService taskFileJobService; private final TransientPayloadStorageService transientPayloadStorageService; + private final PlatformTransactionManager transactionManager; @Transactional public AppearancePatentParseVo parseAndCreateTask(AppearancePatentParseRequest request) { @@ -259,8 +264,16 @@ public class AppearancePatentTaskService { return vo; } - @Transactional public void submitResult(Long taskId, AppearancePatentSubmitResultRequest request) { + if (transactionManager != null) { + SubmitContext context = inNewTransaction(() -> persistSubmittedChunk(taskId, request)); + flushPendingRowsIfNeeded(context.task(), context.forceFlush()); + inNewTransaction(() -> { + completeSubmittedChunk(context); + return null; + }); + return; + } FileTaskEntity task = fileTaskMapper.selectById(taskId); if (task == null || !MODULE_TYPE.equals(task.getModuleType())) { throw new BusinessException("任务不存在"); @@ -302,19 +315,21 @@ public class AppearancePatentTaskService { taskChunkMapper.insert(chunk); } catch (DuplicateKeyException ex) { inserted = false; - transientPayloadStorageService.deletePayloadIfPresent(storedPayload); + // storeChunkPayload uses a deterministic key like chunk-{index}. When two + // concurrent callbacks submit the same chunk, deleting the loser payload here + // can also remove the winner's shared object and break later assembly. log.info("[appearance-patent] duplicate chunk inserted concurrently taskId={} scope={} chunk={}", taskId, scopeKey, chunkIndex); } if (inserted) { for (AppearancePatentResultRowDto rawRow : rawRows) { - taskCacheService.appendPendingRow(taskId, chunkIndex, rawRow); + taskCacheService.appendPendingRow(taskId, scopeHash, chunkIndex, rawRow); } } } else { log.info("[appearance-patent] duplicate chunk ignored taskId={} scope={} chunk={}", taskId, scopeKey, chunkIndex); } - flushPendingRowsIfNeeded(task, scopeHash, Boolean.TRUE.equals(request.getDone())); + flushPendingRowsIfNeeded(task, Boolean.TRUE.equals(request.getDone())); TaskScopeStateEntity scope = taskScopeStateMapper.selectOne(new LambdaQueryWrapper() .eq(TaskScopeStateEntity::getTaskId, taskId) @@ -394,8 +409,28 @@ public class AppearancePatentTaskService { } @Scheduled(cron = "${aiimage.appearance-patent.stale-finalize-cron:0 */2 * * * *}") - @Transactional public void finalizeStaleTasks() { + if (transactionManager != null) { + LocalDateTime threshold = LocalDateTime.now().minusMinutes(Math.max(1, properties.getStaleTimeoutMinutes())); + long thresholdMillis = threshold.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + List tasks = fileTaskMapper.selectList(new LambdaQueryWrapper() + .eq(FileTaskEntity::getModuleType, MODULE_TYPE) + .eq(FileTaskEntity::getStatus, STATUS_RUNNING) + .lt(FileTaskEntity::getUpdatedAt, threshold) + .last("limit 50")); + for (FileTaskEntity task : tasks) { + long heartbeatMillis = taskCacheService.getTaskHeartbeatMillis(task.getId()); + if (heartbeatMillis > thresholdMillis) { + continue; + } + flushPendingRowsIfNeeded(task, true); + inNewTransaction(() -> { + finalizeStaleTask(task.getId(), "Python interrupted before uploading final appearance patent result"); + return null; + }); + } + return; + } LocalDateTime threshold = LocalDateTime.now().minusMinutes(Math.max(1, properties.getStaleTimeoutMinutes())); long thresholdMillis = threshold.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); List tasks = fileTaskMapper.selectList(new LambdaQueryWrapper() @@ -408,11 +443,168 @@ public class AppearancePatentTaskService { if (heartbeatMillis > thresholdMillis) { continue; } - flushPendingRowsIfNeeded(task, DigestUtil.sha256Hex("appearance-patent-" + task.getId()), true); + flushPendingRowsIfNeeded(task, true); finalizeTask(task, "Python interrupted before uploading final appearance patent result", allRowCount(task), true); } } + private SubmitContext persistSubmittedChunk(Long taskId, AppearancePatentSubmitResultRequest request) { + FileTaskEntity task = fileTaskMapper.selectById(taskId); + if (task == null || !MODULE_TYPE.equals(task.getModuleType())) { + throw new BusinessException("浠诲姟涓嶅瓨鍦?"); + } + if (!STATUS_RUNNING.equals(task.getStatus())) { + throw new BusinessException("浠诲姟涓嶆槸杩愯涓姸鎬?"); + } + + int chunkIndex = request.getChunkIndex() == null ? 0 : request.getChunkIndex(); + int chunkTotal = request.getChunkTotal() == null ? 1 : request.getChunkTotal(); + boolean done = Boolean.TRUE.equals(request.getDone()); + String scopeKey = firstNonBlank(request.getSubmissionId(), "task:" + taskId); + String scopeHash = DigestUtil.sha256Hex(scopeKey); + taskCacheService.touchTaskHeartbeat(taskId); + + TaskChunkEntity existing = taskChunkMapper.selectOne(new LambdaQueryWrapper() + .eq(TaskChunkEntity::getTaskId, taskId) + .eq(TaskChunkEntity::getModuleType, MODULE_TYPE) + .eq(TaskChunkEntity::getScopeHash, scopeHash) + .eq(TaskChunkEntity::getChunkIndex, chunkIndex) + .last("limit 1")); + if (existing == null) { + List rawRows = request.getItems() == null ? List.of() : request.getItems(); + String payloadJson = writeJson(rawRows, "缁撴灉搴忓垪鍖栧け璐?"); + + TaskChunkEntity chunk = new TaskChunkEntity(); + chunk.setTaskId(taskId); + chunk.setModuleType(MODULE_TYPE); + chunk.setScopeKey(scopeKey); + chunk.setScopeHash(scopeHash); + chunk.setChunkIndex(chunkIndex); + chunk.setChunkTotal(chunkTotal); + String storedPayload = transientPayloadStorageService.storeChunkPayload(MODULE_TYPE, taskId, scopeHash, chunkIndex, payloadJson); + chunk.setPayloadJson(storedPayload); + chunk.setPayloadHash(DigestUtil.sha256Hex(payloadJson)); + chunk.setCreatedAt(LocalDateTime.now()); + chunk.setUpdatedAt(LocalDateTime.now()); + boolean inserted = true; + try { + taskChunkMapper.insert(chunk); + } catch (DuplicateKeyException ex) { + inserted = false; + log.info("[appearance-patent] duplicate chunk inserted concurrently taskId={} scope={} chunk={}", taskId, scopeKey, chunkIndex); + } + if (inserted) { + for (AppearancePatentResultRowDto rawRow : rawRows) { + taskCacheService.appendPendingRow(taskId, scopeHash, chunkIndex, rawRow); + } + } + } else { + log.info("[appearance-patent] duplicate chunk ignored taskId={} scope={} chunk={}", taskId, scopeKey, chunkIndex); + } + + upsertScopeState(taskId, scopeKey, scopeHash, chunkTotal, request.getError(), done, false); + task.setUpdatedAt(LocalDateTime.now()); + fileTaskMapper.updateById(task); + return new SubmitContext(task, scopeKey, scopeHash, done, request.getError()); + } + + private void completeSubmittedChunk(SubmitContext context) { + FileTaskEntity task = fileTaskMapper.selectById(context.task().getId()); + if (task == null || !MODULE_TYPE.equals(task.getModuleType())) { + throw new BusinessException("浠诲姟涓嶅瓨鍦?"); + } + if (!STATUS_RUNNING.equals(task.getStatus())) { + log.info("[appearance-patent] skip completion because task already finalized taskId={} status={}", + task.getId(), task.getStatus()); + return; + } + upsertScopeState(task.getId(), context.scopeKey(), context.scopeHash(), null, context.error(), context.forceFlush(), true); + if (context.forceFlush() || context.error() != null && !context.error().isBlank()) { + finalizeTask(task, context.error(), allRowCount(task), true); + return; + } + task.setUpdatedAt(LocalDateTime.now()); + fileTaskMapper.updateById(task); + } + + private void finalizeStaleTask(Long taskId, String error) { + FileTaskEntity task = fileTaskMapper.selectById(taskId); + if (task == null || !MODULE_TYPE.equals(task.getModuleType()) || !STATUS_RUNNING.equals(task.getStatus())) { + return; + } + finalizeTask(task, error, allRowCount(task), true); + } + + private void upsertScopeState(Long taskId, + String scopeKey, + String scopeHash, + Integer chunkTotal, + String error, + boolean completed, + boolean cozeDone) { + TaskScopeStateEntity scope = taskScopeStateMapper.selectOne(new LambdaQueryWrapper() + .eq(TaskScopeStateEntity::getTaskId, taskId) + .eq(TaskScopeStateEntity::getModuleType, MODULE_TYPE) + .eq(TaskScopeStateEntity::getScopeHash, scopeHash) + .last("limit 1")); + LocalDateTime now = LocalDateTime.now(); + if (scope == null) { + scope = new TaskScopeStateEntity(); + scope.setTaskId(taskId); + scope.setModuleType(MODULE_TYPE); + scope.setScopeKey(scopeKey); + scope.setScopeHash(scopeHash); + scope.setCreatedAt(now); + } + if (chunkTotal != null) { + scope.setChunkTotal(chunkTotal); + } + scope.setReceivedChunkCount(countChunks(taskId, scopeHash)); + scope.setLastChunkAt(now); + scope.setLastError(error); + scope.setCompleted(completed ? 1 : 0); + scope.setUpdatedAt(now); + scope.setStateJson(cozeDone + ? "{\"phase\":\"RECEIVED\",\"coze\":\"DONE\"}" + : "{\"phase\":\"RECEIVED\",\"coze\":\"PENDING\"}"); + if (scope.getId() == null) { + try { + taskScopeStateMapper.insert(scope); + return; + } catch (DuplicateKeyException ex) { + log.info("[appearance-patent] duplicate scope state inserted concurrently taskId={} scope={}", taskId, scopeKey); + scope = taskScopeStateMapper.selectOne(new LambdaQueryWrapper() + .eq(TaskScopeStateEntity::getTaskId, taskId) + .eq(TaskScopeStateEntity::getModuleType, MODULE_TYPE) + .eq(TaskScopeStateEntity::getScopeHash, scopeHash) + .last("limit 1")); + if (scope == null) { + log.info("[appearance-patent] scope state winner not committed yet, skip duplicate updater taskId={} scope={}", + taskId, scopeKey); + return; + } + if (chunkTotal != null) { + scope.setChunkTotal(chunkTotal); + } + scope.setReceivedChunkCount(countChunks(taskId, scopeHash)); + scope.setLastChunkAt(now); + scope.setLastError(error); + scope.setCompleted(completed ? 1 : 0); + scope.setUpdatedAt(now); + scope.setStateJson(cozeDone + ? "{\"phase\":\"RECEIVED\",\"coze\":\"DONE\"}" + : "{\"phase\":\"RECEIVED\",\"coze\":\"PENDING\"}"); + } + } + taskScopeStateMapper.updateById(scope); + } + + private T inNewTransaction(Supplier action) { + TransactionTemplate template = new TransactionTemplate(transactionManager); + template.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); + return template.execute(status -> action.get()); + } + private List applyCozeInBatches(List items, FileTaskEntity task) { if (items == null || items.isEmpty()) { return List.of(); @@ -426,8 +618,10 @@ public class AppearancePatentTaskService { return result; } - private void flushPendingRowsIfNeeded(FileTaskEntity task, String scopeHash, boolean force) { + private void flushPendingRowsIfNeeded(FileTaskEntity task, boolean force) { int batchSize = Math.max(1, properties.getCozeBatchSize()); + Map> allRowsByBaseId = loadAllRowsByBaseId(task); + String prompt = readAiPrompt(task); while (force ? taskCacheService.pendingRowCount(task.getId()) > 0 : taskCacheService.pendingRowCount(task.getId()) >= batchSize) { int drainSize = force ? batchSize : Math.max(1, batchSize); List pendingRows = taskCacheService.drainPendingRows(task.getId(), drainSize); @@ -437,27 +631,47 @@ public class AppearancePatentTaskService { List rawRows = pendingRows.stream() .map(AppearancePatentTaskCacheService.PendingRow::row) .toList(); - List cozeRows = cozeClient.inspect(rawRows, readAiPrompt(task)); + List cozeRows = cozeClient.inspect(rawRows, prompt); + Map> chunkRows = new LinkedHashMap<>(); for (int i = 0; i < pendingRows.size(); i++) { AppearancePatentTaskCacheService.PendingRow pending = pendingRows.get(i); AppearancePatentResultRowDto resultRow = i < cozeRows.size() ? cozeRows.get(i) : pending.row(); - List persistedRows = expandRows(task, List.of(resultRow)); - updateChunkPayload(task.getId(), scopeHash, pending.chunkIndex(), persistedRows); + List expandedRows = expandRows(List.of(resultRow), allRowsByBaseId); + String chunkKey = pending.scopeHash() + "::" + pending.chunkIndex(); + Map mergedRows = + chunkRows.computeIfAbsent(chunkKey, key -> new LinkedHashMap<>()); + for (AppearancePatentResultRowDto expandedRow : expandedRows) { + mergedRows.put(rowKey(expandedRow.getId(), expandedRow.getAsin(), expandedRow.getCountry()), expandedRow); + } + } + for (Map.Entry> entry : chunkRows.entrySet()) { + int delimiter = entry.getKey().lastIndexOf("::"); + if (delimiter <= 0) { + continue; + } + String scopeHash = entry.getKey().substring(0, delimiter); + Integer chunkIndex = Integer.parseInt(entry.getKey().substring(delimiter + 2)); + mergeChunkPayload(task.getId(), scopeHash, chunkIndex, new ArrayList<>(entry.getValue().values())); } } } - private void updateChunkPayload(Long taskId, String scopeHash, Integer chunkIndex, List rows) { + private void mergeChunkPayload(Long taskId, String scopeHash, Integer chunkIndex, List rows) { TaskChunkEntity chunk = taskChunkMapper.selectOne(new LambdaQueryWrapper() .eq(TaskChunkEntity::getTaskId, taskId) .eq(TaskChunkEntity::getModuleType, MODULE_TYPE) .eq(TaskChunkEntity::getScopeHash, scopeHash) .eq(TaskChunkEntity::getChunkIndex, chunkIndex) .last("limit 1")); - if (chunk == null) { + if (chunk == null || rows == null || rows.isEmpty()) { return; } + Map persistedRows = readChunkRows(chunk); + for (AppearancePatentResultRowDto row : rows) { + persistedRows.put(rowKey(row.getId(), row.getAsin(), row.getCountry()), row); + } String payloadJson = writeJson(rows == null ? List.of() : rows, "结果序列化失败"); + payloadJson = writeJson(new ArrayList<>(persistedRows.values()), "appearance patent chunk payload merge failed"); String storedPayload = transientPayloadStorageService.storeChunkPayload(MODULE_TYPE, taskId, scopeHash, chunkIndex, payloadJson); transientPayloadStorageService.deleteReplacedPayloadIfNeeded(chunk.getPayloadJson(), storedPayload); chunk.setPayloadJson(storedPayload); @@ -466,16 +680,15 @@ public class AppearancePatentTaskService { taskChunkMapper.updateById(chunk); } - private List expandRows(FileTaskEntity task, List representatives) { + private List expandRows(List representatives, + Map> allRowsByBaseId) { if (representatives == null || representatives.isEmpty()) { return List.of(); } - Map> allRowsByBaseId = loadAllRowsByBaseId(task); List result = new ArrayList<>(); Set emittedKeys = new LinkedHashSet<>(); for (AppearancePatentResultRowDto representative : representatives) { - String baseId = baseId(representative.getId()); - List siblings = allRowsByBaseId.get(baseId); + List siblings = resolveSiblingRows(representative, allRowsByBaseId); if (siblings == null || siblings.isEmpty()) { result.add(representative); continue; @@ -491,14 +704,40 @@ public class AppearancePatentTaskService { return result; } - private Map> loadAllRowsByBaseId(FileTaskEntity task) { - Map> result = new LinkedHashMap<>(); - try { - for (AppearancePatentParsedRowVo row : readParsedPayload(task).getAllItems()) { - result.computeIfAbsent(baseId(row.getDisplayId()), key -> new ArrayList<>()).add(row); + private List resolveSiblingRows(AppearancePatentResultRowDto representative, + Map> groupedRows) { + if (representative == null || groupedRows == null || groupedRows.isEmpty()) { + return List.of(); + } + String representativeKey = rowKey(representative.getId(), representative.getAsin(), representative.getCountry()); + for (List rows : groupedRows.values()) { + for (AppearancePatentParsedRowVo row : rows) { + String rowKey = rowKey(row.getDisplayId(), row.getAsin(), row.getCountry()); + if (Objects.equals(representativeKey, rowKey)) { + return rows; + } } + } + return List.of(); + } + + private Map> loadAllRowsByBaseId(FileTaskEntity task) { + try { + return groupRowsByBaseId(readParsedPayload(task).getAllItems()); } catch (Exception ex) { log.warn("[appearance-patent] read all rows failed taskId={} err={}", task.getId(), ex.getMessage()); + return new LinkedHashMap<>(); + } + } + + private Map> groupRowsByBaseId(List rows) { + Map> result = new LinkedHashMap<>(); + if (rows == null) { + return result; + } + for (AppearancePatentParsedRowVo row : rows) { + String key = firstNonBlank(normalize(row.getGroupKey()), baseId(row.getDisplayId())); + result.computeIfAbsent(key, ignored -> new ArrayList<>()).add(row); } return result; } @@ -517,8 +756,8 @@ public class AppearancePatentTaskService { row.setId(sibling.getDisplayId()); row.setAsin(sibling.getAsin()); row.setCountry(sibling.getCountry()); - row.setUrl(sibling.getUrl()); - row.setTitle(sibling.getTitle()); + row.setUrl(firstNonBlank(representative.getUrl(), sibling.getUrl())); + row.setTitle(firstNonBlank(representative.getTitle(), sibling.getTitle())); row.setError(representative.getError()); row.setTitleRisk(representative.getTitleRisk()); row.setAppearanceRisk(representative.getAppearanceRisk()); @@ -644,22 +883,7 @@ public class AppearancePatentTaskService { .eq(TaskChunkEntity::getModuleType, MODULE_TYPE) .orderByAsc(TaskChunkEntity::getChunkIndex)); for (TaskChunkEntity chunk : chunks) { - if (chunk.getPayloadJson() == null || chunk.getPayloadJson().isBlank()) { - continue; - } - try { - String payloadJson = transientPayloadStorageService.resolvePayload(chunk.getPayloadJson(), "read appearance patent chunk failed"); - JsonNode array = objectMapper.readTree(payloadJson); - if (!array.isArray()) { - continue; - } - for (JsonNode node : array) { - AppearancePatentResultRowDto row = objectMapper.treeToValue(node, AppearancePatentResultRowDto.class); - result.put(rowKey(row.getId(), row.getAsin(), row.getCountry()), row); - } - } catch (Exception ex) { - log.warn("[appearance-patent] read chunk payload failed taskId={} chunk={} err={}", taskId, chunk.getChunkIndex(), ex.getMessage()); - } + result.putAll(readChunkRows(chunk)); } return result; } @@ -672,26 +896,35 @@ public class AppearancePatentTaskService { font.setBold(true); headerStyle.setFont(font); + List resultHeaders = new ArrayList<>(RESULT_HEADERS); + resultHeaders.add(4, "title"); + resultHeaders.add(5, "url"); Row header = sheet.createRow(0); - for (int i = 0; i < RESULT_HEADERS.size(); i++) { + for (int i = 0; i < resultHeaders.size(); i++) { Cell cell = header.createCell(i); - cell.setCellValue(RESULT_HEADERS.get(i)); + cell.setCellValue(resultHeaders.get(i)); cell.setCellStyle(headerStyle); } int rowIndex = 1; for (AppearancePatentParsedRowVo parsedRow : parsed.getAllItems()) { AppearancePatentResultRowDto resultRow = resultMap.get(rowKey(parsedRow.getDisplayId(), parsedRow.getAsin(), parsedRow.getCountry())); + String missingReason = ""; + if (resultRow == null) { + missingReason = hasPromptFields(parsedRow) ? "未匹配到检测结果" : "未送检:缺少标题或图片"; + } Row row = sheet.createRow(rowIndex++); int col = 0; row.createCell(col++).setCellValue(firstNonBlank(parsedRow.getDisplayId(), parsedRow.getSourceId())); row.createCell(col++).setCellValue(firstNonBlank(parsedRow.getAsin(), "")); row.createCell(col++).setCellValue(firstNonBlank(parsedRow.getCountry(), "")); row.createCell(col++).setCellValue(readValueByHeader(parsedRow, "价格", "price")); - row.createCell(col++).setCellValue(resultRow == null ? "" : firstNonBlank(resultRow.getTitleRisk(), "")); - row.createCell(col++).setCellValue(resultRow == null ? "" : firstNonBlank(resultRow.getAppearanceRisk(), "")); - row.createCell(col++).setCellValue(resultRow == null ? "" : firstNonBlank(resultRow.getPatentRisk(), "")); - row.createCell(col).setCellValue(resultRow == null ? "" : firstNonBlank(resultRow.getConclusion(), "")); + row.createCell(col++).setCellValue(resultRow == null ? firstNonBlank(parsedRow.getTitle(), "") : firstNonBlank(resultRow.getTitle(), parsedRow.getTitle())); + row.createCell(col++).setCellValue(resultRow == null ? firstNonBlank(parsedRow.getUrl(), "") : firstNonBlank(resultRow.getUrl(), parsedRow.getUrl())); + row.createCell(col++).setCellValue(resultRow == null ? missingReason : firstNonBlank(resultRow.getTitleRisk(), "")); + row.createCell(col++).setCellValue(resultRow == null ? missingReason : firstNonBlank(resultRow.getAppearanceRisk(), "")); + row.createCell(col++).setCellValue(resultRow == null ? missingReason : firstNonBlank(resultRow.getPatentRisk(), "")); + row.createCell(col).setCellValue(resultRow == null ? "未送检" : firstNonBlank(resultRow.getConclusion(), "")); } workbook.write(fos); workbook.dispose(); @@ -713,13 +946,18 @@ public class AppearancePatentTaskService { int idCol = findRequiredHeader(headerMap, "id"); int asinCol = findRequiredHeader(headerMap, "asin"); int countryCol = findRequiredHeader(headerMap, "国家", "country"); - int urlCol = findOptionalHeader(headerMap, "url", "rul", "链接", "商品链接"); - int titleCol = findOptionalHeader(headerMap, "标题", "title", "商品标题"); + int urlCol = findOptionalHeaderExact(headerMap, + "url", "rul", "link", "image", "img", "pic", "picture", + "链接", "商品链接", "图片", "商品图片", "主图", "商品主图", "图片链接", "主图链接"); + int titleCol = findOptionalHeaderExact(headerMap, + "标题", "title", "listing title", "product title", "商品标题", "商品名称", "产品名称"); List accepted = new ArrayList<>(); List allRows = new ArrayList<>(); int total = 0; int dropped = 0; + String currentBlockBaseId = ""; + String currentGroupKey = ""; for (int i = 1; i <= sheet.getLastRowNum(); i++) { Row row = sheet.getRow(i); if (row == null) { @@ -740,6 +978,12 @@ public class AppearancePatentTaskService { vo.setRowIndex(i + 1); vo.setSourceId(id); vo.setDisplayId(normalizeDisplayId(id)); + String rowBaseId = baseId(vo.getDisplayId()); + if (!Objects.equals(currentBlockBaseId, rowBaseId)) { + currentBlockBaseId = rowBaseId; + currentGroupKey = rowBaseId + "@" + vo.getRowIndex(); + } + vo.setGroupKey(currentGroupKey); vo.setAsin(asin); vo.setCountry(country); vo.setUrl(urlCol >= 0 ? cell(row, urlCol, formatter) : ""); @@ -752,6 +996,10 @@ public class AppearancePatentTaskService { dropped++; } } + hydratePromptFields(allRows); + if (accepted.isEmpty()) { + throw new BusinessException("no valid appearance patent rows"); + } return new ParsedWorkbook(total, dropped, headers, accepted, allRows); } catch (BusinessException ex) { throw ex; @@ -761,6 +1009,33 @@ public class AppearancePatentTaskService { } } + private void hydratePromptFields(List rows) { + if (rows == null || rows.isEmpty()) { + return; + } + Map> rowsByBaseId = groupRowsByBaseId(rows); + for (List siblings : rowsByBaseId.values()) { + String title = ""; + String url = ""; + for (AppearancePatentParsedRowVo sibling : siblings) { + title = firstNonBlank(title, sibling.getTitle()); + url = firstNonBlank(url, sibling.getUrl()); + } + for (AppearancePatentParsedRowVo sibling : siblings) { + if (normalize(sibling.getTitle()).isBlank()) { + sibling.setTitle(title); + } + if (normalize(sibling.getUrl()).isBlank()) { + sibling.setUrl(url); + } + } + } + } + + private boolean hasPromptFields(AppearancePatentParsedRowVo row) { + return row != null && (!normalize(row.getTitle()).isBlank() || !normalize(row.getUrl()).isBlank()); + } + private Map buildHeaderMap(Row header, DataFormatter formatter) { Map map = new LinkedHashMap<>(); for (int i = 0; i < header.getLastCellNum(); i++) { @@ -808,6 +1083,23 @@ public class AppearancePatentTaskService { return -1; } + private int findOptionalHeaderExact(Map map, String... names) { + for (Map.Entry entry : map.entrySet()) { + String normalizedHeader = normalizeHeaderAlias(entry.getKey()); + for (String name : names) { + if (normalizedHeader.equals(normalizeHeaderAlias(name))) { + return entry.getValue(); + } + } + } + return -1; + } + + private String normalizeHeaderAlias(String value) { + String normalized = normalize(value).toLowerCase(Locale.ROOT); + return normalized.replaceAll("[\\s_\\-()()\\[\\]{}::/\\\\]+", ""); + } + private String cell(Row row, int col, DataFormatter formatter) { return col < 0 ? "" : normalize(formatter.formatCellValue(row.getCell(col))); } @@ -933,6 +1225,28 @@ public class AppearancePatentTaskService { return new AppearancePatentParsedPayloadDto(); } + private Map readChunkRows(TaskChunkEntity chunk) { + Map rows = new LinkedHashMap<>(); + if (chunk == null || chunk.getPayloadJson() == null || chunk.getPayloadJson().isBlank()) { + return rows; + } + try { + String payloadJson = transientPayloadStorageService.resolvePayload(chunk.getPayloadJson(), "read appearance patent chunk failed"); + JsonNode array = objectMapper.readTree(payloadJson); + if (!array.isArray()) { + return rows; + } + for (JsonNode node : array) { + AppearancePatentResultRowDto row = objectMapper.treeToValue(node, AppearancePatentResultRowDto.class); + rows.put(rowKey(row.getId(), row.getAsin(), row.getCountry()), row); + } + } catch (Exception ex) { + log.warn("[appearance-patent] read chunk payload failed taskId={} chunk={} err={}", + chunk.getTaskId(), chunk.getChunkIndex(), ex.getMessage()); + } + return rows; + } + private String rowKey(String id, String asin, String country) { return normalize(id) + "::" + normalize(asin).toUpperCase(Locale.ROOT) + "::" + normalize(country); } @@ -996,6 +1310,13 @@ public class AppearancePatentTaskService { } } + private record SubmitContext(FileTaskEntity task, + String scopeKey, + String scopeHash, + boolean forceFlush, + String error) { + } + private record ParsedWorkbook(int totalRows, int droppedRows, List headers, List acceptedRows, List allRows) { } } diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/deletebrand/service/DeleteBrandStaleTaskService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/deletebrand/service/DeleteBrandStaleTaskService.java index d47ecba..50ee53c 100644 --- a/backend-java/src/main/java/com/nanri/aiimage/modules/deletebrand/service/DeleteBrandStaleTaskService.java +++ b/backend-java/src/main/java/com/nanri/aiimage/modules/deletebrand/service/DeleteBrandStaleTaskService.java @@ -27,7 +27,6 @@ import java.nio.file.attribute.FileTime; import java.time.Duration; import java.time.Instant; import java.time.LocalDateTime; -import java.time.ZoneId; import java.util.List; import java.util.Map; @@ -47,6 +46,7 @@ public class DeleteBrandStaleTaskService { private final FileTaskMapper fileTaskMapper; private final DeleteBrandTaskCacheService deleteBrandTaskCacheService; + private final DeleteBrandTaskStorageService deleteBrandTaskStorageService; private final DeleteBrandRunService deleteBrandRunService; private final ProductRiskTaskService productRiskTaskService; private final ProductRiskTaskCacheService productRiskTaskCacheService; @@ -110,7 +110,13 @@ public class DeleteBrandStaleTaskService { } private void failStaleDeleteBrandTasks() { - LocalDateTime threshold = LocalDateTime.now().minusMinutes(deleteBrandProgressProperties.getHeartbeatTimeoutMinutes()); + long minutes = Math.max(1L, deleteBrandProgressProperties.getHeartbeatTimeoutMinutes()); + long initialMinutes = Math.max(minutes, deleteBrandProgressProperties.getDeleteBrandInitialTimeoutMinutes()); + long staleTimeoutMillis = Duration.ofMinutes(minutes).toMillis(); + long nowMillis = System.currentTimeMillis(); + LocalDateTime now = LocalDateTime.now(); + LocalDateTime threshold = now.minusMinutes(minutes); + LocalDateTime initialThreshold = now.minusMinutes(initialMinutes); java.util.List runningTasks = fileTaskMapper.selectList(new LambdaQueryWrapper() .eq(FileTaskEntity::getModuleType, MODULE_TYPE_DELETE_BRAND) @@ -120,36 +126,35 @@ public class DeleteBrandStaleTaskService { for (FileTaskEntity task : runningTasks) { Map progress = deleteBrandTaskCacheService.getProgress(task.getId()); - boolean hasProgress = progress != null && !progress.isEmpty() && progress.containsKey("last_heartbeat_at"); - - boolean isActive = false; - if (progress != null && progress.containsKey("has_progress")) { - Object hpObj = progress.get("has_progress"); - if (hpObj instanceof Boolean) { - isActive = (Boolean) hpObj; - } else if (hpObj instanceof String) { - isActive = Boolean.parseBoolean((String) hpObj); - } - } - long lastHeartbeatAt = 0L; - if (hasProgress) { + if (progress != null && !progress.isEmpty() && progress.containsKey("last_heartbeat_at")) { try { lastHeartbeatAt = Long.parseLong(String.valueOf(progress.get("last_heartbeat_at"))); } catch (Exception ignored) { } } + boolean hasStartedProgress = lastHeartbeatAt > 0L; + if (!hasStartedProgress) { + hasStartedProgress = deleteBrandTaskStorageService.countCompletedScopes(task.getId()) > 0 + || deleteBrandTaskStorageService.countParsedPayloadScopes(task.getId()) > 0; + } - if (lastHeartbeatAt > 0 && isActive) { - LocalDateTime lastActivityTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(lastHeartbeatAt), ZoneId.systemDefault()); - if (lastActivityTime.isAfter(threshold)) { - continue; - } - } else { - LocalDateTime queuedThreshold = LocalDateTime.now().minusHours(12); - if (task.getCreatedAt() != null && task.getCreatedAt().isAfter(queuedThreshold)) { + if (lastHeartbeatAt > 0L && nowMillis - lastHeartbeatAt < staleTimeoutMillis) { + continue; + } + if (!hasStartedProgress && task.getCreatedAt() != null && task.getCreatedAt().isAfter(initialThreshold)) { + continue; + } + + try { + deleteBrandRunService.tryFinalizeTask(task.getId(), true); + FileTaskEntity refreshed = fileTaskMapper.selectById(task.getId()); + if (refreshed != null && !"RUNNING".equals(refreshed.getStatus())) { + deleteBrandTaskCacheService.saveTaskCache(refreshed); continue; } + } catch (Exception ex) { + log.warn("[stale-check] delete-brand finalize threw taskId={} msg={}", task.getId(), ex.getMessage()); } int updated = fileTaskMapper.update(null, new LambdaUpdateWrapper() diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/patroldelete/service/PatrolDeleteTaskService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/patroldelete/service/PatrolDeleteTaskService.java index d8387ca..21298b0 100644 --- a/backend-java/src/main/java/com/nanri/aiimage/modules/patroldelete/service/PatrolDeleteTaskService.java +++ b/backend-java/src/main/java/com/nanri/aiimage/modules/patroldelete/service/PatrolDeleteTaskService.java @@ -49,6 +49,9 @@ public class PatrolDeleteTaskService { private static final String MODULE_TYPE = "PATROL_DELETE"; private static final String CONTENT_TYPE_XLSX = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; + private static final int RESULT_PENDING = -1; + private static final int RESULT_FAILED = 0; + private static final int RESULT_SUCCESS = 1; private static final String INTERRUPTED_MESSAGE = "Python 在该店铺结果提交完成前中断"; private final FileTaskMapper fileTaskMapper; @@ -264,12 +267,13 @@ public class PatrolDeleteTaskService { result.setSourceFilename(normalizedShopName); result.setSourceFileUrl(item.getShopId()); result.setUserId(request.getUserId()); - result.setSuccess(null); + result.setSuccess(RESULT_PENDING); result.setCreatedAt(now); fileResultMapper.insert(result); snapshots.add(toSnapshotVo(result, item, task.getStatus(), null)); } persistTaskJson(task, uniqueItems, snapshots); + taskCacheService.saveTaskCache(task); PatrolDeleteCreateTaskVo vo = new PatrolDeleteCreateTaskVo(); vo.setTaskId(task.getId()); @@ -326,6 +330,7 @@ public class PatrolDeleteTaskService { persistSnapshotJson(task, snapshots); fileTaskMapper.updateById(task); + taskCacheService.saveTaskCache(task); tryFinalizeTask(taskId, false); } @@ -402,6 +407,7 @@ public class PatrolDeleteTaskService { persistSnapshotJson(task, snapshots); fileTaskMapper.updateById(task); + taskCacheService.saveTaskCache(task); return changed; } @@ -448,6 +454,7 @@ public class PatrolDeleteTaskService { updateTaskStatusFromRows(task, rows); persistSnapshotJson(task, buildSnapshotFromDb(task, rows)); fileTaskMapper.updateById(task); + taskCacheService.saveTaskCache(task); } private long countTasks(Long userId, List statuses) { @@ -502,9 +509,9 @@ public class PatrolDeleteTaskService { private Map> buildSnapshotMap(Map taskMap) { Map> out = new LinkedHashMap<>(); for (Map.Entry entry : taskMap.entrySet()) { - List snapshots = taskResultItemService.listResultSnapshots(entry.getKey(), MODULE_TYPE, PatrolDeleteResultItemVo.class); + List snapshots = parseTaskSnapshots(entry.getValue().getResultJson()); if (snapshots.isEmpty()) { - snapshots = parseTaskSnapshots(entry.getValue().getResultJson()); + snapshots = taskResultItemService.listResultSnapshots(entry.getKey(), MODULE_TYPE, PatrolDeleteResultItemVo.class); } out.put(entry.getKey(), indexSnapshotByResultId(snapshots)); } @@ -518,7 +525,7 @@ public class PatrolDeleteTaskService { item.setShopName(firstNonBlank(item.getShopName(), entity.getSourceFilename())); item.setShopId(firstNonBlank(item.getShopId(), entity.getSourceFileUrl())); item.setTaskStatus(task != null ? task.getStatus() : item.getTaskStatus()); - item.setSuccess(entity.getSuccess() == null ? item.getSuccess() : entity.getSuccess() == 1); + item.setSuccess(toSuccessFlag(entity.getSuccess(), item.getSuccess())); item.setError(!blank(entity.getErrorMessage()) ? entity.getErrorMessage() : item.getError()); item.setCreatedAt(entity.getCreatedAt()); item.setFinishedAt(task != null ? task.getFinishedAt() : item.getFinishedAt()); @@ -575,7 +582,7 @@ public class PatrolDeleteTaskService { vo.setMatchStatus(item.getMatchStatus()); vo.setMatchMessage(item.getMatchMessage()); vo.setTaskStatus(taskStatus); - vo.setSuccess(result.getSuccess() == null ? null : result.getSuccess() == 1); + vo.setSuccess(toSuccessFlag(result.getSuccess(), null)); vo.setError(result.getErrorMessage()); vo.setCreatedAt(result.getCreatedAt()); vo.setFinishedAt(finishedAt); @@ -616,7 +623,7 @@ public class PatrolDeleteTaskService { item.setShopName(row.getSourceFilename()); item.setShopId(row.getSourceFileUrl()); item.setTaskStatus(task == null ? null : task.getStatus()); - item.setSuccess(row.getSuccess() == null ? null : row.getSuccess() == 1); + item.setSuccess(toSuccessFlag(row.getSuccess(), null)); item.setError(row.getErrorMessage()); item.setCreatedAt(row.getCreatedAt()); item.setFinishedAt(task == null ? null : task.getFinishedAt()); @@ -642,8 +649,8 @@ public class PatrolDeleteTaskService { } private void updateTaskStatusFromRows(FileTaskEntity task, List rows) { - long successCount = rows.stream().filter(row -> Integer.valueOf(1).equals(row.getSuccess())).count(); - long failedCount = rows.stream().filter(row -> Integer.valueOf(0).equals(row.getSuccess())).count(); + long successCount = rows.stream().filter(row -> Integer.valueOf(RESULT_SUCCESS).equals(row.getSuccess())).count(); + long failedCount = rows.stream().filter(row -> Integer.valueOf(RESULT_FAILED).equals(row.getSuccess())).count(); long pendingCount = rows.stream().filter(row -> !isResultFinished(row)).count(); task.setSuccessFileCount((int) successCount); task.setFailedFileCount((int) failedCount); @@ -796,11 +803,11 @@ public class PatrolDeleteTaskService { .filter(item -> Boolean.TRUE.equals(item.getSuccess())) .toList(); if (!successItems.isEmpty()) { - String filename = safeFileStem("patrol-delete-" + task.getId()) + ".xlsx"; + String filename = buildTaskWorkbookFilename(task); int rowCount = excelAssemblyService.countRows(successItems); FileResultEntity firstSuccessRow = null; for (FileResultEntity row : rows) { - if (Integer.valueOf(1).equals(row.getSuccess())) { + if (Integer.valueOf(RESULT_SUCCESS).equals(row.getSuccess())) { row.setResultFilename(filename); row.setResultFileUrl(null); row.setResultFileSize(0L); @@ -843,7 +850,7 @@ public class PatrolDeleteTaskService { throw new BusinessException("没有可生成的巡店删除结果"); } File workRoot = FileUtil.mkdir(FileUtil.file(System.getProperty("java.io.tmpdir"), "patrol-delete-result", String.valueOf(task.getId()))); - String filename = safeFileStem("patrol-delete-" + task.getId()) + ".xlsx"; + String filename = buildTaskWorkbookFilename(task); File xlsx = FileUtil.file(workRoot, filename); try { excelAssemblyService.writeWorkbook(xlsx, successItems); @@ -851,7 +858,7 @@ public class PatrolDeleteTaskService { long fileSize = xlsx.length(); int rowCount = excelAssemblyService.countRows(successItems); for (FileResultEntity row : rows) { - if (Integer.valueOf(1).equals(row.getSuccess())) { + if (Integer.valueOf(RESULT_SUCCESS).equals(row.getSuccess())) { row.setResultFilename(filename); row.setResultFileUrl(objectKey); row.setResultFileSize(fileSize); @@ -866,13 +873,13 @@ public class PatrolDeleteTaskService { } private void markResultSuccess(FileResultEntity row) { - row.setSuccess(1); + row.setSuccess(RESULT_SUCCESS); row.setErrorMessage(null); fileResultMapper.updateById(row); } private void markResultFailed(FileResultEntity row, String message) { - row.setSuccess(0); + row.setSuccess(RESULT_FAILED); row.setErrorMessage(blankToNull(message)); row.setResultFilename(null); row.setResultFileUrl(null); @@ -883,7 +890,14 @@ public class PatrolDeleteTaskService { } private boolean isResultFinished(FileResultEntity row) { - return Integer.valueOf(1).equals(row.getSuccess()) || Integer.valueOf(0).equals(row.getSuccess()); + return Integer.valueOf(RESULT_SUCCESS).equals(row.getSuccess()) || Integer.valueOf(RESULT_FAILED).equals(row.getSuccess()); + } + + private Boolean toSuccessFlag(Integer dbValue, Boolean fallback) { + if (dbValue == null || Integer.valueOf(RESULT_PENDING).equals(dbValue)) { + return fallback; + } + return Integer.valueOf(RESULT_SUCCESS).equals(dbValue); } private List parseTaskSnapshots(String json) { @@ -972,6 +986,11 @@ public class PatrolDeleteTaskService { return !blank(first) ? first : second; } + private String buildTaskWorkbookFilename(FileTaskEntity task) { + Long taskId = task == null ? null : task.getId(); + return safeFileStem("巡店删除-" + (taskId == null ? "result" : taskId)) + ".xlsx"; + } + private String safeFileStem(String value) { String raw = value == null ? "result" : value.trim(); String safe = raw.replaceAll("[\\\\/:*?\"<>|]+", "_"); diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/queryasin/service/QueryAsinTaskService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/queryasin/service/QueryAsinTaskService.java index 344a744..b266054 100644 --- a/backend-java/src/main/java/com/nanri/aiimage/modules/queryasin/service/QueryAsinTaskService.java +++ b/backend-java/src/main/java/com/nanri/aiimage/modules/queryasin/service/QueryAsinTaskService.java @@ -49,6 +49,9 @@ public class QueryAsinTaskService { private static final String MODULE_TYPE = "QUERY_ASIN"; private static final String CONTENT_TYPE_XLSX = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; + private static final int RESULT_PENDING = -1; + private static final int RESULT_FAILED = 0; + private static final int RESULT_SUCCESS = 1; private static final String INTERRUPTED_MESSAGE = "Python 在该店铺结果提交完成前中断"; private final FileTaskMapper fileTaskMapper; @@ -270,12 +273,13 @@ public class QueryAsinTaskService { result.setSourceFilename(normalizedShopName); result.setSourceFileUrl(item.getShopId()); result.setUserId(request.getUserId()); - result.setSuccess(null); + result.setSuccess(RESULT_PENDING); result.setCreatedAt(now); fileResultMapper.insert(result); snapshots.add(toSnapshotVo(result, item, task.getStatus(), null)); } persistTaskJson(task, uniqueItems, snapshots); + taskCacheService.saveTaskCache(task); QueryAsinCreateTaskVo vo = new QueryAsinCreateTaskVo(); vo.setTaskId(task.getId()); @@ -332,6 +336,7 @@ public class QueryAsinTaskService { persistSnapshotJson(task, snapshots); fileTaskMapper.updateById(task); + taskCacheService.saveTaskCache(task); tryFinalizeTask(taskId, false); } @@ -408,6 +413,7 @@ public class QueryAsinTaskService { persistSnapshotJson(task, snapshots); fileTaskMapper.updateById(task); + taskCacheService.saveTaskCache(task); return changed; } @@ -454,6 +460,7 @@ public class QueryAsinTaskService { updateTaskStatusFromRows(task, rows); persistSnapshotJson(task, buildSnapshotFromDb(task, rows)); fileTaskMapper.updateById(task); + taskCacheService.saveTaskCache(task); } private long countTasks(Long userId, List statuses) { @@ -508,9 +515,9 @@ public class QueryAsinTaskService { private Map> buildSnapshotMap(Map taskMap) { Map> out = new LinkedHashMap<>(); for (Map.Entry entry : taskMap.entrySet()) { - List snapshots = taskResultItemService.listResultSnapshots(entry.getKey(), MODULE_TYPE, QueryAsinResultItemVo.class); + List snapshots = parseTaskSnapshots(entry.getValue().getResultJson()); if (snapshots.isEmpty()) { - snapshots = parseTaskSnapshots(entry.getValue().getResultJson()); + snapshots = taskResultItemService.listResultSnapshots(entry.getKey(), MODULE_TYPE, QueryAsinResultItemVo.class); } out.put(entry.getKey(), indexSnapshotByResultId(snapshots)); } @@ -524,7 +531,7 @@ public class QueryAsinTaskService { item.setShopName(firstNonBlank(item.getShopName(), entity.getSourceFilename())); item.setShopId(firstNonBlank(item.getShopId(), entity.getSourceFileUrl())); item.setTaskStatus(task != null ? task.getStatus() : item.getTaskStatus()); - item.setSuccess(entity.getSuccess() == null ? item.getSuccess() : entity.getSuccess() == 1); + item.setSuccess(toSuccessFlag(entity.getSuccess(), item.getSuccess())); item.setError(!blank(entity.getErrorMessage()) ? entity.getErrorMessage() : item.getError()); item.setCreatedAt(entity.getCreatedAt()); item.setFinishedAt(task != null ? task.getFinishedAt() : item.getFinishedAt()); @@ -581,7 +588,7 @@ public class QueryAsinTaskService { vo.setMatchStatus(item.getMatchStatus()); vo.setMatchMessage(item.getMatchMessage()); vo.setTaskStatus(taskStatus); - vo.setSuccess(result.getSuccess() == null ? null : result.getSuccess() == 1); + vo.setSuccess(toSuccessFlag(result.getSuccess(), null)); vo.setError(result.getErrorMessage()); vo.setCreatedAt(result.getCreatedAt()); vo.setFinishedAt(finishedAt); @@ -622,7 +629,7 @@ public class QueryAsinTaskService { item.setShopName(row.getSourceFilename()); item.setShopId(row.getSourceFileUrl()); item.setTaskStatus(task == null ? null : task.getStatus()); - item.setSuccess(row.getSuccess() == null ? null : row.getSuccess() == 1); + item.setSuccess(toSuccessFlag(row.getSuccess(), null)); item.setError(row.getErrorMessage()); item.setCreatedAt(row.getCreatedAt()); item.setFinishedAt(task == null ? null : task.getFinishedAt()); @@ -648,8 +655,8 @@ public class QueryAsinTaskService { } private void updateTaskStatusFromRows(FileTaskEntity task, List rows) { - long successCount = rows.stream().filter(row -> Integer.valueOf(1).equals(row.getSuccess())).count(); - long failedCount = rows.stream().filter(row -> Integer.valueOf(0).equals(row.getSuccess())).count(); + long successCount = rows.stream().filter(row -> Integer.valueOf(RESULT_SUCCESS).equals(row.getSuccess())).count(); + long failedCount = rows.stream().filter(row -> Integer.valueOf(RESULT_FAILED).equals(row.getSuccess())).count(); long pendingCount = rows.stream().filter(row -> !isResultFinished(row)).count(); task.setSuccessFileCount((int) successCount); task.setFailedFileCount((int) failedCount); @@ -816,11 +823,11 @@ public class QueryAsinTaskService { .filter(item -> Boolean.TRUE.equals(item.getSuccess())) .toList(); if (!successItems.isEmpty()) { - String filename = safeFileStem("query-asin-" + task.getId()) + ".xlsx"; + String filename = buildTaskWorkbookFilename(task); int rowCount = excelAssemblyService.countRows(successItems); FileResultEntity firstSuccessRow = null; for (FileResultEntity row : rows) { - if (Integer.valueOf(1).equals(row.getSuccess())) { + if (Integer.valueOf(RESULT_SUCCESS).equals(row.getSuccess())) { row.setResultFilename(filename); row.setResultFileUrl(null); row.setResultFileSize(0L); @@ -863,7 +870,7 @@ public class QueryAsinTaskService { throw new BusinessException("没有可生成的查询ASIN结果"); } File workRoot = FileUtil.mkdir(FileUtil.file(System.getProperty("java.io.tmpdir"), "query-asin-result", String.valueOf(task.getId()))); - String filename = safeFileStem("query-asin-" + task.getId()) + ".xlsx"; + String filename = buildTaskWorkbookFilename(task); File xlsx = FileUtil.file(workRoot, filename); try { excelAssemblyService.writeWorkbook(xlsx, successItems); @@ -871,7 +878,7 @@ public class QueryAsinTaskService { long fileSize = xlsx.length(); int rowCount = excelAssemblyService.countRows(successItems); for (FileResultEntity row : rows) { - if (Integer.valueOf(1).equals(row.getSuccess())) { + if (Integer.valueOf(RESULT_SUCCESS).equals(row.getSuccess())) { row.setResultFilename(filename); row.setResultFileUrl(objectKey); row.setResultFileSize(fileSize); @@ -886,13 +893,13 @@ public class QueryAsinTaskService { } private void markResultSuccess(FileResultEntity row) { - row.setSuccess(1); + row.setSuccess(RESULT_SUCCESS); row.setErrorMessage(null); fileResultMapper.updateById(row); } private void markResultFailed(FileResultEntity row, String message) { - row.setSuccess(0); + row.setSuccess(RESULT_FAILED); row.setErrorMessage(blankToNull(message)); row.setResultFilename(null); row.setResultFileUrl(null); @@ -903,7 +910,14 @@ public class QueryAsinTaskService { } private boolean isResultFinished(FileResultEntity row) { - return Integer.valueOf(1).equals(row.getSuccess()) || Integer.valueOf(0).equals(row.getSuccess()); + return Integer.valueOf(RESULT_SUCCESS).equals(row.getSuccess()) || Integer.valueOf(RESULT_FAILED).equals(row.getSuccess()); + } + + private Boolean toSuccessFlag(Integer dbValue, Boolean fallback) { + if (dbValue == null || Integer.valueOf(RESULT_PENDING).equals(dbValue)) { + return fallback; + } + return Integer.valueOf(RESULT_SUCCESS).equals(dbValue); } private List parseTaskSnapshots(String json) { @@ -1034,6 +1048,11 @@ public class QueryAsinTaskService { return asin == null ? "" : asin.trim().toUpperCase(); } + private String buildTaskWorkbookFilename(FileTaskEntity task) { + Long taskId = task == null ? null : task.getId(); + return safeFileStem("查询ASIN-" + (taskId == null ? "result" : taskId)) + ".xlsx"; + } + private String safeFileStem(String value) { String raw = value == null ? "result" : value.trim(); String safe = raw.replaceAll("[\\\\/:*?\"<>|]+", "_"); diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/service/QueryAsinService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/service/QueryAsinService.java index e8a39d7..2e89945 100644 --- a/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/service/QueryAsinService.java +++ b/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/service/QueryAsinService.java @@ -15,6 +15,7 @@ import com.nanri.aiimage.modules.shopkey.model.vo.QueryAsinItemVo; import com.nanri.aiimage.modules.shopkey.model.vo.QueryAsinPageVo; import com.nanri.aiimage.modules.shopkey.model.vo.ShopManageGroupItemVo; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; @@ -30,9 +31,11 @@ import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Predicate; @Service @RequiredArgsConstructor +@Slf4j public class QueryAsinService { private static final List SUPPORTED_COUNTRIES = List.of("DE", "UK", "FR", "IT", "ES"); @@ -364,13 +367,32 @@ public class QueryAsinService { } private String normalizeCountry(String country) { - String normalized = normalizeBlank(country).toUpperCase(Locale.ROOT); + String normalized = normalizeCountryAlias(country); if (!SUPPORTED_COUNTRIES.contains(normalized)) { throw new BusinessException("国家参数不支持: " + country); } return normalized; } + private String normalizeCountryAlias(String country) { + String normalized = normalizeBlank(country); + if (normalized.isEmpty()) { + return ""; + } + String upper = normalized.toUpperCase(Locale.ROOT); + if (SUPPORTED_COUNTRIES.contains(upper)) { + return upper; + } + return switch (normalizeHeaderKey(normalized)) { + case "\u5fb7\u56fd", "\u5fb7", "de", "germany", "german", "deutschland" -> "DE"; + case "\u82f1\u56fd", "\u82f1", "uk", "gb", "greatbritain", "britain", "unitedkingdom" -> "UK"; + case "\u6cd5\u56fd", "\u6cd5", "fr", "france", "french" -> "FR"; + case "\u610f\u5927\u5229", "\u610f", "it", "italy", "italian" -> "IT"; + case "\u897f\u73ed\u7259", "\u897f", "es", "spain", "spanish", "espana" -> "ES"; + default -> ""; + }; + } + private String normalizeAsin(String asin) { String normalized = normalizeRequired(asin, "ASIN 不能为空").toUpperCase(Locale.ROOT); if (normalized.length() > 64) { @@ -505,13 +527,19 @@ public class QueryAsinService { } } headerLoaded = true; - if (findGroupIndex() == null && (fallbackGroupId == null || fallbackGroupId <= 0)) { + Integer resolvedGroupIndex = resolveGroupHeaderIndex(); + Integer resolvedShopIndex = resolveShopHeaderIndex(); + boolean hasWideCountries = hasWideCountryColumns(); + boolean hasSingleCountryAsin = hasSingleCountryAsinColumns(); + log.info("[query-asin-import] headerMap={} normalizedKeys={} groupIndex={} shopIndex={} hasWideCountries={} hasSingleCountryAsin={}", + headerMap, headerIndexByKey.keySet(), resolvedGroupIndex, resolvedShopIndex, hasWideCountries, hasSingleCountryAsin); + if (resolvedGroupIndex == null && (fallbackGroupId == null || fallbackGroupId <= 0)) { throw new BusinessException("Excel 缺少分组列,请在页面选择分组或提供分组列"); } - if (findShopIndex() == null) { + if (resolvedShopIndex == null) { throw new BusinessException("Excel 缺少店铺名列"); } - if (SUPPORTED_COUNTRIES.stream().noneMatch(country -> findCountryIndex(country) != null)) { + if (!hasWideCountries && !hasSingleCountryAsin) { throw new BusinessException("Excel 至少需要包含一个国家 ASIN 列"); } } @@ -524,7 +552,7 @@ public class QueryAsinService { progress.setProcessedRows(rowIndex); Long groupId = resolveGroupId(rowMap); - String shopName = cell(rowMap, findShopIndex()); + String shopName = cell(rowMap, resolveShopHeaderIndex()); if (groupId == null || shopName.isEmpty()) { skippedCount++; updateProgress(); @@ -532,27 +560,8 @@ public class QueryAsinService { } QueryAsinEntity row = newImportEntity(groupId, shopName); - int rowAsinCount = 0; - int rowAcceptedCount = 0; - for (String country : SUPPORTED_COUNTRIES) { - Integer index = findCountryIndex(country); - if (index == null) { - continue; - } - String asin = normalizeBlank(cell(rowMap, index)).toUpperCase(Locale.ROOT); - if (asin.isEmpty()) { - continue; - } - if (asin.length() > 64) { - skippedCount++; - continue; - } - asinCount++; - rowAsinCount++; - setCountryAsin(row, country, asin); - rowAcceptedCount++; - } - if (rowAsinCount == 0) { + int rowAcceptedCount = collectRowCountryAsins(row, rowMap); + if (countCountryCells(row) == 0) { skippedCount++; } if (rowAcceptedCount > 0) { @@ -754,7 +763,7 @@ public class QueryAsinService { } private Long resolveGroupId(Map rowMap) { - Integer groupIndex = findGroupIndex(); + Integer groupIndex = resolveGroupHeaderIndex(); String groupText = cell(rowMap, groupIndex); if (groupText.isEmpty()) { return fallbackGroupId != null && fallbackGroupId > 0 ? fallbackGroupId : null; @@ -767,6 +776,51 @@ public class QueryAsinService { return id; } + private int collectRowCountryAsins(QueryAsinEntity row, Map rowMap) { + int acceptedCount = 0; + for (String country : SUPPORTED_COUNTRIES) { + Integer index = resolveCountryHeaderIndex(country); + if (index == null) { + continue; + } + acceptedCount += acceptCountryAsin(row, country, cell(rowMap, index)); + } + Integer singleCountryIndex = findSingleCountryIndex(); + Integer singleAsinIndex = findSingleAsinIndex(); + if (singleCountryIndex != null && singleAsinIndex != null) { + acceptedCount += acceptCountryAsin( + row, + normalizeCountryAlias(cell(rowMap, singleCountryIndex)), + cell(rowMap, singleAsinIndex)); + } + return acceptedCount; + } + + private int acceptCountryAsin(QueryAsinEntity row, String country, String asin) { + String normalizedAsin = normalizeBlank(asin).toUpperCase(Locale.ROOT); + if (normalizedAsin.isEmpty()) { + return 0; + } + asinCount++; + if (country == null || country.isEmpty() || !SUPPORTED_COUNTRIES.contains(country)) { + skippedCount++; + return 0; + } + if (normalizedAsin.length() > 64) { + skippedCount++; + return 0; + } + String existing = getCountryAsin(row, country); + if (!existing.isEmpty()) { + if (!existing.equals(normalizedAsin)) { + skippedCount++; + } + return 0; + } + setCountryAsin(row, country, normalizedAsin); + return 1; + } + private void loadGroups() { if (groupsLoaded) { return; @@ -801,6 +855,88 @@ public class QueryAsinService { }; } + private Integer resolveGroupHeaderIndex() { + Integer index = findGroupIndex(); + if (index != null) { + return index; + } + index = firstHeader("groupname", "\u5206\u7ec4id", "\u5206\u7ec4"); + if (index != null) { + return index; + } + return findHeaderByPredicate(key -> key.contains("\u5206\u7ec4") || key.contains("group")); + } + + private Integer resolveShopHeaderIndex() { + Integer index = findShopIndex(); + if (index != null) { + return index; + } + index = firstHeader( + "\u5e97\u94fa\u540d", "\u5e97\u94fa\u540d\u79f0", "\u5e97\u94fa\u8d26\u53f7", "\u8d26\u53f7", + "account", "selleraccount", "storeaccount", "storename"); + if (index != null) { + return index; + } + return findHeaderByPredicate(key -> + (key.contains("\u5e97\u94fa") && (key.contains("\u540d") || key.contains("\u8d26\u53f7"))) + || (key.contains("shop") && (key.contains("name") || key.contains("account"))) + || key.contains("selleraccount") + || key.contains("storeaccount")); + } + + private Integer resolveCountryHeaderIndex(String country) { + Integer index = findCountryIndex(country); + if (index != null) { + return index; + } + return switch (country) { + case "DE" -> firstHeader("\u5fb7\u56fd", "\u5fb7", "\u5fb7\u56fdasin"); + case "UK" -> firstHeader("\u82f1\u56fd", "\u82f1", "\u82f1\u56fdasin"); + case "FR" -> firstHeader("\u6cd5\u56fd", "\u6cd5", "\u6cd5\u56fdasin"); + case "IT" -> firstHeader("\u610f\u5927\u5229", "\u610f", "\u610f\u5927\u5229asin"); + case "ES" -> firstHeader("\u897f\u73ed\u7259", "\u897f", "\u897f\u73ed\u7259asin"); + default -> null; + }; + } + + private Integer findSingleCountryIndex() { + Integer index = firstHeader( + "\u4e2d\u6587\u56fd\u5bb6\u540d", "\u56fd\u5bb6", "\u56fd\u5bb6\u540d", "\u56fd\u5bb6\u7ad9\u70b9", + "\u7ad9\u70b9", "country", "countryname", "marketplace", "site"); + if (index != null) { + return index; + } + return findHeaderByPredicate(key -> + key.contains("\u56fd\u5bb6") + || key.contains("\u7ad9\u70b9") + || key.contains("country") + || key.contains("marketplace") + || key.equals("site")); + } + + private Integer findSingleAsinIndex() { + return firstHeader("asin", "\u67e5\u8be2asin", "\u4ea7\u54c1asin", "\u94fe\u63a5asin"); + } + + private boolean hasWideCountryColumns() { + return SUPPORTED_COUNTRIES.stream().anyMatch(country -> resolveCountryHeaderIndex(country) != null); + } + + private boolean hasSingleCountryAsinColumns() { + return findSingleCountryIndex() != null && findSingleAsinIndex() != null; + } + + private Integer findHeaderByPredicate(Predicate predicate) { + for (Map.Entry entry : headerIndexByKey.entrySet()) { + String key = entry.getKey(); + if (key != null && predicate.test(key)) { + return entry.getValue(); + } + } + return null; + } + private Integer firstHeader(String... names) { for (String name : names) { Integer index = headerIndexByKey.get(normalizeHeaderKey(name)); diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/task/service/TaskResultItemService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/task/service/TaskResultItemService.java index 1b6f23f..d955967 100644 --- a/backend-java/src/main/java/com/nanri/aiimage/modules/task/service/TaskResultItemService.java +++ b/backend-java/src/main/java/com/nanri/aiimage/modules/task/service/TaskResultItemService.java @@ -125,15 +125,18 @@ public class TaskResultItemService { String status, Object payload) { String payloadJson = writeJson(payload); + String payloadHash = hash(payloadJson); String normalizedScopeKey = firstNonBlank(scopeKey, "task:" + taskId).trim(); String normalizedItemKey = firstNonBlank(itemKey, "result:" + resultId).trim(); String scopeHash = hash(normalizedScopeKey); - String storedPayload = transientPayloadStorageService.storeResultItemPayload( - moduleType, taskId, scopeHash, normalizedItemKey, payloadJson); - String payloadHash = hash(payloadJson); LocalDateTime now = LocalDateTime.now(); TaskResultItemEntity existing = find(taskId, moduleType, scopeHash, normalizedItemKey); + if (isUnchanged(existing, resultId, normalizedScopeKey, countryCode, asin, status, payloadHash)) { + return; + } + String storedPayload = transientPayloadStorageService.storeResultItemPayload( + moduleType, taskId, scopeHash, normalizedItemKey, payloadJson); if (existing == null) { TaskResultItemEntity entity = new TaskResultItemEntity(); entity.setTaskId(taskId); @@ -159,6 +162,10 @@ public class TaskResultItemService { if (existing == null) { throw new BusinessException("保存任务结果明细失败"); } + if (isUnchanged(existing, resultId, normalizedScopeKey, countryCode, asin, status, payloadHash)) { + transientPayloadStorageService.deleteReplacedPayloadIfNeeded(storedPayload, existing.getPayloadJson()); + return; + } transientPayloadStorageService.deleteReplacedPayloadIfNeeded(existing.getPayloadJson(), storedPayload); taskResultItemMapper.update(null, new LambdaUpdateWrapper() .eq(TaskResultItemEntity::getId, existing.getId()) @@ -172,6 +179,24 @@ public class TaskResultItemService { .set(TaskResultItemEntity::getUpdatedAt, now)); } + private boolean isUnchanged(TaskResultItemEntity existing, + Long resultId, + String scopeKey, + String countryCode, + String asin, + String status, + String payloadHash) { + if (existing == null) { + return false; + } + return java.util.Objects.equals(existing.getResultId(), resultId) + && java.util.Objects.equals(existing.getScopeKey(), scopeKey) + && java.util.Objects.equals(existing.getCountryCode(), countryCode) + && java.util.Objects.equals(existing.getAsin(), asin) + && java.util.Objects.equals(existing.getStatus(), status) + && java.util.Objects.equals(existing.getPayloadHash(), payloadHash); + } + private TaskResultItemEntity find(Long taskId, String moduleType, String scopeHash, String itemKey) { return taskResultItemMapper.selectOne(new LambdaQueryWrapper() .eq(TaskResultItemEntity::getTaskId, taskId) diff --git a/backend-java/src/main/resources/application-local.example.yml b/backend-java/src/main/resources/application-local.example.yml index 3c5db4e..f734238 100644 --- a/backend-java/src/main/resources/application-local.example.yml +++ b/backend-java/src/main/resources/application-local.example.yml @@ -31,6 +31,7 @@ AIIMAGE_APPEARANCE_PATENT_COZE_WORKFLOW_PATH=/v1/workflow/run AIIMAGE_APPEARANCE_PATENT_COZE_WORKFLOW_ID=7632683471312355338 AIIMAGE_APPEARANCE_PATENT_COZE_TOKEN= AIIMAGE_APPEARANCE_PATENT_COZE_BATCH_SIZE=10 +AIIMAGE_APPEARANCE_PATENT_COZE_READ_TIMEOUT_MILLIS=60000 AIIMAGE_APPEARANCE_PATENT_STALE_TIMEOUT_MINUTES=20 AIIMAGE_ROCKETMQ_NAME_SERVER=121.196.149.225:9876 diff --git a/backend-java/src/main/resources/application.yml b/backend-java/src/main/resources/application.yml index 1220fbd..481a12a 100644 --- a/backend-java/src/main/resources/application.yml +++ b/backend-java/src/main/resources/application.yml @@ -136,6 +136,8 @@ aiimage: coze-workflow-id: ${AIIMAGE_APPEARANCE_PATENT_COZE_WORKFLOW_ID:7632683471312355338} coze-token: ${AIIMAGE_APPEARANCE_PATENT_COZE_TOKEN:} coze-batch-size: ${AIIMAGE_APPEARANCE_PATENT_COZE_BATCH_SIZE:10} + coze-connect-timeout-millis: ${AIIMAGE_APPEARANCE_PATENT_COZE_CONNECT_TIMEOUT_MILLIS:10000} + coze-read-timeout-millis: ${AIIMAGE_APPEARANCE_PATENT_COZE_READ_TIMEOUT_MILLIS:60000} stale-timeout-minutes: ${AIIMAGE_APPEARANCE_PATENT_STALE_TIMEOUT_MINUTES:20} stale-finalize-cron: ${AIIMAGE_APPEARANCE_PATENT_STALE_FINALIZE_CRON:0 */2 * * * *} security: diff --git a/backend/__pycache__/config.cpython-312.pyc b/backend/__pycache__/config.cpython-312.pyc index 08477f7..82ec769 100644 Binary files a/backend/__pycache__/config.cpython-312.pyc and b/backend/__pycache__/config.cpython-312.pyc differ diff --git a/backend/codex-flask-local.err.log b/backend/codex-flask-local.err.log new file mode 100644 index 0000000..3362254 --- /dev/null +++ b/backend/codex-flask-local.err.log @@ -0,0 +1,45 @@ +WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:15124 + * Running on http://192.168.9.14:15124 +Press CTRL+C to quit +127.0.0.1 - - [27/Apr/2026 18:32:53] "POST /login HTTP/1.1" 302 - +127.0.0.1 - - [27/Apr/2026 18:32:54] "GET /api/admin/shop-manage-groups HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:32:55] "POST /api/admin/query-asins/import HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:32:55] "GET /api/admin/query-asins/import/3bda54f728ff447f9edc6d80aa47986c HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:32:56] "GET /api/admin/query-asins?page=1&page_size=20&group_id=13&shop_name=codex_qasin_wide_20260427181749 HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:32:57] "POST /api/admin/query-asins/import HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:32:57] "GET /api/admin/query-asins/import/7779a5fac0c840138579f549d297e016 HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:32:58] "GET /api/admin/query-asins?page=1&page_size=20&group_id=13&shop_name=codex_qasin_narrow_a_20260427181749 HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:32:59] "GET /api/admin/query-asins?page=1&page_size=20&group_id=13&shop_name=codex_qasin_narrow_b_20260427181749 HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:32:59] "POST /api/admin/query-asins/delete-import HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:33:00] "GET /api/admin/query-asins/delete-import/474b72f74c414d94a534069668cdbe69 HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:33:01] "GET /api/admin/query-asins?page=1&page_size=20&group_id=13&shop_name=codex_qasin_wide_20260427181749 HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:33:01] "POST /api/admin/query-asins/delete-import HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:33:02] "GET /api/admin/query-asins/delete-import/4cdd99881e174c638a531f0805612722 HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:33:03] "GET /api/admin/query-asins?page=1&page_size=20&group_id=13&shop_name=codex_qasin_narrow_a_20260427181749 HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:33:03] "GET /api/admin/query-asins?page=1&page_size=20&group_id=13&shop_name=codex_qasin_narrow_b_20260427181749 HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:35:51] "GET /admin HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:35:51] "GET /api/admin/current-user HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:35:51] "GET /api/admin/current-user/menus HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:35:52] "GET /api/admin/shop-manage-groups HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:35:52] "GET /api/admin/users?page=1&page_size=999 HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:35:53] "GET /api/admin/users?page=1&page_size=15 HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:35:54] "GET /api/admin/columns HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:35:54] "GET /api/admin/skip-price-asins?page=1&page_size=15 HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:35:56] "GET /api/admin/columns HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:38:01] "GET /api/admin/dedupe-total-data?page=1&page_size=15 HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:38:04] "GET /api/admin/skip-price-asins?page=1&page_size=15 HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:38:06] "GET /api/admin/query-asins?page=1&page_size=15 HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:39:08] "GET /admin HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:39:08] "GET /api/admin/current-user HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:39:09] "GET /api/admin/shop-manage-groups HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:39:09] "GET /api/admin/current-user/menus HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:39:09] "GET /api/admin/users?page=1&page_size=999 HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:39:10] "GET /api/admin/users?page=1&page_size=15 HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:39:11] "GET /api/admin/columns HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:39:11] "GET /api/admin/skip-price-asins?page=1&page_size=15 HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:39:14] "GET /api/admin/columns HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:39:19] "GET /api/admin/query-asins?page=1&page_size=15 HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:39:22] "GET /api/admin/skip-price-asins?page=1&page_size=15 HTTP/1.1" 200 - +127.0.0.1 - - [27/Apr/2026 18:40:07] "GET /api/admin/query-asins?page=1&page_size=15 HTTP/1.1" 200 - diff --git a/backend/codex-flask-local.out.log b/backend/codex-flask-local.out.log new file mode 100644 index 0000000..5d577fd --- /dev/null +++ b/backend/codex-flask-local.out.log @@ -0,0 +1,2 @@ + * Serving Flask app 'app' + * Debug mode: off diff --git a/backend/config.py b/backend/config.py index 6692838..97a3eb7 100644 --- a/backend/config.py +++ b/backend/config.py @@ -23,8 +23,8 @@ bucket_path = "nanri-image/" file_url_pre = f"https://{bucket}.oss-cn-hangzhou.aliyuncs.com/" import os -# backend_java_base_url = os.environ.get('BACKEND_JAVA_BASE_URL', 'http://127.0.0.1:18080').rstrip('/') -backend_java_base_url = os.environ.get('BACKEND_JAVA_BASE_URL', 'http://8.136.19.173:18080').rstrip('/') +backend_java_base_url = os.environ.get('BACKEND_JAVA_BASE_URL', 'http://127.0.0.1:18080').rstrip('/') +# backend_java_base_url = os.environ.get('BACKEND_JAVA_BASE_URL', 'http://8.136.19.173:18080').rstrip('/') os.environ['OSS_ACCESS_KEY_ID'] = accessKeyId os.environ['OSS_ACCESS_KEY_SECRET'] = accessKeySecret os.environ['SECRET_KEY'] = "ddffc7c1d02121d9554d7b080b2511b6" diff --git a/backend/web_source/admin.html b/backend/web_source/admin.html index 6094daf..ccfb20b 100644 --- a/backend/web_source/admin.html +++ b/backend/web_source/admin.html @@ -862,7 +862,7 @@

导入添加

- +
@@ -879,7 +879,7 @@

导入删除

- +