tt-help-cli-ycl 1.4.134 → 1.4.136

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -29,7 +29,10 @@ import {
29
29
  getDb,
30
30
  getDbPath,
31
31
  initDb,
32
+ initTagDb,
33
+ deriveTagDbPath,
32
34
  resetDbConnection,
35
+ resetTagDbConnection,
33
36
  loadLegacyUsersFromFiles,
34
37
  loadLegacyVideosFromFile,
35
38
  } from "./db-schema.js";
@@ -123,7 +126,9 @@ export function importLegacyJsonToDb({
123
126
  videosFilePath,
124
127
  }) {
125
128
  resetDbConnection();
129
+ resetTagDbConnection();
126
130
  initDb(dbFilePath);
131
+ initTagDb(deriveTagDbPath(dbFilePath));
127
132
 
128
133
  const db = getDb();
129
134
  const dbPath = getDbPath();
@@ -193,6 +198,7 @@ export function importLegacyJsonToDb({
193
198
 
194
199
  export function closeStoreDb() {
195
200
  resetDbConnection();
201
+ resetTagDbConnection();
196
202
  }
197
203
 
198
204
  function getUsersPageFromDb({
@@ -207,7 +213,7 @@ function getUsersPageFromDb({
207
213
  }) {
208
214
  if (!getDb()) return null;
209
215
 
210
- const safeLimit = Math.max(1, Math.min(200, parseInt(limit) || 50));
216
+ const safeLimit = Math.max(1, Math.min(50, parseInt(limit) || 50));
211
217
  const safeOffset = Math.max(0, parseInt(offset) || 0);
212
218
  const where = [];
213
219
  const args = [];
@@ -258,16 +264,20 @@ function getUsersPageFromDb({
258
264
  const jobsOrder = `pinned DESC, COALESCE(processed_at, 0) DESC, COALESCE(follower_count, 0) DESC, unique_id ASC`;
259
265
 
260
266
  // ── COUNT 查询 ──
267
+ // done 状态分布在 jobs(商家 done)+ jobs_done(非商家 done 冷归档),需 UNION
268
+ const doneUnionSql = `SELECT ${selectCols} FROM jobs ${whereSql}
269
+ UNION ALL
270
+ SELECT ${selectCols} FROM jobs_done ${whereSql}`;
261
271
  let countSql;
262
272
  let countArgs = args;
263
273
 
264
274
  if (status === "pending" || status === "processing") {
265
275
  countSql = `SELECT COUNT(*) as c FROM task_queue ${whereSql}`;
266
- } else if (
267
- status === "done" ||
268
- status === "error" ||
269
- status === "restricted"
270
- ) {
276
+ } else if (status === "done") {
277
+ countSql = `SELECT COUNT(*) as c FROM (${doneUnionSql})`;
278
+ // UNION 的两个子查询各自含 whereSql 占位符,参数需复制两份
279
+ countArgs = [...args, ...args];
280
+ } else if (status === "error" || status === "restricted") {
271
281
  countSql = `SELECT COUNT(*) as c FROM jobs ${whereSql}`;
272
282
  } else {
273
283
  // all: 只查 task_queue(处理中+待处理),不碰 jobs 表
@@ -303,11 +313,16 @@ function getUsersPageFromDb({
303
313
  .prepare(sql)
304
314
  .all(...args, safeLimit, safeOffset)
305
315
  .map(mapJobRow);
306
- } else if (
307
- status === "done" ||
308
- status === "error" ||
309
- status === "restricted"
310
- ) {
316
+ } else if (status === "done") {
317
+ // done 跨 jobs + jobs_done 两表(参数复制两份给两个子查询)
318
+ const sql = `SELECT * FROM (${doneUnionSql})
319
+ ORDER BY ${jobsOrder}
320
+ LIMIT ? OFFSET ?`;
321
+ rows = getDb()
322
+ .prepare(sql)
323
+ .all(...args, ...args, safeLimit, safeOffset)
324
+ .map(mapJobRow);
325
+ } else if (status === "error" || status === "restricted") {
311
326
  // 单表:直接查
312
327
  const sql = `SELECT ${selectCols} FROM jobs ${whereSql}
313
328
  ORDER BY ${jobsOrder}
@@ -752,6 +767,8 @@ export function createStore(filePath, options = {}) {
752
767
  if (filePath) {
753
768
  // 初始化 SQLite 用户表(用于判重)
754
769
  initDb(filePath);
770
+ // 初始化独立 tag 库(tags.db 由主库路径派生)
771
+ initTagDb(deriveTagDbPath(filePath));
755
772
  // 从数据库恢复偏移量
756
773
  }
757
774
 
@@ -778,7 +795,11 @@ export function createStore(filePath, options = {}) {
778
795
  .prepare(
779
796
  `
780
797
  SELECT status, COUNT(*) as count
781
- FROM jobs
798
+ FROM (
799
+ SELECT status FROM jobs
800
+ UNION ALL
801
+ SELECT status FROM jobs_done
802
+ )
782
803
  GROUP BY status
783
804
  `,
784
805
  )
@@ -2027,15 +2048,19 @@ export function createStore(filePath, options = {}) {
2027
2048
  updateUserFromResult(user, result);
2028
2049
  user.claimedAt = null;
2029
2050
  const newUsers = processDiscoveredUsers(result);
2030
- // Phase 1: 先更新 task_queue,再搬移到 jobs 归档,最后从 task_queue 删除
2051
+ // Phase 1: 先更新 task_queue,再搬移到归档表,最后从 task_queue 删除
2031
2052
  const persistRet = updateJobInfo(uniqueId, user, false, "task_queue");
2032
2053
  if (persistRet.error) {
2033
2054
  return { saved: false, error: persistRet.error };
2034
2055
  }
2035
- // 整行搬移到 jobs 归档
2056
+ // 分流:status='done' 且非商家(ttSeller 非 true)→ jobs_done 冷归档;
2057
+ // 其余(商家 done / error 等)→ jobs
2058
+ const isDoneNonSeller = user.status === "done" && !user.ttSeller;
2059
+ const targetTable = isDoneNonSeller ? "jobs_done" : "jobs";
2060
+ // 整行搬移到归档表
2036
2061
  getDb()
2037
2062
  .prepare(
2038
- `INSERT OR REPLACE INTO jobs SELECT * FROM task_queue WHERE unique_id = ?`,
2063
+ `INSERT OR REPLACE INTO ${targetTable} SELECT * FROM task_queue WHERE unique_id = ?`,
2039
2064
  )
2040
2065
  .run(uniqueId);
2041
2066
  getDb()
@@ -2135,18 +2160,24 @@ export function createStore(filePath, options = {}) {
2135
2160
  const inTq = getDb()
2136
2161
  .prepare("SELECT 1 FROM task_queue WHERE unique_id = ?")
2137
2162
  .get(uniqueId);
2138
- const tableName = inTq ? "task_queue" : "jobs";
2163
+ const inJobs = getDb()
2164
+ .prepare("SELECT 1 FROM jobs WHERE unique_id = ?")
2165
+ .get(uniqueId);
2166
+ // 非商家 done 在 jobs_done 冷归档,也支持直接重置回流程
2167
+ const tableName = inTq ? "task_queue" : inJobs ? "jobs" : "jobs_done";
2139
2168
  const ret = updateJobInfo(uniqueId, user, false, tableName);
2140
2169
  if (ret.error) return { saved: false, error: ret.error };
2141
2170
 
2142
- // 如果原来在 jobs,重置后需移入 task_queue(pending 应在热表)
2171
+ // 如果原来在 jobs / jobs_done,重置后需移入 task_queue(pending 应在热表)
2143
2172
  if (!inTq) {
2144
2173
  getDb()
2145
2174
  .prepare(
2146
- "INSERT OR REPLACE INTO task_queue SELECT * FROM jobs WHERE unique_id = ?",
2175
+ `INSERT OR REPLACE INTO task_queue SELECT * FROM ${tableName} WHERE unique_id = ?`,
2147
2176
  )
2148
2177
  .run(uniqueId);
2149
- getDb().prepare("DELETE FROM jobs WHERE unique_id = ?").run(uniqueId);
2178
+ getDb()
2179
+ .prepare(`DELETE FROM ${tableName} WHERE unique_id = ?`)
2180
+ .run(uniqueId);
2150
2181
  }
2151
2182
  markStatsDirty();
2152
2183
  return { saved: true };
@@ -2171,11 +2202,8 @@ export function createStore(filePath, options = {}) {
2171
2202
  const user = getJob(uniqueId);
2172
2203
  if (!user) return { saved: false, error: "user not found" };
2173
2204
  const nextPinned = !user.pinned;
2174
- // Phase 2: 判断用户在哪个表
2175
- const inTq = getDb()
2176
- .prepare("SELECT 1 FROM task_queue WHERE unique_id = ?")
2177
- .get(uniqueId);
2178
- const tableName = inTq ? "task_queue" : "jobs";
2205
+ // Phase 2: 判断用户在哪个表(task_queue / jobs / jobs_done)
2206
+ const tableName = getUserTable(uniqueId) || "jobs";
2179
2207
  const ret = updateJobInfo(
2180
2208
  uniqueId,
2181
2209
  { pinned: nextPinned },
@@ -2733,7 +2761,7 @@ export function createStore(filePath, options = {}) {
2733
2761
  return { ok: false, error: "db not initialized" };
2734
2762
  }
2735
2763
 
2736
- // ===== recheck-jobs:重检 jobs 表中 done 非商家用户 =====
2764
+ // ===== recheck-jobs:重检 jobs_done 表中 done 非商家用户 =====
2737
2765
 
2738
2766
  function getRecheckJobs(limit, countries) {
2739
2767
  const targetCountries = countries
@@ -2745,9 +2773,10 @@ export function createStore(filePath, options = {}) {
2745
2773
  const l = Math.max(1, parseInt(limit) || 5);
2746
2774
  const sevenDaysAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;
2747
2775
 
2776
+ // 非商家 done 已迁移到 jobs_done 冷归档表
2748
2777
  let sql = `
2749
2778
  SELECT *
2750
- FROM jobs
2779
+ FROM jobs_done
2751
2780
  WHERE status = 'done'
2752
2781
  AND (tt_seller IS NULL OR tt_seller != 1)
2753
2782
  AND (updated_at IS NULL OR updated_at < ?)
@@ -2770,7 +2799,7 @@ export function createStore(filePath, options = {}) {
2770
2799
 
2771
2800
  const now = Date.now();
2772
2801
  const bumpStmt = getDb().prepare(
2773
- `UPDATE jobs SET updated_at = ? WHERE unique_id = ?`,
2802
+ `UPDATE jobs_done SET updated_at = ? WHERE unique_id = ?`,
2774
2803
  );
2775
2804
  const bumpTxn = getDb().transaction((items) => {
2776
2805
  for (const item of items) {
@@ -2813,7 +2842,7 @@ export function createStore(filePath, options = {}) {
2813
2842
  const results = [];
2814
2843
  const now = Date.now();
2815
2844
  const bumpStmt = getDb().prepare(
2816
- `UPDATE jobs SET updated_at = ? WHERE unique_id = ?`,
2845
+ `UPDATE jobs_done SET updated_at = ? WHERE unique_id = ?`,
2817
2846
  );
2818
2847
  const txn = getDb().transaction((items) => {
2819
2848
  items.forEach((item) => {
@@ -2824,19 +2853,35 @@ export function createStore(filePath, options = {}) {
2824
2853
  bumpStmt.run(now, uniqueId);
2825
2854
  results.push({ uniqueId, ok: true, skipped: true });
2826
2855
  } else {
2827
- // 过滤 info 中的 null/undefined/空值,避免覆盖 jobs 表已有数据
2856
+ // 过滤 info 中的 null/undefined/空值,避免覆盖 jobs_done 表已有数据
2828
2857
  const cleanInfo = {};
2829
2858
  for (const [k, v] of Object.entries(info || {})) {
2830
2859
  if (v !== null && v !== undefined && v !== "") {
2831
2860
  cleanInfo[k] = v;
2832
2861
  }
2833
2862
  }
2834
- const ret = updateJobInfo(uniqueId, cleanInfo, true);
2835
- results.push(
2836
- ret.error
2837
- ? { uniqueId, error: ret.error }
2838
- : { uniqueId, ok: true },
2839
- );
2863
+ const ret = updateJobInfo(uniqueId, cleanInfo, true, "jobs_done");
2864
+ if (ret.error) {
2865
+ results.push({ uniqueId, error: ret.error });
2866
+ } else {
2867
+ // 重检后确认是商家(tt_seller=1)→ 从 jobs_done 回移到 jobs,
2868
+ // 保持"jobs_done=非商家 done / jobs=商家 done"的冷热表语义,
2869
+ // 否则该商家会从 targetUsers/product-fill/redo 等商家查询中消失
2870
+ const row = getDb()
2871
+ .prepare("SELECT tt_seller FROM jobs_done WHERE unique_id = ?")
2872
+ .get(uniqueId);
2873
+ if (row && row.tt_seller === 1) {
2874
+ getDb()
2875
+ .prepare(
2876
+ `INSERT OR REPLACE INTO jobs SELECT * FROM jobs_done WHERE unique_id = ?`,
2877
+ )
2878
+ .run(uniqueId);
2879
+ getDb()
2880
+ .prepare(`DELETE FROM jobs_done WHERE unique_id = ?`)
2881
+ .run(uniqueId);
2882
+ }
2883
+ results.push({ uniqueId, ok: true });
2884
+ }
2840
2885
  }
2841
2886
  });
2842
2887
  });
@@ -2872,7 +2917,9 @@ export function createStore(filePath, options = {}) {
2872
2917
 
2873
2918
  function updateUserInfo(uniqueId, info) {
2874
2919
  if (getDb()) {
2875
- return updateJobInfo(uniqueId, info, true);
2920
+ // 按用户实际所在表更新(task_queue / jobs / jobs_done)
2921
+ const tableName = getUserTable(uniqueId) || "jobs";
2922
+ return updateJobInfo(uniqueId, info, true, tableName);
2876
2923
  }
2877
2924
 
2878
2925
  const user = getUser(uniqueId);
@@ -2891,14 +2938,13 @@ export function createStore(filePath, options = {}) {
2891
2938
 
2892
2939
  function updateUserLocation(uniqueId, location) {
2893
2940
  if (getDb()) {
2894
- const existing = getDb()
2895
- .prepare("SELECT * FROM jobs WHERE unique_id = ?")
2896
- .get(uniqueId);
2941
+ const existing = getJobRow(uniqueId);
2897
2942
  if (!existing) return { error: "user not found" };
2943
+ const tableName = getUserTable(uniqueId) || "jobs";
2898
2944
  const now = Date.now();
2899
2945
  getDb()
2900
2946
  .prepare(
2901
- "UPDATE jobs SET location_created = ?, modified_at = ?, updated_at = ? WHERE unique_id = ?",
2947
+ `UPDATE ${tableName} SET location_created = ?, modified_at = ?, updated_at = ? WHERE unique_id = ?`,
2902
2948
  )
2903
2949
  .run(location, now, now, uniqueId);
2904
2950
  return { ok: true, location, modifiedAt: now };
@@ -2916,14 +2962,13 @@ export function createStore(filePath, options = {}) {
2916
2962
 
2917
2963
  function setNonSeller(uniqueId) {
2918
2964
  if (getDb()) {
2919
- const existing = getDb()
2920
- .prepare("SELECT * FROM jobs WHERE unique_id = ?")
2921
- .get(uniqueId);
2965
+ const existing = getJobRow(uniqueId);
2922
2966
  if (!existing) return { error: "user not found" };
2967
+ const tableName = getUserTable(uniqueId) || "jobs";
2923
2968
  const now = Date.now();
2924
2969
  getDb()
2925
2970
  .prepare(
2926
- "UPDATE jobs SET tt_seller = 0, updated_at = ? WHERE unique_id = ?",
2971
+ `UPDATE ${tableName} SET tt_seller = 0, updated_at = ? WHERE unique_id = ?`,
2927
2972
  )
2928
2973
  .run(now, uniqueId);
2929
2974
  console.error(`[DB] setNonSeller: ${uniqueId} → tt_seller=0`);
@@ -2982,12 +3027,14 @@ export function createStore(filePath, options = {}) {
2982
3027
  ].join(", ");
2983
3028
 
2984
3029
  const moveSingleTxn = getDb().transaction(() => {
2985
- // Phase 2: 先查 task_queue,再查 jobs
3030
+ // 先查 task_queue,再查 jobs,最后 jobs_done
2986
3031
  const fromTable = getDb()
2987
3032
  .prepare("SELECT 1 FROM task_queue WHERE unique_id = ?")
2988
3033
  .get(safeId)
2989
3034
  ? "task_queue"
2990
- : "jobs";
3035
+ : getDb().prepare("SELECT 1 FROM jobs WHERE unique_id = ?").get(safeId)
3036
+ ? "jobs"
3037
+ : "jobs_done";
2991
3038
 
2992
3039
  getDb()
2993
3040
  .prepare(
@@ -3734,7 +3781,7 @@ export function createStore(filePath, options = {}) {
3734
3781
  return { ok: true, moved: deleted.changes || 0 };
3735
3782
  }
3736
3783
 
3737
- // 将 jobs 中 status='done' 且无国家数据的记录移动到 jobs_base
3784
+ // 将 jobs / jobs_done 中 status='done' 且无国家数据的记录移动到 jobs_base
3738
3785
  function moveNoCountryJobsToBase() {
3739
3786
  const db = getDb();
3740
3787
  if (!db) return { ok: false, error: "db not ready" };
@@ -3745,41 +3792,48 @@ export function createStore(filePath, options = {}) {
3745
3792
  AND (location_created IS NULL OR location_created = '')
3746
3793
  AND (confirmed_location IS NULL OR confirmed_location = '')`;
3747
3794
 
3748
- const countRow = db
3749
- .prepare(`SELECT COUNT(*) as c FROM jobs WHERE ${condition}`)
3750
- .get();
3751
- if (!countRow?.c) return { ok: true, moved: 0 };
3795
+ let total = 0;
3796
+ for (const srcTable of ["jobs", "jobs_done"]) {
3797
+ const countRow = db
3798
+ .prepare(`SELECT COUNT(*) as c FROM ${srcTable} WHERE ${condition}`)
3799
+ .get();
3800
+ const cnt = countRow?.c || 0;
3801
+ if (!cnt) continue;
3752
3802
 
3753
- db.prepare(
3754
- `
3755
- INSERT OR REPLACE INTO jobs_base (
3756
- unique_id, nickname, sources, pinned, no_video, user_update_count,
3757
- tt_seller, verified, video_count, comment_count, guessed_location,
3758
- location_created, follower_count, following_count, heart_count,
3759
- refresh_time, region, signature, sec_uid, status_code,
3760
- latest_video_time, bio_link, status, restricted, error, claimed_by,
3761
- claimed_at, processed, processed_at, created_at, updated_at
3762
- )
3763
- SELECT
3764
- unique_id, nickname, sources, pinned, no_video, 0,
3765
- CASE WHEN tt_seller = 0 THEN NULL ELSE tt_seller END,
3766
- verified, video_count, comment_count, guessed_location,
3767
- location_created, follower_count, following_count, heart_count,
3768
- refresh_time, region, signature, sec_uid, status_code,
3769
- latest_video_time, bio_link,
3770
- 'pending', 0, NULL, NULL, NULL, 0, NULL,
3771
- COALESCE(created_at, ?), ?
3772
- FROM jobs WHERE ${condition}
3773
- `,
3774
- ).run(now, now);
3803
+ db.prepare(
3804
+ `
3805
+ INSERT OR REPLACE INTO jobs_base (
3806
+ unique_id, nickname, sources, pinned, no_video, user_update_count,
3807
+ tt_seller, verified, video_count, comment_count, guessed_location,
3808
+ location_created, follower_count, following_count, heart_count,
3809
+ refresh_time, region, signature, sec_uid, status_code,
3810
+ latest_video_time, bio_link, status, restricted, error, claimed_by,
3811
+ claimed_at, processed, processed_at, created_at, updated_at
3812
+ )
3813
+ SELECT
3814
+ unique_id, nickname, sources, pinned, no_video, 0,
3815
+ CASE WHEN tt_seller = 0 THEN NULL ELSE tt_seller END,
3816
+ verified, video_count, comment_count, guessed_location,
3817
+ location_created, follower_count, following_count, heart_count,
3818
+ refresh_time, region, signature, sec_uid, status_code,
3819
+ latest_video_time, bio_link,
3820
+ 'pending', 0, NULL, NULL, NULL, 0, NULL,
3821
+ COALESCE(created_at, ?), ?
3822
+ FROM ${srcTable} WHERE ${condition}
3823
+ `,
3824
+ ).run(now, now);
3775
3825
 
3776
- const deleted = db.prepare(`DELETE FROM jobs WHERE ${condition}`).run();
3826
+ const deleted = db
3827
+ .prepare(`DELETE FROM ${srcTable} WHERE ${condition}`)
3828
+ .run();
3829
+ total += deleted.changes || 0;
3830
+ }
3777
3831
 
3778
- markStatsDirty();
3832
+ if (total > 0) markStatsDirty();
3779
3833
  return {
3780
3834
  ok: true,
3781
- moved: deleted.changes || 0,
3782
- total: countRow.c,
3835
+ moved: total,
3836
+ total,
3783
3837
  };
3784
3838
  }
3785
3839
 
@@ -312,25 +312,37 @@ export function addJob(user) {
312
312
  export function getJobRow(uniqueId) {
313
313
  const db = getDb();
314
314
  if (!db) return null;
315
- // 优先查 task_queue(热数据),回退 jobs(归档)
315
+ // 优先查 task_queue(热数据),回退 jobs(归档),再回退 jobs_done(冷归档)
316
316
  const row = db
317
317
  .prepare("SELECT * FROM task_queue WHERE unique_id = ?")
318
318
  .get(uniqueId);
319
319
  if (row) return row;
320
- return db.prepare("SELECT * FROM jobs WHERE unique_id = ?").get(uniqueId);
320
+ const row2 = db
321
+ .prepare("SELECT * FROM jobs WHERE unique_id = ?")
322
+ .get(uniqueId);
323
+ if (row2) return row2;
324
+ return db
325
+ .prepare("SELECT * FROM jobs_done WHERE unique_id = ?")
326
+ .get(uniqueId);
321
327
  }
322
328
 
323
329
  /**
324
- * 判断用户在哪个表,返回 'task_queue' 或 'jobs'
330
+ * 判断用户在哪个表,返回 'task_queue' | 'jobs' | 'jobs_done'
325
331
  * 用于需要更新正确表的场景(置顶、重置、移动等)
326
332
  */
327
333
  export function getUserTable(uniqueId) {
328
334
  const db = getDb();
329
335
  if (!db) return null;
330
- return db
331
- .prepare("SELECT 1 FROM task_queue WHERE unique_id = ?")
332
- .get(uniqueId)
333
- ? "task_queue"
336
+ if (
337
+ db.prepare("SELECT 1 FROM task_queue WHERE unique_id = ?").get(uniqueId)
338
+ ) {
339
+ return "task_queue";
340
+ }
341
+ if (db.prepare("SELECT 1 FROM jobs WHERE unique_id = ?").get(uniqueId)) {
342
+ return "jobs";
343
+ }
344
+ return db.prepare("SELECT 1 FROM jobs_done WHERE unique_id = ?").get(uniqueId)
345
+ ? "jobs_done"
334
346
  : "jobs";
335
347
  }
336
348
 
@@ -361,10 +373,13 @@ export function getBulkAuthorLocations(uniqueIds) {
361
373
  SELECT unique_id, location_created FROM jobs
362
374
  WHERE unique_id IN (${placeholders}) AND location_created IS NOT NULL AND location_created != ''
363
375
  UNION
376
+ SELECT unique_id, location_created FROM jobs_done
377
+ WHERE unique_id IN (${placeholders}) AND location_created IS NOT NULL AND location_created != ''
378
+ UNION
364
379
  SELECT unique_id, location_created FROM jobs_base
365
380
  WHERE unique_id IN (${placeholders}) AND location_created IS NOT NULL AND location_created != ''
366
381
  `;
367
- const rows = db.prepare(sql).all(...uniqueIds, ...uniqueIds);
382
+ const rows = db.prepare(sql).all(...uniqueIds, ...uniqueIds, ...uniqueIds);
368
383
  const result = {};
369
384
  for (const r of rows) {
370
385
  if (!result[r.unique_id]) {
@@ -377,7 +392,11 @@ export function getBulkAuthorLocations(uniqueIds) {
377
392
  export function getAllJobs() {
378
393
  const db = getDb();
379
394
  if (!db) return [];
380
- return db.prepare("SELECT * FROM jobs").all().map(mapJobRow);
395
+ // jobs(商家 done + error/restricted 等)+ jobs_done(非商家 done 冷归档)
396
+ return db
397
+ .prepare("SELECT * FROM jobs UNION ALL SELECT * FROM jobs_done")
398
+ .all()
399
+ .map(mapJobRow);
381
400
  }
382
401
 
383
402
  // ===== 查询 Video =====
@@ -482,7 +501,13 @@ export function getUserDbCount() {
482
501
  export function getJobsCount() {
483
502
  const db = getDb();
484
503
  if (!db) return 0;
485
- return db.prepare("SELECT COUNT(*) as c FROM jobs").get().c;
504
+ // jobs + jobs_done 冷归档合并计数
505
+ const row = db
506
+ .prepare(
507
+ "SELECT (SELECT COUNT(*) FROM jobs) + (SELECT COUNT(*) FROM jobs_done) as c",
508
+ )
509
+ .get();
510
+ return row?.c || 0;
486
511
  }
487
512
 
488
513
  export function getPendingJobsCount() {