| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- /**
- * 删除重复的项目
- * 用途:查找并删除标题相同的重复项目,保留最新的一个
- *
- * 使用方法:
- * 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}`);
- }
- });
|