zjk
2026-09-16 d41fd9347b2ec9a40286a7921f4909ac0b0f4ecc
utils/syncEngine.js
@@ -1,10 +1,19 @@
import { getPendingSyncRecords, markRecordSynced, cleanOldRecords, getOfflineConfig, saveOfflineConfig } from './db';
import { getPendingSyncRecords, getPendingSyncRecordCount, markRecordSynced, cleanOldRecords, getOfflineConfig, saveOfflineConfig } from './db';
import { deleteLocalImage } from './fs';
import { uploadImage } from '../api/upload';
import { addVisitorRecord, idCardSingOut, pullSigninData, pullSignoutData } from '../api/visitorRecord';
import { getVisitAreaTree } from '../api/visitArea';
let isSyncing = false;
const getCurrentNetworkType = async () => {
  return new Promise(resolve => {
    uni.getNetworkType({
      success: (res) => resolve(res.networkType),
      fail: () => resolve('unknown')
    });
  });
};
/**
 * 启动离线数据同步队列
@@ -15,19 +24,20 @@
 * 4. 成功后:将数据库记录置为已同步,并删除本地沙盒图片释放内存
 */
export const startSyncQueue = async () => {
  console.log('[Sync] startSyncQueue 进入,当前同步状态:', {
    isSyncing,
  });
  if (isSyncing) {
    console.warn('[Sync] 同步队列已在执行中,本次跳过');
    return;
  }
  // 简单判断当前网络是否可用
  const networkType = await new Promise(resolve => {
    uni.getNetworkType({
      success: (res) => resolve(res.networkType),
      fail: () => resolve('unknown')
    });
  });
  const networkType = await getCurrentNetworkType();
  console.log('[Sync] 当前网络状态:', networkType);
  if (networkType === 'none') {
    console.warn('[Sync] 当前离线,跳过同步队列');
    return;
  }
@@ -37,12 +47,22 @@
    let pendingRecords = [];
    try {
      pendingRecords = await getPendingSyncRecords();
      console.log('[Sync] 读取到待同步记录:', {
        count: pendingRecords.length,
        records: pendingRecords.map((record) => ({
          id: record.id,
          type: record.type,
          localImgPath: record.local_img_path,
          createTime: record.create_time,
        })),
      });
    } catch (dbErr) {
      console.error('[Sync] 读取待同步记录失败:', dbErr);
      return;
    }
    if (pendingRecords.length === 0) {
      isSyncing = false;
      console.warn('[Sync] 当前没有待同步记录');
      return;
    }
@@ -62,7 +82,18 @@
        // 1. 如果存在本地图片,先上传图片,再把云端地址回填到业务 payload
        if (record.local_img_path) {
          console.log('[Sync] 开始上传本地图片:', {
            recordId: record.id,
            type: record.type,
            localImgPath: record.local_img_path,
          });
          const uploadRes = await uploadImage(record.local_img_path);
          console.log('[Sync] 图片上传结果:', {
            recordId: record.id,
            type: record.type,
            localImgPath: record.local_img_path,
            uploadRes,
          });
          if (uploadRes && uploadRes.code === 200 && uploadRes.url) {
            // 根据记录类型,将云端 URL 赋值给不同的业务 payload 字段
            if (record.type === 'signout') {
@@ -97,11 +128,46 @@
      try {
        let submitRes;
        if (item.type === 'signout') {
          console.log('[Sync] 开始调用签离接口:', {
            recordId: item.recordId,
            payload: item.payload,
          });
          submitRes = await idCardSingOut(item.payload);
        //   await new Promise((resolve) => {
        //     uni.showModal({
        //       title: '调试: 签离接口返回',
        //       content: `记录ID: ${item.recordId}\n结果: ${JSON.stringify(submitRes).substring(0, 500)}`,
        //       showCancel: false,
        //       success: resolve,
        //       fail: resolve
        //     });
        //   });
        } else {
          // type 为 appointment, online, 或它们加上 _signedout 后缀时,都作为进场记录同步
          console.log('[Sync] 开始调用登记接口:', {
            recordId: item.recordId,
            type: item.type,
            payload: item.payload,
          });
          submitRes = await addVisitorRecord(item.payload);
        //   await new Promise((resolve) => {
        //     uni.showModal({
        //       title: '调试: 登记接口返回',
        //       content: `类型: ${item.type}\n记录ID: ${item.recordId}\n结果: ${JSON.stringify(submitRes).substring(0, 500)}`,
        //       showCancel: false,
        //       success: resolve,
        //       fail: resolve
        //     });
        //   });
        }
        console.log('[Sync] 单条业务接口返回:', {
          recordId: item.recordId,
          type: item.type,
          submitRes,
        });
        if (submitRes && submitRes.code === 200) {
          await markRecordSynced(item.recordId);
@@ -116,7 +182,12 @@
    if (signinDataToPull.length > 0) {
       try {
         const payloads = signinDataToPull.map(item => item.payload);
         console.log('[Sync] 开始调用批量补发签到接口:', {
           count: payloads.length,
           payloads,
         });
         const pullRes = await pullSigninData(payloads);
         console.log('[Sync] 批量补发签到接口结果:', pullRes);
         if (pullRes && pullRes.code === 200) {
            for (let item of signinDataToPull) {
               await markRecordSynced(item.recordId);
@@ -132,7 +203,12 @@
    if (signoutDataToPull.length > 0) {
       try {
         const payloads = signoutDataToPull.map(item => item.payload);
         console.log('[Sync] 开始调用批量补发签离接口:', {
           count: payloads.length,
           payloads,
         });
         const pullRes = await pullSignoutData(payloads);
         console.log('[Sync] 批量补发签离接口结果:', pullRes);
         if (pullRes && pullRes.code === 200) {
            for (let item of signoutDataToPull) {
               await markRecordSynced(item.recordId);
@@ -152,6 +228,78 @@
};
/**
 * 手动触发全量离线同步:
 * 持续执行同步队列,直到待同步记录清空,或检测到本轮没有任何推进为止。
 */
export const syncAllPendingRecords = async () => {
  if (isSyncing) {
    return {
      success: false,
      reason: 'syncing',
      total: 0,
      synced: 0,
      remaining: await getPendingSyncRecordCount(),
    };
  }
  const networkType = await getCurrentNetworkType();
  if (networkType === 'none') {
    return {
      success: false,
      reason: 'offline',
      total: 0,
      synced: 0,
      remaining: await getPendingSyncRecordCount(),
    };
  }
  const totalPending = await getPendingSyncRecordCount();
  if (totalPending === 0) {
    return {
      success: true,
      reason: 'empty',
      total: 0,
      synced: 0,
      remaining: 0,
    };
  }
  let syncedCount = 0;
  let remainingCount = totalPending;
  let round = 0;
  const maxRounds = 20;
  while (remainingCount > 0 && round < maxRounds) {
    const beforeCount = await getPendingSyncRecordCount();
    if (beforeCount <= 0) {
      remainingCount = 0;
      break;
    }
    await startSyncQueue();
    const afterCount = await getPendingSyncRecordCount();
    const currentRoundSynced = Math.max(beforeCount - afterCount, 0);
    syncedCount += currentRoundSynced;
    remainingCount = afterCount;
    round += 1;
    // 本轮没有任何推进,说明存在上传失败或脏数据,避免死循环卡死按钮。
    if (afterCount >= beforeCount) {
      break;
    }
  }
  return {
    success: remainingCount === 0,
    reason: remainingCount === 0 ? 'completed' : 'partial',
    total: totalPending,
    synced: syncedCount,
    remaining: remainingCount,
  };
};
/**
 * 每日凌晨或空闲时清理超期废弃数据并同步最新配置字典
 */
export const runDailyCleanup = async () => {