0.0.9.0
This commit is contained in:
@@ -1,133 +0,0 @@
|
||||
import { db, type ActivityLog } from "./db"
|
||||
import { v4 as uuidv4 } from "uuid"
|
||||
|
||||
/**
|
||||
* 记录活动日志
|
||||
*/
|
||||
export async function logActivity(
|
||||
action: ActivityLog["action"],
|
||||
entityType: ActivityLog["entityType"],
|
||||
entityId?: string,
|
||||
entityName?: string,
|
||||
changes?: any
|
||||
): Promise<void> {
|
||||
try {
|
||||
const log: ActivityLog = {
|
||||
id: uuidv4(),
|
||||
action,
|
||||
entityType,
|
||||
entityId,
|
||||
entityName,
|
||||
changes,
|
||||
timestamp: new Date().toISOString(),
|
||||
userId: "local-user", // 本地版本暂时使用固定用户ID
|
||||
userName: "本地用户",
|
||||
}
|
||||
|
||||
await db.activityLogs.add(log)
|
||||
} catch (error) {
|
||||
console.error("记录活动日志失败:", error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取活动日志列表
|
||||
*/
|
||||
export async function getActivityLogs(limit: number = 50): Promise<ActivityLog[]> {
|
||||
try {
|
||||
return await db.activityLogs
|
||||
.orderBy("timestamp")
|
||||
.reverse()
|
||||
.limit(limit)
|
||||
.toArray()
|
||||
} catch (error) {
|
||||
console.error("获取活动日志失败:", error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取特定成员的活动日志
|
||||
*/
|
||||
export async function getMemberActivityLogs(memberId: string): Promise<ActivityLog[]> {
|
||||
try {
|
||||
return await db.activityLogs
|
||||
.where("entityId")
|
||||
.equals(memberId)
|
||||
.reverse()
|
||||
.sortBy("timestamp")
|
||||
} catch (error) {
|
||||
console.error("获取成员活动日志失败:", error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除旧日志(保留最近N天)
|
||||
*/
|
||||
export async function cleanOldLogs(daysToKeep: number = 90): Promise<number> {
|
||||
try {
|
||||
const cutoffDate = new Date()
|
||||
cutoffDate.setDate(cutoffDate.getDate() - daysToKeep)
|
||||
const cutoffTimestamp = cutoffDate.toISOString()
|
||||
|
||||
const oldLogs = await db.activityLogs
|
||||
.where("timestamp")
|
||||
.below(cutoffTimestamp)
|
||||
.toArray()
|
||||
|
||||
await db.activityLogs
|
||||
.where("timestamp")
|
||||
.below(cutoffTimestamp)
|
||||
.delete()
|
||||
|
||||
return oldLogs.length
|
||||
} catch (error) {
|
||||
console.error("清除旧日志失败:", error)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取活动统计
|
||||
*/
|
||||
export async function getActivityStats(): Promise<{
|
||||
total: number
|
||||
byAction: Record<string, number>
|
||||
byEntityType: Record<string, number>
|
||||
recentCount: number
|
||||
}> {
|
||||
try {
|
||||
const allLogs = await db.activityLogs.toArray()
|
||||
const oneDayAgo = new Date()
|
||||
oneDayAgo.setDate(oneDayAgo.getDate() - 1)
|
||||
|
||||
const byAction: Record<string, number> = {}
|
||||
const byEntityType: Record<string, number> = {}
|
||||
let recentCount = 0
|
||||
|
||||
allLogs.forEach((log) => {
|
||||
byAction[log.action] = (byAction[log.action] || 0) + 1
|
||||
byEntityType[log.entityType] = (byEntityType[log.entityType] || 0) + 1
|
||||
|
||||
if (new Date(log.timestamp) > oneDayAgo) {
|
||||
recentCount++
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
total: allLogs.length,
|
||||
byAction,
|
||||
byEntityType,
|
||||
recentCount,
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取活动统计失败:", error)
|
||||
return {
|
||||
total: 0,
|
||||
byAction: {},
|
||||
byEntityType: {},
|
||||
recentCount: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
import Dexie from "dexie"
|
||||
import { db } from "./db"
|
||||
|
||||
/**
|
||||
* 检查数据库版本并升级
|
||||
*/
|
||||
export async function checkAndUpgradeDatabase(): Promise<boolean> {
|
||||
try {
|
||||
// 检查 activityLogs 表是否存在
|
||||
const tables = db.tables.map(t => t.name)
|
||||
|
||||
if (!tables.includes("activityLogs")) {
|
||||
console.log("检测到需要升级数据库...")
|
||||
|
||||
// 关闭当前连接
|
||||
db.close()
|
||||
|
||||
// 删除旧数据库(注意:这会清除所有数据)
|
||||
// 在生产环境中,应该做数据迁移而不是删除
|
||||
await Dexie.delete("FamilyTreeDB")
|
||||
|
||||
// 重新打开数据库(会自动创建最新版本)
|
||||
await db.open()
|
||||
|
||||
console.log("数据库升级完成")
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
} catch (error) {
|
||||
console.error("数据库升级失败:", error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动升级数据库(保留数据)
|
||||
*/
|
||||
export async function upgradeDatabase(): Promise<void> {
|
||||
try {
|
||||
// 1. 备份现有数据
|
||||
const members = await db.members.toArray()
|
||||
const settings = await db.settings.toArray()
|
||||
const images = await db.images.toArray()
|
||||
|
||||
console.log("备份数据:", { members: members.length, settings: settings.length, images: images.length })
|
||||
|
||||
// 2. 关闭并删除数据库
|
||||
db.close()
|
||||
await Dexie.delete("FamilyTreeDB")
|
||||
|
||||
// 3. 重新打开(会创建新版本)
|
||||
await db.open()
|
||||
|
||||
// 4. 恢复数据
|
||||
if (members.length > 0) {
|
||||
await db.members.bulkAdd(members)
|
||||
}
|
||||
if (settings.length > 0) {
|
||||
await db.settings.bulkAdd(settings)
|
||||
}
|
||||
if (images.length > 0) {
|
||||
await db.images.bulkAdd(images)
|
||||
}
|
||||
|
||||
console.log("数据库升级完成,数据已恢复")
|
||||
} catch (error) {
|
||||
console.error("数据库升级失败:", error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
import Dexie, { type Table } from "dexie"
|
||||
import type { FamilyMember } from "@/types/family"
|
||||
|
||||
export interface GlobalSettings {
|
||||
key: string
|
||||
value: any
|
||||
}
|
||||
|
||||
export interface StoredImage {
|
||||
id: string
|
||||
blob: Blob
|
||||
mimeType: string
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export interface ActivityLog {
|
||||
id: string
|
||||
action: "create" | "update" | "delete" | "import" | "export"
|
||||
entityType: "member" | "photo" | "story" | "settings"
|
||||
entityId?: string
|
||||
entityName?: string
|
||||
changes?: any
|
||||
timestamp: string
|
||||
userId?: string
|
||||
userName?: string
|
||||
}
|
||||
|
||||
export class FamilyTreeDB extends Dexie {
|
||||
members!: Table<FamilyMember>
|
||||
settings!: Table<GlobalSettings>
|
||||
images!: Table<StoredImage>
|
||||
activityLogs!: Table<ActivityLog>
|
||||
|
||||
constructor() {
|
||||
super("FamilyTreeDB")
|
||||
this.version(1).stores({
|
||||
members: "id, fullName, fatherId, motherId, [fatherId+motherId]", // Index for searching
|
||||
settings: "key", // Key-value store for rootId etc.
|
||||
images: "id", // Store images by ID
|
||||
})
|
||||
this.version(2).stores({
|
||||
members: "id, fullName, fatherId, motherId, [fatherId+motherId]", // Index for searching
|
||||
settings: "key", // Key-value store for rootId etc.
|
||||
images: "id", // Store images by ID
|
||||
activityLogs: "id, timestamp, action, entityType, entityId",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const db = new FamilyTreeDB()
|
||||
@@ -24,10 +24,10 @@ export async function recordMemberHistory(params: {
|
||||
const nextVersion = (lastHistory?.version || 0) + 1
|
||||
|
||||
// 计算变更的字段
|
||||
let changes: Record<string, { old: any, new: any }> | null = null
|
||||
let changes: Record<string, { old: any, new: any }> | undefined = undefined
|
||||
|
||||
if (changeType === 'UPDATE' && oldData) {
|
||||
changes = {}
|
||||
const tempChanges: Record<string, { old: any, new: any }> = {}
|
||||
const fieldsToTrack = [
|
||||
'fullName', 'surname', 'givenName', 'gender', 'birthDate', 'deathDate',
|
||||
'birthPlace', 'ancestralHome', 'generation', 'bio', 'phone', 'email',
|
||||
@@ -43,14 +43,16 @@ export async function recordMemberHistory(params: {
|
||||
// 比较值是否真的变化了
|
||||
const isChanged = JSON.stringify(oldValue) !== JSON.stringify(newValue)
|
||||
if (isChanged) {
|
||||
changes[field] = { old: oldValue, new: newValue }
|
||||
tempChanges[field] = { old: oldValue, new: newValue }
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有实际变更,不记录
|
||||
if (Object.keys(changes).length === 0) {
|
||||
if (Object.keys(tempChanges).length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
changes = tempChanges
|
||||
}
|
||||
|
||||
// 创建历史记录
|
||||
@@ -62,7 +64,7 @@ export async function recordMemberHistory(params: {
|
||||
snapshot: newData,
|
||||
changedBy,
|
||||
changeType,
|
||||
changes,
|
||||
changes: changes || undefined,
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
+11
-1
@@ -1,6 +1,16 @@
|
||||
export function registerServiceWorker() {
|
||||
if (typeof window === "undefined" || !("serviceWorker" in navigator)) {
|
||||
console.log("Service Worker 不被支持")
|
||||
// Service Worker 不被支持,静默跳过
|
||||
return
|
||||
}
|
||||
|
||||
// 只在 HTTPS 或 localhost 环境下注册 Service Worker
|
||||
const isSecureContext = window.location.protocol === "https:" ||
|
||||
window.location.hostname === "localhost" ||
|
||||
window.location.hostname === "127.0.0.1"
|
||||
|
||||
if (!isSecureContext) {
|
||||
// 非安全环境,静默跳过 Service Worker 注册
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user