Files
dsh_mcn_agent/profiles/web/node_modules/dsh-plugin-mcn/lib/data.js
T

106 lines
5.4 KiB
JavaScript
Raw Normal View History

// 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 };