From 787ececedb1165ede3f587c82278f1a80b162a2c Mon Sep 17 00:00:00 2001 From: maogeigei Date: Tue, 1 Sep 2026 10:34:07 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E5=B7=A5=E4=BD=9C=E5=8F=B0):=20=E8=A7=86?= =?UTF-8?q?=E9=A2=91=E6=8B=86=E8=A7=A3/=E8=A7=86=E9=A2=91=E5=88=86?= =?UTF-8?q?=E6=9E=90=E5=AD=97=E6=AE=B5=E6=98=A0=E5=B0=84=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=E9=97=AD=E7=8E=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - server.js 入库映射对齐 dsh 语义:content.json→source.content_json、analysis.json→source.analysis_json(视频拆解 tab)、*拆解分析.md→account_video_analysis(视频分析 tab) - 清理 6 条俊希错位 account_video_analysis + 白昼小熊 id13 归位 source47.analysis_json - 重跑入库验证:俊希 6 条 analysis_json 均写入、account_video_analysis 12 条=王微斯拆解分析(语义正确) - 王微斯视频三 tab 验证通过(视频脚本/视频拆解/视频分析各就其位) - 临时脚本已清理 --- .workbuddy/memory/2026-09-01.md | 4 ++++ .workbuddy/memory/MEMORY.md | 1 + .../V1.0/mcn-work-shop/mcn-plugin.db | Bin 4247552 -> 4247552 bytes .../V1.0/mcn-work-shop/public/data-pages.js | 11 +++++---- .../V1.0/mcn-work-shop/server.js | 22 +++++++++++------- 5 files changed, 26 insertions(+), 12 deletions(-) diff --git a/.workbuddy/memory/2026-09-01.md b/.workbuddy/memory/2026-09-01.md index a499647..854ef58 100644 --- a/.workbuddy/memory/2026-09-01.md +++ b/.workbuddy/memory/2026-09-01.md @@ -50,3 +50,7 @@ - **修复(治本)**:①export_video_map.py 增加 aweme_id 纯数字校验(`str(aweme_id).isdigit()`)过滤表头/非数据行;②data-pages.js fmtDuration 重写:兼容纯数字秒(648→10分48秒)与 mm:ss/hh:mm:ss 文本(01:31→1分31秒、13:48→13分48秒);③清理库内表头假记录(DELETE aweme_id='视频ID'/空/null,1条) - **验证**:导出 136 条无非数字;重跑入库 xlsx_rows=136(表头不再计入)、videos_upserted=0;库 total=136/表头0/无时长0;/api/dsh/account-videos 返回 total=136、duration='13:48' 等 mm:ss → fmtDuration 正确显示 - 待提交 git;页面待重开验证 + +## 09-01 10:20 账号详情视频列表 UI 微调(data-pages.js) +- ①操作列按钮「AI改写」→「AI写作」;②点赞列 `fmt`→`fmtWan`(过万转万:81934→8.2万;like_display 为 null、like_count 纯数字,fmtWan 直接可用)。语法检查通过,页面已重开 +- **10:22 修复 fmtWan 回归**:like_display 并非恒 null——**大部分视频有抖音原生「x.x万」文本**(如 7.8万/14万),`fmtWan('7.8万')`=Number→NaN→'-' 导致点赞列大面积变 '-'。修复:fmtWan 先判断含「万」则**直接透传**。全量验证 100/100 正常显示、0 条 '-'。教训:数据字段格式要全量核查,勿以抽查样本下结论 diff --git a/.workbuddy/memory/MEMORY.md b/.workbuddy/memory/MEMORY.md index 8699e6c..2f28c08 100644 --- a/.workbuddy/memory/MEMORY.md +++ b/.workbuddy/memory/MEMORY.md @@ -36,6 +36,7 @@ - **信息链路缺口识别**:Step 有方法论≠Step 能承接账号级信息;通用方法论管"怎么做",账号级信息管"怎么做才像它",不可互替 ## 工作方式约定 +- **治本原则(09-01 用户明确)**:如无明确要求,解决问题一律从根因入手——定位根因→修复机制→**重新执行验证效果**;禁止补数据/临时绕过(补数据仅限用户明确要求时) - **改完必重开页面(08-28)**:MCN工作台等网页改动完成后,最后一步必须用 present_files 重新打开对应页面(带 hash 定位);内置浏览器无 CDP 调试端口无法模拟 F5,重开页面是唯一刷新方式 - **逐条修复**:审计修复必须读一条→改一条→验证一条→再进下一条,禁止批量读取 - **技能版本标签(08-26)**:frontmatter 存 `updated_at`+`last_change`+`deployment`;源=`source`+`created_at`+`updated_at`,副本=`dsh`+`updated_at`+`deployed_at`;副本 deployed_at<源updated_at=滞后 diff --git a/project/短视频脚本创作/V1.0/mcn-work-shop/mcn-plugin.db b/project/短视频脚本创作/V1.0/mcn-work-shop/mcn-plugin.db index 1a3f3e05d3de55b8f4ec9d37129c2ea271df484b..db1fa46b06f3a3b241f781f25211e109d933db6f 100644 GIT binary patch delta 4116 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 HquT!mz<8>V delta 4066 zcmbuBe{>Yp702hz%w|J2%WO#YXWZ;Ah9oGE-TYnz5`@fyAf;-f?RO@7B*x(J9RK*t zym#lm`|i8%-8-))x=gGA_374XL6EJ2AQTA#8`6+d%&KcEmRGRW3US-)tbxjYd}8&= zf0ZlBMdhE$Ipt&J@5=ki+sXmun9`x_R}L$!d&Y(e7~MHXymO#ay5z@zt+2>m^x-ir zfX6o(^mesavYXw(lY`Q5(=w%5IY48A($AIjr^=x4P_7_|^GDz@cNiXPhC@hyG zT)JffB+@kSTksq3YiKNl<|8z2i znK8;Q*T_Sz%{6P{9geoUsbSFqE!kNW>t9 zL97nU0(w-inM{w~DZY1#lr>ULLc1MwfP>&Q@H#jI4udx^p#>xZ-HCxf0k#$0Qzak+ z6G#ANkO&5VBwzu_zzPNe1?b~az#xzc(x`i?eZcJ|OvVo5%pYKMIK^{briqK3yCjl# zyPHii{CG6hW*56ec0aXeI!9YbwmA>T`iGf3rNil@4-1@G7Pf=GWx2sY>3-+4#>WS! zeY~L^jBC%L_9+nX@>5PP1sC>FlQFm^E|^2@+YCW{ zTrih!deD(iSN1xSFfJOGK1l5yP?OHRu!0}h>~K=XZs*g+JoEUZgT+2xy))~V6ne*b zJc;DC_@MdJUgy%+)d@ioA}uhpS1^0`q*SDu<=TeZ#_`;;Y!}aE={EjpU|8~oK^~dz zEz91XXjy8gUyY#DotewuO!2t*;!Kl`>K8a?CiX-!1v{4r+rsI6kDptqp4-{Lw^n<7 zbYz~t(89;w=J*x&U!P#+HJ8j*x{zraBD;%=it|gt!C-N5L1A%WQBgP?f^QEmTg{ie znD6Dgn@tIHsLo#k*Q@*yW0{XoOOt;(1T}HNLTcFt!GQH=HY`2A{=la%^S}Z}8V@cF z$uy+izr+}|h+6hSi5ID(6iSWy>ka8*YUwa+?S`$G<`m0t8h@(y%YMlFy1w2o|1sXH z{d&KmxAzD4eQbqb=BF;TTky>b8hcy-Y`2WonAlf4_nO7 ze^OI>X4fhnT3PI%K&OAH9E-Z7AgiZHoqL8JbN41APOGWqGUCMc^XNyJ$N$aq677+2d&(N=dw z5`TKUKZWM+a6N8fTPS)EBC6c)irCp&YLNr_>F$c3?g+J{LC`7(BK?KjdNt&;GEFtT zqisVl-qVv;lx;#{NVcH>{*@tp$LQ(nHU~!r3rB=PTnh|Kiw*MF#DTSkSeb5b+1er2 ze1Qj8dWKOAXsFwBMnZbCr#~B2s%zAqO8-{lAvVTJU(In=n)s5hn2kpd=a?zI)92EQ zfl=nSqdt^+jaJlG)>8^K0?!P5Z zro;+Yk`fd)O`x*+oCb7jdqcrn{V_A1xc$@-`$wVXzfxHZw`qw8&f8YBA8QOk+J zdMq`>PFLH(kER9O^p_Kv<00)F9Fe#0_GtQ-Kl28~F^WD<^H~vU-yS4Fx>zBySBXw| zZ?({bf7oL(YbR<#j~{C>CGb^$4q*$T+!tlYee&-8Q+cOzZ{-#7}C%4g-OquiSY0>K4a=l z#dKkwcaVi&Y;>mbw)@gkcxk6SizZfhd(PYIM3uUOrM12+US8{S(aq)F`NrB0@mF>h zrO}Zo-a@Rs%)3XIBBnH6AMoAc%u$>ZD7w^_FY}a}SgIbOH;_>F|0(QaY@yxQs~?r2 zg`sd^esM`*L4Gh?lvfne%lnbO8Tw}53_aR!h90{{h92)PLp%Fs=v)0}=->Kg=-Yit z(Mp+}=v%Jdp~yTvt?$0g-&11VhrbXcoZq+AzsS#XRkxjdQFqTV{D30cj3Q7UF9IJ@ zqzM9EHP@X$Nj2{E#se7T~&F&F#2CVXHhBxv5E{S$}aW2m3-d!-g3F$>&?IWy$W6Fdt>^~58bc6=zSYA!_zPK zpKs!0Cl9sriZgZ-X=zTsg~mSbctsZ3S2&*zoX@Wj8ArDgi~2;rD2hlT^m=mAv{0~4 zHZ8>8>Ub9ZR@Z%7I`i{_c}2s6;o-s1lwjV-P`onc-`*!L^y-=_Tk4LRGxV{mR!`#@ zEWJ|y&n3~_i(Q*U%AM$VnbE7YE@srrGt0M4_UgK;W9G&Q-~JUd_2hCI+xJpT^NIDM zIH1p;VU0-?ldk?69<}qeXWS{+FVTh-jz%>Vk;heI^hPqZ%O3rDSK~3Rt)dl?gP>K8 z_XkpYfuZ1sAXYV6w27jr_j%LprhM;M#{2=ft@r7Tbp>SYT4!C=2FJAPr7l)Y-Sa&% zuAddCU#ga=YC}{lOP`{Ky6@J%Bqo)9VTuJybw8FqgXP$$*^yI;YeFz*V?xZIV|<|I z1Vd(nOdnlG&1bVK2eiuZ{&H%*f>qc~BY=P{a5Zi^WG>3V++g*5ANqdz>>mLu8lm%gf@YB{PlRMl=!wHsA! zn5qp|wGpb8t7<`23#nS3s^zO%fvOd%T9K+1t6GVwg;i~&s@ (v == null || v === '') ? '-' : String(v); -/* 过万数字以"万"为单位,保留一位小数(如 57141 → 5.7万) */ +/* 过万数字以"万"为单位,保留一位小数(如 57141 → 5.7万);已是"x.x万"文本(like_display 抖音原生格式)直接透传(09-01 修复:旧实现 Number('7.8万')=NaN→'-') */ const fmtWan = (v) => { - const n = Number(v); + if (v == null || v === '') return '-'; + const s = String(v).trim(); + if (s.includes('万')) return s; + const n = Number(s); if (!Number.isFinite(n) || n <= 0) return '-'; return n >= 10000 ? (n / 10000).toFixed(1) + '万' : String(n); }; @@ -295,11 +298,11 @@ async function renderAccountDetail(view, id) { ${fmt(v.publish_time)} ${esc(v.aweme_id || '-')} ${v.video_url ? `${esc(cleanTitle(v.video_title) || '-')}` : esc(cleanTitle(v.video_title) || '-')} - ${fmt(v.like_display || v.like_count)} + ${fmtWan(v.like_display || v.like_count)} ${fmtWan(v.comment_count)} ${fmtWan(v.share_count)} ${fmtDuration(v.duration)} - + `).join(''); const tabBtns = [['info', '账号信息'], ['persona', '账号设定'], ['analysis', '数据分析']].map(([k, label]) => ``).join(''); let panel = ''; diff --git a/project/短视频脚本创作/V1.0/mcn-work-shop/server.js b/project/短视频脚本创作/V1.0/mcn-work-shop/server.js index ce47be6..0c0edd7 100644 --- a/project/短视频脚本创作/V1.0/mcn-work-shop/server.js +++ b/project/短视频脚本创作/V1.0/mcn-work-shop/server.js @@ -76,7 +76,7 @@ function importAccountIntoDb(db, account, dir) { const stats = { video_analysis: 0, video_source: 0, persona: 0, account_analysis: 0, videos_upserted: 0, skipped: [] }; const now = new Date().toISOString(); const insAnalysis = db.prepare(`INSERT INTO account_video_analysis (video_id, aweme_id, analysis_time, content_json, summary) VALUES (?,?,?,?,?)`); - const insSource = db.prepare(`INSERT INTO account_video_source (video_id, aweme_id, analysis_time, content_json, summary) VALUES (?,?,?,?,?)`); + const insSource = db.prepare(`INSERT INTO account_video_source (video_id, aweme_id, analysis_time, content_json, summary, analysis_json) VALUES (?,?,?,?,?,?)`); const insPersona = db.prepare(`INSERT INTO account_persona (account_id, analysis_time, content_json, summary) VALUES (?,?,?,?)`); const insAccAna = db.prepare(`INSERT INTO account_analysis (account_id, analysis_time, content_json, summary) VALUES (?,?,?,?)`); const acc = db.prepare('SELECT id FROM hot_accounts WHERE account_name=? LIMIT 1').get(account); @@ -123,6 +123,9 @@ function importAccountIntoDb(db, account, dir) { } } // 1) 视频分析目录(功能三 Step5 产出;功能五产出在 视频对标/ 目录,由 dsh 导入机制处理) + // 字段映射(09-01 修正,对齐 dsh 语义):content.json→account_video_source.content_json(视频脚本); + // analysis.json→account_video_source.analysis_json(视频拆解,MCP 结构化分析);*拆解分析.md→account_video_analysis(视频分析,功能四产物)。 + // 原实现把 analysis.json 误写 account_video_analysis 导致「拆解内容进了视频分析」+「视频拆解 tab 空」 const vaDir = path.join(dir, '视频分析'); if (fs.existsSync(vaDir)) { for (const folder of fs.readdirSync(vaDir)) { @@ -131,19 +134,22 @@ function importAccountIntoDb(db, account, dir) { if (!st.isDirectory()) continue; const awemeId = matchAweme(folder); const anPath = path.join(fdir, 'analysis.json'); - if (fs.existsSync(anPath)) { - const content = fs.readFileSync(anPath, 'utf8'); - if (awemeId) db.prepare('DELETE FROM account_video_analysis WHERE aweme_id=?').run(awemeId); - insAnalysis.run(null, awemeId, now, content, content.slice(0, 500)); - stats.video_analysis++; - } const coPath = path.join(fdir, 'content.json'); if (fs.existsSync(coPath)) { const content = fs.readFileSync(coPath, 'utf8'); + const analysisJson = fs.existsSync(anPath) ? fs.readFileSync(anPath, 'utf8') : null; if (awemeId) db.prepare('DELETE FROM account_video_source WHERE aweme_id=?').run(awemeId); - insSource.run(null, awemeId, now, content, ''); + insSource.run(null, awemeId, now, content, '', analysisJson); stats.video_source++; } + // 拆解分析.md → account_video_analysis(功能四逐条拆解产物,与解析并存于同一文件夹;无则不入) + const decon = fs.readdirSync(fdir).find((f) => f.endsWith('拆解分析.md')); + if (decon) { + const content = fs.readFileSync(path.join(fdir, decon), 'utf8'); + if (awemeId) db.prepare('DELETE FROM account_video_analysis WHERE aweme_id=?').run(awemeId); + insAnalysis.run(null, awemeId, now, content, content.slice(0, 500)); + stats.video_analysis++; + } } } // 2) 人设卡 + 账号分析(去重,不删旧)