AI-accounting-soft-uniApp/utils/sync.js
ni ziyi 70715bb0c8 feat(新增):
新增ocr获取信息后,批量直接入库功能
2025-12-25 14:55:08 +08:00

374 lines
11 KiB
JavaScript

// 数据同步功能实现
import { db } from './db'
import * as onlineBillApi from '../api/bill'
import * as onlineCategoryApi from '../api/category'
import * as onlineAccountApi from '../api/account'
import * as onlineBudgetApi from '../api/budget'
// 获取当前用户ID
function getCurrentUserId() {
const userInfo = uni.getStorageSync('userInfo') || {}
return userInfo.userId || 'local_user'
}
// 检查是否为离线用户
function isOfflineUser() {
const userInfo = uni.getStorageSync('userInfo') || {}
return userInfo.isOffline === true || userInfo.userId === 'local_user'
}
// 账单同步逻辑
const syncBills = async () => {
if (isOfflineUser()) {
throw new Error('离线用户无法同步数据')
}
const userId = getCurrentUserId()
try {
// 1. 获取本地未同步的账单
const localBillsSql = `SELECT * FROM bill WHERE user_id = ? AND is_synced = 0`
const localBills = await db.query(localBillsSql, [userId])
// 2. 获取服务器端账单
const serverBills = await onlineBillApi.getBills({ userId })
// 3. 比较并同步数据
for (const localBill of localBills) {
// 检查服务器是否已有相同账单
const existingBill = serverBills.find(bill => {
return bill.amount === localBill.amount &&
bill.type === localBill.type &&
bill.bill_date === localBill.bill_date &&
bill.description === localBill.description &&
bill.category_id === localBill.category_id
})
if (existingBill) {
// 已有相同账单,标记为已同步
const updateSql = `UPDATE bill SET is_synced = 1 WHERE id = ?`
await db.update(updateSql, [localBill.id])
} else {
// 没有相同账单,上传到服务器
await onlineBillApi.createBill({
categoryId: localBill.category_id,
amount: localBill.amount,
description: localBill.description,
billDate: localBill.bill_date,
type: localBill.type
})
// 标记为已同步
const updateSql = `UPDATE bill SET is_synced = 1 WHERE id = ?`
await db.update(updateSql, [localBill.id])
}
}
// 4. 将服务器端数据同步到本地
for (const serverBill of serverBills) {
// 检查本地是否已有相同账单
const checkSql = `SELECT * FROM bill WHERE user_id = ? AND amount = ? AND type = ? AND bill_date = ? AND description = ? AND category_id = ?`
const existingBills = await db.query(checkSql, [
userId,
serverBill.amount,
serverBill.type,
serverBill.bill_date,
serverBill.description,
serverBill.category_id
])
if (existingBills.length === 0) {
// 本地没有相同账单,添加到本地
const insertSql = `INSERT INTO bill (user_id, category_id, amount, description, bill_date, type, is_synced)
VALUES (?, ?, ?, ?, ?, ?, 1)`
await db.update(insertSql, [
userId,
serverBill.category_id,
serverBill.amount,
serverBill.description,
serverBill.bill_date,
serverBill.type
])
}
}
return { success: true, message: '账单同步成功', syncedCount: localBills.length }
} catch (error) {
console.error('同步账单失败:', error)
throw error
}
}
// 分类同步逻辑
const syncCategories = async () => {
if (isOfflineUser()) {
throw new Error('离线用户无法同步数据')
}
const userId = getCurrentUserId()
try {
// 1. 获取本地用户自定义分类
const localCategoriesSql = `SELECT * FROM category WHERE user_id = ?`
const localCategories = await db.query(localCategoriesSql, [userId])
// 2. 获取服务器端分类
const serverCategories = await onlineCategoryApi.getCategories({ userId })
// 3. 比较并同步数据
for (const localCategory of localCategories) {
// 检查服务器是否已有相同分类
const existingCategory = serverCategories.find(category => {
return category.name === localCategory.name &&
category.type === localCategory.type
})
if (!existingCategory) {
// 没有相同分类,上传到服务器
await onlineCategoryApi.createCategory({
name: localCategory.name,
icon: localCategory.icon,
type: localCategory.type,
sortOrder: localCategory.sort_order
})
}
}
// 4. 将服务器端分类同步到本地
for (const serverCategory of serverCategories) {
// 检查本地是否已有相同分类
const checkSql = `SELECT * FROM category WHERE user_id = ? AND name = ? AND type = ?`
const existingCategories = await db.query(checkSql, [
userId,
serverCategory.name,
serverCategory.type
])
if (existingCategories.length === 0) {
// 本地没有相同分类,添加到本地
const insertSql = `INSERT INTO category (user_id, name, icon, type, sort_order)
VALUES (?, ?, ?, ?, ?)`
await db.update(insertSql, [
userId,
serverCategory.name,
serverCategory.icon,
serverCategory.type,
serverCategory.sort_order
])
}
}
return { success: true, message: '分类同步成功' }
} catch (error) {
console.error('同步分类失败:', error)
throw error
}
}
// 账户同步逻辑
const syncAccount = async () => {
if (isOfflineUser()) {
throw new Error('离线用户无法同步数据')
}
const userId = getCurrentUserId()
try {
// 1. 获取本地账户信息
const localAccountSql = `SELECT * FROM account WHERE user_id = ?`
const localAccounts = await db.query(localAccountSql, [userId])
if (localAccounts.length === 0) {
return { success: true, message: '本地没有账户信息需要同步' }
}
const localAccount = localAccounts[0]
// 2. 获取服务器端账户信息
const serverAccount = await onlineAccountApi.getAccount({ userId })
// 3. 比较并同步数据
if (serverAccount) {
// 服务器有账户信息,比较并更新
if (localAccount.name !== serverAccount.name || localAccount.initial_balance !== serverAccount.initial_balance) {
// 本地与服务器信息不一致,更新服务器端
await onlineAccountApi.updateAccount({
name: localAccount.name,
initialBalance: localAccount.initial_balance
})
}
} else {
// 服务器没有账户信息,创建新账户
await onlineAccountApi.updateAccount({
name: localAccount.name,
initialBalance: localAccount.initial_balance
})
}
// 4. 将服务器端账户信息同步到本地
const updatedServerAccount = await onlineAccountApi.getAccount({ userId })
const updateSql = `UPDATE account SET name = ?, initial_balance = ? WHERE user_id = ?`
await db.update(updateSql, [
updatedServerAccount.name,
updatedServerAccount.initial_balance,
userId
])
return { success: true, message: '账户同步成功' }
} catch (error) {
console.error('同步账户失败:', error)
throw error
}
}
// 预算同步逻辑
const syncBudgets = async () => {
if (isOfflineUser()) {
throw new Error('离线用户无法同步数据')
}
const userId = getCurrentUserId()
try {
// 1. 获取本地预算
const localBudgetsSql = `SELECT * FROM budget WHERE user_id = ?`
const localBudgets = await db.query(localBudgetsSql, [userId])
// 2. 获取服务器端预算
const serverBudget = await onlineBudgetApi.getBudget({ userId })
// 3. 比较并同步数据
for (const localBudget of localBudgets) {
// 检查服务器是否已有相同月份预算
const checkSql = `SELECT * FROM budget WHERE user_id = ? AND year = ? AND month = ?`
const existingBudgets = await db.query(checkSql, [
userId,
localBudget.year,
localBudget.month
])
if (existingBudgets.length === 0) {
// 没有相同月份预算,上传到服务器
await onlineBudgetApi.setBudget({
userId,
year: localBudget.year,
month: localBudget.month,
amount: localBudget.amount
})
} else if (existingBudgets[0].amount !== localBudget.amount) {
// 预算金额不一致,更新服务器
await onlineBudgetApi.setBudget({
userId,
year: localBudget.year,
month: localBudget.month,
amount: localBudget.amount
})
}
}
// 4. 将服务器端预算同步到本地
if (serverBudget) {
// 检查本地是否已有相同月份预算
const checkSql = `SELECT * FROM budget WHERE user_id = ? AND year = ? AND month = ?`
const existingBudgets = await db.query(checkSql, [
userId,
serverBudget.year,
serverBudget.month
])
if (existingBudgets.length === 0) {
// 本地没有相同月份预算,添加到本地
const insertSql = `INSERT INTO budget (user_id, year, month, amount, is_synced)
VALUES (?, ?, ?, ?, 1)`
await db.update(insertSql, [
userId,
serverBudget.year,
serverBudget.month,
serverBudget.amount
])
} else if (existingBudgets[0].amount !== serverBudget.amount) {
// 预算金额不一致,更新本地
const updateSql = `UPDATE budget SET amount = ?, is_synced = 1 WHERE user_id = ? AND year = ? AND month = ?`
await db.update(updateSql, [
serverBudget.amount,
userId,
serverBudget.year,
serverBudget.month
])
}
}
return { success: true, message: '预算同步成功' }
} catch (error) {
console.error('同步预算失败:', error)
throw error
}
}
// 全量同步数据
const syncAllData = async () => {
if (isOfflineUser()) {
throw new Error('离线用户无法同步数据')
}
try {
// 1. 同步账单
const billResult = await syncBills()
// 2. 同步分类
const categoryResult = await syncCategories()
// 3. 同步账户
const accountResult = await syncAccount()
// 4. 同步预算
const budgetResult = await syncBudgets()
return {
success: true,
message: '数据同步成功',
details: {
bills: billResult,
categories: categoryResult,
account: accountResult,
budgets: budgetResult
}
}
} catch (error) {
console.error('全量同步失败:', error)
throw error
}
}
// 数据同步模块导出
export const sync = {
// 全量同步
syncAll: syncAllData,
// 账单同步
syncBills,
// 分类同步
syncCategories,
// 账户同步
syncAccount,
// 预算同步
syncBudgets,
// 检查同步状态
checkSyncStatus: () => {
const userInfo = uni.getStorageSync('userInfo') || {}
const lastSyncTime = uni.getStorageSync('lastSyncTime') || null
return {
isOffline: userInfo.isOffline === true || userInfo.userId === 'local_user',
lastSyncTime
}
}
}
export default sync