/** * 删除重复的项目 * 用途:查找并删除标题相同的重复项目,保留最新的一个 * * 使用方法: * 1. 在 Parse Dashboard 的 Cloud Code 页面运行此脚本 * 2. 或者通过 API 调用:POST /parse/functions/removeDuplicateProjects */ Parse.Cloud.define('removeDuplicateProjects', async (request) => { const { title, dryRun = true } = request.params; console.log('🔍 开始查找重复项目...'); console.log('查询条件:', { title, dryRun: dryRun ? '预览模式' : '执行删除' }); try { const query = new Parse.Query('Project'); // 如果提供了 title,只查找该标题的项目 if (title) { query.equalTo('title', title); } query.notEqualTo('isDeleted', true); query.ascending('createdAt'); // 按创建时间升序,保留最新的 query.limit(1000); const projects = await query.find({ useMasterKey: true }); console.log(`📊 找到 ${projects.length} 个项目`); // 按标题分组 const projectsByTitle = new Map(); projects.forEach(project => { const projectTitle = project.get('title') || '无标题'; if (!projectsByTitle.has(projectTitle)) { projectsByTitle.set(projectTitle, []); } projectsByTitle.get(projectTitle).push(project); }); // 查找重复的项目 const duplicates = []; const summary = []; for (const [projectTitle, projectList] of projectsByTitle.entries()) { if (projectList.length > 1) { console.log(`\n⚠️ 发现重复项目: "${projectTitle}" (${projectList.length} 个)`); // 按创建时间排序,保留最新的 projectList.sort((a, b) => { const aTime = a.get('createdAt') || a.createdAt; const bTime = b.get('createdAt') || b.createdAt; return bTime.getTime() - aTime.getTime(); }); // 第一个是最新的,保留 const keepProject = projectList[0]; const deleteProjects = projectList.slice(1); console.log(` ✅ 保留: ${keepProject.id} (创建于: ${keepProject.get('createdAt')})`); deleteProjects.forEach(project => { console.log(` ❌ 删除: ${project.id} (创建于: ${project.get('createdAt')})`); duplicates.push({ id: project.id, title: projectTitle, createdAt: project.get('createdAt'), currentStage: project.get('currentStage'), status: project.get('status') }); if (!dryRun) { // 标记为已删除 project.set('isDeleted', true); project.set('deletedAt', new Date()); project.set('deleteReason', '重复项目(自动清理)'); } }); summary.push({ title: projectTitle, total: projectList.length, kept: 1, deleted: deleteProjects.length, keptProjectId: keepProject.id }); } } // 执行删除 if (!dryRun && duplicates.length > 0) { const projectsToDelete = duplicates.map(d => { const project = new Parse.Object('Project'); project.id = d.id; project.set('isDeleted', true); project.set('deletedAt', new Date()); project.set('deleteReason', '重复项目(自动清理)'); return project; }); await Parse.Object.saveAll(projectsToDelete, { useMasterKey: true }); console.log(`\n✅ 已删除 ${duplicates.length} 个重复项目`); } return { success: true, dryRun, summary: { totalProjects: projects.length, duplicatesFound: duplicates.length, titlesWithDuplicates: summary.length }, details: summary, duplicates: duplicates.map(d => ({ id: d.id, title: d.title, createdAt: d.createdAt, stage: d.currentStage, status: d.status })) }; } catch (error) { console.error('❌ 删除重复项目失败:', error); throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, `删除失败: ${error.message}`); } }); /** * 删除指定的单个项目(用于手动删除) */ Parse.Cloud.define('deleteProjectById', async (request) => { const { projectId, reason = '手动删除' } = request.params; if (!projectId) { throw new Parse.Error(Parse.Error.INVALID_QUERY, '缺少 projectId 参数'); } try { const query = new Parse.Query('Project'); const project = await query.get(projectId, { useMasterKey: true }); if (!project) { throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, '项目不存在'); } const projectTitle = project.get('title'); console.log(`🗑️ 删除项目: ${projectTitle} (${projectId})`); // 软删除 project.set('isDeleted', true); project.set('deletedAt', new Date()); project.set('deleteReason', reason); await project.save(null, { useMasterKey: true }); return { success: true, message: `项目 "${projectTitle}" 已删除`, projectId: projectId, title: projectTitle }; } catch (error) { console.error('❌ 删除项目失败:', error); throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, `删除失败: ${error.message}`); } }); /** * 查找指定标题的所有项目(用于预览) */ Parse.Cloud.define('findProjectsByTitle', async (request) => { const { title } = request.params; if (!title) { throw new Parse.Error(Parse.Error.INVALID_QUERY, '缺少 title 参数'); } try { const query = new Parse.Query('Project'); query.equalTo('title', title); query.notEqualTo('isDeleted', true); query.ascending('createdAt'); query.limit(100); const projects = await query.find({ useMasterKey: true }); return { success: true, total: projects.length, projects: projects.map(p => ({ id: p.id, title: p.get('title'), createdAt: p.get('createdAt'), currentStage: p.get('currentStage'), status: p.get('status'), assignee: p.get('assignee')?.get('name') || '未分配' })) }; } catch (error) { console.error('❌ 查找项目失败:', error); throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, `查找失败: ${error.message}`); } });