本文档主要讨论了在不同业务单据转换场景中,如何在苍穹系统中实现附件在不同类型(附件面板与附件字段)之间的上传与转换。详细描述了附件面板与附件字段的特点,以及通过AttachmentServiceHelper和AttachmentFieldModel等工具类进行附件数据获取、处理和上传的方法。具体方案包括附件面板到附件面板、附件面板到附件字段、附件字段到附件字段、以及附件字段到附件面板的转换过程,每个过程都包含数据获取、数据加工处理、数据上传以及赋值等关键步骤,并给出了相应的代码示例。此外,还提供了在转换过程中可能遇到的问题及解决方案,如共用附件对象数据的注意事项等。
关键词:
页面开发,附件面板,附件字段
一、需求
在BOTP等有业务单据转换的场景,需要把某张单据的附件上传到另外一张单据中,怎么实现?
二、思路与方案
2.1 分析思路
苍穹系统附件类型有附件面板和附件字段。
附件面板是面板控件,其附件对象数据存储于bos_attachment,只有控件编程模型,没有数据模型,但我们可以通过AttachmentServiceHelper.getAttachments(String, Object, String)获取附件面板数据、
AttachmentServiceHelper.upload(String, Object, String, List<Map<String, Object>>)或者AttachmentServiceHelper.saveTempAttachments(String, Object, String, Map<String, Object>)上传附件面板数据。
附件字段继承自多选基础资料,具有数据模型,其关联的基础资料类型是bd_attachment,附件字段赋值类似多选基础资料,也可通过fbasedataid实现。
那么借助这些方法即可随心所欲实现附件面板、附件字段之间的携带转换。
2.2 实现方案
1. 附件面板To附件面板
先获取源附件面板数据,页面前台用AttachmentPanel.getAttachmentData()或者插件后台用AttachmentServiceHelper.getAttachments(String, Object, String),然后对lastModified、url等进行数据加工处理,调用AttachmentServiceHelper.upload(String, Object, String, List<Map<String, Object>>)上传到目标附件面板。
2. 附件面板To附件字段
先获取源附件面板数据,页面前台用AttachmentPanel.getAttachmentData()或者插件后台用AttachmentServiceHelper.getAttachments(String, Object, String),然后对uid、关联单据实体编码、关联单据实体id等进行数据加工处理,调用AttachmentFieldModel.saveAttachments(IDataModel, String, String, List<Map<String, Object>>)将附件数据上传到文件服务器,生成bd_attachment附件对象,再赋值给目标附件字段。
3. 附件字段To附件字段
参考多选基础资料字段,先加载源附件字段的数据值集合,取出fbasedataid赋值给目标附件字段。
4. 附件字段To附件面板
先加载源附件字段的数据值集合,通过bd_attachment附件对象里的url将附件上传到文件服务器,并自己构造附件面板数据,再调用AttachmentServiceHelper.saveTempAttachments(String, Object, String, Map<String, Object>)将数据绑定到目标附件面板。
三、实现过程
1. 附件面板To附件面板
(1) 获取源附件面板数据并进行数据加工
关键代码:
//通过AttachmentServiceHelper.getAttachments(formId, pkId, attachKey)获取源单据附件面板附件数据 List<Map<String, Object>> attachmentData = AttachmentServiceHelper.getAttachments("kded_sourcebill", sourcebill.getLong("id"), "attachmentpanel"); attachmentData.forEach(attach -> { try { //源附件数据的lastModified是timestamp,会出现强转long错误在此置null attach.put("lastModified", null); //源附件数据的url已经过URL编码,需要在此先解码获取原始下载路径,AttachmentServiceHelper.upload()会进行URL编码 attach.put("url", getPathfromDownloadUrl(URLDecoder.decode(String.valueOf(attach.get("url")), "UTF-8"))); } catch (IOException e) { //do something with log. e.printStackTrace(); } }) /** * 获取文件服务器相对路径 * @param url * @return * @throws IOException */ private String getPathfromDownloadUrl(String url) throws IOException { String path = StringUtils.substringAfter(url, "path="); path = URLDecoder.decode(path, "UTF-8"); return FilePathUtil.dealPath(path, "attach"); }
(2) 将附件数据上传到目标附件面板
关键代码:
//调用AttachmentServiceHelper.upload(formId, pkId, attachKey, attachments)将附件数据上传到目标附件面板 AttachmentServiceHelper.upload(getView().getEntityId(), getModel().getValue("id"), "attachmentpanel", attachmentData);
2. 附件面板To附件字段
(1) 获取源附件面板数据并进行数据加工
关键代码:
//通过AttachmentServiceHelper.getAttachments(formId, pkId, attachKey)获取源单据附件面板附件数据 List<Map<String, Object>> attachmentData1 = AttachmentServiceHelper.getAttachments("kded_sourcebill", sourcebill1.getLong("id"), "attachmentpanel"); attachmentData1.forEach(attach -> { //修改附件数据的uid、关联单据实体编码、关联单据实体id attach.put("uid", getUid().toString()); attach.put("entityNum", getView().getEntityId()); attach.put("billPkId", String.valueOf(getModel().getValue("id"))); }); /** * 获取附件uid * @return */ private StringBuffer getUid() { StringBuffer uid = new StringBuffer("rc-upload-"); uid.append((new Date()).getTime()); uid.append("-"); int index = (int)(1.0D + Math.random() * 10.0D); uid.append(index); return uid; }
(2) 调用AttachmentFieldModel.saveAttachments(IDataModel, String, String, List<Map<String, Object>>)将附件数据上传到文件服务器
关键代码:
AttachmentEdit attEdit = this.getView().getControl("kded_attachment"); List<DynamicObject> saveAttachments = attEdit.getAttachmentModel().saveAttachments(attEdit.getModel(), this.getView().getPageId(), this.getModel().getDataEntityType().getName(), attachmentData1);
(3) 给目标附件字段赋值
关键代码:
List<Long> idSet = new ArrayList<>(); //获取保存后的bd_attachment附件对象id saveAttachments.forEach(save -> idSet.add(save.getLong("id"))); //给目标附件字段赋值 getModel().setValue("kded_attachment", idSet.toArray());
3. 附件字段To附件字段
(1) 获取源附件字段附件对象(bd_attachment)的id集合
关键代码:
//获取源附件字段的值 DynamicObject sourceBill2 = BusinessDataServiceHelper.loadSingle("kded_sourcebill", "id,kded_attachment", qFilter2.toArray()); DynamicObjectCollection sourceAttachcol = (DynamicObjectCollection) sourceBill2.get("kded_attachment"); //获取源附件字段附件对象id集合 List<Long> attchIdSet = new ArrayList<>(); sourceAttachcol.forEach(attach -> { attchIdSet.add(attach.getDynamicObject("fbasedataId").getLong("id")); });
(2) 给目标附件字段赋值
方案一:因为在目标单据页面操作,获取源附件字段附件对象bd_attachment的id通过setValue赋值给目标附件字段即可。
关键代码:
getModel().setValue("kded_attachment", attchIdSet.toArray());
方案二:后台处理可先获取目标单据附件字段的集合,根据源附件字段附件对象id(bd_attachment)加载附件数据再通过fbasedataid赋值给目标附件字段。
关键代码:
DynamicObjectCollection targetAttachCol = this.getModel().getDataEntity(true).getDynamicObjectCollection("kded_attachment"); DynamicObject[] sourceAttachments = BusinessDataServiceHelper.load(attchIdSet.toArray(), EntityMetadataCache.getDataEntityType("bd_attachment")); //将bd_attachment查出来的附件对象绑定到目标附件字段上,即跟源单共用附件基础资料 Arrays.asList(sourceAttachments).forEach(attachObj -> targetAttachCol.addNew().set("fbasedataid", attachObj) ); //刷新当前页面验证效果 getView().updateView("kded_attachment");
注意:案例中此时源字段与目标字段共用同个附件对象数据,有一方删除附件则另一方也同时删除。可通过附件url获取文件流再自行上传至文件服务器创建另一个附件对象。
4. 附件字段To附件面板
(1) 获取源附件字段附件对象(bd_attachment)数据
关键代码:
//获取源附件字段的值 DynamicObject sourceBill3 = BusinessDataServiceHelper.loadSingle("kded_sourcebill", "id,kded_attachment", qFilter3.toArray()); DynamicObjectCollection sourceAttachCol3 = (DynamicObjectCollection) sourceBill3.get("kded_attachment");
(2) 根据附件字段数据构造附件面板数据,通过FileServiceFactory获取AttachmentFileService,并通过附件字段附件对象数据获取源附件文件流上传到文件服务器,拿到返回的url构造附件面板数据集。
关键代码:
//根据附件字段数据构造附件面板数据 Map<String, Object> attachemnts = new HashMap<>(); List<Map<String, Object>> attachmentData3 = buildAttachmentDataFromEdit(sourceAttachCol3); //key:目标附件面板标识,value:目标附件面板附件数据 attachemnts.put("attachmentpanel", attachmentData3); /** * 根据附件字段数据构造附件面板数据 * @param sourceAttachCol * @return */ private List<Map<String, Object>> buildAttachmentDataFromEdit(DynamicObjectCollection sourceAttachCol) { List<Map<String, Object>> attachDataList = new ArrayList<>(); sourceAttachCol.forEach(attach -> { DynamicObject attachObj = attach.getDynamicObject("fbasedataid"); Map<String, Object> attachMap = new HashMap<>(); //description attachMap.put("description", attachObj.getString("description")); attachMap.put("type", attachObj.getString("type")); //获取附件inputstream上传到缓存服务 InputStream inputStream = FileServiceFactory.getAttachmentFileService().getInputStream(attachObj.getString("url")); String saveUrl = CacheFactory.getCommonCacheFactory().getTempFileCache().saveAsFullUrl(attachObj.getString("name"), new BufferedInputStream(inputStream), 2*3600); //url attachMap.put("url", saveUrl); //uid attachMap.put("uid", getUid()); //name attachMap.put("name", attachObj.getString("name")); //size attachMap.put("size", attachObj.get("size")); attachMap.put("fattachmentpanel", "attachmentpanel"); //entityNum attachMap.put("entityNum", getView().getEntityId()); attachMap.put("billPkId", String.valueOf(getModel().getValue("id"))); //lastModified attachMap.put("lastModified", new Date().getTime()); attachMap.put("status", "success"); //client attachMap.put("client", null); attachDataList.add(attachMap); }); return attachDataList; }
(3) 调用AttachmentServiceHelper.saveTempAttachments(String, Object, String, Map<String, Object>)将数据绑定到目标附件面板。
关键代码:
AttachmentServiceHelper.saveTempAttachments(getView().getEntityId(), getModel().getValue("id"), "kded_clztest", attachemnts); //刷新页面 getView().updateView("attachmentpanel");
四、效果图
1. 附件面板To附件面板,如图1-图3。
图 1 源单据附件面板上传图片
图 2 通过插件将源附件面板图片上传到目标附件面板
图 3 目标附件面板图片可正常浏览
2. 附件面板To附件字段,借用图1附件面板的数据0.0,如图4,也可正常浏览的噢。
图 4 通过插件将源附件面板图片上传到目标附件字段
3. 附件字段To附件字段,如图5-图6,还是可以正常浏览哒。
图 5 源单据附件字段上传图片
图 6 通过插件将源附件字段图片上传到目标附件字段
4. 附件字段To附件面板,借用图5附件面板的数据0-0,如图7。
图 7 通过插件将源附件字段图片上传到目标附件面板
插件实现附件面板和附件字段的携带转换_元数据.zip(16.25KB)
插件实现附件面板和附件字段的携带转换_代码.zip(2.74KB)