如何编写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
相关产品推荐
相关产品推荐

