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.
- package/package.json +1 -1
- package/scripts/backup-db.mjs +73 -0
- package/scripts/check-queues.sh +37 -0
- package/scripts/migrate-jobs-done.mjs +124 -0
- package/scripts/migrate-tags-db.mjs +163 -0
- package/src/cli/tag.js +68 -9
- package/src/lib/tag-discover.js +21 -0
- package/src/watch/data-store.js +131 -77
- package/src/watch/db-crud.js +35 -10
- package/src/watch/db-schema.js +249 -100
- package/src/watch/db-stats.js +53 -23
- package/src/watch/db-tags.js +5 -2
- package/src/watch/llm-config.js +31 -1
- package/src/watch/public/app.js +162 -11
- package/src/watch/public/index.html +19 -1
- package/src/watch/server.js +338 -62
- package/src/watch/tag-selflearn.js +2 -1
- package/src/watch/watchdog-server.js +129 -1
- package/src/watchdog/agent.js +76 -0
- package/src/watchdog/process-manager.js +21 -0
- package/src/watchdog/self-log-buffer.js +25 -0
package/src/watch/data-store.js
CHANGED
|
@@ -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(
|
|
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
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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
|
|
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
|
|
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
|
-
//
|
|
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
|
|
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
|
|
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
|
-
|
|
2175
|
+
`INSERT OR REPLACE INTO task_queue SELECT * FROM ${tableName} WHERE unique_id = ?`,
|
|
2147
2176
|
)
|
|
2148
2177
|
.run(uniqueId);
|
|
2149
|
-
getDb()
|
|
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
|
|
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:重检
|
|
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
|
|
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
|
|
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
|
|
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/空值,避免覆盖
|
|
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
|
-
|
|
2836
|
-
ret.error
|
|
2837
|
-
|
|
2838
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
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
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
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
|
-
|
|
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:
|
|
3782
|
-
total
|
|
3835
|
+
moved: total,
|
|
3836
|
+
total,
|
|
3783
3837
|
};
|
|
3784
3838
|
}
|
|
3785
3839
|
|
package/src/watch/db-crud.js
CHANGED
|
@@ -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
|
-
|
|
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'
|
|
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
|
-
|
|
331
|
-
.prepare("SELECT 1 FROM task_queue WHERE unique_id = ?")
|
|
332
|
-
|
|
333
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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() {
|