useCheckinDraft.js 5.65 KB
/**
 * 打卡草稿缓存逻辑封装
 * @module useCheckinDraft
 * @description
 * 提供打卡草稿的增删改查能力,基于 localStorage。
 * 
 * 核心功能:
 * 1. 构建唯一的缓存 Key(用户+任务+日期+状态维度)。
 * 2. 保存草稿:自动过滤未完成的附件,只存 meta_id/url 等元数据。
 * 3. 读取草稿:支持过期检查(TTL 7天)。
 * 4. 自动清理:支持惰性清理和全量扫描清理过期项。
 * 
 * 使用场景:
 * - CheckinDetailPage 自动保存与恢复。
 */
import { useRoute } from 'vue-router'

export function useCheckinDraft() {
    const DRAFT_PREFIX = 'CHECKIN_DRAFT_V1'
    const TTL_MS = 7 * 24 * 60 * 60 * 1000 // 7天过期

    /**
     * 判断功能是否开启
     * @returns {boolean}
     */
    const is_enabled = () => {
        // 支持 URL 参数覆盖 (CheckinDetailPage 会用到 route,但此处为纯函数逻辑,依赖调用方传入或在组件内判断)
        // 这里仅读取环境变量
        // 实际使用时建议结合 route.query.enable_draft 判断
        return import.meta.env.VITE_CHECKIN_DRAFT_CACHE === '1'
    }

    /**
     * 构建缓存 Key
     * @param {Object} context
     * @param {string} context.user_id 用户ID
     * @param {string} context.task_id 任务ID
     * @param {string} context.date 日期
     * @param {string} context.task_type 任务类型
     * @param {string} context.status 状态 (create/edit)
     * @returns {string} Key
     */
    const build_key = ({ user_id, task_id, date, task_type, status }) => {
        // 确保所有字段都存在,避免生成 undefined
        const parts = [
            user_id || 'guest',
            task_id || 'default',
            date || 'nodate',
            task_type || 'default',
            status || 'create'
        ]
        return `${DRAFT_PREFIX}:${parts.join(':')}`
    }

    /**
     * 保存草稿
     * @param {string} key 缓存Key
     * @param {Object} payload 业务数据
     * @param {string} payload.message 文本
     * @param {Array} payload.file_list 文件列表
     * @param {string} payload.active_type 当前类型
     * @param {string|number} payload.subtask_id 子任务ID
     * @param {Array} payload.selected_task_value 选中的任务值
     * @param {Object} payload.count 计数打卡数据
     */
    const save_draft = (key, payload) => {
        if (!key) return

        try {
            // 过滤附件:只保存已上传成功且有 meta_id 的
            const validFiles = (payload.file_list || []).filter(item => 
                item.status === 'done' && item.meta_id
            ).map(item => ({
                meta_id: item.meta_id,
                url: item.url,
                name: item.name,
                file_type: item.file_type
            }))

            const data = {
                version: 1,
                saved_at: Date.now(),
                expires_at: Date.now() + TTL_MS,
                payload: {
                    ...payload,
                    file_list: validFiles
                }
            }

            localStorage.setItem(key, JSON.stringify(data))
            console.log(`[草稿缓存] 已保存: ${key}`, data.payload)
        } catch (e) {
            console.error('[草稿缓存] 保存失败:', e)
        }
    }

    /**
     * 读取草稿
     * @param {string} key 缓存Key
     * @returns {Object|null} 草稿数据或null
     */
    const read_draft = (key) => {
        if (!key) return null

        try {
            const raw = localStorage.getItem(key)
            if (!raw) return null

            const data = JSON.parse(raw)
            
            // 检查版本
            if (data.version !== 1) {
                console.warn('[草稿缓存] 版本不匹配,丢弃旧数据')
                clear_draft(key)
                return null
            }

            // 检查过期
            if (data.expires_at && Date.now() > data.expires_at) {
                console.log('[草稿缓存] 数据已过期,自动清理')
                clear_draft(key)
                return null
            }

            return data
        } catch (e) {
            console.error('[草稿缓存] 读取失败:', e)
            return null
        }
    }

    /**
     * 清除指定草稿
     * @param {string} key 
     */
    const clear_draft = (key) => {
        if (!key) return
        localStorage.removeItem(key)
        console.log(`[草稿缓存] 已清除: ${key}`)
    }

    /**
     * 清理所有过期草稿
     */
    const cleanup_expired = () => {
        try {
            const keysToRemove = []
            const now = Date.now()

            for (let i = 0; i < localStorage.length; i++) {
                const key = localStorage.key(i)
                if (key && key.startsWith(DRAFT_PREFIX)) {
                    try {
                        const raw = localStorage.getItem(key)
                        const data = JSON.parse(raw)
                        if (data.expires_at && now > data.expires_at) {
                            keysToRemove.push(key)
                        }
                    } catch (e) {
                        // 解析失败的脏数据也清理掉
                        keysToRemove.push(key)
                    }
                }
            }

            keysToRemove.forEach(key => {
                localStorage.removeItem(key)
                console.log(`[草稿缓存] 自动清理过期项: ${key}`)
            })
        } catch (e) {
            console.error('[草稿缓存] 批量清理失败:', e)
        }
    }

    return {
        is_enabled,
        build_key,
        save_draft,
        read_draft,
        clear_draft,
        cleanup_expired
    }
}