文件服务.二开案例.第三方文件服务集成至金蝶云星空

栏目:云星空知识作者:金蝶来源:金蝶云社区发布:2024-09-23浏览:2

文件服务.二开案例.第三方文件服务集成至金蝶云星空

本文档将介绍如何将一个第三方的文件服务实例集成到金蝶云星空下,使得后续我们可以在金蝶云星空直接使用该文件服务实例上传下载附件。

发布版本:7.5

上线日期:2020年4月30日

补丁号:PT-146833

新特性介绍

  • 可实现将一个第三方的文件服务实例集成到金蝶云星空下,使得后续我们可以在金蝶云星空直接使用该文件服务实例上传下载附件。

操作指引

1元数据设计,创建连接配置界面

2元数据设计,拓展文件服务设置界面

3编码实现,连接配置表单插件

4编码实现,第三方存储服务插件

5预插脚本,存储服务插件表格

特性效果展示

本文档将以金蝶云盘·企业云存储作为示例,下图为实现后的效果。




1、操作步骤

1.1、元数据设计

1.1.1、创建连接配置界面

1)首先新建动态表单,必须继承自 BOS => 基对象模板 => 动态表单 => 云存储配置模板,下图为初始界面。

2)然后开始添加连接配置信息需要使用到的相关字段,如账号密码,如服务器地址等等,关键是看你使用的文件服务实例需要提供什么连接配置信息。

此处以金蝶云盘·企业云存储为例,需用户提供账号、密码以及企业编码。下图为示例界面,其中如果不希望填写内容被展示,可勾选密码字段属性。

3)注册表单插件,待1.2.1步骤表单插件编写完毕,可回来注册表单插件。

1.1.2、拓展文件服务设置界面

二开拓展BOS_FileServerConfig文件服务设置动态表单,此为初始界面。

1)首先向“文件存储位置”枚举类型下添加“金蝶云盘·企业云存储”枚举项,如下图。可选中“单选按钮组”字段,通过枚举类型属性添加新的枚举项。

注意:此处的枚举值,为给标准产品预留枚举值防止冲突,自定义的枚举值可用英文字符从a开始。同时记住该枚举值,后续做预插数据时,需要通过该枚举值映射1.2.2第三方存储服务插件


2)接着拖“单选按钮”字段到“单选按钮组”里面,单选按钮的字段的名称、标题改为“金蝶云盘·企业云存储”,组别属性选择“单选按钮组”,枚举项属性选择新添加的“金蝶云盘·企业云存储”枚举项,勾选即时触发值更新事件属性,如下图所示。

1.2、编码实现

1.2.1、连接配置表单插件

1)创建表单插件,继承Kingdee.BOS.Business.PlugIn.CloudServiceCfgPlugin, Kingdee.BOS.Business.PlugIn 存储配置基类。

2)必须实现的方法有四个,GetStorageServiceClassName返回第三方存储服务插件名、BuildKernelXml读取界面信息构建配置信息的xml字符串、FillFieldDataByKernelXml解析配置信息xml字符串绑定界面数据、BuildConnectConfigDic读取界面信息构建配置信息字段,用以实现连接配置信息存储跟界面数据绑定。

注意:GetStorageServiceClassName()方法需要返回1.2.2步骤的第三方存储服务插件名。

3)可选实现的方法有两个,SetKernelFieldDisabled、CheckKernelFieldIsModified,用以实现连接配置被使用后,相关字段是否可修改的控制。

4)插件编写完成后,需注册至连接配置界面的表单插件上,如下图:

5)如下为示例代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Xml;
using Kingdee.BOS.Business.PlugIn;
using Kingdee.BOS.Util;
 
namespace Running.Sample.PlugIn.Others
{
    /// <summary>
    /// 金蝶云盘·企业云存储连接配置的表单插件。
    /// </summary>
    [Description("金蝶云盘·企业云存储连接配置的表单插件"), HotUpdate]
    public class KdPanEntpCloudCfgPlugIn : CloudServiceCfgPlugin
    {
        //字段标识。
        private const string FUserName = "FUserName"; //用户名。
        private const string FPassword = "FPassword"; //密码。
        private const string FOrgId = "FOrgId"; //企业编码。
 
        /// <summary>
        /// 返回存储服务插件类名。
        /// </summary>
        /// <returns></returns>
        public override string GetStorageServiceClassName()
        {
            return "Running.Sample.PlugIn.Others.KdPanEntpCloudStorageServicePlugin, Running.Sample.PlugIn";
        }
 
        /// <summary>
        /// 构建并返回配置信息的自定义XML字符串,用以保存文件服务的连接配置信息进数据库。
        /// </summary>
        /// <returns></returns>
        protected override string BuildKernelXml()
        {
            StringBuilder strXml = new StringBuilder();
            strXml.Append("<ConfigMetadata>\n");
            //调整此处逻辑,改为自己的连接配置字段即可。
            strXml.AppendFormat("<UserName>{0}</UserName>\n", GetParamValue(FUserName));
            strXml.AppendFormat("<Password>{0}</Password>\n", GetParamValue(FPassword));
            strXml.AppendFormat("<OrgId>{0}</OrgId>\n", GetParamValue(FOrgId));
            strXml.Append("</ConfigMetadata>\n");
            return strXml.ToString();
        }
 
        /// <summary>
        /// 传入 BuildKernelXml 方法构建的自定义配置信息,将其解析并填充到界面各自定义字段上,
        /// </summary>
        /// <param name="kernelXml">由 BuildKernelXml 方法构建的自定义配置信息。</param>
        protected override void FillFieldDataByKernelXml(string kernelXml)
        {
            XmlNode node = ConvertKernelXmlToXmlNode(kernelXml);
            if (node == null) return;
 
            foreach (XmlNode nod in node.ChildNodes)
            {
                switch (nod.Name)
                {
                    case "UserName": this.View.Model.SetValue(FUserName, nod.InnerText); break;
                    case "Password": this.View.Model.SetValue(FPassword, nod.InnerText); break;
                    case "OrgId": this.View.Model.SetValue(FOrgId, nod.InnerText); break;
                }
            }
        }
 
        /// <summary>
        /// 构建并返回云存储配置信息的字典集合,用以在云存储服务插件内解析该云存储配置是否可用。
        /// </summary>
        /// <returns></returns>
        protected override Dictionary<string, string> BuildConnectConfigDic()
        {
            Dictionary<string, string> dicConnConfig = new Dictionary<string, string>();
            dicConnConfig["UserName"] = GetParamValue(FUserName);
            dicConnConfig["Password"] = GetParamValue(FPassword);
            dicConnConfig["OrgId"] = GetParamValue(FOrgId);
            return dicConnConfig;
        }
 
        /// <summary>
        /// 连接配置一旦被使用过了,本质上要求核心字段不可再更改,此处可设置哪些字段不可用,
        /// 如此处要求连接配置一旦被使用,用户名及企业编码不再允许更改。
        /// </summary>
        protected override void SetKernelFieldDisabled()
        {
            //不允许修改账号及企业编码,但允许修改密码及是否启用等,防止密码变了但是不可改配置信息的情况。
            this.View.GetControl(FUserName).Enabled = false;
            this.View.GetControl(FOrgId).Enabled = false;
        }
 
        /// <summary>
        /// 保存时,判断连接配置是否已使用,同时检查配置信息核心字段是否已被修改过。
        /// </summary>
        /// <param name="kernelXml">由 BuildKernelXml 方法构建的存储自定义配置信息的XML字段值。</param>
        /// <returns>返回配置信息核心字段是否已被修改过。</returns>
        protected override bool CheckKernelFieldIsModified(string kernelXml)
        {
            //界面填写值。
            string userName = GetParamValue(FUserName);
            string orgId = GetParamValue(FOrgId);
 
            //数据库存储值。
            XmlNode node = ConvertKernelXmlToXmlNode(kernelXml);
            if (node == null) return false;
 
            foreach (XmlNode nod in node.ChildNodes)
            {
                if (nod.Name == "UserName" && nod.InnerText != userName)
                    return true;
                if (nod.Name == "OrgId" && nod.InnerText != orgId)
                    return true;
            }
            return false;
        }
    }
}

 

1.2.2、第三方存储服务插件

1)创建插件类,继承Kingdee.BOS.Core.FileServer.AbstractCloudStorageServicePlugin, Kingdee.BOS.Core 存储服务插件基类。

2)必须实现的方法:

KdPanEntpCloudStorageServicePlugin()、KdPanEntpCloudStorageServicePlugin(int fileServiceId)构造函数,GetFormId返回第三方连接配置表单的唯一标识,即1.1.1创建的动态表单唯一标识,TestConnect测试云存储实例是否可用,SaveFile文件保存,DeleteFile文件删除,DownloadFile文件下载、DownloadFileByPart文件分块下载。

3)可选实现的方法:

GetFormTitle返回连接配置界面标题,默认为:云存储配置模板。

4)存储服务插件编写完成后,需执行预插脚本,可参考1.3步骤的格式,用以关联第三方连接配置。

5)如下为示例代码:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Text;
using Kingdee.BOS;
using Kingdee.BOS.Core.FileServer;
using Kingdee.BOS.Core.Objects.CloudObject;
using Kingdee.BOS.FileServer.Core.StorageService;
using Kingdee.BOS.JSON;
using Kingdee.BOS.ServiceHelper;
 
namespace Running.Sample.PlugIn.Others
{
    [Description("金蝶云盘·企业云存储服务插件")]
    public class KdPanEntpCloudStorageServicePlugin : AbstractCloudStorageServicePlugin
    {
        private readonly KdEntpCloudStorageService _service = new KdEntpCloudStorageService();
        private readonly ConcurrentDictionary<int, KdEntpCloudStorageServiceArgs> _connArgDic =
            new ConcurrentDictionary<int, KdEntpCloudStorageServiceArgs>();
 
        public KdPanEntpCloudStorageServicePlugin()
            : base(0)
        { }
 
        public KdPanEntpCloudStorageServicePlugin(int fileServiceId)
            : base(fileServiceId)
        { }
 
        /// <summary>
        /// 返回第三方连接配置表单的唯一标识。
        /// </summary>
        /// <returns></returns>
        public override string GetFormId()
        {
            return "YZDS_KingdeeEntpCloudStorageCfg";
        }
 
        /// <summary>
        /// 测试云存储实例是否可用。
        /// </summary>
        /// <param name="jArray">云存储连接配置信息。</param>
        /// <returns>返回是否可用。</returns>
        public override bool TestConnect(JSONArray jArray)
        {
            //此处拿到连接配置信息JSONArray,可根据实际第三方文件服务要求,验证连接是否可用。
            if (jArray == null || jArray.Count <= 0) return false;
            Dictionary<string, string> dict = jArray[0] as Dictionary<string, string>;
            if (dict == null) return false;
 
            KdEntpCloudStorageServiceArgs connArgs = new KdEntpCloudStorageServiceArgs
            {
                UserName = dict["UserName"],
                Password = dict["Password"],
                OrgId = dict["OrgId"]
            };
            return _service.TestConnect(connArgs);
        }
 
        /// <summary>
        /// 传入登录上下文环境及云存储服务参数以保存文件,返回文件编码。
        /// </summary>
        /// <param name="ctx">登录上下文环境。</param>
        /// <param name="file">
        /// <p>云存储服务参数信息,须传入参数:</p>
        /// 1、CloudFile.FileUrl:文件在本地的物理路径。
        /// 2、CloudFile.FileName:文件名。
        /// 3、CloudFile.UploadDir:文件上传至云存储的目录。
        /// </param>
        /// <returns>返回文件编码。</returns>
        public override string SaveFile(Context ctx, CloudFile file)
        {
            //此处拿到文件信息,通过 FileServiceId 连接配置内码拿到连接配置信息,保存文件至第三方文件服务实例,返回文件唯一编码。
            file.UploadDir = string.Format("{0}/{1}", ctx.DBId, file.UploadDir);
            KdEntpCloudStorageServiceArgs connArgs = LoadConfigInfo(ctx, FileServiceId);
            return _service.SaveFile(connArgs, file);
        }
 
        /// <summary>
        /// 传入登录上下文环境及云存储服务参数以删除文件,返回是否删除成功。
        /// </summary>
        /// <param name="ctx">登录上下文环境。</param>
        /// <param name="file">
        /// <p>云存储服务参数信息,须传入参数:</p>
        /// 1、CloudFile.FileUrl:文件编码。
        /// 2、CloudFile.FileStoragePluginID:连接配置内码。
        /// </param>
        /// <returns>返回是否删除成功。</returns>
        public override bool DeleteFile(Context ctx, CloudFile file)
        {
            //此处拿到文件编码,通过 FileStoragePluginID 连接配置内码拿到连接配置信息,从第三方文件服务实例上删除该附件。
            KdEntpCloudStorageServiceArgs connArgs = LoadConfigInfo(ctx, file.FileStoragePluginID);
            return _service.DeleteFile(connArgs, file);
        }
 
        /// <summary>
        /// 传入登录上下文环境及云存储服务参数以下载文件,返回文件完整字节数组。
        /// </summary>
        /// <param name="ctx">登录上下文环境。</param>
        /// <param name="file">
        /// <p>云存储服务参数信息,须传入参数:</p>
        /// 1、CloudFile.FileUrl:文件编码。
        /// 2、CloudFile.FileStoragePluginID:连接配置内码。
        /// </param>
        /// <returns>返回文件完整字节数组。</returns>
        public override byte[] DownloadFile(Context ctx, CloudFile file)
        {
            //此处拿到文件编码,通过 FileStoragePluginID 连接配置内码拿到连接配置信息,
            //  从第三方文件服务实例上下载该附件,返回完整的字节数组。
            KdEntpCloudStorageServiceArgs connArgs = LoadConfigInfo(ctx, file.FileStoragePluginID);
            return _service.DownloadFile(connArgs, file);
        }
 
        /// <summary>
        /// 分块下载附件,默认分块大小建议为4M,按分块返回附件字节数组。
        /// </summary>
        /// <param name="ctx">登录上下文环境。</param>
        /// <param name="file">
        /// <p>云存储服务参数信息,须传入参数:</p>
        /// 1、CloudFile.FileStoragePluginID:连接配置内码。
        /// 2、CloudFile.FileUrl:文件编码。
        /// 3、CloudFile.StartIndex:分块下载时的起始字节下标。
        /// </param>
        /// <returns>按分块返回附件字节数组。</returns>
        public override byte[] DownloadFileByPart(Context ctx, CloudFile file)
        {
            //此处拿到文件编码及起始字节下标,通过 FileStoragePluginID 连接配置内码拿到连接配置信息,
            //  从第三方文件服务实例上下载该附件,返回分块的字节数组。
            KdEntpCloudStorageServiceArgs connArgs = LoadConfigInfo(ctx, file.FileStoragePluginID);
            return _service.DownloadFileByPart(connArgs, file);
        }
 
        /// <summary>
        /// 传入登录上下文环境,返回表单标题。
        /// </summary>
        /// <param name="ctx">登录上下文环境。</param>
        /// <returns>返回表单标题。</returns>
        public override LocaleValue GetFormTitle(Context ctx)
        {
            return new LocaleValue("金蝶云盘·企业云存储", ctx.UserLocale.LCID);
        }
 
        /// <summary>
        /// 传入登录上下文环境及连接配置内码,返回连接参数对象。
        /// </summary>
        /// <param name="ctx">登录上下文环境。</param>
        /// <param name="fileServiceId">连接配置内码.</param>
        /// <returns>返回连接参数对象。</returns>
        private KdEntpCloudStorageServiceArgs LoadConfigInfo(Context ctx, int fileServiceId)
        {
            KdEntpCloudStorageServiceArgs connArgs;
            if (_connArgDic.TryGetValue(fileServiceId, out connArgs))
            {
                return connArgs;
            }
 
            string configXml = string.Empty;
            string sSql = string.Format("SELECT FKERNELXML FROM T_Meta_StorageService WHERE FID={0}", fileServiceId);
            using (IDataReader reader = DBServiceHelper.ExecuteReader(ctx, sSql))
            {
                if (reader.Read())
                {
                    configXml = reader["FKERNELXML"].ToString();
                }
            }
            Dictionary<string, string> dictConfig = base.ParseEncConnConfig(configXml);
 
            connArgs = new KdEntpCloudStorageServiceArgs
            {
                UserName = dictConfig.ContainsKey("UserName") ? dictConfig["UserName"] : string.Empty,
                Password = dictConfig.ContainsKey("Password") ? dictConfig["Password"] : string.Empty,
                OrgId = dictConfig.ContainsKey("OrgId") ? dictConfig["OrgId"] : string.Empty
            };
            _connArgDic.TryAdd(fileServiceId, connArgs);
            return connArgs;
        }
    }
}

1.3、预插脚本

1.3.1、存储服务插件表格

1)此处的FID内码以及FValue,为给标准产品预留一定区间以防止冲突,可从100开始,同时需留意,该值必须与1.1.1连接配置界面添加的枚举值保持一致,用于建立连接配置界面与存储服务插件的关联关系。

/****** Object:Data       Script Date: 2020-03-23 17:44:00 ******/
DELETE T_BAS_STORAGESERVICEPLUGIN WHERE FID = 100;
INSERT INTO T_BAS_STORAGESERVICEPLUGIN(FID,FSTORAGESERVICECLASS,FVALUE) VALUES (100,'Running.Sample.PlugIn.Others.KdPanEntpCloudStorageServicePlugin, Running.Sample.PlugIn',100) ;

2)同时由于历史版本附件信息表FFILESTORAGE附件存储类型字段存在兼容性,还需执行下面脚本调整字段长度。

/****** Object:Data       Script Date: 2020-03-23 17:44:00 ******/
EXEC p_AlterColumn 'T_BAS_Attachment','FFILESTORAGE','VARCHAR(3)','NULL','0101','''0''';
EXEC p_AlterColumn 'T_BAS_FILESERVICETRANHIS','FFILESTORAGETYPE','VARCHAR(3)','NULL','0101','''0''';


文件服务.二开案例.第三方文件服务集成至金蝶云星空

本文档将介绍如何将一个第三方的文件服务实例集成到金蝶云星空下,使得后续我们可以在金蝶云星空直接使用该文件服务实例上传下载附件。发布...
点击下载文档
确认删除?
回到顶部
客服QQ
  • 客服QQ点击这里给我发消息