You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何编写SuiteScript Map Reduce脚本处理CSV/JSON数据并创建Customer记录

基于SuiteScript 2.x Map/Reduce脚本创建Customer的兼容多数据源实现方案

1. 前置依赖说明

  • 适用版本:SuiteScript 2.1 及以上(支持箭头函数、解构赋值等语法,无需额外引入polyfill)
  • 权限要求:脚本部署时需分配创建客户、文件柜读取权限,输入文件需存放于NetSuite文件柜指定目录
  • 输入规则:
    • JSON格式输入:根节点为数组,每个元素对应单条客户数据,字段名与Customer记录标准/自定义字段ID一一对应
    • CSV格式输入:表头字段名与Customer记录标准/自定义字段ID一一对应,编码为UTF-8无BOM格式

2. 核心逻辑设计

2.1 阶段拆分说明

Map/Reduce脚本分四个阶段适配双数据源:

  • getInputData阶段:自动识别输入文件格式,统一转换为可迭代的对象数组输出
  • map阶段:校验单条客户数据必填字段,过滤非法数据
  • reduce阶段:执行客户记录创建逻辑,记录成功/失败日志
  • summarize阶段:输出全量执行统计结果

3. 完整可运行代码

/**
 * @NApiVersion 2.1
 * @NScriptType MapReduceScript
 * @NModuleScope SameAccount
 */
define(['N/file', 'N/record', 'N/log', 'N/format'], (file, record, log, format) => {
    // 可自定义配置项
    const CONFIG = {
        REQUIRED_FIELDS: ['entityid', 'subsidiary', 'email'], // 客户必填字段ID
        INPUT_FILE_ID: 12345 // 替换为输入文件在文件柜的内部ID
    }

    const getInputData = () => {
        // 读取输入文件
        const inputFile = file.load({id: CONFIG.INPUT_FILE_ID})
        const fileExt = inputFile.name.split('.').pop().toLowerCase()
        let dataArr = []

        // 处理JSON格式输入
        if (fileExt === 'json') {
            dataArr = JSON.parse(inputFile.getContents())
        }
        // 处理CSV格式输入
        else if (fileExt === 'csv') {
            const fileContent = inputFile.getContents()
            const lines = fileContent.split(/\r?\n/).filter(line => line.trim() !== '')
            const headers = lines[0].split(',').map(h => h.trim())
            // 遍历行转换为对象
            for (let i = 1; i < lines.length; i++) {
                const rowVals = lines[i].split(',')
                const rowObj = {}
                headers.forEach((header, idx) => {
                    rowObj[header] = rowVals[idx] ? rowVals[idx].trim() : ''
                })
                dataArr.push(rowObj)
            }
        } else {
            log.error({title: '不支持的文件格式', details: `仅支持JSON/CSV,当前格式为${fileExt}`})
            return []
        }
        return dataArr
    }

    const map = (context) => {
        const rowData = JSON.parse(context.value)
        // 必填字段校验
        const missingFields = CONFIG.REQUIRED_FIELDS.filter(field => !rowData[field])
        if (missingFields.length > 0) {
            log.error({
                title: `行数据${context.key}缺失必填字段`,
                details: `缺失字段:${missingFields.join(',')},原始数据:${JSON.stringify(rowData)}`
            })
            return
        }
        // 校验通过写入map阶段输出
        context.write({key: context.key, value: rowData})
    }

    const reduce = (context) => {
        const rowData = JSON.parse(context.values[0])
        try {
            // 创建客户记录
            const customerRec = record.create({type: record.Type.CUSTOMER, isDynamic: true})
            // 批量赋值字段
            Object.keys(rowData).forEach(fieldId => {
                // 特殊字段类型可在此处增加处理逻辑,比如日期、多选、下拉字段等
                customerRec.setValue({fieldId: fieldId, value: rowData[fieldId]})
            })
            const customerId = customerRec.save({ignoreMandatoryFields: false, enableSourcing: true})
            log.audit({title: '客户创建成功', details: `客户内部ID:${customerId},客户名称:${rowData.entityid}`})
        } catch (e) {
            log.error({
                title: `客户${rowData.entityid}创建失败`,
                details: `错误原因:${e.message},原始数据:${JSON.stringify(rowData)}`
            })
        }
    }

    const summarize = (summary) => {
        // 统计执行结果
        log.audit({title: '脚本执行总览', details: `输入数据总条数:${summary.inputSummary.total}`})
        log.audit({title: 'Map阶段统计', details: `处理成功:${summary.mapSummary.count},处理失败:${summary.mapSummary.errors.size}`})
        log.audit({title: 'Reduce阶段统计', details: `处理成功:${summary.reduceSummary.count},处理失败:${summary.reduceSummary.errors.size}`})

        // 输出所有错误日志
        if (summary.mapSummary.errors.size > 0 || summary.reduceSummary.errors.size > 0) {
            const allErrors = [...summary.mapSummary.errors.entries(), ...summary.reduceSummary.errors.entries()]
            allErrors.forEach(([key, err]) => {
                log.error({title: `执行错误key:${key}`, details: JSON.parse(err).message})
            })
        }
    }

    return {
        getInputData,
        map,
        reduce,
        summarize
    }
})

4. 部署与使用说明

  • 替换代码中CONFIG.INPUT_FILE_ID为实际输入文件的内部ID
  • 按需修改CONFIG.REQUIRED_FIELDS的必填字段列表,可扩展自定义字段
  • 若需处理包含逗号的CSV字段内容,可调整CSV解析逻辑,先匹配双引号包裹的内容再做拆分
  • 脚本部署时可设置定时触发,或关联自定义按钮手动触发

5. 常见问题处理

  • CSV解析异常:检查CSV是否存在未转义的特殊字符,建议导出CSV时统一用双引号包裹所有字段值
  • 保存失败:检查字段值是否符合NetSuite字段约束,比如子公司ID是否存在、邮箱格式是否正确、字段长度是否超出限制等
  • 大数据量处理:若数据量超过10万条,可在getInputData阶段拆分数据分片,避免单阶段处理超时

内容的提问来源于stack exchange,提问作者ANUSHA DIBBIDI

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 05:45:04