using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TellerSystem.Contract.ContractBase;
using Platform.Print;
using System.IO;
using TellerSystem.ServiceProxy.Ext.TradeBusinessEntitys;
using TellerSystem.Communication;
using TellerSystem.PrintTemplate.Base;
using Platform.Common.LogSystem;
using TellerSystem.ServiceProxy.Ext.ServiceHelper;
using Platform.Presentation.PageFunctions;
using System.Windows;
using TellerSystem.Communication.SocketListener;
using TellerSystem.Library.Ext.Function;
using TellerSystem.PrintTemplate.ElecSeal;
using System.Collections;
using Platform.ServiceProxy.ServiceHelper;
using Platform.Common.RunningParameters;
using System.Linq.Expressions;
using System.Windows.Controls;
using Newtonsoft.Json;
using TellerSystem.Contract.Function;
using System.Data;
using System.Threading.Tasks;
using TellerSystem.ServiceProxy.Ext;
using Platform.Common.BasicFunctions;
using Platform.Presentation.Interfaces;
using System.Text.RegularExpressions;

namespace TellerSystem.Contract.Entry
{
    /// <summary>
    /// 无纸化处理类
    /// </summary>
    public class PaperlessEntry : EntryBase
    {
        #region Prop
        private Dictionary<string, string> customizeIndexMap = new Dictionary<string, string>();
        private Dictionary<Scene.Biz, string> bizMap = new Dictionary<Scene.Biz, string>();

        /// <summary>
        /// 交易自定义索引赋值
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public string this[string key]
        {
            get
            {
                var val = string.Empty;
                if (customizeIndexMap.ContainsKey(key))
                    val = customizeIndexMap[key];
                return val;
            }
            set
            {
                if (!string.IsNullOrEmpty(key))
                {
                    customizeIndexMap[key] = value;
                    if (CurrentScene != null)
                    {
                        var item = CurrentScene.IndexMap.FirstOrDefault(x => x.Key.Item1 == key);
                        if (item.Key != null)
                            CurrentScene.IndexMap[item.Key] = value;
                    }
                }
            }
        }

        /// <summary>
        /// 设置业务规则赋值
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public string this[Scene.Biz key]
        {
            get
            {
                var val = string.Empty;
                if (bizMap.ContainsKey(key))
                    val = bizMap[key];
                return val;
            }
            set
            {
                if (key != Scene.Biz.SerialNo)//排除业务流水号的设置，交易中无法获取
                    bizMap[key] = value;
            }
        }

        /// <summary>
        /// 档案采集状态，是否完成采集工作
        /// </summary>
        public bool IsPicked { get; private set; }

        /// <summary>
        /// 客户是否在现场
        /// </summary>
        public bool HasCustomer { get; private set; }

        protected override bool OnLoadData(bool changed)
        {
            //如果配置参数和流水号未变化，则无需更新
            if (!changed && (CurrentScene != null && CurrentScene.Sid == PageData.TradeInfo.SystemSerialNumber)) return true;
            Clear();//进行清理动作
            //初始化无纸化对象
            var scene = new Scene(null);
            scene.LoadData(PageData, MessageData);
            if (scene.Init(customizeIndexMap, bizMap))
                CurrentScene = scene;
            return true;
        }

        /// <summary>
        /// 当前场景配置
        /// </summary>
        public Scene CurrentScene
        {
            get;
            private set;
        }

        #endregion

        #region Method
        /// <summary>
        /// 清理资源，防止干扰
        /// </summary>
        public void Clear()
        {
            CurrentScene = null;
            _paperlessList = null;
            HasCustomer = false;
            IsPicked = false;
        }

        /// <summary>
        /// 需要进行无纸化的凭证清单
        /// </summary>
        Dictionary<TemplateBase, Scene.Doc> _paperlessList;

        /// <summary>
        /// 预处理凭证效果
        /// </summary>
        /// <param name="list"></param>
        /// <param name="target"></param>
        /// <returns></returns>
        public bool Check(List<PrintContainer> list, IPage target)
        {
            //若未进行初始化，需进行补偿操作
            if (PageData == null && target is PageBase)
            {
                //提取最近一次通讯对象，尝试作为message对象
                if (!LoadData(target as PageBase, target.GetTradeInfoData().LatestMessage ?? new Message()))
                    return false;
            }
            PageData.Log2PagePrinted("PaperlessEntry.Check");
            var page = PageData;
            //检查交易的无纸化配置情况，包括索引结构和凭证清单
            if (CurrentScene == null) return false;
            //检索凭证配置，哪些需要做无纸化
            var paperlessList = new Dictionary<TemplateBase, Scene.Doc>();
            list.ForEach(x =>
            {
                //依据配置情况，确认凭证是参与无纸化
                var doc = CurrentScene.MatchVoucher(x as TemplateBase);
                if (doc != null)
                {
                    //呈现打印容器,否则无法正确判断凭证的印章情况
                    if (x.Parent == null)
                    {
                        //获取背景画刷
                        var background = x.Background;
                        x.Background = System.Windows.Media.Brushes.Transparent;
                        var window = new Window { Content = new ScrollViewer { Content = x, VerticalScrollBarVisibility = ScrollBarVisibility.Visible, HorizontalScrollBarVisibility = ScrollBarVisibility.Visible }, SizeToContent = System.Windows.SizeToContent.WidthAndHeight };
                        window.ShowInTaskbar = false;
                        window.WindowState = WindowState.Normal;
                        window.Top = 0 - 768;
                        window.Left = 0 - 1024;
                        window.Show();
                        window.Close();
                        x.Background = background;
                    }
                    //若凭证未设置机构印章，也不参与无纸化处理
                    if (x is TemplateBase && (x as TemplateBase).ElecSealHelper.GenBranchSignature().Count > 0)
                        paperlessList.Add(x as TemplateBase, doc);
                }
            });
            if (paperlessList.Count == 0) return false;
            //检查是否需要提醒柜员选择模式
            var sign2Customer = paperlessList.Keys.ToList().Exists(p => { return p.SignPoint.Count > 0; });//检查是否存在客户签字
            //采集索引数据
            if (!CurrentScene.IndexPick()) return false;
            if (sign2Customer && !new string[] { "1", "2", "" }.Contains(CurrentScene.Ref(Scene.Biz.CusKind)))//若当笔业务无客户参与，则CusKind为空
            {
                page.TradeAPIs.ShowWarnMessageBox("当前业务需要签字且非个人或对公客户，无法执行无纸化流程！");
                return false;
            }
            //采集档案
            if (DocPicker() != true) return false;
            _paperlessList = paperlessList;
            return true;
        }

        /// <summary>
        /// 处理打印清单，进行无纸化筛选处理
        /// </summary>
        /// <param name="pList"></param>
        public void Proc(Dictionary<PrintContainer, Tuple<FileInfo, TT_PrintData>> pList)
        {
            if (_paperlessList == null || _paperlessList.Count == 0) return;
            //遍历处理无纸化
            var list = _paperlessList.Select(x =>
             {
                 var tp = x.Key;
                 var doc = x.Value;
                 if (!pList.ContainsKey(tp)) return null;
                 var vid = pList[tp].Item2.Id;
                 var xps = pList[tp].Item1;
                 //获取凭证的机构签署情况，(印章类型,关键字)
                 var dict = tp.ElecSealHelper.GenBranchSignature();
                 if (dict == null || dict.Count == 0) return null;
                 return new Tuple<TemplateBase, Scene.Doc, string, FileInfo>(x.Key, x.Value, vid, xps);
             }).Where(x => x != null).ToList();
            //后续处理
            if (list.Count > 0)
            {
                //执行预览和加签操作
                var docv = PageFactory.CreatePage("Trade.OperationManage.DOCVIEWER");
                if (docv == null)
                {
                    PageData.TradeAPIs.ShowWarnMessageBox("无法打开无纸化传票预览页面！请检查...");
                    return;
                }
                docv.SetViewStateData(new Dictionary<string, object> { { "xpsList", list }, { "scene", CurrentScene }, { "hasCustomer", HasCustomer } });
                PageData.ShowPage(docv, "无纸化传票预览");
                //预览结果
                if (!docv.ViewStateData.ContainsKey("signResult") || docv.ViewStateData["signResult"] != "通过")
                {
                    PageData.TradeAPIs.ShowWarnMessageBox("转到纸质凭证处理流程！");
                    return;
                }
                CurrentScene.DoStatus(4);//进入归档阶段
                //补充无纸化流水到拓展表
                PageData.TradeAPIs.TraderecordsExp("PaperlessNo", CurrentScene.Id);
                //移除打印列表
                list.ForEach(p => pList.Remove(p.Item1));
                PageData.PagePrinted += PageData_PagePrinted;
                PageData.Log2PagePrinted("PaperlessEntry.Proc");
                //异步触发影像档案归档
                Task task = new Task(() =>
                {
                    //触发影像档案归档
                    CurrentScene.Archive();
                });
                task.Start();
            }
        }

        void PageData_PagePrinted(object sender, EventArgs e)
        {
            PageData.PagePrinted -= PageData_PagePrinted;
            //检查附件数量,若为0则不提示跳转
            int cnt = CurrentScene.CountMarkup();
            if (cnt == 0) return;
            //触发附件补录流水交易
            if (CurrentScene["End2Link"] != "无"
                && PageData.TradeAPIs.ShowQuestionMessageBox("本次无纸化交易包含附件(" + cnt + "份)，是否联动附件流水打印？") == MessageBoxResult.Yes)
            {
                PageData.OpenTradePage("Trade.OperationManage.PAPERLESS", TradePageExtension.OpenOptions.Tab,
                    new Dictionary<string, object> { 
                    { "Paperless.Id", CurrentScene.Id } 
                });
            }
        }

        internal bool? DocPicker(Dictionary<string, string> notify = null)
        {
            //非无纸化场景，不处理
            if (CurrentScene == null) return null;
            if (IsPicked) return true;
            //配置无影像附件
            if (CurrentScene["Annex"] == "无") return true;
            do
            {
                //调用采集页面
                var picker = PageFactory.CreatePage("Trade.OperationManage.DOCPICKER");
                if (picker == null)
                {
                    PageData.TradeAPIs.ShowInfoMessageBox("无法打开影像档案采集页面！请检查...");
                    return false;
                }
                picker.SetViewStateData(new Dictionary<string, object> { { "Scene", CurrentScene }, { "NotifyDict", notify } });
                var ret = PageData.ShowPage(picker, "影像档案采集");
                //检查返回数据
                if (ret != null && ret.ContainsKey("status") && true.Equals(ret["status"]))
                {
                    HasCustomer = true.Equals(ret["hasCustomer"]);
                    IsPicked = true;
                    //更新数据库状态
                    CurrentScene.DoStatus(2);
                    return true;
                }
                //柜外清推送，客户取消确认的情况，直接中断业务流程
                if (ret != null && ret.ContainsKey("failed_Notify2Customer") && true.Equals(ret["failed_Notify2Customer"]))
                    return false;
                //失败情况下，询问重试
                var flag = PageData.TradeAPIs.ShowQuestionMessageBox("影像档案采集未完成，是否重新采集？\n选择“否”，则该笔业务按照有纸化流程进行！\n选择“取消”，则中断该笔业务！", new string[] { "是", "否", "取消" }, 0, "询问");
                if (flag == "取消") return false;
                if (flag == "否")
                {
                    CurrentScene.DoStatus(33);
                    Clear();//清理数据，转向有纸化流程
                    return null;
                }
            } while (true);
        }
        #endregion

        #region Scene
        /// <summary>
        /// 无纸化场景实例
        /// </summary>
        public class Scene : EntryBase
        {
            /// <summary>
            /// 无纸化实例id
            /// </summary>
            public String Id { get; private set; }

            /// <summary>
            /// 柜面流水id
            /// </summary>
            public String Sid { get; private set; }

            /// <summary>
            /// 场景id
            /// </summary>
            public String Pid { get; private set; }

            /// <summary>
            /// 场景编码，影像平台用
            /// </summary>
            public String Node { get; private set; }

            /// <summary>
            /// 影像档案id
            /// </summary>
            public string DocId { get; private set; }

            /// <summary>
            /// 执行状态
            /// </summary>
            public int Status { get; private set; }

            /// <summary>
            /// 场景描述
            /// </summary>
            public string Rem { get; private set; }

            /// <summary>
            /// 发起时间
            /// </summary>
            public DateTime? Initiated { get; private set; }

            /// <summary>
            /// 完成时间
            /// </summary>
            public DateTime? Completed { get; private set; }

            /// <summary>
            /// 描述
            /// </summary>
            public String Summary { get; private set; }

            /// <summary>
            /// 索引模型，(key,name,picker),value
            /// </summary>
            public Dictionary<Tuple<String, String, String>, string> IndexMap { get; private set; }

            /// <summary>
            /// 档案模型
            /// </summary>
            public List<Doc> Docs { get; private set; }

            #region Ext
            private Dictionary<string, string> ext = null;
            private void ParseExt(string str)
            {
                try
                {
                    ext = JsonConvert.DeserializeObject<Dictionary<string, string>>(str ?? string.Empty);
                }
                catch (Exception)
                {
                    throw new FormatException("无纸化场景扩展参数格式错误！\n" + str);
                }
            }

            /// <summary>
            /// 场景扩展参数
            /// </summary>
            /// <param name="key">参数关键字</param>
            /// <param name="def">默认值，若未配置则返回</param>
            /// <returns></returns>
            public string this[string key, string def = ""]
            {
                get
                {
                    if (ext != null && ext.ContainsKey(key))
                        return ext[key];
                    return def;
                }
            }
            #endregion

            /// <summary>
            /// 档案格式
            /// </summary>
            public class Doc
            {
                /// <summary>
                /// 0-采集，1-凭证（签字数据暂定放凭证同一目录）
                /// </summary>
                public int Kind { get; set; }

                /// <summary>
                /// 影像平台节点
                /// </summary>
                public string Node { get; set; }

                /// <summary>
                /// 档案类型描述
                /// </summary>
                public string Summary { get; set; }

                /// <summary>
                /// 最低数量要求
                /// </summary>
                public int Min { get; set; }

                /// <summary>
                /// 最高数量要求
                /// </summary>
                public int Max { get; set; }

                /// <summary>
                /// 凭证类型，当Kind=1时有效
                /// </summary>
                public List<string> Voucher { get; set; }
            }

            #region ctor
            /// <summary>
            /// 通过无纸化id加载对象，注意部分api不可以
            /// </summary>
            /// <param name="id"></param>
            public Scene(string id)
            {
                if (string.IsNullOrEmpty(id)) return;
                var rows = UserManagerHandle.ExecuteQuery("SELECT p.*,s.* FROM TPP_SCENE p LEFT JOIN TPS_PAPERLESS s ON p.ID =s.PID WHERE s.ID='" + id + "'");
                if (rows == null || rows.Length == 0) return;
                var cols = rows[0].Split('|');
                Pid = cols[0];
                Node = cols[2];
                Summary = cols[3];
                ParseExt(cols[5]);
                Id = cols[6];
                Sid = cols[7];
                var indexMap = JsonConvert.DeserializeObject<Dictionary<string, string>>(cols[9]);
                DocId = cols[10];
                Status = int.Parse(cols[11]);
                Rem = cols[12];
                Initiated = DateTime.ParseExact(cols[13], "yyyyMMddHHmmss", null);
                Completed = string.IsNullOrEmpty(cols[14]) ? (DateTime?)null : DateTime.ParseExact(cols[14], "yyyyMMddHHmmss", null);
                //继续加载模型
                if (!LoadModel()) return;
                //加载索引数据
                foreach (var item in indexMap)
                {
                    var f = IndexMap.Keys.FirstOrDefault(x => x.Item1 == item.Key);
                    if (f != null) IndexMap[f] = item.Value;
                }
            }
            #endregion

            #region Method 封装服务端接口（集中处理）
            /// <summary>
            /// 日期格式化，注意Oracle格式化串的特殊
            /// </summary>
            const string _SysDate_ = "to_char(sysdate,'yyyyMMddHH24miss')";

            /// <summary>
            /// 初始化场景实例
            /// </summary>
            /// <returns></returns>
            internal bool Init(Dictionary<string, string> customizeIndexMap, Dictionary<Biz, string> bizMap)
            {
                if (!string.IsNullOrEmpty(Id))
                    return false;
                //检查无纸化开关，已包含机构检查
                if (ElecSealHelper._MODE_ != SealMode.Paperless) return false;
                //加载
                var page = PageData;
                var msg = MessageData;
                //读取数据库配置，构建场景配置对象
                var rows = UserManagerHandle.ExecuteQuery("SELECT ID, CODE, NODE, SUMMARY, COND, EXT FROM TPP_SCENE WHERE CODE='" + page.TradeModel.Code + "' ORDER BY COND DESC ");
                if (rows == null || rows.Length == 0) return false;
                //进行场景匹配
                foreach (var row in rows)
                {
                    var cols = row.Split('|');
                    var id = cols[0];
                    var node = cols[2];
                    var summary = cols[3];
                    var cond = cols[4];
                    var ext = cols[5];
                    if (Match(page, msg, cond))
                    {
                        Pid = id;
                        Node = node;
                        Summary = summary;
                        ParseExt(ext);
                    }
                }
                if (String.IsNullOrEmpty(Pid)) return false;
                //获取索引模型和档案模型
                if (!LoadModel()) return false;
                _refMap2Biz = bizMap;
                //进行业务索引模型的数据抓取
                var indexMap = PickIndex(false);
                //填充交易自定义索引
                foreach (var item in customizeIndexMap)
                {
                    var key = IndexMap.Keys.FirstOrDefault(x => x.Item1 == item.Key);
                    if (key != null) IndexMap[key] = item.Value;
                }
                //需要添加无纸化实例，tps_paperless表
                Id = Guid.NewGuid().ToString("N").ToUpper();
                Sid = page.CreateSerialNumber();//.TradeInfo.SystemSerialNumber;//杜绝获取不到流水的情况
                Status = 0;
                Rem = Summary;
                var list = new ArrayList
                {
                    "DELETE FROM TPS_PAPERLESS WHERE SID = '" + Sid + "'",//去重，删除之前提交失败产生的无纸化流水
                    "INSERT INTO TPS_PAPERLESS(ID, SID, PID, INDEXMAP, DOCID, STATUS, REM, INITIATED, COMPLETED) "
                     + "VALUES('" + Id + "', '" + Sid + "', '" + Pid + "', '" + indexMap + "', '', " + Status + ", '" + Rem + "', " + _SysDate_ + ", '')"
                };
                return UserManagerHandle.ExecuteSQLTran(list);
            }

            internal bool IndexPick()
            {
                try
                {
                    var json = PickIndex(true);
                    //更新数据
                    return UserManagerHandle.ExecuteSql("UPDATE TPS_PAPERLESS SET INDEXMAP='" + json + "',STATUS = 1 WHERE ID='" + Id + "'") == 1;
                }
                catch (Exception ex)
                {
                    PageData.TradeAPIs.ShowErrorMessageBox("无纸化索引构建异常=>" + ex.Message);
                    return false;
                }
            }

            string PickIndex(bool completed)
            {
                var page = PageData;
                var msg = MessageData;
                foreach (var item in IndexMap.Keys.ToList())
                {
                    //如条目已经赋值，则无需重新赋值
                    if (IndexMap[item] != null) continue;
                    //逐条处理数据抓取，存入value
                    string val = null;
                    var index = item.Item3.IndexOf(':');
                    if (index <= 0) continue;
                    var picker = item.Item3.Substring(0, index);
                    var parm = item.Item3.Substring(index + 1);
                    //截取业务数据赋值操作
                    index = parm.IndexOf("=>");
                    var biz = Biz.None;
                    if (index > 0)
                    {
                        Enum.TryParse(parm.Substring(index + 2), out biz);
                        parm = parm.Substring(0, index);
                    }
                    switch (picker)
                    {
                        case "Env"://环境中获取
                            if (!completed) break;
                            var env = Env.None;
                            if (Enum.TryParse(parm, out env))
                                val = Ref(env);
                            break;
                        case "Biz"://依据业务流水获取
                            if (!completed) break;
                            if (Enum.TryParse(parm, out biz))
                                val = Ref(biz);
                            break;
                        case "Msg"://报文中获取
                            if (!completed) break;
                            val = (string)msg.GetFdVaule(parm);
                            break;
                        case "Msg0"://报文中获取，发送前
                            if (completed) break;
                            val = (string)msg.GetFdVaule(parm);
                            if (biz != Biz.None)
                                DoBiz(biz, val);
                            break;
                        case "UI"://页面中获取
                            if (completed) break;
                            var dict = page.PositioningUIText(parm.Split('&').ToList(), true);
                            if (dict.Count > 0)
                            {
                                val = dict.ElementAt(0).Value.Last().Value;
                                if (biz != Biz.None)
                                    DoBiz(biz, val);
                            }
                            break;
                        default:
                            throw new NotSupportedException("PickIndex:不支持的提取方式=>" + picker);
                    }
                    if (val != null)
                        IndexMap[item] = item.Item2 == "证件类型" ? PageData.TradeAPIs.ChgLicKind(val) : val;
                }
                //组织业务数据集合字段，json格式
                return JsonConvert.SerializeObject(IndexMap.ToDictionary(x => x.Key.Item1, x => x.Value));
            }

            private bool LoadModel()
            {
                //加载索引模型
                var rows = UserManagerHandle.ExecuteQuery("SELECT ID, IKEY, INAME, PICKER FROM TPP_INDEXMAP WHERE (ID = '" + Pid + "' OR ID is NULL) ORDER BY ID DESC");
                IndexMap = new Dictionary<Tuple<string, string, string>, string>();
                foreach (var row in rows)
                {
                    var cols = row.Split('|');
                    //检查重复项，以ikey为准
                    var item = IndexMap.FirstOrDefault(x => x.Key.Item1 == cols[1]);
                    if (item.Key != null) IndexMap.Remove(item.Key);
                    //若未指定名称，则视为无效
                    if (string.IsNullOrEmpty(cols[2])) continue;
                    IndexMap.Add(new Tuple<string, string, string>(cols[1], cols[2], cols[3]), null);
                }
                //加载档案模型
                rows = UserManagerHandle.ExecuteQuery("SELECT ID, KIND, NODE, SUMMARY, MINV, MAXV, VOUCHER FROM TPP_SCENEDOCS WHERE (ID = '" + Pid + "' OR ID is NULL) ORDER BY ID DESC");
                Docs = new List<Doc>();
                foreach (var row in rows)
                {
                    var cols = row.Split('|');
                    //检查重复项，以Summary为准
                    Docs.RemoveAll(x => x.Summary == cols[3]);
                    //若未指定node，则视为无效
                    if (string.IsNullOrEmpty(cols[2])) continue;
                    var min = int.Parse(cols[4]);
                    var max = int.Parse(cols[5]);
                    //重新计算max
                    if (max < min || max == 0) max = int.MaxValue;
                    Docs.Add(new Doc
                    {
                        Kind = int.Parse(cols[1]),
                        Node = string.Format(cols[2], Node),//支持node节点命名，嵌入大类节点名称。如：{0}FJ
                        Summary = cols[3],
                        Min = min,
                        Max = max,
                        Voucher = cols[6].Split('&').ToList(),
                    });
                }
                return true;
            }

            private bool Match(PageBase page, Message msg, string cond)
            {
                if (string.IsNullOrEmpty(cond)) return true;
                //解析规则，暂支持简单规则
                var tmp = cond.Split('=');
                if (tmp.Length == 2)//即域值判断0016=2001
                {
                    var val = msg.GetFdVaule(tmp[0]);
                    //支持总线的判断,左侧变量以@开头认定为总线取数
                    if (tmp[0].StartsWith("@"))
                        val = page.TradeBus[tmp[0].Substring(1)];
                    return tmp[1].Equals(val);
                }
                else if (tmp.Length == 1)//判断子交易类
                    return page.GetType().FullName == cond;
                throw new NotSupportedException("Match:不支持的无纸化场景场景参数=>" + cond);
            }

            /// <summary>
            /// 上传档案,返回服务端path
            /// </summary>
            /// <param name="doc">文档要求</param>
            /// <param name="file">文件</param>
            /// <param name="name">文档名称，如无默认用文件名</param>
            /// <param name="vid">对应的凭证实例id</param>
            /// <returns></returns>
            public string Upload(Doc doc, FileInfo file, String name = null, String vid = null)
            {
                if (!File.Exists(file.FullName)) return null;
                if (string.IsNullOrEmpty(name))
                    name = file.Name;
                return Upload(doc, File.ReadAllBytes(file.FullName), name, vid);
            }

            /// <summary>
            /// 上传档案,返回服务端path
            /// </summary>
            /// <param name="doc">文档要求</param>
            /// <param name="fileData">文件数据</param>
            /// <param name="name">文档名称</param>
            /// <param name="vid">对应的凭证实例id</param>
            /// <returns></returns>
            public string Upload(Doc doc, byte[] fileData, String name, String vid = null)
            {
                if (fileData == null || fileData.Length == 0 || string.IsNullOrEmpty(name)) return null;
                //指定服务端路径
                var fpath = "paperless/" + LoginUserInfo.TradeDate + "/" + LoginUserInfo.TellerNo + "/" + Id + "/" + doc.Node + "/" + name;
                TradeHandle.ExcuteShell("mkdir -p " + fpath.Substring(0, fpath.Length - name.Length));
                //上传文件
                if (!FilesHandle.SaveFileData(fpath, fileData, false)) return null;
                var cnt = UserManagerHandle.ExecuteSql("INSERT INTO TPS_DOCS(ID, NAME, NODE, FPATH, INITIATED, VID) "
                    + "VALUES('" + Id + "', '" + name + "', '" + doc.Node + "', '" + fpath + "', " + _SysDate_ + ", '" + vid + "')");
                if (cnt == 0) return null;
                return fpath;
            }

            public bool Signature2Bank(string vid, string reason, string place, Dictionary<SealKind, string> dict)
            {
                var cname = Ref(Env.KinbrName);
                var sqls = new ArrayList();
                //组织语句
                dict.ToList().ForEach(item =>
                {
                    var kind = 1;
                    var kindVal = new Dictionary<string, string>
                    {
                        {"BranchName",LoginUserInfo.KinbrName},
                        {"SealKind", item.Key.ToString()},
                    };
                    sqls.Add("INSERT INTO TPS_SIGNATURE(ID, VID, KIND, KINDVAL, POS, FSIZE, FPATH, KEYCODE, INITIATED, REASON, PLACE) "
                        + "VALUES('" + Id + "', '" + vid + "', " + kind + ", '" + JsonConvert.SerializeObject(kindVal)
                        + "', '0,0', '', '', '" + item.Value + "', " + _SysDate_ + ", '" + reason + "', '" + place + "')");
                });
                return UserManagerHandle.ExecuteSQLTran(sqls);
            }

            public bool Signature2Customer(string vid, string reason, string place, string fpath, Size size, List<Point> posList)
            {
                if (string.IsNullOrEmpty(fpath)) return false;
                //查找客户信息
                var kind = -1;
                var kindVal = new Dictionary<string, string>
                { 
                    {"Name",Ref(Biz.CusName)},
                    {"LicNo",Ref(Biz.LicNo)},
                    {"LicKind",Ref(Biz.LicKind)},
                };
                switch (Ref(Biz.CusKind))
                {
                    case "1":
                        kind = 2;//可补充其他信息
                        break;
                    case "2":
                        kind = 3;//可补充其他信息
                        break;
                    default:
                        throw new NotSupportedException("Signature2Customer:不支持的客户类型=>" + Ref(Biz.CusKind));
                }
                var sqls = new ArrayList();
                //组织语句
                posList.ForEach(pos =>
                {
                    sqls.Add("INSERT INTO TPS_SIGNATURE(ID, VID, KIND, KINDVAL, POS, FSIZE, FPATH, KEYCODE, INITIATED, REASON, PLACE) "
                        + "VALUES('" + Id + "', '" + vid + "', " + kind + ", '"
                        + JsonConvert.SerializeObject(kindVal) + "', '" + pos + "', '" + size + "', '" + fpath + "', '', " + _SysDate_ + ", '" + reason + "', '" + place + "')");
                });
                return UserManagerHandle.ExecuteSQLTran(sqls);
            }

            /// <summary>
            /// 调用电子签章系统进行签署，返回签署后文件的路径
            /// </summary>
            /// <param name="vid"></param>
            /// <returns></returns>
            public string Sign(string vid = null)
            {
                //调用服务端插件，触发电子签署
                string input = Sid + "," + vid;
                string output = "";
                PluginHandle.CallPlugin2Simple("paperless-1.0-SNAPSHOT", "com.dhcc.cloud.plugins.paperless.method.Sign", input, out output);
                return output;
            }

            /// <summary>
            /// 更新状态
            /// </summary>
            /// <param name="step"></param>
            /// <returns></returns>
            public bool DoStatus(int step)
            {
                var cnt = UserManagerHandle.ExecuteSql("UPDATE TPS_PAPERLESS SET STATUS=" + step + " WHERE ID='" + Id + "' ");
                this.Status = step;
                return cnt == 1;
            }

            /// <summary>
            /// 触发归档操作
            /// </summary>
            public void Archive()
            {
                //调用服务端插件，触发电子签署
                string input = JsonConvert.SerializeObject(new Dictionary<string, string> { { "sid", Sid } });
                string output = "";
                PluginHandle.CallPlugin2Simple("paperless-1.0-SNAPSHOT", "com.dhcc.cloud.plugins.paperless.method.DocManagerUpload", input, out output);
            }

            internal Doc MatchVoucher(TemplateBase voucher)
            {
                if (voucher == null) return null;
                return Docs.Find(d =>
                    {
                        if (d.Kind != 1 || d.Voucher.Count == 0) return false;
                        return d.Voucher.Exists(x =>
                            {
                                if (x == voucher.DocumentId) return true;
                                //是否存在继承关系,暂只支持从TemplateBase所在程序集中寻找
                                var t = System.Reflection.Assembly.GetAssembly(typeof(TemplateBase)).GetType(x);
                                return t != null && t.IsInstanceOfType(voucher);
                            });
                    });
            }

            /// <summary>
            /// 跳转到档案系统
            /// </summary>
            /// <param name="isReadonly">查看/编辑</param>
            public void Link2Dms(bool isReadonly)
            {
                if (string.IsNullOrEmpty(DocId))
                {
                    PageData.TradeAPIs.ShowWarnMessageBox("该笔交易还未上送影像档案系统，请检查...");
                    return;
                }
                //调用采集页面
                var docms = PageFactory.CreatePage(ServiceSettings.GetConfig("Link2Dms"));
                if (docms == null)
                {
                    PageData.TradeAPIs.ShowInfoMessageBox("无法打开影像档案联动页面！请检查...");
                    return;
                }
                docms.SetViewStateData(new Dictionary<string, object> { { "Scene", this }, { "isReadonly", isReadonly } });
                PageData.ShowPage(docms, "影像档案采集");
            }

            /// <summary>
            /// 统计附件补打流水标签的数量
            /// </summary>
            /// <returns></returns>
            public int CountMarkup()
            {
                return int.Parse(UserManagerHandle.ExecuteQuery("SELECT COUNT(*) FROM TPS_DOCS WHERE NAME LIKE '附件%' AND ID ='" + Id + "'")[0]);
            }
            #endregion

            #region Picker
            /// <summary>
            /// 环境变量的数据支持
            /// </summary>
            public enum Env
            {
                /// <summary>
                /// 无
                /// </summary>
                None,
                /// <summary>
                /// 机构号
                /// </summary>
                KinbrNo,
                /// <summary>
                /// 机构名称
                /// </summary>
                KinbrName,
                /// <summary>
                /// 柜员号
                /// </summary>
                TellerNo,
                /// <summary>
                /// 柜员姓名
                /// </summary>
                TellerName,
                /// <summary>
                /// 交易代码
                /// </summary>
                TradeCode,
                /// <summary>
                /// 交易名称
                /// </summary>
                TradeName,
                /// <summary>
                /// 系统流水号
                /// </summary>
                SystemSerialNumber,
                /// <summary>
                /// 柜员流水号
                /// </summary>
                SerialNumber,
                /// <summary>
                /// 业务流水号
                /// </summary>
                BizSerialNo,
                /// <summary>
                /// 交易时间
                /// </summary>
                TradeDatetime
            }

            private Dictionary<Env, string> _refMap2Env;

            /// <summary>
            /// 环境变量的关联引用
            /// </summary>
            /// <param name="env"></param>
            /// <returns></returns>
            internal string Ref(Env env)
            {
                if (_refMap2Env == null)
                {
                    var page = PageData;
                    var bizNo = MessageData.Fd4;//注意不要用page.GetPrintAttributes()取数，该api有点毛病
                    var date = DateTime.ParseExact(LoginUserInfo.TradeDate + TellerSystem.Library.Ext.Variables.LoginUserInfoExt.TradeTime, "yyyyMMddHHmmss", null);
                    _refMap2Env = new Dictionary<Env, string>
                    {
                        {Env.None,string.Empty},
                        {Env.KinbrNo,LoginUserInfo.KinbrNo},
                        {Env.KinbrName,LoginUserInfo.KinbrName},
                        {Env.TellerNo,LoginUserInfo.TellerNo},
                        {Env.TellerName,LoginUserInfo.TellerName},
                        {Env.TradeCode,page.TradeModel.Code},
                        {Env.TradeName,string.IsNullOrEmpty(Summary) ? page.TradeModel.Name : Summary},
                        {Env.SystemSerialNumber,page.TradeInfo.SystemSerialNumber},
                        {Env.SerialNumber,page.TradeInfo.SerialNumber},
                        {Env.BizSerialNo,bizNo},
                        {Env.TradeDatetime,date.ToString("yyyy-MM-dd HH:mm:ss")},
                    };
                }
                return _refMap2Env[env];
            }

            /// <summary>
            /// 业务流水的数据支持
            /// </summary>
            public enum Biz
            {
                /// <summary>
                /// 无
                /// </summary>
                None,
                /// <summary>
                /// 业务流水号
                /// </summary>
                SerialNo,
                /// <summary>
                /// 交易日期
                /// </summary>
                TradeDate,
                /// <summary>
                /// 交易时间
                /// </summary>
                TradeTime,
                /// <summary>
                /// 账号
                /// </summary>
                AccNo,
                /// <summary>
                /// 账户序号
                /// </summary>
                AccSeqn,
                /// <summary>
                /// 账户类型, 0：科目 1：活期；2：定期；3：贷款；4：透支；9：资金(内部账)
                /// </summary>
                AccKind,
                /// <summary>
                /// 户名
                /// </summary>
                AccName,
                /// <summary>
                /// 发生额
                /// </summary>
                Amt,
                /// <summary>
                /// 客户编号
                /// </summary>
                CusNo,
                /// <summary>
                /// 客户户名
                /// </summary>
                CusName,
                /// <summary>
                /// 客户类型, 0公用 1个人 2公司 3机构（指政府机关等组织） 4金融同业 5内部员工 6金融机构
                /// </summary>
                CusKind,
                /// <summary>
                /// 证件类型,1 身份证|2 户口簿|3 护照|4 军人证|5 非中国籍港澳内地通行证|6 士兵证|7 港澳居民来往通行证|8 组织机构代码证|9 经营许可证|A 营业执照|B 事业法人证书|C 工商核准号|D 其它对公证件|F 外国人永久居留身份证|G 警官证|H 其他证件|I 台湾同胞来往通行证|J 军官证|K 文职干部证|L 军官退休证|M 文职干部退休证|N 武警身份证|O 武警士兵证|P 武警文职干部证|Q 武警军官退休证|R 武警文职干部退休证 |S 统一社会信用代码证|T 贷款卡 |U 个体工商户营业执照|V 文件|Z 验资户证件
                /// </summary>
                LicKind,
                /// <summary>
                /// 证件号码
                /// </summary>
                LicNo,
                /// <summary>
                /// 业务起始日期
                /// </summary>
                BizStd,
                /// <summary>
                /// 业务截止日期
                /// </summary>
                BizEnd,
            }

            private Dictionary<Biz, string> _refMap2Biz;

            private List<Dictionary<string, string>> T8801(string bizNo)
            {
                var list = new List<Dictionary<string, string>>();
                if (!string.IsNullOrEmpty(bizNo))
                {
                    var page = PageData;
                    //查询核心流水
                    var message = new Message();
                    page.TradeAPIs.CommSend(message);
                    message.Fd16 = "8801";
                    //message.Fd24 = "";
                    message.Fd29 = bizNo;
                    //message.Fd66 = "";
                    //message.Fd71 = "0";
                    //message.Fd70 = "";
                    message.Fd64 = Ref(Env.TellerNo);
                    message.Fd91 = Ref(Env.KinbrNo);
                    PageData.TradeAPIs.CallServer(message);
                    if (message.Fd12 == "0000")
                    {
                        //解析附件，~交易日期|交易时间|操作员|业务流水号|账号|序号|户名|借贷|@发生额|现转|摘要|凭证种类|凭证号|状态|交易名称|
                        list.AddRange(TFInput.AnilyzeFileData<Dictionary<string, string>>(message.FileData, "~交易日期|交易时间|操作员|业务流水号|账号|序号|户名|借贷|@发生额|现转|摘要|凭证种类|凭证号|状态|交易名称|", x => x));
                    }
                }
                return list;
            }

            private string DoBiz(Biz biz, string val = null, bool force = false)
            {
                if (val == null)
                    return _refMap2Biz.ContainsKey(biz) ? _refMap2Biz[biz] : string.Empty;
                if (force || !_refMap2Biz.ContainsKey(biz))
                    _refMap2Biz[biz] = val;
                return _refMap2Biz[biz];
            }

            /// <summary>
            /// 业务流水的关联引用
            /// </summary>
            /// <param name="biz"></param>
            /// <returns></returns>
            internal string Ref(Biz biz)
            {
                //如已设置值，则直接返回
                if (_refMap2Biz.ContainsKey(biz))
                    return _refMap2Biz[biz];
                //若存在业务流水号，则代表加载完毕，仍无该项数据值
                if (_refMap2Biz.ContainsKey(Biz.SerialNo))
                    return string.Empty;
                var bizNo = Ref(Env.BizSerialNo);//业务流水号
                DoBiz(Biz.SerialNo, bizNo);//填充业务流水号，充当标志
                var AccBelong = this["AccBelong"];//账户借贷属性
                //若配置无客账户信息，则无需继续处理
                if (AccBelong == "无") return string.Empty;
                var cusNo = DoBiz(Biz.CusNo);
                var licKind = DoBiz(Biz.LicKind);
                var licNo = DoBiz(Biz.LicNo);
                var accNo = DoBiz(Biz.AccNo);
                var accSeqn = DoBiz(Biz.AccSeqn);
                //获取流水信息，进行筛选
                var list = T8801(bizNo);
                var candidate = list.Where(x =>
                {
                    DoBiz(Biz.TradeDate, x["~交易日期"]);
                    DoBiz(Biz.TradeTime, x["交易时间"]);
                    DoBiz(Biz.Amt, x["@发生额"]);
                    DoBiz(Biz.CusName, x["户名"]);
                    DoBiz(Biz.AccName, x["户名"]);
                    //优先匹配AccKind
                    switch (AccBelong)
                    {
                        case "借":
                        case "贷":
                            if (x["借贷"] != AccBelong)
                                return false;
                            break;
                        default:
                            break;
                    }
                    //检查账号数据
                    var zh = x["账号"];
                    //内部账9开头，长度每个地方不一样
                    //个人1开头13位或者6开头19位(莱山存在18位卡号)
                    //对公5或者6开头13位
                    //提取有效账号：剔除内部账号（9开头）、凭证号（小号），保留客户号（8位）
                    return zh.Length == 8 || (zh.Length == 13 && new string[] { "1", "5", "6" }.Contains(zh[0].ToString())) || (zh.Length == 19 && new string[] { "6" }.Contains(zh[0].ToString()));
                }).ToList();
                //检查候选列表,当匹配数唯一且未前置关键变量，则采用候选记录
                if (candidate.Count == 1)
                {
                    var x = candidate[0];
                    var zh = x["账号"];
                    if (zh.Length == 8)
                    {
                        if (string.IsNullOrEmpty(cusNo))
                            cusNo = zh;
                    }
                    else
                    {
                        if (string.IsNullOrEmpty(accNo))
                        {
                            accNo = zh;
                            accSeqn = x["序号"];
                        }
                    }
                }
                //检查账号情况
                if (!string.IsNullOrEmpty(accNo))
                {
                    //查询账户信息，该接口无法查询内部户，需要处理
                    var ret = PageData.TradeAPIs.TF_Deposit_QueryDepositAccount(new QueryDepositAccount.Input
                    {
                        ZH = accNo,
                        XH = accSeqn,
                    });
                    if (!ret.IsFailed)
                    {
                        DoBiz(Biz.AccKind, ret.ZHLX, true);
                        DoBiz(Biz.CusNo, ret.KHH, true);
                        DoBiz(Biz.CusKind, ret.KHLX, true);
                        DoBiz(Biz.CusName, ret.KHMC, true);
                        DoBiz(Biz.LicKind, ret.ZJLX, true);
                        DoBiz(Biz.LicNo, ret.ZJHM, true);
                        DoBiz(Biz.AccName, ret.HM, true);
                        DoBiz(Biz.AccNo, ret.ZH, true);
                        DoBiz(Biz.AccSeqn, ret.XH, true);
                        //定期业务需补充，业务起始日期，业务截止日期
                        if (ret.ZHLX == "2")
                        {
                            DoBiz(Biz.BizStd, ret.KHRQ, true);
                            DoBiz(Biz.BizEnd, ret.DQRQ, true);
                        }
                    }
                    else
                        throw new InvalidOperationException("无纸化处理:无法正确获取业务数据,账户错误=>\n" + ret.FailureCause);
                }
                else if (!string.IsNullOrEmpty(cusNo) || (!string.IsNullOrEmpty(licKind) && !string.IsNullOrEmpty(licNo)))
                {
                    //查询客户信息
                    var ret = PageData.TradeAPIs.TF_Customer_QueryCustomerInformation(new QueryCustomerInformation.Input
                    {
                        KHH = cusNo,
                        ZJHM = licNo,
                        ZJLX = licKind,
                    });
                    if (!ret.IsFailed)
                    {
                        DoBiz(Biz.CusNo, ret.KHH, true);
                        DoBiz(Biz.CusName, ret.HM, true);
                        DoBiz(Biz.CusKind, ret.KHLX, true);
                        DoBiz(Biz.LicKind, ret.ZJLX, true);
                        DoBiz(Biz.LicNo, ret.ZJHM, true);
                    }
                    else
                        throw new InvalidOperationException("无纸化处理:无法正确获取业务数据,客户错误=>\n" + ret.FailureCause);
                }
                else
                    throw new InvalidOperationException("无纸化处理:无法正确获取业务数据,请检查配置=>\n" + bizNo);
                return DoBiz(biz);
            }
            #endregion
        }
        #endregion
    }
}
