// 审批单同步到台账

const objectql = require('@steedos/objectql');
const crypto = require('crypto');
const fs = require('fs');
const pathModule = require('path');

// ========== 文件同步辅助函数 ==========

function getStorageConfig() {
    try {
        const config = objectql.getSteedosConfig();
        return {
            store: config?.cfs?.store || 'local',
            storageDir: pathModule.resolve(config?.cfs?.storePath || process.env.STEEDOS_STORAGE_DIR || './steedos-storage')
        };
    } catch (e) {
        return { store: 'local', storageDir: pathModule.resolve('./steedos-storage') };
    }
}

function getCollectionFolderName(collectionName) {
    const match = collectionName.match(/^cfs\.(\w+)\.filerecord$/);
    return match ? match[1] : collectionName;
}

async function readFileBuffer(collectionName, fileRecord) {
    // 优先使用 moleculer broker 读取（支持 local 和 S3）
    try {
        const broker = objectql.getSteedosSchema()?.broker;
        if (broker) {
            const folderName = getCollectionFolderName(collectionName);
            return await broker.call('@builder6/files.getFileById', {
                collectionName: folderName,
                fileId: fileRecord._id
            });
        }
    } catch (e) {
        console.warn(`[syncAttach] broker.call getFileById failed for ${fileRecord._id}: ${e.message}`);
    }

    // 回退：从本地文件系统读取
    const config = getStorageConfig();
    if (config.store !== 'local') {
        console.error(`[syncAttach] Cannot read file ${fileRecord._id}: S3 storage requires broker`);
        return null;
    }

    const folderName = getCollectionFolderName(collectionName);
    const storeKey = fileRecord.copies?.[folderName]?.key;
    if (!storeKey) {
        console.error(`[syncAttach] No storage key found for file ${fileRecord._id}`);
        return null;
    }

    const filePath = pathModule.join(config.storageDir, 'files', folderName, storeKey);
    if (fs.existsSync(filePath)) {
        return fs.readFileSync(filePath);
    }
    console.error(`[syncAttach] File not found on disk: ${filePath}`);
    return null;
}

async function uploadFileToCollection(collectionName, buffer, metadata, originalInfo) {
    const config = getStorageConfig();
    const folderName = getCollectionFolderName(collectionName);
    const fileId = crypto.randomUUID();
    const md5Hash = crypto.createHash('md5').update(buffer).digest('hex');
    const now = new Date();
    const year = now.getFullYear();
    const month = String(now.getMonth() + 1).padStart(2, '0');
    const fileName = originalInfo.name || 'file';
    const objName = metadata.object_name || 'default';
    const key = `${objName}/${year}/${month}/${fileId}-${fileName}`;

    if (config.store === 'local') {
        const filePath = pathModule.join(config.storageDir, 'files', folderName, key);
        fs.mkdirSync(pathModule.dirname(filePath), { recursive: true });
        fs.writeFileSync(filePath, buffer);
    } else {
        // S3: 通过 broker 上传（如果可用）
        try {
            const broker = objectql.getSteedosSchema()?.broker;
            if (broker) {
                // 目前 @builder6/files moleculer 没有 uploadFile action
                // 回退到直接 MongoDB 记录（文件需要通过其他方式上传到 S3）
                console.warn(`[syncAttach] S3 upload not supported via broker, file ${fileId} record created but binary not stored`);
            }
        } catch (e) {
            console.error(`[syncAttach] S3 upload failed for ${fileId}: ${e.message}`);
        }
    }

    const record = {
        _id: fileId,
        original: { type: originalInfo.type, size: buffer.length, name: fileName, md5: md5Hash },
        metadata: { ...metadata },
        uploadedAt: now,
        copies: {
            [folderName]: { name: fileName, type: originalInfo.type, size: buffer.length, key, updatedAt: now, createdAt: now }
        }
    };

    const coll = await getCollection(collectionName);
    await coll.insertOne(record);
    return record;
}

async function getCollection(name) {
    const adapter = objectql.getDataSource('default').adapter
    await adapter.connect()
    return adapter.collection(name)
}

const InstanceRecordQueue = {};

async function init(){
    const adapter = objectql.getDataSource('default').adapter
    await adapter.connect()
    InstanceRecordQueue.collection =  adapter.collection('instance_record_queue')
}

init()

var runQuoted = async function (objectName, recordId) {
    await objectql.runQuotedByObjectFieldFormulas(objectName, recordId);
    await objectql.runQuotedByObjectFieldSummaries(objectName, recordId);
}

const getObjectFields = async function (objectApiName) {
    return await objectql.getObject(objectApiName).getFields()
}

const getObjectNameFieldKey = async function (objectApiName) {
    return await objectql.getObject(objectApiName).getNameFieldKey()
}

const getObjectConfig = async function (objectApiName) {
    return await objectql.getObject(objectApiName).toConfig()
}

const makeNewID = async function (objectApiName) {
    return await objectql.getObject(objectApiName)._makeNewID()
}

const objectInsert = async function (objectApiName, data) {
    return await objectql.getObject(objectApiName).insert(data)
}

const objectUpdate = async function (objectApiName, id, data) {
    return await objectql.getObject(objectApiName).update(id, data);
}

const objectUpdateMany = async function (objectApiName, filters, data) {
    return await objectql.getObject(objectApiName).updateMany(filters, data);
}

const objectRemove = async function (objectApiName, id) {
    return await objectql.getObject(objectApiName).delete(id);
}

const getRelateds = async function (objectApiName) {
    return await objectql.getObject(objectApiName).getRelateds();
}

const objectFindOne = async function (objectApiName, query) {
    const result = await objectql.getObject(objectApiName).find(query);
    if (result && result.length > 0) {
        return result[0];
    }
    return null;
}

const objectFind = async function (objectApiName, query) {
    return await objectql.getObject(objectApiName).find(query);
}

var _eval = require('eval');
var isConfigured = false;

var sendWorker = function (task, interval) {
    if (InstanceRecordQueue.debug) {
        console.log('InstanceRecordQueue: Send worker started, using interval: ' + interval);
    }

    return setInterval(async function () {
        try {
           await task();
        } catch (error) {
            console.log('InstanceRecordQueue: Error while sending: ', error);
        }
    }, interval);
};

const getRelatedObjectField = function (relatedObjectsKeys, key) {
    return _.find(relatedObjectsKeys, function (relatedObjectsKey) {
        return key.startsWith(relatedObjectsKey + '.');
    })
};

const getFormTableField = function (formTableFieldsCode, key) {
    return _.find(formTableFieldsCode, function (formTableFieldCode) {
        return key.startsWith(formTableFieldCode + '.');
    })
};

const getFormField = function (_formFields, _fieldCode) {
    var formField = null;
    _.each(_formFields, function (ff) {
        if (!formField) {
            if (ff.code === _fieldCode) {
                formField = ff;
            } else if (ff.type === 'section') {
                _.each(ff.fields, function (f) {
                    if (!formField) {
                        if (f.code === _fieldCode) {
                            formField = f;
                        }
                    }
                })
            } else if (ff.type === 'table') {
                _.each(ff.fields, function (f) {
                    if (!formField) {
                        if (f.code === _fieldCode) {
                            formField = f;
                        }
                    }
                })
            }
        }
    });
    return formField;
}

const getFileFieldValue = async function (recordFieldId, fType) {
    if (_.isEmpty(recordFieldId)) {
        return;
    }

    var targetCollection;
    if (fType === 'image') {
        targetCollection = 'cfs.images.filerecord';
    } else if (fType === 'file') {
        targetCollection = 'cfs.files.filerecord';
    } else {
        return;
    }

    var ids = _.isString(recordFieldId) ? [recordFieldId] : recordFieldId;

    // 源文件在 cfs.instances.filerecord 中（workflow 上传的附件）
    var sourceCollection = 'cfs.instances.filerecord';
    var sourceColl = await getCollection(sourceCollection);
    var files = await sourceColl.find({ _id: { $in: ids } }).toArray();

    // 如果在 instances 中找不到，尝试从目标集合中查找（兼容旧数据）
    if (files.length === 0) {
        var targetColl = await getCollection(targetCollection);
        files = await targetColl.find({ _id: { $in: ids } }).toArray();
        if (files.length > 0) {
            sourceCollection = targetCollection;
        }
    }

    var value = [];
    for (const f of files) {
        try {
            var buffer = await readFileBuffer(sourceCollection, f);
            if (!buffer) {
                console.error(`[getFileFieldValue] Cannot read file ${f._id}`);
                continue;
            }

            var newRecord = await uploadFileToCollection(targetCollection, buffer, {
                owner: f.metadata?.owner
            }, {
                name: f.original?.name,
                type: f.original?.type
            });

            value.push(newRecord._id);
        } catch (err) {
            console.error(`[getFileFieldValue] Error copying file ${f._id}: ${err.message}`);
        }
    }

    if (value.length > 0) {
        if (_.isString(recordFieldId)) {
            return value[0];
        } else {
            return value;
        }
    }
};

const getRecordFieldValue = async function (oField, wField, ofValue, wfValue, spaceId, enableAmisform) {
    if (enableAmisform && wField.steedos_field) {
        return wfValue
    }
    let value;
    var oFieldType = oField.data_type || oField.type;
    // 表单选人选组字段 至 对象 lookup master_detail类型字段同步
    if (['lookup', 'master_detail'].includes(oFieldType) && !_.isEmpty(wfValue)) {
        // 检查是否是表单的 member/memberMulti 类型字段（对应对象的 users reference_to）
        if ((wField.type === 'user' || wField.code === 'member' || wField.code === 'memberMulti') &&
            ['users', 'space_users'].includes(oField.reference_to)) {
            let referToField = oField.reference_to_field || '_id'

            // 判断是否是新对象格式：{id, name, ...} 或 [{id, name, ...}]
            let isNewObjectFormat = false;
            if (Array.isArray(wfValue)) {
                // 数组格式：检查第一个元素是否有 id 属性
                isNewObjectFormat = wfValue.length > 0 && typeof wfValue[0] === 'object' && wfValue[0] !== null && wfValue[0].id;
            } else if (typeof wfValue === 'object' && wfValue !== null) {
                // 对象格式：检查是否有 id 属性（不是 _id）
                isNewObjectFormat = wfValue.id || wfValue._id;
            }

            if (isNewObjectFormat) {
                // 处理新对象格式：{id, name, sort_no, organization, organizations, hr, roles}
                if (Array.isArray(wfValue)) {
                    // 多选：数组对象格式
                    if (oField.multiple) {
                        value = _.compact(_.pluck(wfValue, wfValue[0]?.id ? 'id' : '_id'))
                    } else {
                        // 单选但是数组：取第一个
                        value = wfValue.length > 0 ? (wfValue[0].id || wfValue[0]._id) : null
                    }
                } else {
                    // 单选：对象格式
                    const idValue = wfValue.id || wfValue._id;
                    if (oField.multiple) {
                        // 多选但是单个对象：包装成数组
                        value = idValue ? [idValue] : null;
                    } else {
                        // 单选对象
                        value = referToField === '_id' && wfValue._id ? wfValue._id : (referToField === '_id' ? wfValue.id : wfValue[referToField])
                    }
                }
            }
            // 处理字符串格式：直接的 ID 或 ID 字符串（和 lookup 字段一样的值）
            else if (typeof wfValue === 'string') {
                // 字符串：可能是单个 ID 或逗号分隔的 ID 列表
                if (oField.multiple) {
                    // 多选：返回原字符串或转换为数组
                    value = wfValue.includes(',') ? wfValue.split(',').map(v => v.trim()) : wfValue;
                } else {
                    // 单选：返回第一个 ID
                    value = wfValue.includes(',') ? wfValue.split(',')[0].trim() : wfValue;
                }
            }
            // 处理数组格式：字符串数组
            else if (Array.isArray(wfValue)) {
                // 字符串数组：["user1", "user2"]
                const stringArray = wfValue.filter(v => typeof v === 'string');
                if (stringArray.length > 0) {
                    if (oField.multiple) {
                        value = stringArray;
                    } else {
                        value = stringArray[0];
                    }
                }
            }
        }
        // 检查是否是表单的 org/orgMulti 类型字段（对应对象的 organizations reference_to）
        else if ((wField.type === 'group' || wField.code === 'org' || wField.code === 'orgMulti') &&
                 oField.reference_to === 'organizations') {
            let referToField = oField.reference_to_field || '_id'

            // 判断是否是新对象格式：{id, name, fullname} 或 [{id, name, fullname}]
            let isNewObjectFormat = false;
            if (Array.isArray(wfValue)) {
                // 数组格式：检查第一个元素是否有 id 属性
                isNewObjectFormat = wfValue.length > 0 && typeof wfValue[0] === 'object' && wfValue[0] !== null && wfValue[0].id;
            } else if (typeof wfValue === 'object' && wfValue !== null) {
                // 对象格式：检查是否有 id 属性（不是 _id）
                isNewObjectFormat = wfValue.id || wfValue._id;
            }

            if (isNewObjectFormat) {
                // 处理新对象格式：{id, name, fullname}
                if (Array.isArray(wfValue)) {
                    // 多选：数组对象格式
                    if (oField.multiple) {
                        value = _.compact(_.pluck(wfValue, wfValue[0]?.id ? 'id' : '_id'))
                    } else {
                        // 单选但是数组：取第一个
                        value = wfValue.length > 0 ? (wfValue[0].id || wfValue[0]._id) : null
                    }
                } else {
                    // 单选：对象格式
                    const idValue = wfValue.id || wfValue._id;
                    if (oField.multiple) {
                        // 多选但是单个对象：包装成数组
                        value = idValue ? [idValue] : null;
                    } else {
                        // 单选对象
                        value = referToField === '_id' && wfValue._id ? wfValue._id : (referToField === '_id' ? wfValue.id : wfValue[referToField])
                    }
                }
            }
            // 处理字符串格式：直接的 ID 或 ID 字符串（和 lookup 字段一样的值）
            else if (typeof wfValue === 'string') {
                // 字符串：可能是单个 ID 或逗号分隔的 ID 列表
                if (oField.multiple) {
                    // 多选：返回原字符串或转换为数组
                    value = wfValue.includes(',') ? wfValue.split(',').map(v => v.trim()) : wfValue;
                } else {
                    // 单选：返回第一个 ID
                    value = wfValue.includes(',') ? wfValue.split(',')[0].trim() : wfValue;
                }
            }
            // 处理数组格式：字符串数组
            else if (Array.isArray(wfValue)) {
                // 字符串数组：["org1", "org2"]
                const stringArray = wfValue.filter(v => typeof v === 'string');
                if (stringArray.length > 0) {
                    if (oField.multiple) {
                        value = stringArray;
                    } else {
                        value = stringArray[0];
                    }
                }
            }
        }
        // 通用处理：lookup/master_detail 字段，wfValue 为对象格式 {id, name, ...} 或对象数组
        // 适用于部门等字段类型，当 reference_to 不是 organizations 或 users 时
        if (value === undefined || value === null) {
            let referToField = oField.reference_to_field || '_id'
            if (Array.isArray(wfValue)) {
                if (wfValue.length > 0 && typeof wfValue[0] === 'object' && wfValue[0] !== null && (wfValue[0].id || wfValue[0]._id)) {
                    if (oField.multiple) {
                        value = _.compact(wfValue.map(v => v.id || v._id))
                    } else {
                        value = wfValue[0].id || wfValue[0]._id
                    }
                }
            } else if (typeof wfValue === 'object' && wfValue !== null && (wfValue.id || wfValue._id)) {
                const idValue = wfValue.id || wfValue._id;
                if (oField.multiple) {
                    value = [idValue];
                } else {
                    value = referToField === '_id' ? (wfValue._id || wfValue.id) : (wfValue[referToField] || idValue);
                }
            }
        }
    }
    else if (!oField.multiple && ['lookup', 'master_detail'].includes(oFieldType) && _.isString(oField.reference_to) && _.isString(wfValue)) {
        // 当是对象类型的 member/org 字段时，应该已在上面处理了，这里只处理字符串值
        var oCollection = objectql.getObject(oField.reference_to)
        var referObjectNameFieldKey = await getObjectNameFieldKey(oField.reference_to);
        if (oCollection && referObjectNameFieldKey) {
            // 先认为此值是referObject _id字段值
            let referToField = oField.reference_to_field || '_id'
            var referData = await objectFindOne(oField.reference_to, { filters: [[referToField, '=', wfValue]], fields: [referToField] });
            if (referData) {
                value = referData[referToField];
            }

            // 其次认为此值是referObject NAME_FIELD_KEY值
            if (!referData) {
                var nameFieldKey = referObjectNameFieldKey;
                var referData = await objectFindOne(oField.reference_to, { filters: [['space', '=', spaceId], [nameFieldKey, '=', wfValue]], fields: ['_id'] });
                if (referData) {
                    value = referData._id;
                }
            }
        }
    }
    else if (oFieldType === "boolean") {
        var tmp_field_value = wfValue;
        if (['true', '是'].includes(tmp_field_value)) {
            value = true;
        } else if (['false', '否'].includes(tmp_field_value)) {
            value = false;
        } else {
            value = tmp_field_value;
        }
    }
    else if (oFieldType === 'number' || oFieldType === 'currency' || oFieldType === 'percent') {
        if (typeof (Number(wfValue)) === 'number' && !isNaN(Number(wfValue))) {
            value = Number(wfValue);
        }
    }
    else if (['lookup', 'master_detail'].includes(oFieldType) && wField.type === 'odata') {
        let referToField = oField.reference_to_field || '_id'
        if (oField.multiple && wField.is_multiselect) {
            value = _.compact(_.pluck(wfValue, referToField))
        } else if (!oField.multiple && !wField.is_multiselect) {
            if (!_.isEmpty(wfValue)) {
                value = wfValue[referToField]
            }
        } else {
            value = wfValue;
        }
    } else if (oFieldType == 'image' && wField.type == 'image') {
        var ids = ofValue;
        if (_.isString(ids)) {
            ids = [ids];
        }
        if (ids && ids.length > 0) {
            var imgColl = await getCollection('cfs.images.filerecord');
            await imgColl.deleteMany({ '_id': { $in: ids } });
        }

        value = await getFileFieldValue(wfValue, 'image')

    } else if (oFieldType == 'file' && wField.type == 'file') {
        var ids = ofValue;
        if (_.isString(ids)) {
            ids = [ids];
        }
        if (ids && ids.length > 0) {
            var fileColl = await getCollection('cfs.files.filerecord');
            await fileColl.deleteMany({ '_id': { $in: ids } });
        }
        value = await getFileFieldValue(wfValue, 'file')
    } else if (['lookup', 'master_detail'].includes(oFieldType) && wField.type == 'lookup') {
        value = wfValue ? wfValue : null
    }
    // 日期、日期时间
    else if (oFieldType === 'date' || oFieldType === 'datetime' || oFieldType === 'time') {
        if (wfValue && _.isDate(new Date(wfValue))) {
            value = new Date(wfValue);
        }
    }
    else if (oField.multiple && oFieldType == 'select' && wField.type == 'multiSelect') {
        value = wfValue ? wfValue.split(',') : null;
    }
    else {
        value = wfValue;
    }
    return value;
};

const updateLookupOrMasterDetailFieldValues = async function (field_map_back, values, ins, objectInfo, record) {
    var field_map_back = field_map_back || [];
    var objectFields = objectInfo.fields;

    for (const fm of field_map_back) {
        if (values.hasOwnProperty(fm.workflow_field)) {
            if (fm.object_field.indexOf('.') > -1) {
                var temObjFields = fm.object_field.split('.');
                if (temObjFields.length === 2) {
                    var objField = temObjFields[0];
                    var referObjField = temObjFields[1];
                    var oField = objectFields[objField];
                    if (oField && !oField.multiple && ['lookup', 'master_detail'].includes(oField.type) && _.isString(oField.reference_to)) {
                        var oCollection = objectql.getObject(oField.reference_to)
                        if (oCollection && record && record[objField]) {
                            var referSetObj = {};
                            referSetObj[referObjField] = values[fm.workflow_field];
                            await objectUpdate(oField.reference_to, record[objField], referSetObj)
                        }
                    }
                }
            }
        } else if (fm.workflow_field.startsWith('instance.')) {
            var insField = fm.workflow_field.split('instance.')[1];
            if (InstanceRecordQueue.syncInsFields.includes(insField)) {
                if (fm.object_field.indexOf('.') > -1) {
                    var temObjFields = fm.object_field.split('.');
                    if (temObjFields.length === 2) {
                        var objField = temObjFields[0];
                        var referObjField = temObjFields[1];
                        var oField = objectFields[objField];
                        if (oField && !oField.multiple && ['lookup', 'master_detail'].includes(oField.type) && _.isString(oField.reference_to)) {
                            var oCollection = objectql.getObject(oField.reference_to)
                            if (oCollection && record && record[objField]) {
                                var referSetObj = {};
                                referSetObj[referObjField] = ins[insField];
                                await objectUpdate(oField.reference_to, record[objField], referSetObj)
                            }
                        }
                    }
                }
            }
        }
    }
}

InstanceRecordQueue.Configure = function (options) {
    var self = this;
    options = _.extend({
        sendTimeout: 60000, // Timeout period
    }, options);

    // Block multiple calls
    if (isConfigured) {
        throw new Error('InstanceRecordQueue.Configure should not be called more than once!');
    }

    isConfigured = true;

    // Add debug info
    if (InstanceRecordQueue.debug) {
        console.log('InstanceRecordQueue.Configure', options);
    }

    // Universal send function
    var _querySend = async function (doc) {
        if (InstanceRecordQueue.sendDoc) {
            try {
                await InstanceRecordQueue.sendDoc(doc);
            } catch (error) {
                console.error(`${new Date().toISOString()} [InstanceRecordQueue._querySend] sendDoc 异常: docId=${doc._id}, error=${error.message}`);
                throw error;
            }
        } else {
            console.warn(`[InstanceRecordQueue._querySend] InstanceRecordQueue.sendDoc 未定义! docId=${doc._id}`);
        }

        return {
            doc: [doc._id]
        };
    };

    self.serverSend = async function (doc) {
        doc = doc || {};
        return await _querySend(doc);
    };

    var isSendingDoc = false;

    if (options.sendInterval !== null) {
        var sendDoc = async function (doc) {
            // Reserve doc
            var now = +new Date();
            var timeoutAt = now + options.sendTimeout;
            var reserved = await InstanceRecordQueue.collection.update({
                _id: doc._id,
                sent: false, // xxx: need to make sure this is set on create
                sending: {
                    $lt: now
                }
            }, {
                $set: {
                    sending: timeoutAt,
                }
            });

            // Make sure we only handle docs reserved by this instance
            if (reserved) {
                // Send
                var result = await self.serverSend(doc);

                if (!options.keepDocs) {
                    // Pr. Default we will remove docs
                    await InstanceRecordQueue.collection.remove({
                        _id: doc._id
                    });
                } else {
                    // Update
                    await InstanceRecordQueue.collection.update({
                        _id: doc._id
                    }, {
                        $set: {
                            // Mark as sent
                            sent: true,
                            // Set the sent date
                            sentAt: new Date(),
                            // Not being sent anymore
                            sending: 0
                        }
                    });
                }
            } else {
                console.warn(`[InstanceRecordQueue.sendDoc-queue] 文档 reserve 失败（可能被其他实例处理）: docId=${doc._id}`);
            } // Else could not reserve
        }; // EO sendDoc

        sendWorker(async function () {
            if (isSendingDoc) {
                return;
            }
            // Set send fence
            isSendingDoc = true;

            var batchSize = options.sendBatchSize || 1;

            var now = +new Date();

            // Find docs that are not being or already sent
            var pendingDocs = await InstanceRecordQueue.collection.find({
                $and: [
                    // Message is not sent
                    {
                        sent: false
                    },
                    // And not being sent by other instances
                    {
                        sending: {
                            $lt: now
                        }
                    },
                    // And no error
                    {
                        errMsg: {
                            $exists: false
                        }
                    }
                ]
            }, {
                // Sort by created date
                sort: {
                    createdAt: 1
                },
                limit: batchSize
            }).toArray();

            for (const doc of pendingDocs) {
                try {
                    console.log(`${new Date().toISOString()} [InstanceRecordQueue.sendWorker] 处理文档: docId=${doc._id}, insId=${doc.info.instance_id}`);
                    await sendDoc(doc);
                } catch (error) {
                    console.error(`${new Date().toISOString()} [InstanceRecordQueue.sendWorker] 文档处理异常: docId=${doc._id}`);
                    console.error(error.stack);
                    console.log('InstanceRecordQueue: Could not send doc id: "' + doc._id + '", Error: ' + error.message);
                    await InstanceRecordQueue.collection.update({
                        _id: doc._id
                    }, {
                        $set: {
                            // error message
                            errMsg: error.message
                        }
                    });
                }
            }

            // Remove the send fence
            isSendingDoc = false;
        }, options.sendInterval || 15000); // Default every 15th sec
    } else {
        if (InstanceRecordQueue.debug) {
            console.log('InstanceRecordQueue: Send server is disabled');
        }
    }
};

InstanceRecordQueue.syncAttach = async function (sync_attachment, insId, spaceId, newRecordId, objectName) {
    if (!sync_attachment || sync_attachment === 'null') {
        return;
    }

    console.log(`[syncAttach] Starting: sync_attachment=${sync_attachment}, insId=${insId}, recordId=${newRecordId}, objectName=${objectName}`);

    var instanceFilesColl = await getCollection('cfs.instances.filerecord');

    if (sync_attachment === 'lastest') {
        var files = await instanceFilesColl.find({
            'metadata.instance': insId,
            'metadata.current': true
        }).toArray();

        console.log(`[syncAttach] Found ${files.length} current files for instance ${insId}`);

        for (const f of files) {
            try {
                var buffer = await readFileBuffer('cfs.instances.filerecord', f);
                if (!buffer) {
                    console.error(`[syncAttach] Cannot read file content: ${f._id}`);
                    continue;
                }

                var cmsFileId = crypto.randomUUID();

                var newFileRecord = await uploadFileToCollection('cfs.files.filerecord', buffer, {
                    owner: f.metadata.owner,
                    owner_name: f.metadata.owner_name,
                    space: spaceId,
                    record_id: newRecordId,
                    object_name: objectName,
                    parent: cmsFileId
                }, {
                    name: f.original?.name,
                    type: f.original?.type
                });

                var extension = f.original?.name ? f.original.name.split('.').pop() : '';
                var cmsColl = await getCollection('cms_files');
                await cmsColl.insertOne({
                    _id: cmsFileId,
                    parent: {
                        o: objectName,
                        ids: [newRecordId]
                    },
                    size: buffer.length,
                    name: f.original?.name,
                    extention: extension,
                    space: spaceId,
                    versions: [newFileRecord._id],
                    owner: f.metadata.owner,
                    created_by: f.metadata.owner,
                    modified_by: f.metadata.owner,
                    created: new Date(),
                    modified: new Date()
                });

                console.log(`[syncAttach] Synced file ${f._id} -> cfs.files ${newFileRecord._id}, cms_files ${cmsFileId}`);
            } catch (err) {
                console.error(`[syncAttach] Error syncing file ${f._id}: ${err.message}`);
            }
        }
    } else if (sync_attachment === 'all') {
        var files = await instanceFilesColl.find({
            'metadata.instance': insId
        }).toArray();

        console.log(`[syncAttach] Found ${files.length} total files for instance ${insId}`);

        var parentMap = {}; // metadata.parent -> cmsFileId

        for (const f of files) {
            try {
                var buffer = await readFileBuffer('cfs.instances.filerecord', f);
                if (!buffer) {
                    console.error(`[syncAttach] Cannot read file content: ${f._id}`);
                    continue;
                }

                var fileParent = f.metadata.parent || f._id;
                var cmsFileId = parentMap[fileParent];

                // 如果该 parent 组还没有创建 cms_files 记录，先创建
                if (!cmsFileId) {
                    cmsFileId = crypto.randomUUID();
                    parentMap[fileParent] = cmsFileId;

                    var cmsColl = await getCollection('cms_files');
                    await cmsColl.insertOne({
                        _id: cmsFileId,
                        parent: {
                            o: objectName,
                            ids: [newRecordId]
                        },
                        space: spaceId,
                        versions: [],
                        owner: f.metadata.owner,
                        created_by: f.metadata.owner,
                        modified_by: f.metadata.owner,
                        created: new Date(),
                        modified: new Date()
                    });
                }

                var newFileRecord = await uploadFileToCollection('cfs.files.filerecord', buffer, {
                    owner: f.metadata.owner,
                    owner_name: f.metadata.owner_name,
                    space: spaceId,
                    record_id: newRecordId,
                    object_name: objectName,
                    parent: cmsFileId
                }, {
                    name: f.original?.name,
                    type: f.original?.type
                });

                // 更新 cms_files：添加版本，如果是 current 则更新名称等信息
                var cmsColl = await getCollection('cms_files');
                if (f.metadata.current === true) {
                    var extension = f.original?.name ? f.original.name.split('.').pop() : '';
                    await cmsColl.updateOne({ _id: cmsFileId }, {
                        $set: {
                            size: buffer.length,
                            name: f.original?.name,
                            extention: extension,
                            modified: new Date()
                        },
                        $addToSet: {
                            versions: newFileRecord._id
                        }
                    });
                } else {
                    await cmsColl.updateOne({ _id: cmsFileId }, {
                        $addToSet: {
                            versions: newFileRecord._id
                        },
                        $set: {
                            modified: new Date()
                        }
                    });
                }

                console.log(`[syncAttach] Synced file ${f._id} -> cfs.files ${newFileRecord._id}, cms_files ${cmsFileId} (current=${f.metadata.current})`);
            } catch (err) {
                console.error(`[syncAttach] Error syncing file ${f._id}: ${err.message}`);
            }
        }
    }

    console.log(`[syncAttach] Completed for instance ${insId}`);
}

InstanceRecordQueue.syncInsFields = ['name', 'submitter_name', 'applicant_name', 'applicant_organization_name', 'applicant_organization_fullname', 'state',
    'current_step_name', 'flow_name', 'category_name', 'submit_date', 'finish_date', 'final_decision', 'applicant_organization', 'applicant_company'
];

InstanceRecordQueue.syncValues = async function (field_map_back, values, ins, objectInfo, field_map_back_script, record, enableAmisform) {
    var
        obj = {},
        tableFieldCodes = [],
        tableFieldMap = [],
        tableToRelatedMap = {};

    field_map_back = field_map_back || [];

    var spaceId = ins.space;

    var form = await objectql.getObject("forms").findOne(ins.form);
    var formFields = null;
    if (form.current._id === ins.form_version) {
        formFields = form.current.fields || [];
    } else {
        var formVersion = _.find(form.historys, function (h) {
            return h._id === ins.form_version;
        })
        formFields = formVersion ? formVersion.fields : [];
    }

    var objectFields = objectInfo.fields;
    var objectFieldKeys = _.keys(objectFields);
    var relatedObjects = await getRelateds(objectInfo.name);
    var relatedObjectsKeys = _.pluck(relatedObjects, 'object_name');
    var formTableFields = _.filter(formFields, function (formField) {
        return formField.type === 'table'
    });
    var formTableFieldsCode = _.pluck(formTableFields, 'code');

    for (const fm of field_map_back) {
        //workflow 的子表到creator object 的相关对象
        var relatedObjectField = getRelatedObjectField(relatedObjectsKeys, fm.object_field);
        var formTableField = getFormTableField(formTableFieldsCode, fm.workflow_field);
        if (relatedObjectField) {
            var oTableCode = fm.object_field.split('.')[0];
            var oTableFieldCode = fm.object_field.split('.')[1];
            var tableToRelatedMapKey = oTableCode;
            if (!tableToRelatedMap[tableToRelatedMapKey]) {
                tableToRelatedMap[tableToRelatedMapKey] = {}
            }

            if (formTableField) {
                var wTableCode = fm.workflow_field.split('.')[0];
                tableToRelatedMap[tableToRelatedMapKey]['_FROM_TABLE_CODE'] = wTableCode
            }

            tableToRelatedMap[tableToRelatedMapKey][oTableFieldCode] = fm.workflow_field
        }
        // 判断是否是子表字段
        else if (fm.workflow_field.indexOf('.$.') > 0 && fm.object_field.indexOf('.$.') > 0) {
            var wTableCode = fm.workflow_field.split('.$.')[0];
            var oTableCode = fm.object_field.split('.$.')[0];
            if (values.hasOwnProperty(wTableCode) && _.isArray(values[wTableCode])) {
                tableFieldCodes.push(JSON.stringify({
                    workflow_table_field_code: wTableCode,
                    object_table_field_code: oTableCode
                }));
                tableFieldMap.push(fm);
            }
        }
        else if (values.hasOwnProperty(fm.workflow_field)) {
            var wField = null;

            _.each(formFields, function (ff) {
                if (!wField) {
                    if (ff.code === fm.workflow_field) {
                        wField = ff;
                    } else if (ff.type === 'section') {
                        _.each(ff.fields, function (f) {
                            if (!wField) {
                                if (f.code === fm.workflow_field) {
                                    wField = f;
                                }
                            }
                        })
                    }
                }
            })

            var oField = objectFields[fm.object_field];

            if (oField) {
                if (!wField) {
                    console.log('fm.workflow_field: ', fm.workflow_field)
                }
                const ofValue = record ? record[fm.object_field] : null
                const wfValue = values[fm.workflow_field]
                obj[fm.object_field] = await getRecordFieldValue(oField, wField, ofValue, wfValue, spaceId, enableAmisform)
            } else {
                if (fm.object_field.indexOf('.') > -1) {
                    var temObjFields = fm.object_field.split('.');
                    if (temObjFields.length === 2) {
                        var objField = temObjFields[0];
                        var referObjField = temObjFields[1];
                        var oField = objectFields[objField];
                        if (!oField.multiple && ['lookup', 'master_detail'].includes(oField.type) && _.isString(oField.reference_to)) {
                            var oCollection = objectql.getObject(oField.reference_to)
                            if (oCollection && record && record[objField]) {
                                var referSetObj = {};
                                referSetObj[referObjField] = values[fm.workflow_field];
                                await objectUpdate(oField.reference_to, record[objField], referSetObj)
                            }
                        }
                    }
                }
            }
        }
        else {
            if (fm.workflow_field.startsWith('instance.')) {
                var insField = fm.workflow_field.split('instance.')[1];
                if (InstanceRecordQueue.syncInsFields.includes(insField)) {
                    if (fm.object_field.indexOf('.') < 0) {
                        obj[fm.object_field] = ins[insField];
                    } else {
                        var temObjFields = fm.object_field.split('.');
                        if (temObjFields.length === 2) {
                            var objField = temObjFields[0];
                            var referObjField = temObjFields[1];
                            var oField = objectFields[objField];
                            if (!oField.multiple && ['lookup', 'master_detail'].includes(oField.type) && _.isString(oField.reference_to)) {
                                var oCollection = objectql.getObject(oField.reference_to)
                                if (oCollection && record && record[objField]) {
                                    var referSetObj = {};
                                    referSetObj[referObjField] = ins[insField];
                                    await objectUpdate(oField.reference_to, record[objField], referSetObj)
                                }
                            }
                        }
                    }
                }
            } else {
                if (ins[fm.workflow_field]) {
                    obj[fm.object_field] = ins[fm.workflow_field];
                }
            }
        }
    }

    for (const tfc of _.uniq(tableFieldCodes)) {
        var c = JSON.parse(tfc);
        obj[c.object_table_field_code] = [];
        const tableFields = objectFields[c.object_table_field_code] && objectFields[c.object_table_field_code].children
            ? objectFields[c.object_table_field_code].children
            : [];

        for (const tr of values[c.workflow_table_field_code]) {
            var newTr = {};
            for (const [k, v] of Object.entries(tr)) {
                var matched = false;
                for (const tfm of tableFieldMap) {
                    if (tfm.workflow_field == (c.workflow_table_field_code + '.$.' + k)) {
                        var oTdCode = tfm.object_field.split('.$.')[1];
                        var oField = _.find(tableFields, f => f.name === oTdCode);
                        var wField = getFormField(formFields, k);

                        // 调用 getRecordFieldValue 进行字段值转换，而不是直接赋值
                        if (oField && wField) {
                            newTr[oTdCode] = await getRecordFieldValue(oField, wField, null, v, spaceId, enableAmisform);
                        } else {
                            newTr[oTdCode] = v;
                        }
                        matched = true;
                        break;
                    }
                }
            }
            if (!_.isEmpty(newTr)) {
                obj[c.object_table_field_code].push(newTr);
            }
        }
    }

    var relatedObjs = {};
    var getRelatedFieldValue = function (valueKey, parent) {
        return valueKey.split('.').reduce(function (o, x) {
            return o[x];
        }, parent);
    };

    for (const [key, map] of Object.entries(tableToRelatedMap)) {
        var tableCode = map._FROM_TABLE_CODE;
        if (!tableCode) {
            console.warn('tableToRelated: [' + key + '] missing corresponding table.')
        } else {
            var relatedObjectName = key;
            var relatedObjectValues = [];
            var relatedObjectFields = await getObjectFields(relatedObjectName)
            if (!values[tableCode] || !Array.isArray(values[tableCode])) {
                console.warn(`[InstanceRecordQueue.syncValues] tableToRelated: values['${tableCode}'] is empty or not an array, skipping related object '${relatedObjectName}'`);
                continue;
            }
            for (const tableValueItem of values[tableCode]) {
                let relateRecord;
                if (tableValueItem._id) {
                    relateRecord = await objectFindOne(relatedObjectName, { filters: [['_id', '=', tableValueItem._id]] });
                }
                var relatedObjectValue = {};
                for (const [fieldKey, valueKey] of Object.entries(map)) {
                    if (fieldKey != '_FROM_TABLE_CODE') {
                        if (valueKey.startsWith('instance.')) {
                            relatedObjectValue[fieldKey] = getRelatedFieldValue(valueKey, { 'instance': ins });
                        }
                        else {
                            var relatedObjectFieldValue, formFieldKey;
                            if (valueKey.startsWith(tableCode + '.')) {
                                formFieldKey = valueKey.split(".")[1];
                                relatedObjectFieldValue = getRelatedFieldValue(valueKey, { [tableCode]: tableValueItem });
                            } else {
                                formFieldKey = valueKey;
                                relatedObjectFieldValue = getRelatedFieldValue(valueKey, values)
                            }
                            var formField = getFormField(formFields, formFieldKey);
                            var relatedObjectField = relatedObjectFields[fieldKey];
                            if (!relatedObjectField || !formField) {
                                continue;
                            }
                            const ofValue = relateRecord ? relateRecord[fieldKey] : null;
                            relatedObjectValue[fieldKey] = await getRecordFieldValue(relatedObjectField, formField, ofValue, relatedObjectFieldValue, spaceId, enableAmisform);
                        }
                    }
                }
                relatedObjectValue['_table'] = {
                    _id: tableValueItem["_id"],
                    _code: tableCode
                };
                relatedObjectValues.push(relatedObjectValue);
            }
            relatedObjs[relatedObjectName] = relatedObjectValues;
        }
    }

    if (field_map_back_script) {
        _.extend(obj, InstanceRecordQueue.evalFieldMapBackScript(field_map_back_script, ins));
    }
    // 过滤掉非法的key
    var filterObj = {};

    _.each(_.keys(obj), function (k) {
        if (objectFieldKeys.includes(k)) {
            filterObj[k] = obj[k];
        }
    })

    return {
        mainObjectValue: filterObj,
        relatedObjectsValue: relatedObjs
    };
}

InstanceRecordQueue.evalFieldMapBackScript = function (field_map_back_script, ins) {
    var script = "module.exports = function (instance) { " + field_map_back_script + " }";
    var func = _eval(script, "field_map_script");
    var values = func(ins);
    if (_.isObject(values)) {
        return values;
    } else {
        console.error("evalFieldMapBackScript: 脚本返回值类型不是对象");
    }
    return {}
}

InstanceRecordQueue.syncRelatedObjectsValue = async function (mainRecordId, relatedObjects, relatedObjectsValue, spaceId, ins, locked = false) {
    var insId = ins._id;

    for (const relatedObject of relatedObjects) {
        var tableMap = {};
        for (const relatedObjectValue of (relatedObjectsValue[relatedObject.object_name] || [])) {
            var table_id = relatedObjectValue._table._id;
            var table_code = relatedObjectValue._table._code;
            if (!tableMap[table_code]) {
                tableMap[table_code] = []
            };
            tableMap[table_code].push(table_id);
            var oldRelatedRecord = await objectFindOne(relatedObject.object_name, {
                filters: [
                    [relatedObject.foreign_key, '=', mainRecordId], 
                    [['_id', '=', relatedObjectValue._table._id], 'or', [['_table._id', '=', relatedObjectValue._table._id], ['_table._code', '=', relatedObjectValue._table._code]]]
                ]
            })
            if (oldRelatedRecord) {
                await objectUpdate(relatedObject.object_name, oldRelatedRecord._id, relatedObjectValue)
            } else {
                relatedObjectValue[relatedObject.foreign_key] = mainRecordId;
                relatedObjectValue.space = spaceId;
                relatedObjectValue.owner = relatedObjectValue.owner || ins.applicant;
                relatedObjectValue.created_by = ins.applicant;
                relatedObjectValue.modified_by = ins.applicant;
                relatedObjectValue._id = await makeNewID(relatedObject.object_name);
                relatedObjectValue.locked = locked
                await objectInsert(relatedObject.object_name, relatedObjectValue)
            }
        }
        //清理申请单上被删除子表记录对应的相关表记录
        for (let [tableCode, tableIds] of Object.entries(tableMap)) {
            tableIds = _.compact(tableIds);
            var docsForRemove = await objectFind(relatedObject.object_name, { filters: [[relatedObject.foreign_key, '=', mainRecordId], ['_table._code', '=', tableCode]] });
            for (const doc of docsForRemove) {
                if (!tableIds.includes(doc._table._id)) {
                    await objectRemove(relatedObject.object_name, doc._id)
                }
            }
        }
    }
}

InstanceRecordQueue.sendDoc = async function (doc) {
    const startTime = new Date();
    const docId = doc._id;
    const insId = doc.info.instance_id;

    console.log(`${new Date().toISOString()} [InstanceRecordQueue.sendDoc] 开始处理: insId=${insId}`);

    if (InstanceRecordQueue.debug) {
        console.log("sendDoc:");
        console.log(JSON.stringify(doc, null, 2));
    }

    var records = doc.info.records;

    var fields = {
        flow: 1,
        values: 1,
        applicant: 1,
        space: 1,
        form: 1,
        form_version: 1,
        traces: 1,
        state: 1,
        name: 1,
        final_decision: 1
    };
    InstanceRecordQueue.syncInsFields.forEach(function (f) {
        fields[f] = 1;
    })

    var ins = await objectql.getObject('instances').findOne(insId, {
        fields: fields
    })

    if (!ins) {
        console.error(`${new Date().toISOString()} [InstanceRecordQueue.sendDoc] 实例不存在: insId=${insId}`);
        throw new Error(`Instance not found: ${insId}`);
    }

    var values = ins.values,
        spaceId = ins.space;

    if (!values) {
        console.warn(`[InstanceRecordQueue.sendDoc] 实例没有values: insId=${insId}`);
        values = {};
    }

    const flowId = ins.flow
    const flow = await objectql.getObject('flows').findOne(flowId, { fields: { enable_amisform: 1 } })

    if (!flow) {
        console.error(`${new Date().toISOString()} [InstanceRecordQueue.sendDoc] 流程不存在: flowId=${flowId}`);
        throw new Error(`Flow not found: ${flowId}`);
    }

    const enableAmisform = flow.enable_amisform

    if (records && !_.isEmpty(records)) {
        // 此情况属于从creator中发起审批，或者已经从Apps同步到了creator
        var objectName = records[0].o;

        var ow = await objectql.getObject('object_workflows').findOne({
            object_name: objectName,
            flow_id: ins.flow
        });

        if (!ow) {
            console.error(`${new Date().toISOString()} [InstanceRecordQueue.sendDoc] 对象工作流配置不存在: objectName=${objectName}, flowId=${ins.flow}`);
            throw new Error(`ObjectWorkflow not found for object=${objectName}, flow=${ins.flow}`);
        }

        console.log(`${new Date().toISOString()} [InstanceRecordQueue.sendDoc] 同步到对象: objectName=${objectName}`);

        var objectCollection = objectql.getObject(objectName),
            sync_attachment = ow.sync_attachment;
        syncDirection = ow.sync_direction || 'both';

        var objectInfo = await getObjectConfig(objectName);

        const recordsToProcess = await objectFind(objectName, { filters: [['_id', 'in', records[0].ids]] });

        for (const record of recordsToProcess) {
            const recordId = record._id;
            try {
                if (!['both', 'ins_to_obj'].includes(syncDirection)) {
                    continue;
                }
                var lock_record_after_approval = ow.lock_record_after_approval || false;
                var syncValues = await InstanceRecordQueue.syncValues(ow.field_map_back, values, ins, objectInfo, ow.field_map_back_script, record, enableAmisform)
                var setObj = syncValues.mainObjectValue;

                if (InstanceRecordQueue.debug) {
                    console.log(`${new Date().toISOString()} [InstanceRecordQueue.sendDoc] 映射值详情:`, JSON.stringify(setObj, null, 2));
                }

                var instance_state = ins.state;
                if (ins.state === 'completed') {
                    setObj.locked = lock_record_after_approval
                    if (ins.final_decision) {
                        instance_state = ins.final_decision;
                    }
                }
                setObj['instances.0.state'] = setObj.instance_state = instance_state;

                await objectUpdate(objectName, record._id, setObj);

                var relatedObjects = await getRelateds(ow.object_name);
                var relatedObjectsValue = syncValues.relatedObjectsValue;
                await InstanceRecordQueue.syncRelatedObjectsValue(record._id, relatedObjects, relatedObjectsValue, spaceId, ins);

                // 以最终申请单附件为准，旧的record中附件删除

                const cfColl = await getCollection('cms_files');
                await cfColl.deleteMany({
                    'parent': {
                        o: objectName,
                        ids: [record._id]
                    }
                });

                const cfsColl = await getCollection('cfs.files.filerecord');
                await cfsColl.deleteMany({
                    'metadata.record_id': record._id
                })

                // 同步新附件
                await InstanceRecordQueue.syncAttach(sync_attachment, insId, record.space, record._id, objectName);
                // 执行公式
                await runQuoted(objectName, record._id);
            } catch (error) {
                console.error(`${new Date().toISOString()} [InstanceRecordQueue.sendDoc] 记录处理失败: recordId=${recordId}, error=${error.message}`);

                await objectUpdateMany(objectName, [['_id', '=', record._id], ['instances._id', '=', insId]], {
                    'instances.$.state': 'pending',
                    'instance_state': 'pending'
                })

                const cfColl = await getCollection('cms_files');
                await cfColl.deleteMany({
                    'parent': {
                        o: objectName,
                        ids: [record._id]
                    }
                });

                const cfsColl = await getCollection('cfs.files.filerecord');
                await cfsColl.deleteMany({
                    'metadata.record_id': record._id
                })

                throw error;
            }
        }
    } else {
        // 此情况属于从apps中发起审批
        const owsColl = await getCollection('object_workflows');
        const ows = await owsColl.find({
            flow_id: ins.flow
        }).toArray();

        for (const ow of ows) {
            const objectName = ow.object_name;
            try {
                var sync_attachment = ow.sync_attachment,
                    newRecordId = await makeNewID(ow.object_name),
                    syncDirection = ow.sync_direction || 'both';
                lock_record_after_approval = ow.lock_record_after_approval || false;
                var recordInserted = false;

                console.log(`${new Date().toISOString()} [InstanceRecordQueue.sendDoc] 新建记录: objectName=${objectName}, recordId=${newRecordId}`);

                if (!['both', 'ins_to_obj'].includes(syncDirection)) {
                    continue;
                }

                var objectInfo = await getObjectConfig(ow.object_name);

                var syncValues = await InstanceRecordQueue.syncValues(ow.field_map_back, values, ins, objectInfo, ow.field_map_back_script, null, enableAmisform);
                var newObj = syncValues.mainObjectValue;

                newObj.space = spaceId;
                newObj.name = newObj.name || ins.name;

                newObj.locked = true;

                var instance_state = ins.state;
                if (ins.state === 'completed' && ins.final_decision) {
                    instance_state = ins.final_decision;
                    newObj.locked = lock_record_after_approval;
                }
                newObj.instances = [{
                    _id: insId,
                    state: instance_state
                }];
                newObj.instance_state = instance_state;

                newObj._id = newRecordId;
                newObj.owner = newObj.owner || ins.applicant;
                newObj.created_by = ins.applicant;
                newObj.modified_by = ins.applicant;

                var r = await objectInsert(ow.object_name, newObj);
                recordInserted = !!r;
                const insColl = await getCollection('instances');
                if (r) {
                    await insColl.update({_id: ins._id}, {
                        $push: {
                            record_ids: {
                                o: objectName,
                                ids: [newRecordId]
                            }
                        },
                        $set: {
                            modified: new Date()
                        }
                    });
                    
                    var relatedObjects = await getRelateds(ow.object_name);
                    var relatedObjectsValue = syncValues.relatedObjectsValue;
                    await InstanceRecordQueue.syncRelatedObjectsValue(newRecordId, relatedObjects, relatedObjectsValue, spaceId, ins, newObj.locked);
                    
                    // workflow里发起审批后，同步时也可以修改相关表的字段值 #1183
                    var record = await objectFindOne(objectName, { filters: [['_id', '=', newRecordId]] });
                    await updateLookupOrMasterDetailFieldValues(ow.field_map_back, values, ins, objectInfo, record)
                }

                // 附件同步
                await InstanceRecordQueue.syncAttach(sync_attachment, insId, spaceId, newRecordId, objectName);
                // 执行公式
                await runQuoted(objectName, newRecordId);
            } catch (error) {
                console.error(error);

                if (recordInserted) {
                    try {
                        await objectRemove(ow.object_name, newRecordId);
                        const insColl = await getObject('instances');
                        await insColl.update(ins._id, {
                            $pull: {
                                record_ids: {
                                    o: objectName,
                                    ids: [newRecordId]
                                }
                            },
                            $set: {
                                modified: new Date()
                            }
                        });

                        const cfColl = await getCollection('cms_files');
                        await cfColl.deleteMany({
                            'parent': {
                                o: objectName,
                                ids: [newRecordId]
                            }
                        });

                        const cfsColl = await getCollection('cfs.files.filerecord');
                        await cfsColl.deleteMany({
                            'metadata.record_id': newRecordId
                        })
                    } catch (cleanupError) {
                        console.error(`${new Date().toISOString()} [InstanceRecordQueue.sendDoc] 清理记录失败: objectName=${objectName}, recordId=${newRecordId}`, cleanupError);
                    }
                }
                throw error;
            }
        }
    }

    if (doc._id) {
        await InstanceRecordQueue.collection.update({_id: doc._id}, {
            $set: {
                'info.sync_date': new Date()
            }
        });
    }
}

module.exports = InstanceRecordQueue;