baseline: dsh 工作区基线快照(2026-09-10,全新历史起点)
本提交为 dsh_mcn_workshop 仓库的首个提交(旧仓 dsh_MCNProject 已停用), 完整固化 C:/Users/Administrator/.dsh 当前磁盘状态。 1) 临时数据清理:移出 sessions/(81M)、logs/、skill-backup-20260906/(14M)、 scripts/__pycache__、storages/workspace.json.bak —— 合计 94M(.dsh 162M → 68M) 2) 凭据脱敏:.credentials.yaml(含明文 DEEPSEEK_API_KEY)移出 git 跟踪并加入 .gitignore;mcn-data-insight/scripts/setup_redfox_key.py 中真实 key 示例改为占位符 3) .gitignore 增补 logs/、skill-backup-*/ 规则 4) 技能结构以当前磁盘状态为准:skills/ = mcn-short-video / impeccable / taste-skill / dsh-multi-user-migration;旧技能目录(mcn-dou-analysis、storyboard-prompt、 short-video-script、browser-harness、mcn-data-insight)与 profiles/web/node_modules/dsh-vision-router 本地已不存在,本提交记为删除
This commit is contained in:
commit
856e632807
880 files changed
+225426
No files matched your search
+106
@@ -0,0 +1,106 @@
|
||||
// dsh-plugin-mcn 数据访问层(拆分自 index.js)
|
||||
import { initDb } from "./db.js";
|
||||
import { callMcpTool } from "./mcp.js";
|
||||
import { STANDARD_TRACKS } from "./config.js";
|
||||
|
||||
async function ensureData() {
|
||||
const d = initDb();
|
||||
const row = d.prepare("SELECT COUNT(*) AS c FROM hot_accounts").get();
|
||||
return { cached: row.c > 0, count: row.c };
|
||||
}
|
||||
|
||||
async function refreshData() {
|
||||
const d = initDb();
|
||||
const result = await callMcpTool("list_hot_accounts", {});
|
||||
// 返回结构:{ structured: {total, accounts: [...]} | null, text: "..." }
|
||||
let accounts = null;
|
||||
if (result?.structured && Array.isArray(result.structured.accounts)) {
|
||||
accounts = result.structured.accounts;
|
||||
}
|
||||
if (!accounts) {
|
||||
throw new Error("MCP 返回缺少结构化账号数组(可能未登录或服务异常),无法入库");
|
||||
}
|
||||
const insert = d.prepare(`INSERT OR REPLACE INTO hot_accounts
|
||||
(id, account_name, content, track, tags, masterpiece, created_by, updated_by, created_time, updated_time, sort, del_flag, account_type)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'signed')`);
|
||||
d.exec("BEGIN");
|
||||
try {
|
||||
for (const a of accounts) {
|
||||
insert.run(
|
||||
a.id, a.accountName ?? "", a.content ?? "", a.track ?? "",
|
||||
a.tags ?? "", a.masterpiece ?? "", a.createdBy ?? "", a.updatedBy ?? "",
|
||||
a.createdTime ?? "", a.updatedTime ?? "", a.sort ?? 0, a.delFlag ?? 0
|
||||
);
|
||||
}
|
||||
d.exec("COMMIT");
|
||||
} catch (e) {
|
||||
d.exec("ROLLBACK");
|
||||
throw e;
|
||||
}
|
||||
return { cached: false, count: accounts.length };
|
||||
}
|
||||
|
||||
function queryAccounts({ page = 1, pageSize = 20, search = "", track = "", type = "all", sortBy = "id", order = "asc" }) {
|
||||
const d = initDb();
|
||||
const where = [];
|
||||
const params = [];
|
||||
// 软删账号不展示(同步 V1.0 工作台:批量删除=del_flag=1)
|
||||
where.push("(del_flag IS NULL OR del_flag <> 1)");
|
||||
if (search) {
|
||||
where.push("(account_name LIKE ? OR content LIKE ? OR track LIKE ?)");
|
||||
const like = `%${search}%`;
|
||||
params.push(like, like, like);
|
||||
}
|
||||
if (track) {
|
||||
where.push("track = ?");
|
||||
params.push(track);
|
||||
}
|
||||
// 账号类型筛选:all / signed(签约)/ external(外部)
|
||||
if (type === "signed" || type === "external") {
|
||||
where.push("account_type = ?");
|
||||
params.push(type);
|
||||
}
|
||||
const whereSql = where.length ? "WHERE " + where.join(" AND ") : "";
|
||||
const allowedSort = ["id", "account_name", "track", "sort"];
|
||||
const sortCol = allowedSort.includes(sortBy) ? sortBy : "id";
|
||||
const orderSql = order === "desc" ? "DESC" : "ASC";
|
||||
const total = d.prepare(`SELECT COUNT(*) AS c FROM hot_accounts ${whereSql}`).get(...params).c;
|
||||
const limit = Math.max(1, Number(pageSize) || 20);
|
||||
const offset = (Math.max(1, Number(page) || 1) - 1) * limit;
|
||||
// 排序:收藏的账号排最前,收藏/未收藏各自内部按粉丝数降序(followers 支持 "万"/"亿" 格式解析为数值)
|
||||
const followersExpr = `CASE WHEN h.followers LIKE '%亿%' THEN CAST(REPLACE(REPLACE(h.followers,'亿',''),'万','') AS REAL) * 100000000 WHEN h.followers LIKE '%万%' THEN CAST(REPLACE(h.followers,'万','') AS REAL) * 10000 ELSE CAST(h.followers AS REAL) END`;
|
||||
const orderBySql = `h.favorite DESC, ${followersExpr} DESC, h.id ASC`;
|
||||
// 附带视频数(account_videos 关联统计,供前端「视频数」列展示)与 AI脚本数(挂该账号视频的 ref 脚本 + video_id 空的原创脚本,同步 V1.0 账号列表「AI脚本」列口径)
|
||||
const rows = d.prepare(`SELECT h.*,
|
||||
(SELECT COUNT(*) FROM account_videos v WHERE v.account_id = h.id) AS video_count,
|
||||
(SELECT COUNT(*) FROM rewrite_log r WHERE r.status='done' AND r.script_text IS NOT NULL AND r.script_text <> '' AND ((r.video_id > 0 AND r.video_id IN (SELECT id FROM account_videos v2 WHERE v2.account_id = h.id)) OR ((r.video_id IS NULL OR r.video_id = 0) AND r.account_id = h.id))) AS rewrite_count
|
||||
FROM hot_accounts h ${whereSql} ORDER BY ${orderBySql} LIMIT ? OFFSET ?`)
|
||||
.all(...params, limit, offset);
|
||||
return { total, page: Math.max(1, Number(page) || 1), pageSize: limit, items: rows };
|
||||
}
|
||||
|
||||
function listTracks() {
|
||||
const d = initDb();
|
||||
const dbTracks = d.prepare(`SELECT DISTINCT track FROM hot_accounts WHERE track IS NOT NULL AND track != '' ORDER BY track`).all().map((r) => r.track);
|
||||
// 标准 28 个 + 库内非标值并集(保证旧数据可筛选)
|
||||
const extras = dbTracks.filter((t) => !STANDARD_TRACKS.includes(t));
|
||||
return STANDARD_TRACKS.concat(extras);
|
||||
}
|
||||
|
||||
function queryAccountVideos(accountId, page = 1, pageSize = 20) {
|
||||
const d = initDb();
|
||||
const acc = d.prepare(`SELECT * FROM hot_accounts WHERE id=? AND (del_flag IS NULL OR del_flag <> 1)`).get(accountId);
|
||||
if (!acc) throw new Error("账号不存在");
|
||||
const total = d.prepare(`SELECT COUNT(*) AS c FROM account_videos WHERE account_id=?`).get(accountId).c;
|
||||
const limit = Math.max(1, Math.min(Number(pageSize) || 20, 100));
|
||||
const offset = (Math.max(1, Number(page) || 1) - 1) * limit;
|
||||
const items = d.prepare(`
|
||||
SELECT v.*,
|
||||
(SELECT COUNT(*) FROM account_video_source s WHERE s.video_id = v.id OR s.aweme_id = v.aweme_id) AS has_source,
|
||||
(SELECT COUNT(*) FROM account_video_analysis a WHERE a.video_id = v.id OR a.aweme_id = v.aweme_id) AS has_analysis
|
||||
FROM account_videos v WHERE v.account_id=? ORDER BY v.id ASC LIMIT ? OFFSET ?
|
||||
`).all(accountId, limit, offset);
|
||||
return { account: acc, total, page: Math.max(1, Number(page) || 1), pageSize: limit, items };
|
||||
}
|
||||
|
||||
export { ensureData, refreshData, queryAccounts, listTracks, queryAccountVideos };
|
||||
Reference in new issue
Block a user