|
@@ -0,0 +1,448 @@
|
|
|
|
+const Parse = require('parse/node');
|
|
|
|
+
|
|
|
|
+// 加载环境变量(建议使用.env文件管理敏感信息)
|
|
|
|
+
|
|
|
|
+// 1. 初始化Parse连接
|
|
|
|
+Parse.initialize(
|
|
|
|
+ process.env.PARSE_APP_ID || 'ncloudmaster', // 替换为你的Parse App ID
|
|
|
|
+ process.env.PARSE_MASTER_KEY || "SnkK12*&sunq2#@20!" // 替换为你的Parse Master Key
|
|
|
|
+);
|
|
|
|
+Parse.serverURL = process.env.PARSE_SERVER_URL || 'https://server.fmode.cn/parse'; // 替换为你的Parse Server地址
|
|
|
|
+Parse.masterKey = process.env.PARSE_MASTER_KEY || "SnkK12*&sunq2#@20!"; // Master Key用于Schema操作
|
|
|
|
+
|
|
|
|
+// 2. 定义所有数据表结构配置(与文档范式完全对应)
|
|
|
|
+const tableConfigs = [
|
|
|
|
+ // 核心租户表:Company
|
|
|
|
+ {
|
|
|
|
+ className: 'Company',
|
|
|
|
+ fields: [
|
|
|
|
+ { name: 'name', type: 'String', required: true },
|
|
|
|
+ { name: 'corpId', type: 'String', required: false },
|
|
|
|
+ { name: 'data', type: 'Object', required: false, defaultValue: {} },
|
|
|
|
+ { name: 'isDeleted', type: 'Boolean', required: false, defaultValue: false }
|
|
|
|
+ ],
|
|
|
|
+ indexes: [
|
|
|
|
+ { name: 'corpId_index', fields: { corpId: 1 } },
|
|
|
|
+ { name: 'name_index', fields: { name: 1 } }
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 部门表:Department
|
|
|
|
+ {
|
|
|
|
+ className: 'Department',
|
|
|
|
+ fields: [
|
|
|
|
+ { name: 'name', type: 'String', required: true },
|
|
|
|
+ { name: 'type', type: 'String', required: true, defaultValue: 'project' },
|
|
|
|
+ { name: 'leader', type: 'Pointer', required: false, targetClass: 'Profile' },
|
|
|
|
+ { name: 'company', type: 'Pointer', required: true, targetClass: 'Company' },
|
|
|
|
+ { name: 'isDeleted', type: 'Boolean', required: false, defaultValue: false }
|
|
|
|
+ ],
|
|
|
|
+ indexes: [
|
|
|
|
+ { name: 'company_isDeleted', fields: { company: 1, isDeleted: 1 } },
|
|
|
|
+ { name: 'leader_index', fields: { leader: 1 } }
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 员工档案表:Profile
|
|
|
|
+ {
|
|
|
|
+ className: 'Profile',
|
|
|
|
+ fields: [
|
|
|
|
+ { name: 'name', type: 'String', required: true },
|
|
|
|
+ { name: 'mobile', type: 'String', required: false },
|
|
|
|
+ { name: 'department', type: 'Pointer', required: true, targetClass: 'Department' },
|
|
|
|
+ { name: 'company', type: 'Pointer', required: true, targetClass: 'Company' },
|
|
|
|
+ { name: 'userId', type: 'String', required: false },
|
|
|
|
+ { name: 'roleName', type: 'String', required: true },
|
|
|
|
+ { name: 'data', type: 'Object', required: false, defaultValue: {} },
|
|
|
|
+ { name: 'isDeleted', type: 'Boolean', required: false, defaultValue: false }
|
|
|
|
+ ],
|
|
|
|
+ indexes: [
|
|
|
|
+ { name: 'company_isDeleted', fields: { company: 1, isDeleted: 1 } },
|
|
|
|
+ { name: 'userId_company', fields: { userId: 1, company: 1 }, unique: true },
|
|
|
|
+ { name: 'roleName_company', fields: { roleName: 1, company: 1 } },
|
|
|
|
+ { name: 'mobile_company', fields: { mobile: 1, company: 1 } }
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 客户信息表:ContactInfo
|
|
|
|
+ {
|
|
|
|
+ className: 'ContactInfo',
|
|
|
|
+ fields: [
|
|
|
|
+ { name: 'name', type: 'String', required: true },
|
|
|
|
+ { name: 'mobile', type: 'String', required: false },
|
|
|
|
+ { name: 'company', type: 'Pointer', required: true, targetClass: 'Company' },
|
|
|
|
+ { name: 'external_userid', type: 'String', required: false },
|
|
|
|
+ { name: 'source', type: 'String', required: false },
|
|
|
|
+ { name: 'data', type: 'Object', required: false, defaultValue: {} },
|
|
|
|
+ { name: 'isDeleted', type: 'Boolean', required: false, defaultValue: false }
|
|
|
|
+ ],
|
|
|
|
+ indexes: [
|
|
|
|
+ { name: 'company_isDeleted', fields: { company: 1, isDeleted: 1 } },
|
|
|
|
+ { name: 'external_userid_company', fields: { external_userid: 1, company: 1 }, unique: true },
|
|
|
|
+ { name: 'mobile_company', fields: { mobile: 1, company: 1 } },
|
|
|
|
+ { name: 'source_company', fields: { source: 1, company: 1 } }
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 企微群聊表:GroupChat
|
|
|
|
+ {
|
|
|
|
+ className: 'GroupChat',
|
|
|
|
+ fields: [
|
|
|
|
+ { name: 'chat_id', type: 'String', required: true },
|
|
|
|
+ { name: 'name', type: 'String', required: true },
|
|
|
|
+ { name: 'company', type: 'Pointer', required: true, targetClass: 'Company' },
|
|
|
|
+ { name: 'project', type: 'Pointer', required: false, targetClass: 'Project' },
|
|
|
|
+ { name: 'member_list', type: 'Array', required: false, defaultValue: [] },
|
|
|
|
+ { name: 'joinUrl', type: 'String', required: false },
|
|
|
|
+ { name: 'data', type: 'Object', required: false, defaultValue: {} },
|
|
|
|
+ { name: 'isDeleted', type: 'Boolean', required: false, defaultValue: false }
|
|
|
|
+ ],
|
|
|
|
+ indexes: [
|
|
|
|
+ { name: 'chat_id_company', fields: { chat_id: 1, company: 1 }, unique: true },
|
|
|
|
+ { name: 'project_isDeleted', fields: { project: 1, isDeleted: 1 } },
|
|
|
|
+ { name: 'company_isDeleted', fields: { company: 1, isDeleted: 1 } }
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 项目群组关联表:ProjectGroup
|
|
|
|
+ {
|
|
|
|
+ className: 'ProjectGroup',
|
|
|
|
+ fields: [
|
|
|
|
+ { name: 'project', type: 'Pointer', required: true, targetClass: 'Project' },
|
|
|
|
+ { name: 'groupChat', type: 'Pointer', required: true, targetClass: 'GroupChat' },
|
|
|
|
+ { name: 'isPrimary', type: 'Boolean', required: false, defaultValue: false }
|
|
|
|
+ ],
|
|
|
|
+ indexes: [
|
|
|
|
+ { name: 'project_groupChat', fields: { project: 1, groupChat: 1 }, unique: true },
|
|
|
|
+ { name: 'groupChat_index', fields: { groupChat: 1 } },
|
|
|
|
+ { name: 'isPrimary_index', fields: { isPrimary: 1 } }
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 项目表:Project
|
|
|
|
+ {
|
|
|
|
+ className: 'Project',
|
|
|
|
+ fields: [
|
|
|
|
+ { name: 'title', type: 'String', required: true },
|
|
|
|
+ { name: 'company', type: 'Pointer', required: true, targetClass: 'Company' },
|
|
|
|
+ { name: 'customer', type: 'Pointer', required: true, targetClass: 'ContactInfo' },
|
|
|
|
+ { name: 'assignee', type: 'Pointer', required: false, targetClass: 'Profile' },
|
|
|
|
+ { name: 'status', type: 'String', required: true, defaultValue: '待分配' },
|
|
|
|
+ { name: 'currentStage', type: 'String', required: true, defaultValue: '订单分配' },
|
|
|
|
+ { name: 'deadline', type: 'Date', required: false },
|
|
|
|
+ { name: 'data', type: 'Object', required: false, defaultValue: {} },
|
|
|
|
+ { name: 'isDeleted', type: 'Boolean', required: false, defaultValue: false }
|
|
|
|
+ ],
|
|
|
|
+ indexes: [
|
|
|
|
+ { name: 'company_isDeleted', fields: { company: 1, isDeleted: 1 } },
|
|
|
|
+ { name: 'assignee_status', fields: { assignee: 1, status: 1 } },
|
|
|
|
+ { name: 'customer_isDeleted', fields: { customer: 1, isDeleted: 1 } },
|
|
|
|
+ { name: 'currentStage_status', fields: { currentStage: 1, status: 1 } },
|
|
|
|
+ { name: 'deadline_index', fields: { deadline: 1 } },
|
|
|
|
+ { name: 'updatedAt_desc', fields: { updatedAt: -1 } }
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 需求信息表:ProjectRequirement
|
|
|
|
+ {
|
|
|
|
+ className: 'ProjectRequirement',
|
|
|
|
+ fields: [
|
|
|
|
+ { name: 'project', type: 'Pointer', required: true, targetClass: 'Project' },
|
|
|
|
+ { name: 'company', type: 'Pointer', required: true, targetClass: 'Company' },
|
|
|
|
+ { name: 'spaces', type: 'Array', required: false, defaultValue: [] },
|
|
|
|
+ { name: 'designRequirements', type: 'Object', required: false, defaultValue: {} },
|
|
|
|
+ { name: 'materialAnalysis', type: 'Object', required: false, defaultValue: {} },
|
|
|
|
+ { name: 'data', type: 'Object', required: false, defaultValue: {} },
|
|
|
|
+ { name: 'isDeleted', type: 'Boolean', required: false, defaultValue: false }
|
|
|
|
+ ],
|
|
|
|
+ indexes: [
|
|
|
|
+ { name: 'project_unique', fields: { project: 1 }, unique: true },
|
|
|
|
+ { name: 'company_isDeleted', fields: { company: 1, isDeleted: 1 } }
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 项目团队表:ProjectTeam
|
|
|
|
+ {
|
|
|
|
+ className: 'ProjectTeam',
|
|
|
|
+ fields: [
|
|
|
|
+ { name: 'project', type: 'Pointer', required: true, targetClass: 'Project' },
|
|
|
|
+ { name: 'profile', type: 'Pointer', required: true, targetClass: 'Profile' },
|
|
|
|
+ { name: 'role', type: 'String', required: true },
|
|
|
|
+ { name: 'workload', type: 'Number', required: false, defaultValue: 0 },
|
|
|
|
+ { name: 'isDeleted', type: 'Boolean', required: false, defaultValue: false }
|
|
|
|
+ ],
|
|
|
|
+ indexes: [
|
|
|
|
+ { name: 'project_isDeleted', fields: { project: 1, isDeleted: 1 } },
|
|
|
|
+ { name: 'profile_project', fields: { profile: 1, project: 1 }, unique: true }
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 交付物表:Product
|
|
|
|
+ {
|
|
|
|
+ className: 'Product',
|
|
|
|
+ fields: [
|
|
|
|
+ { name: 'project', type: 'Pointer', required: true, targetClass: 'Project' },
|
|
|
|
+ { name: 'company', type: 'Pointer', required: true, targetClass: 'Company' },
|
|
|
|
+ { name: 'stage', type: 'String', required: true },
|
|
|
|
+ { name: 'processType', type: 'String', required: false },
|
|
|
|
+ { name: 'space', type: 'String', required: false },
|
|
|
|
+ { name: 'fileUrl', type: 'String', required: true },
|
|
|
|
+ { name: 'reviewStatus', type: 'String', required: true, defaultValue: 'pending' },
|
|
|
|
+ { name: 'quotation', type: 'Object', required: false, defaultValue: {} },
|
|
|
|
+ { name: 'data', type: 'Object', required: false, defaultValue: {} },
|
|
|
|
+ { name: 'isDeleted', type: 'Boolean', required: false, defaultValue: false }
|
|
|
|
+ ],
|
|
|
|
+ indexes: [
|
|
|
|
+ { name: 'project_stage_isDeleted', fields: { project: 1, stage: 1, isDeleted: 1 } },
|
|
|
|
+ { name: 'project_space', fields: { project: 1, space: 1 } },
|
|
|
|
+ { name: 'reviewStatus_project', fields: { reviewStatus: 1, project: 1 } }
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 项目文件表:ProjectFile
|
|
|
|
+ {
|
|
|
|
+ className: 'ProjectFile',
|
|
|
|
+ fields: [
|
|
|
|
+ { name: 'project', type: 'Pointer', required: true, targetClass: 'Project' },
|
|
|
|
+ { name: 'uploadedBy', type: 'Pointer', required: true, targetClass: 'Profile' },
|
|
|
|
+ { name: 'fileType', type: 'String', required: true },
|
|
|
|
+ { name: 'fileUrl', type: 'String', required: true },
|
|
|
|
+ { name: 'fileName', type: 'String', required: true },
|
|
|
|
+ { name: 'fileSize', type: 'Number', required: false, defaultValue: 0 },
|
|
|
|
+ { name: 'stage', type: 'String', required: false },
|
|
|
|
+ { name: 'data', type: 'Object', required: false, defaultValue: {} },
|
|
|
|
+ { name: 'isDeleted', type: 'Boolean', required: false, defaultValue: false }
|
|
|
|
+ ],
|
|
|
|
+ indexes: [
|
|
|
|
+ { name: 'project_fileType_isDeleted', fields: { project: 1, fileType: 1, isDeleted: 1 } },
|
|
|
|
+ { name: 'uploadedBy_project', fields: { uploadedBy: 1, project: 1 } }
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 结算记录表:ProjectSettlement
|
|
|
|
+ {
|
|
|
|
+ className: 'ProjectSettlement',
|
|
|
|
+ fields: [
|
|
|
|
+ { name: 'project', type: 'Pointer', required: true, targetClass: 'Project' },
|
|
|
|
+ { name: 'company', type: 'Pointer', required: true, targetClass: 'Company' },
|
|
|
|
+ { name: 'stage', type: 'String', required: true },
|
|
|
|
+ { name: 'amount', type: 'Number', required: true },
|
|
|
|
+ { name: 'percentage', type: 'Number', required: false, defaultValue: 0 },
|
|
|
|
+ { name: 'status', type: 'String', required: true, defaultValue: '待结算' },
|
|
|
|
+ { name: 'dueDate', type: 'Date', required: false },
|
|
|
|
+ { name: 'settledAt', type: 'Date', required: false },
|
|
|
|
+ { name: 'data', type: 'Object', required: false, defaultValue: {} },
|
|
|
|
+ { name: 'isDeleted', type: 'Boolean', required: false, defaultValue: false }
|
|
|
|
+ ],
|
|
|
|
+ indexes: [
|
|
|
|
+ { name: 'project_stage', fields: { project: 1, stage: 1 }, unique: true },
|
|
|
|
+ { name: 'status_company', fields: { status: 1, company: 1 } },
|
|
|
|
+ { name: 'dueDate_index', fields: { dueDate: 1 } }
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 付款凭证表:ProjectVoucher
|
|
|
|
+ {
|
|
|
|
+ className: 'ProjectVoucher',
|
|
|
|
+ fields: [
|
|
|
|
+ { name: 'settlement', type: 'Pointer', required: true, targetClass: 'ProjectSettlement' },
|
|
|
|
+ { name: 'project', type: 'Pointer', required: true, targetClass: 'Project' },
|
|
|
|
+ { name: 'amount', type: 'Number', required: true },
|
|
|
|
+ { name: 'voucherUrl', type: 'String', required: true },
|
|
|
|
+ { name: 'recognizedInfo', type: 'Object', required: false, defaultValue: {} },
|
|
|
|
+ { name: 'verifiedBy', type: 'Pointer', required: false, targetClass: 'Profile' },
|
|
|
|
+ { name: 'data', type: 'Object', required: false, defaultValue: {} },
|
|
|
|
+ { name: 'isDeleted', type: 'Boolean', required: false, defaultValue: false }
|
|
|
|
+ ],
|
|
|
|
+ indexes: [
|
|
|
|
+ { name: 'settlement_project', fields: { settlement: 1, project: 1 } },
|
|
|
|
+ { name: 'project_isDeleted', fields: { project: 1, isDeleted: 1 } }
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 客户反馈表:ProjectFeedback
|
|
|
|
+ {
|
|
|
|
+ className: 'ProjectFeedback',
|
|
|
|
+ fields: [
|
|
|
|
+ { name: 'project', type: 'Pointer', required: true, targetClass: 'Project' },
|
|
|
|
+ { name: 'customer', type: 'Pointer', required: true, targetClass: 'ContactInfo' },
|
|
|
|
+ { name: 'stage', type: 'String', required: true },
|
|
|
|
+ { name: 'feedbackType', type: 'String', required: true },
|
|
|
|
+ { name: 'content', type: 'String', required: true },
|
|
|
|
+ { name: 'rating', type: 'Number', required: false, defaultValue: 0 },
|
|
|
|
+ { name: 'status', type: 'String', required: true, defaultValue: '待处理' },
|
|
|
|
+ { name: 'data', type: 'Object', required: false, defaultValue: {} },
|
|
|
|
+ { name: 'isDeleted', type: 'Boolean', required: false, defaultValue: false }
|
|
|
|
+ ],
|
|
|
|
+ indexes: [
|
|
|
|
+ { name: 'project_status_isDeleted', fields: { project: 1, status: 1, isDeleted: 1 } },
|
|
|
|
+ { name: 'customer_project', fields: { customer: 1, project: 1 } },
|
|
|
|
+ { name: 'feedbackType_stage', fields: { feedbackType: 1, stage: 1 } }
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 产品质量检查表:ProductCheck
|
|
|
|
+ {
|
|
|
|
+ className: 'ProductCheck',
|
|
|
|
+ fields: [
|
|
|
|
+ { name: 'project', type: 'Pointer', required: true, targetClass: 'Project' },
|
|
|
|
+ { name: 'checkType', type: 'String', required: true },
|
|
|
|
+ { name: 'checkedBy', type: 'Pointer', required: true, targetClass: 'Profile' },
|
|
|
|
+ { name: 'checkedAt', type: 'Date', required: true },
|
|
|
|
+ { name: 'isPassed', type: 'Boolean', required: true },
|
|
|
|
+ { name: 'items', type: 'Array', required: false, defaultValue: [] },
|
|
|
|
+ { name: 'data', type: 'Object', required: false, defaultValue: {} },
|
|
|
|
+ { name: 'isDeleted', type: 'Boolean', required: false, defaultValue: false }
|
|
|
|
+ ],
|
|
|
|
+ indexes: [
|
|
|
|
+ { name: 'project_checkType_isDeleted', fields: { project: 1, checkType: 1, isDeleted: 1 } },
|
|
|
|
+ { name: 'checkedBy_isPassed', fields: { checkedBy: 1, isPassed: 1 } }
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 异常记录表:ProjectIssue
|
|
|
|
+ {
|
|
|
|
+ className: 'ProjectIssue',
|
|
|
|
+ fields: [
|
|
|
|
+ { name: 'project', type: 'Pointer', required: true, targetClass: 'Project' },
|
|
|
|
+ { name: 'reportedBy', type: 'Pointer', required: true, targetClass: 'Profile' },
|
|
|
|
+ { name: 'exceptionType', type: 'String', required: true },
|
|
|
|
+ { name: 'severity', type: 'String', required: true },
|
|
|
|
+ { name: 'description', type: 'String', required: true },
|
|
|
|
+ { name: 'status', type: 'String', required: true, defaultValue: '待处理' },
|
|
|
|
+ { name: 'resolution', type: 'String', required: false },
|
|
|
|
+ { name: 'data', type: 'Object', required: false, defaultValue: {} },
|
|
|
|
+ { name: 'isDeleted', type: 'Boolean', required: false, defaultValue: false }
|
|
|
|
+ ],
|
|
|
|
+ indexes: [
|
|
|
|
+ { name: 'project_status_isDeleted', fields: { project: 1, status: 1, isDeleted: 1 } },
|
|
|
|
+ { name: 'exceptionType_severity', fields: { exceptionType: 1, severity: 1 } },
|
|
|
|
+ { name: 'reportedBy_index', fields: { reportedBy: 1 } }
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 跟进记录表:ContactFollow
|
|
|
|
+ {
|
|
|
|
+ className: 'ContactFollow',
|
|
|
|
+ fields: [
|
|
|
|
+ { name: 'project', type: 'Pointer', required: true, targetClass: 'Project' },
|
|
|
|
+ { name: 'profile', type: 'Pointer', required: true, targetClass: 'Profile' },
|
|
|
|
+ { name: 'contact', type: 'Pointer', required: true, targetClass: 'ContactInfo' },
|
|
|
|
+ { name: 'content', type: 'String', required: true },
|
|
|
|
+ { name: 'type', type: 'String', required: true },
|
|
|
|
+ { name: 'stage', type: 'String', required: false },
|
|
|
|
+ { name: 'attachments', type: 'Array', required: false, defaultValue: [] },
|
|
|
|
+ { name: 'data', type: 'Object', required: false, defaultValue: {} },
|
|
|
|
+ { name: 'isDeleted', type: 'Boolean', required: false, defaultValue: false }
|
|
|
|
+ ],
|
|
|
|
+ indexes: [
|
|
|
|
+ { name: 'project_createdAt', fields: { project: 1, createdAt: -1 } },
|
|
|
|
+ { name: 'profile_project', fields: { profile: 1, project: 1 } },
|
|
|
|
+ { name: 'stage_index', fields: { stage: 1 } }
|
|
|
|
+ ]
|
|
|
|
+ }
|
|
|
|
+];
|
|
|
|
+
|
|
|
|
+// 3. 核心工具函数:创建/更新数据表结构
|
|
|
|
+async function setupTableSchema(tableConfig) {
|
|
|
|
+ const { className, fields, indexes } = tableConfig;
|
|
|
|
+ let schema;
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ // 尝试获取已有Schema
|
|
|
|
+ schema = new Parse.Schema(className);
|
|
|
|
+ await schema.get();
|
|
|
|
+ console.log(`✅ 找到已有表 [${className}],开始补充缺失字段和索引`);
|
|
|
|
+ } catch (error) {
|
|
|
|
+ // 表不存在,创建新Schema
|
|
|
|
+ schema = new Parse.Schema(className);
|
|
|
|
+ console.log(`🆕 未找到表 [${className}],开始创建新表`);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 3.1 添加/更新字段
|
|
|
|
+ for (const field of fields) {
|
|
|
|
+ try {
|
|
|
|
+ // 处理Pointer类型(需特殊配置targetClass)
|
|
|
|
+ if (field.type === 'Pointer') {
|
|
|
|
+ schema.addPointer(field.name, field.targetClass);
|
|
|
|
+ }
|
|
|
|
+ // 处理普通类型
|
|
|
|
+ else {
|
|
|
|
+ schema.addField(field.name, field.type);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ console.log(` 📝 配置字段 [${className}.${field.name}] (类型: ${field.type})`);
|
|
|
|
+ } catch (error) {
|
|
|
|
+ // 字段已存在时忽略错误
|
|
|
|
+ if (error.message.includes('Field already exists')) {
|
|
|
|
+ console.log(` ⚠️ 字段 [${className}.${field.name}] 已存在,跳过`);
|
|
|
|
+ } else {
|
|
|
|
+ console.error(` ❌ 配置字段 [${className}.${field.name}] 失败:`, error.message);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 3.2 添加/更新索引
|
|
|
|
+ for (const index of indexes) {
|
|
|
|
+ try {
|
|
|
|
+ schema.addIndex(index.name, index.fields, index.unique || false);
|
|
|
|
+ console.log(` 🔍 配置索引 [${className}.${index.name}] (字段: ${JSON.stringify(index.fields)})`);
|
|
|
|
+ } catch (error) {
|
|
|
|
+ // 索引已存在时忽略错误
|
|
|
|
+ if (error.message.includes('Index already exists')) {
|
|
|
|
+ console.log(` ⚠️ 索引 [${className}.${index.name}] 已存在,跳过`);
|
|
|
|
+ } else {
|
|
|
|
+ console.error(` ❌ 配置索引 [${className}.${index.name}] 失败:`, error.message);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 3.3 保存Schema变更
|
|
|
|
+ try {
|
|
|
|
+ await schema.save();
|
|
|
|
+ console.log(`🎉 表 [${className}] 配置完成\n`);
|
|
|
|
+ } catch (error) {
|
|
|
|
+ console.error(`❌ 保存表 [${className}] 配置失败:`, error.message, '\n');
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// 4. 批量执行所有表的初始化
|
|
|
|
+async function initAllTables() {
|
|
|
|
+ console.log('=====================================================');
|
|
|
|
+ console.log('🚀 开始初始化YSS项目管理系统数据表结构');
|
|
|
|
+ console.log(`📌 Parse Server地址: ${Parse.serverURL}`);
|
|
|
|
+ console.log(`📌 应用ID: ${Parse.applicationId}`);
|
|
|
|
+ console.log('=====================================================\n');
|
|
|
|
+
|
|
|
|
+ // 按依赖顺序执行(先创建基础表,再创建依赖表)
|
|
|
|
+ const dependencyOrder = [
|
|
|
|
+ 'Company', 'Department', 'Profile', 'ContactInfo', // 基础人员表
|
|
|
|
+ 'Project', 'ProjectRequirement', 'ProjectTeam', // 核心项目表
|
|
|
|
+ 'GroupChat', 'ProjectGroup', // 企微关联表
|
|
|
|
+ 'Product', 'ProjectFile', // 交付物/文件表
|
|
|
|
+ 'ProjectSettlement', 'ProjectVoucher', // 财务表
|
|
|
|
+ 'ProjectFeedback', 'ProductCheck', 'ProjectIssue',// 质量反馈表
|
|
|
|
+ 'ContactFollow' // 跟进记录表
|
|
|
|
+ ];
|
|
|
|
+
|
|
|
|
+ // 按依赖顺序处理每个表
|
|
|
|
+ for (const className of dependencyOrder) {
|
|
|
|
+ const tableConfig = tableConfigs.find(config => config.className === className);
|
|
|
|
+ if (tableConfig) {
|
|
|
|
+ await setupTableSchema(tableConfig);
|
|
|
|
+ } else {
|
|
|
|
+ console.error(`❌ 未找到表 [${className}] 的配置,跳过\n`);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ console.log('=====================================================');
|
|
|
|
+ console.log('🏁 所有数据表结构初始化完成!');
|
|
|
|
+ console.log('💡 提示:请检查控制台输出,确认是否有失败的配置项');
|
|
|
|
+ console.log('=====================================================');
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// 5. 执行初始化
|
|
|
|
+initAllTables().catch(error => {
|
|
|
|
+ console.error('💥 初始化过程中发生致命错误:', error.message);
|
|
|
|
+ process.exit(1);
|
|
|
|
+});
|