1 导入导出功能总体设计
数据导入功能的展现形式是在应用界面上通过注册导出导入按钮的方式进行展现,用户可以根据单据模板动态选择需要的导出导入项。同时系统支持多种格式文件的导入(xls,xlsx,csv),由于导入的翻译和保存接口都是在后台调用,系统会将导入过程中出现的问题回写到导入文件中,用户可以通过导入日志界面很方便的对本次导入的状态进行查看。
1.1 导入导出设计思路
1、 配置文件:所有需要数据导入导出功能的档案必须要在home中注册对应档案的配置文件。
2、 确定导出项:通过输出的格式文件规范导入数据的格式,以便于进行数据的转换(即通信协议是底层程序已经确定的)。通过前端传入的appcode和pagecode及配置文件record中属性arecord找到对应单据模板区域需要的导出项。
3、 导入数据处理:将格式文件中的数据解析后传到数据导入器中,数据导入器根据其传入的参数以及配置文件找到对应的单据模板,并通过单据模板对解析后导入数据进行翻译,完成了和业务逻辑的衔接(这一步操作实际上模拟的是界面录入数据的过程)。循环对导入的所有数据完成这个操作。如果档案不允许导入则直接抛出异常给用户提示。
4、 错误信息提示和日志功能:数据导入工具提供导入时的错误信息提示和日志功能,能够将调用各个档案(或单据)的保存方法时产生的业务异常信息回写到导入的excel文件中,同时提供导入日志查询节点对回写之后的导入文件进行下载查看,因为只是对错误信息进行回写,并不会改变文件格式,可以很方便的修改之后重新导入。
1.2 模板导出流程图
1.3 数据导入流程图
1.4 导入导出注册表及导入日志表结构
2 导入导出功能的实现
2.1 前端代码实现
在initTemplate.js文件夹里引入导出导入参数工具类:
import { high, excelImportconfig } from ‘nc-lightapp-front’; const { ExcelImport } = high;
在initTemplate.js对注册的导入按钮设置参数:
//示例代码 //单据类型命名自定义,但要与xml配置文件名一致 let excelimportconfig = excelImportconfig(props,’riamm’,’defdoclist’,true,’’,{appcode: ‘10140UDDDB’,pagecode: pagecode},()=>{ this.getData();//导入完成后回调列表查询方法 }); props.button.setUploadConfig(“import”,excelimportconfig);
在buttonClick.js中处理导出事件
case ‘export’://导出按钮点击事件 let pks = []; //获取界面中的数据主键放入到pks中,若不用导出数据则不用处理 start //…pks组装代码 //获取界面中的数据主键放入到pks中,若不用导出数据则不用处理 end this.setState({pks: pks},()=>{ this.props.modal.show(‘exportFileModal’);//‘exportFileModal’为固定写法 }); break;
在ExcelImport组件里传入appcode,pagecode字段
2.2 配置文件创建与设置
2.2.1 功能说明
配置文件实现导入档案主子vo关系配置,导入字段顺序设置,动态为参照字段设置参数等功能
2.2.2 配置文件说明
record字段说明:以物料-业务单元进行说明,$nchome\resources\excel\billdefine\uapbd\material_org.xml
| 属性 | 说明 |
|---|---|
| displayName 、 typeName | “record名称”对应 无实际意义 |
| displayNameResid | 多语id,保证唯一,规范:excel_ + 单据类型+billhead(标识表头)+_0001(编码可不写)。将对应值统一添加到多语文件中,多语文件位置:nchome\langlib\riart_nccloud_sim_langresLevel1.jar\lang\simpchn\excel_import\excelrecord.properties; |
| metadataid | 对应vo的元数据id 可以从md_class表中进行查询 |
| stabletag=“header” | 固定写法 |
| tablename=“recbill“ | 对应导出excel表头编码 |
| tabletype | 当改record下存储的为表头信息时候 ,设为tabletype = “head” ,为表体信息时tabletype = “body”; |
| areaname | 区域编码,对应单据模板区域 |

- Field属性说明:
| 属性 | 说明 |
| —- | —- |
| matchtag | 可以和name相同,但是需要在该配置文件下保证field中的唯一性 |
| name | 对应vo中字段编码,保证和模板中字段编码保持相同 |
| type | 字段类型该字段在vo中的类型 String, UFDate…,其中标识关联关系的filed type类型为
ref(item) 实例如下:refitem对应表体record中的元数据metadataid; | | variablename |pk_org_x 字段为翻译后pk记录在上下文中,后续其他地方翻译时从内存中获取该值,需要保证配置文件内唯一 | | translatorProperty | translatorProperty 为新加属性,格式如下单个属性,不设置basdoc走平台参照翻译,并且需要向参照中注入属性,translatorProperty必须配置!单个属性pk_org_x=pk_org 多个属性pk_org_x=pk_org;xxxx1=xxxxa 其中pk_org_x与translatorParams属性中值对应,pk_org为模板项对应参照属性,例如导入部门时会首先设置组织信息pk_org,平台为根据该值找到对应set方法,完成属性注入,然后再进行翻译 | | translator | translator为自定义翻译器属性,内部值为翻译器注册表excel_translator数据主键pk_translator,eg:1001Z010000000012DEF |
2.3 后端代码实现
2.3.1 导入导出继承类说明
导入继承类:nccloud.bs.excel.plugin. AbstractImportProceeWithContext void processBillWithContext(Object vo,IXChangeContext changcontext) 需要业务组类继承后写此方法的具体实现,包括数据校验,默认赋值以及数据保存。 object ProcessRequestWithReturn (ExtendedAggregatedValueObject extendaggvalue,IXChangeContext context) 处理导入数据,并带有前端传入上下文并返回处理后的单据vo
导出实现类:nccloud.bs.excel.process.AbstractExcelOutputProcessor Object[] getObjectValueByPks(String[] pks) 返回单据vo数组 参数:pks- 前端传入单据pks这里输入一些正文内容; ExportDataInfo getValue(Object[] vos, List
AbstractRefTranslator 参照默认翻译类 void translateExToNC(String srcValue, String metaDataID, ITranslateContext translateContext); 直接翻译,不走默认参照翻译类。 void matchParamRefModel(AbstractRefModel refmodel,ITranslateContext translateContext); 根据翻译上下文对参照进行处理。
2.3.2 导出处理类实例如下

package nccloud.pubimpl.riamm.defdoclist.impl;
import java.util.List;
import nc.bs.framework.common.NCLocator;
import nc.itf.bd.defdoc.IDefdoclistQryService;
import nc.vo.pub.BusinessException;
import nc.vo.pub.ExtendedAggregatedValueObject;
import nc.vo.pub.lang.UFBoolean;
import nccloud.bs.excel.process.AbstractExcelOutputProcessor;
import nccloud.itf.trade.excelimport.ExportDataInfo;
import nccloud.ui.trade.excelimport.InputItem;
import nccloud.vo.excel.scheme.BillDefination;
public class DefdocListExcelOutputProcess extends AbstractExcelOutputProcessor {
@Override public Object[] getObjectValueByPks(String[] pks) throws BusinessException { IDefdoclistQryService defdoclistQryService = NCLocator.getInstance().lookup(IDefdoclistQryService.class); return defdoclistQryService.queryVOByPks(pks); } @Override protected ExportDataInfo getValue(Object[] vos, List<InputItem> exportItems, BillDefination billdefination) throws BusinessException { ExtendedAggregatedValueObject[] aggvos = getConvertorForTemp().convertDataFromEditorData(billdefination, vos, exportItems); for (ExtendedAggregatedValueObject aggvo : aggvos) { Object obj = aggvo.getParentVO().getAttributeValue("isgrade"); if (obj != null && ((UFBoolean) obj).booleanValue()) { aggvo.getParentVO().setAttributeValue("isgrade", "是"); } else { aggvo.getParentVO().setAttributeValue("isgrade", "否"); } } return new ExportDataInfo(aggvos); }
}
2.3.3 导入处理类实例如下

package nccloud.pubimpl.uapbd.material.plugin;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import nc.bs.dao.BaseDAO;
import nc.bs.dao.DAOException;
import nc.bs.framework.common.InvocationInfoProxy;
import nc.bs.framework.common.NCLocator;
import nc.bs.logging.Logger;
import nc.itf.bd.material.baseinfo.IMaterialBaseInfoService;
import nc.login.vo.INCUserTypeConstant;
import nc.vo.bd.errorlog.ErrLogReturnValue;
import nc.vo.bd.errorlog.ErrorMsgVO;
import nc.vo.bd.material.MaterialConvertVO;
import nc.vo.bd.material.MaterialTaxTypeVO;
import nc.vo.bd.material.MaterialVO;
import nc.vo.bd.pub.IPubEnumConst;
import nc.vo.ml.NCLangRes4VoTransl;
import nc.vo.pub.BusinessException;
import nc.vo.pub.VOStatus;
import nc.vo.pub.lang.UFBoolean;
import nccloud.bs.excel.IXChangeContext;
import nccloud.bs.excel.plugin.AbstractImportProceeWithContext;
import nccloud.commons.lang.ArrayUtils;
import nccloud.commons.lang.StringUtils;
import nccloud.pubitf.baseapp.apprbac.IAppAndOrgPermQueryPubService;
public class MaterialImportProcess extends AbstractImportProceeWithContext{
private BaseDAO baseDAO = null; private IMaterialBaseInfoService service = null; @Override protected void processBillWithContext(Object vo, IXChangeContext context) throws BusinessException { String billtype = context.getSwapContext().getBilltype(); String appcode = context.getXChangeConfigInfo().getPagetemplet().getAppcode(); //1.得到转换后的VO数据,取决于向导第一步注册的VO信息 MaterialVO materialVO = (MaterialVO) vo; if(null != materialVO) { // 导入前设置默认值 setDefaultValue(materialVO, billtype); // 业务校验 String msg = checkBillVO(materialVO, billtype, appcode); if(!StringUtils.isEmpty(msg)) { throw new BusinessException(msg); } this.insertMaterialVO(materialVO); } } private void setDefaultValue(MaterialVO resvo, String billtype) { //增加需要的默认值 String pk_group = resvo.getPk_group(); String pk_org = resvo.getPk_org(); String group = InvocationInfoProxy.getInstance().getGroupId(); if("material_grp".equals(billtype)){ if(StringUtils.isEmpty(pk_group)) { resvo.setPk_group(group); resvo.setPk_org(group); }else if(StringUtils.isEmpty(pk_org)) { resvo.setPk_org(pk_group); } }else{ if(StringUtils.isEmpty(pk_group)) { resvo.setPk_group(group); } } //默认赋值 if(resvo.getFee()==null)//服务类 resvo.setFee(UFBoolean.FALSE); if(resvo.getDiscountflag() ==null)//价格折扣 resvo.setDiscountflag(UFBoolean.FALSE); if(resvo.getElectronicsale() ==null)//电子销售 resvo.setElectronicsale(UFBoolean.FALSE); if(resvo.getIselectrans() ==null)//电子采购 resvo.setIselectrans(UFBoolean.FALSE); if(resvo.getIsfeature() ==null)//特征件 resvo.setIsfeature(UFBoolean.FALSE); if(resvo.getRetail() ==null)//适用零售 resvo.setRetail(UFBoolean.FALSE); if(resvo.getSetpartsflag() ==null)//成套件 resvo.setSetpartsflag(UFBoolean.FALSE); } private String checkBillVO(MaterialVO resvo, String billtype, String appcode) throws BusinessException { StringBuilder msg = new StringBuilder(); String userid = InvocationInfoProxy.getInstance().getUserId(); String pk_group = InvocationInfoProxy.getInstance().getGroupId(); String billorg = resvo.getPk_org(); String billgroup = resvo.getPk_group(); if("material_org".equals(billtype)){ if(StringUtils.isEmpty(billorg)) { return NCLangRes4VoTransl.getNCLangRes().getStrByID("nccriauapbd0", "0nccriauapbd00178")/ res 导入数据的组织为空或者未在系统中找到对应组织 /; }else if(!pk_group.equals(billgroup)) { return NCLangRes4VoTransl.getNCLangRes().getStrByID("nccriauapbd0", "0nccriauapbd00179")/ res 只可导入当前集团的数据 /; } boolean flag = true; //判断用户是否是集团管理员,集团管理员可导所有组织数据,需判断是否为当前集团 Integer type = nc.vo.uap.rbac.util.UserTypeUtil.getUserType(userid); if(type != INCUserTypeConstant.USER_TYPE_GROUP_ADM){ String[] orgs = NCLocator.getInstance().lookup(IAppAndOrgPermQueryPubService.class).queryUserPermOrgPksByApp(userid, appcode, pk_group); if(!StringUtils.isEmpty(billorg) && !ArrayUtils.isEmpty(orgs)) { for(String pk_org : orgs) { if(pk_org.equals(billorg)){ flag = false; break; } } } if(flag){ msg.append(NCLangRes4VoTransl.getNCLangRes().getStrByID("nccriauapbd0", "0nccriauapbd00180")/ res 导入数据的组织未在当前用户的权限范围内 /); } } }else if("material_grp".equals(billtype)){ if(!pk_group.equals(billgroup)) { msg.append(NCLangRes4VoTransl.getNCLangRes().getStrByID("nccriauapbd0", "0nccriauapbd00179")/ res 只能导入当前集团的数据 /); } } if(resvo.getMaterialconvert()==null){ msg = msg.length()>0 ? msg.append(NCLangRes4VoTransl.getNCLangRes().getStrByID("nccriauapbd0", "0nccriauapbd00181")/ ,且没有与主表行号相匹配的子表数据 / ) : msg.append(NCLangRes4VoTransl.getNCLangRes().getStrByID("nccriauapbd0", "0nccriauapbd00182")/ 没有与主表行号相匹配的子表数据 /); } return msg.toString(); } private MaterialVO insertMaterialVO(MaterialVO materialVO) throws BusinessException, DAOException { if (StringUtils.isBlank(materialVO.getPk_source())) { materialVO = this.getService().insertMaterial(this.getInsertVO(materialVO)); } else { ErrLogReturnValue value = this.getService().createMaterialVersion(materialVO, materialVO.getPk_source()); if (value.getReturnValue() == null || !value.getReturnValue().getClass().isArray()) { return materialVO; } materialVO = (MaterialVO) ((Object[]) value.getReturnValue())[0]; this.LogErrorMessage(value); } return materialVO; } private MaterialVO getInsertVO(MaterialVO materialVO) throws DAOException { materialVO.setEnablestate(IPubEnumConst.ENABLESTATE_ENABLE); materialVO.setStatus(VOStatus.NEW); materialVO.setMaterialconvert(this.getMaterialConvertVOs(materialVO)); materialVO.setMaterialtaxtype(this.getMaterialTaxTypeVOs(materialVO)); return materialVO; } @SuppressWarnings("unchecked") private MaterialConvertVO[] getMaterialConvertVOs(MaterialVO MaterialVO) throws DAOException { List<MaterialConvertVO> newConverts = new ArrayList<MaterialConvertVO>(); if (StringUtils.isNotBlank(MaterialVO.getPrimaryKey())) { Collection<MaterialConvertVO> oldAgentStores = this.getBaseDAO().retrieveByClause(MaterialConvertVO.class, MaterialConvertVO.PK_MATERIAL + " = ‘" + MaterialVO.getPrimaryKey() + "’", new String[] { MaterialConvertVO.PK_MATERIALCONVERT }); for (MaterialConvertVO agentstore : oldAgentStores) { agentstore.setStatus(VOStatus.DELETED); newConverts.add(agentstore); } } if (MaterialVO.getMaterialconvert() != null && MaterialVO.getMaterialconvert().length > 0) { for (MaterialConvertVO agentstore : MaterialVO.getMaterialconvert()) { agentstore.setStatus(VOStatus.NEW); newConverts.add(agentstore); } } return newConverts.toArray(new MaterialConvertVO[0]); } @SuppressWarnings("unchecked") private MaterialTaxTypeVO[] getMaterialTaxTypeVOs(MaterialVO MaterialVO) throws DAOException { List<MaterialTaxTypeVO> newConverts = new ArrayList<MaterialTaxTypeVO>(); if (StringUtils.isNotBlank(MaterialVO.getPrimaryKey())) { Collection<MaterialTaxTypeVO> oldAgentStores = this.getBaseDAO().retrieveByClause(MaterialTaxTypeVO.class, MaterialTaxTypeVO.PK_MATERIAL + " = ‘" + MaterialVO.getPrimaryKey() + "’", new String[] { MaterialTaxTypeVO.PK_MATERIALTAXTYPE }); for (MaterialTaxTypeVO agentstore : oldAgentStores) { agentstore.setStatus(VOStatus.DELETED); newConverts.add(agentstore); } } if (MaterialVO.getMaterialtaxtype() != null && MaterialVO.getMaterialtaxtype().length > 0) { for (MaterialTaxTypeVO agentstore : MaterialVO.getMaterialtaxtype()) { agentstore.setStatus(VOStatus.NEW); newConverts.add(agentstore); } } return newConverts.toArray(new MaterialTaxTypeVO[0]); } private void LogErrorMessage(ErrLogReturnValue value) { ErrorMsgVO[] vos = value.getErrLogResult().getErrorMsgs(); if (!ArrayUtils.isEmpty(vos)) { String message = nc.vo.ml.NCLangRes4VoTransl.getNCLangRes().getStrByID( "10140mag", "010140mag0200", null, new String[] { Integer.toString(value.getTotalNum()), Integer.toString(value.getErrLogResult().getErrorMessagegNum()) }) / @res "外部交换平台导入物料新版本数据时,分配操作部分成功,共处理了{0}条记录,其中有{1}条处理失败:" /+ "\n"; for (int i = 0; i < vos.length; i++) { message += vos[i].getErrormsg() + "\n"; } Logger.debug(message); } } private BaseDAO getBaseDAO() { if (this.baseDAO == null) { this.baseDAO = new BaseDAO(); } return this.baseDAO; } private IMaterialBaseInfoService getService() { if (this.service == null) { this.service = NCLocator.getInstance().lookup(IMaterialBaseInfoService.class); } return this.service; }
}
2.3.4 翻译处理类实例如下

package nccloud.pubimpl.riamm.defdoc.impl;
import java.util.Map;
import nc.bs.dao.BaseDAO;
import nc.bs.framework.common.InvocationInfoProxy;
import nc.bs.logging.Logger;
import nc.bs.sec.esapi.NCESAPI;
import nc.jdbc.framework.processor.ColumnProcessor;
import nc.ui.bd.ref.AbstractRefModel;
import nc.vo.pfxx.exception.PfxxException;
import nc.vo.pub.BusinessException;
import nccloud.bs.excel.ITranslateContext;
import nccloud.bs.excel.bdtranslate.AbstractRefTranslator;
public class DefdocTranslator extends AbstractRefTranslator {
@Override public String translateExToNC(String srcValue, String metaDataID, ITranslateContext translateContext) throws PfxxException { try { String code = translateContext.getProperty().getCode(); if("pid".equalsIgnoreCase(code)) {//上级档案 // 查询服务 BaseDAO dao = new BaseDAO(); Map<String, Object> userdata = translateContext.getUserdata(); Object pk_defdoclist = userdata.get("pk_defdoclist"); Object pk_org = userdata.get("pk_org"); String pk_group = InvocationInfoProxy.getInstance().getGroupId(); String sql = " select pk_defdoc from bd_defdoc where pk_defdoclist=’" + pk_defdoclist + "’ and (name=’" + NCESAPI.sqlEncode(srcValue) + "’ or code=’" + NCESAPI.sqlEncode(srcValue) + "’)" + " and (pk_org=’GLOBLE00000000000000’ or pk_org=’"+pk_group+"’ or pk_org=’"+pk_org+"’) "; Object pk_defdoc = dao.executeQuery(sql, new ColumnProcessor()); return pk_defdoc==null?"":(String) pk_defdoc; } }catch(BusinessException e) { Logger.error(e.getMessage()); } return null; } @Override protected void processRefModel(AbstractRefModel refmodel, ITranslateContext translateContext) { }
}
2.4 注册后端代码类
2.4.1 导出插件注册
只有导出带有数据的功能才需要注册,只导出模板不需要进行注册,及后端代码开发,只需要前端配置,home中对配置文件进行配置即可,Appcode: 应用编码,pagecode:页面编码,后台需要根据appcode和pagecode 在导出注册表中找到对应的导出实现类。
2.4.2 导入插件注册
在导入注册表中注册导入处理类,根据moduleName 和billtype获取导入插件处理类进行实例化。
2.4.3 翻译类插件注册
表名:excel_translator; 字段:module: 模块名,pk_translator: 主键, traclassname: 翻译器类名; 功用:用于导入数据时特殊字段的翻译。

