tt-help-cli-ycl 1.3.40 → 1.3.43

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.
@@ -116,6 +116,38 @@ function initUserDb(filePath) {
116
116
  sec_uid TEXT
117
117
  )
118
118
  `);
119
+ db.exec(`
120
+ CREATE TABLE IF NOT EXISTS raw_jobs (
121
+ unique_id TEXT PRIMARY KEY,
122
+ nickname TEXT,
123
+ status TEXT DEFAULT 'pending',
124
+ sources TEXT,
125
+ claimed_by TEXT,
126
+ claimed_at INTEGER,
127
+ error TEXT,
128
+ pinned INTEGER DEFAULT 0,
129
+ no_video INTEGER DEFAULT 0,
130
+ restricted INTEGER DEFAULT 0,
131
+ user_update_count INTEGER DEFAULT 0,
132
+ tt_seller INTEGER,
133
+ verified INTEGER,
134
+ video_count INTEGER DEFAULT 0,
135
+ comment_count INTEGER DEFAULT 0,
136
+ guessed_location TEXT,
137
+ location_created TEXT,
138
+ follower_count INTEGER DEFAULT 0,
139
+ following_count INTEGER DEFAULT 0,
140
+ heart_count INTEGER DEFAULT 0,
141
+ refresh_time INTEGER,
142
+ processed INTEGER DEFAULT 0,
143
+ processed_at INTEGER,
144
+ created_at INTEGER,
145
+ updated_at INTEGER,
146
+ region TEXT,
147
+ signature TEXT,
148
+ sec_uid TEXT
149
+ )
150
+ `);
119
151
  db.exec(`
120
152
  CREATE TABLE IF NOT EXISTS videos (
121
153
  id TEXT PRIMARY KEY,
@@ -454,6 +486,11 @@ function getPendingJobsUserUpdateCount() {
454
486
  .get().c;
455
487
  }
456
488
 
489
+ function getRawJobsCount() {
490
+ if (!db) return 0;
491
+ return db.prepare("SELECT COUNT(*) as c FROM raw_jobs").get().c;
492
+ }
493
+
457
494
  function getDashboardStatsFromDb(targetLocations = []) {
458
495
  if (!db) return null;
459
496
 
@@ -560,6 +597,7 @@ function getDashboardStatsFromDb(targetLocations = []) {
560
597
 
561
598
  return {
562
599
  totalUsers: total,
600
+ rawJobs: getRawJobsCount(),
563
601
  dbTotalUsers: getUserDbCount(),
564
602
  jobsTotal: total,
565
603
  jobsPending: getPendingJobsCount(),
@@ -587,6 +625,415 @@ function getDashboardStatsFromDb(targetLocations = []) {
587
625
  };
588
626
  }
589
627
 
628
+ function getPendingByCountryFromDb() {
629
+ if (!db) return [];
630
+
631
+ // 按 guessed_location 分组统计待处理任务
632
+ const rows = db
633
+ .prepare(
634
+ `
635
+ SELECT
636
+ COALESCE(guessed_location, '未知') as country,
637
+ COUNT(*) as count
638
+ FROM jobs
639
+ WHERE status = 'pending'
640
+ GROUP BY COALESCE(guessed_location, '未知')
641
+ ORDER BY count DESC
642
+ `,
643
+ )
644
+ .all();
645
+
646
+ return rows;
647
+ }
648
+
649
+ function getUserUpdateByCountryFromDb() {
650
+ if (!db) return [];
651
+
652
+ // 按 guessed_location 分组统计待补资料任务
653
+ const rows = db
654
+ .prepare(
655
+ `
656
+ SELECT
657
+ COALESCE(guessed_location, '未知') as country,
658
+ COUNT(*) as count
659
+ FROM jobs
660
+ WHERE COALESCE(tt_seller, '') = ''
661
+ AND COALESCE(user_update_count, 0) <= 0
662
+ GROUP BY COALESCE(guessed_location, '未知')
663
+ ORDER BY count DESC
664
+ `,
665
+ )
666
+ .all();
667
+
668
+ return rows;
669
+ }
670
+
671
+ function getAttachStuckByCountryFromDb() {
672
+ if (!db) return [];
673
+
674
+ return db
675
+ .prepare(
676
+ `
677
+ SELECT
678
+ COALESCE(guessed_location, '未知') as country,
679
+ COUNT(*) as count
680
+ FROM jobs
681
+ WHERE COALESCE(tt_seller, '') = ''
682
+ AND COALESCE(user_update_count, 0) = 1
683
+ GROUP BY COALESCE(guessed_location, '未知')
684
+ ORDER BY count DESC
685
+ `,
686
+ )
687
+ .all();
688
+ }
689
+
690
+ function restoreAttachStuckByCountry(country) {
691
+ if (!db) {
692
+ return { restored: 0, country, error: "db not ready" };
693
+ }
694
+
695
+ const normalizedCountry = String(country == null ? "未知" : country).trim();
696
+ if (!normalizedCountry) {
697
+ return {
698
+ restored: 0,
699
+ country: normalizedCountry,
700
+ error: "country is required",
701
+ };
702
+ }
703
+
704
+ const whereSql = `
705
+ COALESCE(tt_seller, '') = ''
706
+ AND COALESCE(user_update_count, 0) = 1
707
+ AND COALESCE(guessed_location, '未知') = ?
708
+ `;
709
+ const count =
710
+ db
711
+ .prepare(
712
+ `
713
+ SELECT COUNT(*) as c
714
+ FROM jobs
715
+ WHERE ${whereSql}
716
+ `,
717
+ )
718
+ .get(normalizedCountry)?.c || 0;
719
+
720
+ if (!count) {
721
+ return { restored: 0, country: normalizedCountry };
722
+ }
723
+
724
+ db.prepare(
725
+ `
726
+ UPDATE jobs
727
+ SET user_update_count = 0,
728
+ updated_at = ?,
729
+ claimed_by = NULL,
730
+ claimed_at = NULL
731
+ WHERE ${whereSql}
732
+ `,
733
+ ).run(Date.now(), normalizedCountry);
734
+
735
+ return { restored: count, country: normalizedCountry };
736
+ }
737
+
738
+ function getRawByCountryFromDb() {
739
+ if (!db) return [];
740
+
741
+ return db
742
+ .prepare(
743
+ `
744
+ SELECT
745
+ COALESCE(guessed_location, '未知') as country,
746
+ COUNT(*) as count
747
+ FROM raw_jobs
748
+ GROUP BY COALESCE(guessed_location, '未知')
749
+ ORDER BY count DESC
750
+ `,
751
+ )
752
+ .all();
753
+ }
754
+
755
+ function moveJobsToRawByCountry(scope, country) {
756
+ if (!db) {
757
+ return { moved: 0, scope, country, error: "db not ready" };
758
+ }
759
+
760
+ const normalizedScope = String(scope || "").trim();
761
+ const normalizedCountry = String(country == null ? "未知" : country).trim();
762
+ if (!normalizedCountry) {
763
+ return {
764
+ moved: 0,
765
+ scope: normalizedScope,
766
+ country: normalizedCountry,
767
+ error: "country is required",
768
+ };
769
+ }
770
+
771
+ let scopeWhere = "";
772
+ if (normalizedScope === "pending") {
773
+ scopeWhere = `status = 'pending'`;
774
+ } else if (normalizedScope === "userUpdate") {
775
+ scopeWhere = `COALESCE(tt_seller, '') = '' AND COALESCE(user_update_count, 0) <= 0`;
776
+ } else {
777
+ return {
778
+ moved: 0,
779
+ scope: normalizedScope,
780
+ country: normalizedCountry,
781
+ error: "unsupported scope",
782
+ };
783
+ }
784
+
785
+ const whereSql = `
786
+ ${scopeWhere}
787
+ AND COALESCE(guessed_location, '未知') = ?
788
+ `;
789
+ const count =
790
+ db
791
+ .prepare(
792
+ `
793
+ SELECT COUNT(*) as c
794
+ FROM jobs
795
+ WHERE ${whereSql}
796
+ `,
797
+ )
798
+ .get(normalizedCountry)?.c || 0;
799
+
800
+ if (!count) {
801
+ return { moved: 0, scope: normalizedScope, country: normalizedCountry };
802
+ }
803
+
804
+ const moveTxn = db.transaction((targetCountry) => {
805
+ db.prepare(
806
+ `
807
+ INSERT OR REPLACE INTO raw_jobs (
808
+ unique_id,
809
+ nickname,
810
+ status,
811
+ sources,
812
+ claimed_by,
813
+ claimed_at,
814
+ error,
815
+ pinned,
816
+ no_video,
817
+ restricted,
818
+ user_update_count,
819
+ tt_seller,
820
+ verified,
821
+ video_count,
822
+ comment_count,
823
+ guessed_location,
824
+ location_created,
825
+ follower_count,
826
+ following_count,
827
+ heart_count,
828
+ refresh_time,
829
+ processed,
830
+ processed_at,
831
+ created_at,
832
+ updated_at,
833
+ region,
834
+ signature,
835
+ sec_uid
836
+ )
837
+ SELECT
838
+ unique_id,
839
+ nickname,
840
+ status,
841
+ sources,
842
+ claimed_by,
843
+ claimed_at,
844
+ error,
845
+ pinned,
846
+ no_video,
847
+ restricted,
848
+ user_update_count,
849
+ tt_seller,
850
+ verified,
851
+ video_count,
852
+ comment_count,
853
+ guessed_location,
854
+ location_created,
855
+ follower_count,
856
+ following_count,
857
+ heart_count,
858
+ refresh_time,
859
+ processed,
860
+ processed_at,
861
+ created_at,
862
+ updated_at,
863
+ region,
864
+ signature,
865
+ sec_uid
866
+ FROM jobs
867
+ WHERE ${whereSql}
868
+ `,
869
+ ).run(targetCountry);
870
+
871
+ db.prepare(
872
+ `
873
+ DELETE FROM jobs
874
+ WHERE ${whereSql}
875
+ `,
876
+ ).run(targetCountry);
877
+ });
878
+
879
+ moveTxn(normalizedCountry);
880
+ return { moved: count, scope: normalizedScope, country: normalizedCountry };
881
+ }
882
+
883
+ function restoreRawJobsByCountry(country) {
884
+ if (!db) {
885
+ return { restored: 0, country, error: "db not ready" };
886
+ }
887
+
888
+ const normalizedCountry = String(country == null ? "未知" : country).trim();
889
+ if (!normalizedCountry) {
890
+ return {
891
+ restored: 0,
892
+ country: normalizedCountry,
893
+ error: "country is required",
894
+ };
895
+ }
896
+
897
+ const whereSql = `COALESCE(guessed_location, '未知') = ?`;
898
+ const count =
899
+ db
900
+ .prepare(
901
+ `
902
+ SELECT COUNT(*) as c
903
+ FROM raw_jobs
904
+ WHERE ${whereSql}
905
+ `,
906
+ )
907
+ .get(normalizedCountry)?.c || 0;
908
+
909
+ if (!count) {
910
+ return { restored: 0, country: normalizedCountry };
911
+ }
912
+
913
+ const restoreTxn = db.transaction((targetCountry) => {
914
+ db.prepare(
915
+ `
916
+ INSERT OR REPLACE INTO jobs (
917
+ unique_id,
918
+ nickname,
919
+ status,
920
+ sources,
921
+ claimed_by,
922
+ claimed_at,
923
+ error,
924
+ pinned,
925
+ no_video,
926
+ restricted,
927
+ user_update_count,
928
+ tt_seller,
929
+ verified,
930
+ video_count,
931
+ comment_count,
932
+ guessed_location,
933
+ location_created,
934
+ follower_count,
935
+ following_count,
936
+ heart_count,
937
+ refresh_time,
938
+ processed,
939
+ processed_at,
940
+ created_at,
941
+ updated_at,
942
+ region,
943
+ signature,
944
+ sec_uid
945
+ )
946
+ SELECT
947
+ unique_id,
948
+ nickname,
949
+ status,
950
+ sources,
951
+ claimed_by,
952
+ claimed_at,
953
+ error,
954
+ pinned,
955
+ no_video,
956
+ restricted,
957
+ user_update_count,
958
+ tt_seller,
959
+ verified,
960
+ video_count,
961
+ comment_count,
962
+ guessed_location,
963
+ location_created,
964
+ follower_count,
965
+ following_count,
966
+ heart_count,
967
+ refresh_time,
968
+ processed,
969
+ processed_at,
970
+ created_at,
971
+ updated_at,
972
+ region,
973
+ signature,
974
+ sec_uid
975
+ FROM raw_jobs
976
+ WHERE ${whereSql}
977
+ `,
978
+ ).run(targetCountry);
979
+
980
+ db.prepare(
981
+ `
982
+ DELETE FROM raw_jobs
983
+ WHERE ${whereSql}
984
+ `,
985
+ ).run(targetCountry);
986
+ });
987
+
988
+ restoreTxn(normalizedCountry);
989
+ return { restored: count, country: normalizedCountry };
990
+ }
991
+
992
+ function getRawJobsPageFromDb({ search, location, limit, offset }) {
993
+ if (!db) return null;
994
+
995
+ const safeLimit = Math.max(1, Math.min(200, parseInt(limit) || 50));
996
+ const safeOffset = Math.max(0, parseInt(offset) || 0);
997
+ const where = [];
998
+ const args = [];
999
+
1000
+ if (search) {
1001
+ where.push(
1002
+ "(LOWER(unique_id) LIKE ? OR LOWER(COALESCE(nickname, '')) LIKE ?)",
1003
+ );
1004
+ const pattern = `%${String(search).toLowerCase()}%`;
1005
+ args.push(pattern, pattern);
1006
+ }
1007
+ if (location) {
1008
+ where.push("COALESCE(guessed_location, '未知') = ?");
1009
+ args.push(location);
1010
+ }
1011
+
1012
+ const whereSql = where.length ? `WHERE ${where.join(" AND ")}` : "";
1013
+ const total = db
1014
+ .prepare(`SELECT COUNT(*) as c FROM raw_jobs ${whereSql}`)
1015
+ .get(...args).c;
1016
+
1017
+ const rows = db
1018
+ .prepare(
1019
+ `
1020
+ SELECT *
1021
+ FROM raw_jobs
1022
+ ${whereSql}
1023
+ ORDER BY created_at DESC, unique_id ASC
1024
+ LIMIT ? OFFSET ?
1025
+ `,
1026
+ )
1027
+ .all(...args, safeLimit, safeOffset);
1028
+
1029
+ return {
1030
+ total,
1031
+ limit: safeLimit,
1032
+ offset: safeOffset,
1033
+ users: rows.map(mapJobRow),
1034
+ };
1035
+ }
1036
+
590
1037
  function getUsersPageFromDb({
591
1038
  status,
592
1039
  search,
@@ -2356,10 +2803,19 @@ export function createStore(filePath) {
2356
2803
  getAllUsers,
2357
2804
  getUserDbCount,
2358
2805
  getJobsCount,
2806
+ getRawJobsCount,
2359
2807
  getPendingJobsCount,
2360
2808
  getPendingJobsUserUpdateCount,
2361
2809
  getDashboardStats: getDashboardStatsFromDb,
2810
+ getPendingByCountry: getPendingByCountryFromDb,
2811
+ getUserUpdateByCountry: getUserUpdateByCountryFromDb,
2812
+ getAttachStuckByCountry: getAttachStuckByCountryFromDb,
2813
+ getRawByCountry: getRawByCountryFromDb,
2814
+ moveJobsToRawByCountry,
2815
+ restoreAttachStuckByCountry,
2816
+ restoreRawJobsByCountry,
2362
2817
  getUsersPage: getUsersPageFromDb,
2818
+ getRawJobsPage: getRawJobsPageFromDb,
2363
2819
  getTargetUsers: getTargetUsersFromDb,
2364
2820
  getStats,
2365
2821
  getStatusGroups,