This commit is contained in:
freedakgmail
2025-11-24 16:07:24 +08:00
parent 3d075c6076
commit b25f4bea02
426 changed files with 3619 additions and 4468 deletions
-133
View File
@@ -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,
}
}
}
-71
View File
@@ -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
}
}
-50
View File
@@ -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()
+7 -5
View File
@@ -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
View File
@@ -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
}