本文详细阐述了金蝶云·苍穹平台内附件字段持久化的实现过程,包括文件上传至Redis缓存、保存附件信息至数据库、最终文件持久化到附件服务器及业务单据数据保存的全过程。分析了多个请求处理及后台服务方法调用,强调了涉及的数据存储和关键工具类。提供了注意事项和参考资料链接。
本文给各位看官详细讲解一下金蝶云·苍穹平台内附件字段持久化的实现过程,希望能给各位在开发过程中提供一点点帮助。
设计器界面
实现过程详解
1. 在前端页面上的附件字段上传文件之后,浏览器F12前端发送了三个请求:
http://localhost:8080/ierp/form/batchInvokeAction.do?appId=kdec_testapp&f=kdec_demobill&ac=beforeUpload
http://localhost:8080/ierp/attachment/uploadFile.do
http://localhost:8080/ierp/form/batchInvokeAction.do?appId=kdec_testapp&f=kdec_demobill&ac=updateValue
首先,在第一个请求中,可以完成上传文件前的业务处理逻辑。这里我们没做任务处理,直接略过该请求。
然后,第二个请求实现了把附件暂存到redis缓存中。根据平台底层框架分析可知,后台针对该请求在 kd.bos.web.actions.AttachmentAction.uploadFile(HttpServletRequest, HttpServletResponse) 中进行响应。反编译源码之后分析可知,该方法中通过 kd.bos.cache.tempfile.RedisTempFileCache.saveAsUrl(String, InputStream, int) 把文件流暂存到redis中,并返回临时缓存地址(含附件服务器访问链接在内的绝对路径)。
在文件临时缓存成功之后,第三个请求实现了保存附件相关信息,即向表 t_bd_attachment (页面标识:bd_attachment)中插件一条数据记录。后台针对该请求在 kd.bos.web.actions.FormAction.batchInvokeAction(HttpServletRequest, HttpServletResponse)中进行响应,
反编译源码分析可知,后台先将请求转发至 kd.bos.mservice.form.FormService.batchInvokeAction(String, String) 。
然后转发至 kd.bos.mvc.form.FormController.postData(List<Map<String, Object>>) 。
最后再转发至附件字段控件编程模型的方法 kd.bos.form.field.AttachmentEdit.postBack(Object, int, int) 。其中,平台通过附件字段模型的方法 kd.bos.mvc.attachment.AttachmentFieldModel.saveAttachments(IDataModel, String, String, List<Map<String, Object>>) 来记录附件信息。
各位看官请注意:在该方法中,平台底层是通过工具类 kd.bos.servicehelper.attachment.AttachmentFieldServiceHelper 中的 saveAttachments(String, String, List<Map<String, Object>>) 来实现该功能。
2. 在用户维护完前端界面数据之后点击“保存/提交”,此时在浏览器可看到只有一次请求:http://localhost:8080/ierp/form/batchInvokeAction.do?appId=kdec_testapp&f=kdec_demobill&ac=save
通过分析可知,后台还是在 kd.bos.web.actions.FormAction.batchInvokeAction(HttpServletRequest, HttpServletResponse) 中进行响应。
然后依次将请求转发至 kd.bos.form.control.Toolbar.itemClick(String, String)
→ kd.bos.mvc.form.FormView.invokeOperation(String, OperateOption)
→ kd.bos.form.operate.FormOperate.execute()
→ kd.bos.form.operate.EntityOperate.invokeOperation()
→ kd.bos.entity.operate.DefaultEntityOperate.callEntityOperate()
→ kd.bos.entity.operate.Save.callBillOperationService()
→ kd.bos.service.operation.OperationServiceImpl.localInvokeOperation(String, DynamicObject[], OperateOption)
→ kd.bos.service.operation.EntityOperateService.excute(DynamicObject[])
→ kd.bos.service.operation.Save.callOperationTransaction(DynamicObject[])
→ kd.bos.service.operation.EntityOperateService.persistAttachments(DynamicObject[])
在这里,我们可以看到平台底层通过 kd.bos.service.attachment.IAttachmentOperateService.persistAttachments(OperateOption, DynamicObject[]) 来实现附件面板 & 附件字段的持久化。在该方法中,通过调用接口 kd.bos.servicehelper.attachment.AttachmentFieldServiceHelper.saveTempAttachments(String) 来实现文件持久化到附件服务器,并更新附件信息(bd_attachment)。
备注:
在 AttachmentFieldServiceHelper.saveTempAttachments(String) 方法中,先查询附件(bd_attachment)信息,然后根据查询到的附件信息调用接口 kd.bos.servicehelper.attachment.AttachmentFieldServiceHelper.saveTempToFileService(String, Object, String) 将 redis缓存中的文件流持久化到附件服务器上,并返回附件在服务器上的存储地址。最后,通过接口 kd.bos.servicehelper.operation.SaveServiceHelper.save(DynamicObject[]) 更新附件信息(bd_attachment)。
附件持久化成功之后,最后一步就是保存单据数据。在 kd.bos.service.operation.Save.callOperationTransaction(DynamicObject[]) 接口中继续将请求转发至 kd.bos.service.operation.Draft.executeOperate(DynamicObject[]) 。
在这个方法中,通过 kd.bos.servicehelper.operation.SaveServiceHelper.save(DynamicObject[]) 实现了单据业务数据的保存。
至此,在前端界面的附件字段上传文件后,点击“保存/提交”将附件和业务单据数据持久化的过程就全部结束了。通过以上分析,我们可知,整个过程涉及 4 个部分数据的存储:附件文件持久化到附件服务器上、向表 t_bd_attachment 中插入一条附件信息数据、保存业务单据数据、向中间表(即:附件字段上配置的表名)插入一条数据记录业务单据和附件信息的关联信息。
功能实现主要过程
注意事项
1. 附件字段支持在单据体列中使用。
2. 附件字段的控件编程模型:kd.bos.form.field.AttachmentEdit,派生自多选基础资料控件编程模型MulBasedataEdit。
3. 附件字段操作工具类:kd.bos.servicehelper.attachment.AttachmentFieldServiceHelper。
参考资料
https://club.kdcloud.com/article/182623
https://vip.kingdee.com/article/198561339718310912