随笔- 4121  评论- 16  文章- 5 

Code-AMS:OAArchiveJob.cs

ylbtech-Code-AMS:OAArchiveJob.cs

 

1.返回顶部
1、
using Autofac.Core.Activators.Reflection;
using Autofac.Features.Metadata;
using DocService;
using EAMSService;
using Grpc.Net.Client;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.VisualBasic;
using NLog.Fluent;
using NLog.Layouts;
using Quartz;
using Quartz.Impl.AdoJobStore.Common;
using Rebex.Net;
using Rebex.Security.Cryptography;
using Sinopec.AMSInterface.Models;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection.Metadata.Ecma335;
using System.ServiceModel;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using System.Xml.XPath;

namespace Sinopec.AMSInterface.Main.Jobs
{
    /// <summary>
    /// 公文归档Job
    /// </summary>
    [DisallowConcurrentExecution]
    public class OAArchiveJob : OAJobHandle,IJob
    {
        private DAL.IAMS_Convert_DAL AMS_Convert_DAL { get; }
        private IConfiguration Configuration { get; }
        private ILogger Logger { get; }

        public OAArchiveJob(ILogger<OAArchiveJob> logger
            , IConfiguration configuration
            , DAL.IAMS_Convert_DAL ams_Convert_DAL)
            :base(logger,configuration,ams_Convert_DAL)
        {
            Logger = logger;
            Configuration = configuration;
            AMS_Convert_DAL = ams_Convert_DAL;
        }

        public async Task Execute(IJobExecutionContext context)
        {
            try
            {
                await ArchiveBatchRun(context.CancellationToken);
            }
            catch (AggregateException exp)
            {
                //忽略Task取消时的异常
                exp.Handle(ex => ex is OperationCanceledException);
            }
            catch (Exception exp)
            {
                Logger.LogError(exp, "调度执行中出现异常");
            }
        }

        /// <summary>
        /// 获取可以归档的批次信息
        /// </summary>
        /// <param name="MaxCount"></param>
        /// <returns></returns>
        private async Task ArchiveBatchRun(CancellationToken token)
        {
            while (true)
            {
                token.ThrowIfCancellationRequested();


                string batch_id = await GetArchiveTask();

                if (string.IsNullOrWhiteSpace(batch_id))
                {
                    Logger.LogInformation("没有需要归档的任务");

                    break;
                }

                try
                {
                    await ArchiveTask(batch_id,token);
                }
                catch (AggregateException exp)
                {
                    //忽略Task取消时的异常
                    exp.Handle(ex => ex is OperationCanceledException);
                }
                catch (ArchiveException exp)
                {
                    AMS_Convert_DAL.SetBatchStatus_Error(batch_id);
                    Logger.LogError(exp, $"批次【{batch_id}】有不符合归档要求的文件");
                    continue;
                }
                catch (Exception exp)
                {
                    AMS_Convert_DAL.SetBatchStatus_Error(batch_id);
                    Logger.LogError(exp, $"批次【{batch_id}】归档执行中出现异常");
                    continue;
                }
            }
        }

        private async Task ArchiveTask(string batchId, CancellationToken token) {
            try
            {
                var (batchInfo, docItems) = GetArchiveByBatchId(batchId);

                if(docItems.Where(item => item.Type != (int)EnumConvertType.办理单文件下载)
                    .Any(item=> item.Status != 2))
                {
                    Logger.LogInformation("尚有未处理的文档待处理");
                    AMS_Convert_DAL.SetBatchStatus_Error(batchId);
                    return;
                }

                if (docItems.Any(item => item.Type == (int)EnumConvertType.办理单文件下载) 
                    && !docItems.Any(item => item.Type == (int)EnumConvertType.办理单文件转换 && item.Status == 2))
                {
                    //如果存在办理单需要下载的情况,则需判断办理单文件已经处理(重新下载办理单)
                    Logger.LogInformation("办理单文件尚未处理");
                    AMS_Convert_DAL.SetBatchStatus_Error(batchId);
                    return;
                }

                //排除需要下载的办理单类型
                var archiveDocItems = docItems?.Where(item => item.Type!=(int)EnumConvertType.办理单文件下载)?.ToArray();

                if(!await CheckArchiveFileExists(archiveDocItems.Select(item=> item.Path).ToArray()))
                {
                    throw new ArchiveException("有文件不在指定位置");
                }

                token.ThrowIfCancellationRequested();

                var batchNumber = batchInfo.Batch_Number;

                Logger.LogInformation($"批次ID【{batchId}】获取公文metas文件");

                var metas = await GetMetasDoc(batchNumber);

                Logger.LogInformation($"批次ID【{batchId}】开始构建归档metas");

                token.ThrowIfCancellationRequested();

                //构造Metas,并将文件上传OSS
                var EAMS_Metas =await MetasBuildAsync(metas.Root, archiveDocItems,token);

                Logger.LogInformation($"批次ID【{batchId}】构建归档metas完成{Environment.NewLine}Metas文件结构:{Environment.NewLine}{EAMS_Metas}");

                using MemoryStream stream = new MemoryStream();
                using System.Xml.XmlWriter writer = System.Xml.XmlWriter.Create(stream);
                EAMS_Metas.WriteTo(writer);
                writer.Flush();
                stream.Seek(0, SeekOrigin.Begin);


                string metas_sign = await FileSign(stream);

                string metasKey = Guid.NewGuid().ToString();
                //metas上传OSS
                await FileUploadToOSS(stream, "metas.xml", metasKey);

                string _batchNumber = metas.XPathSelectElement("//BatchNumber").Value;
                //记录上传结果
                //调用归档接口
                string archive_id = Guid.NewGuid().ToString();
                if(OAArchive(archive_id, _batchNumber, metasKey, metas_sign))
                {
                    AMS_Convert_DAL.SetBatchStatus_Finished(batchId);
                }
                else
                {
                    AMS_Convert_DAL.SetBatchStatus_Error(batchId);
                }

            }
            catch (Exception)
            {

                throw;
            }
            
        }

        private async Task<bool> CheckArchiveFileExists(params string[] remotePathList)
        {
            using FileTransferClient client = GetFileTransferClientClient();

            string RemotePath = Configuration["DocumentSFTP:DocumnetFolder"];

            string incomeDir = Configuration["DocumentSFTP:IncomeFolder"];

            try
            {
                return await Task.FromResult(remotePathList.All(filePath => client.FileExists(filePath)));
            }
            catch (Exception)
            {
                return await Task.FromResult(false);
            }
        }

        /// 根据批次ID获取该归档批次的信息
        /// </summary>

        private async ValueTask<XDocument> GetMetasDoc(string batchNumber)
        {
            string incomeDir = Configuration["DocumentSFTP:IncomeFolder"];

            string metasPath = $"{incomeDir}\\{batchNumber}\\metas.xml";

            var (metas, _) = await DownLoadFile(metasPath);

            if(metas.Position != 0)
            {
                metas.Seek(0, SeekOrigin.Begin);
            }

            return XDocument.Load(metas);
        }

        /// <summary>
        /// <param name="batchId"></param>
        /// <returns></returns>
        private (AMS_Convert_Batch, IEnumerable<AMS_Conver_Doc> docItems) GetArchiveByBatchId(string batchId)
        {
            return AMS_Convert_DAL.GetArchiveInfoByBatchId(batchId);
        }

        /// <summary>
        /// 文件签名
        /// </summary>
        /// <param name="stream"></param>
        /// <returns></returns>
        private async Task<string> FileSign(Stream stream)
        {
            if (stream == null)
            {
                throw new NullReferenceException("需要签名文件流为空");
            }

            if (stream.Position != 0)
            {
                stream.Seek(0, SeekOrigin.Begin);
            }

            var binding = new BasicHttpBinding() {
                MaxReceivedMessageSize = 1024 * 1024 * 1024,
                TransferMode = TransferMode.Streamed
            };

            var SignWSAddress = new EndpointAddress(Configuration["OAConfiguration:SignServiceUrl"]);
            //

            SignServiceClient client = null;
            try
            {
                client = new SignServiceClient(binding, SignWSAddress);

                return await client.SignAsync(stream);
            }
            finally
            {
                await client?.CloseAsync();
            }
            
        }

        /// <summary>
        /// 文件上传OSS
        /// </summary>
        /// <param name="stream"></param>
        private async Task FileUploadToOSS(Stream stream,string fileName,string key)
        {
            if(stream == null)
            {
                throw new NullReferenceException("上传OSS系统文件流为空");
            }

            if(stream.Position != 0)
            {
                stream.Seek(0, SeekOrigin.Begin);
            }

            var binding = new BasicHttpBinding() {
                MaxReceivedMessageSize = 1024 * 1024 * 1024,
                TransferMode = TransferMode.Streamed
            };

            var SignWSAddress = new EndpointAddress(Configuration["OAConfiguration:OSSTransferServiceUrl"]);
            string bucketName = Configuration["EAMSOAArchive:BucketName"];

            OSSServiceClient client = null;
            try
            {
                client = new OSSServiceClient(binding, SignWSAddress);

                await client.UploadToOSSAsync(bucketName ,fileName, key, stream);
            }
            finally
            {
                await client?.CloseAsync();
            }
           
        }

        private async ValueTask<(Stream filestream,long size)> DownLoadFile(string remotePath)
        {
            using FileTransferClient client = GetFileTransferClientClient();

            string RemotePath = Configuration["DocumentSFTP:DocumnetFolder"];
            string incomeDir = Configuration["DocumentSFTP:IncomeFolder"];

            MemoryStream stream = new MemoryStream();

            long filesize = await client.GetFileAsync(remotePath, stream);

            return (stream, filesize);
        }

        private async Task<XDocument> MetasBuildAsync(XElement OAMetas
            , IEnumerable<AMS_Conver_Doc> itemDocs,CancellationToken token)
        {
            string batchNumber = OAMetas.XPathSelectElement("//BatchNumber").Value;//批次号
            string submitUserName = OAMetas.XPathSelectElement("//Entries/Entry/gdr").Value;//提交人
            string submitTime = OAMetas.XPathSelectElement("//Entries/Entry/gdrq").Value;//提交时间
            string packageID = Guid.NewGuid().ToString();

            XElement ele_data = new XElement("Data");
            Logger.LogInformation($"构建CommonInfo");
            XElement ele_CommonInfo = new XElement("CommonInfo", new XElement[] {
                new XElement("Metadata",
                    new XAttribute("Name","SystemID"),new XAttribute("value",Configuration["EAMSOAArchive:SystemID"])),
                new XElement("Metadata",
                    new XAttribute("Name","PackageID"),new XAttribute("value",packageID)),
                new XElement("Metadata",
                    new XAttribute("Name","BusinessID"),new XAttribute("value",batchNumber)),
                new XElement("Metadata",
                    new XAttribute("Name","Type"),new XAttribute("value",Configuration["EAMSOAArchive:RecordType"])),
                new XElement("Metadata",
                    new XAttribute("Name","SubmitUserName"),new XAttribute("value",submitUserName)),
                new XElement("Metadata",
                    new XAttribute("Name","SubmitTime"),new XAttribute("value",submitTime)),
                new XElement("Metadata",
                    new XAttribute("Name","FileCount"),new XAttribute("value",itemDocs.Count())),
                new XElement("Metadata",
                    new XAttribute("Name","Flag"),new XAttribute("value",0))
            });

            /*
             new XElement[] {
                new XElement("SystemID",Configuration["EAMSOAArchive:SystemID"]),
                new XElement("PackageID",packageID),
                new XElement("BusinessID",batchNumber),
                new XElement("Type",Configuration["EAMSOAArchive:RecordType"]),
                new XElement("SubmitUserName",submitUserName),
                new XElement("SubmitTime",submitTime),
                new XElement("FileCount",itemDocs.Count()),
                new XElement("Flag","0")
            }
             */

            ele_data.Add(ele_CommonInfo);
            ele_data.Add(new XElement("ProcessInfos", "过程信息"));

            Logger.LogInformation($"构建BusinessMetas");
            ele_data.Add(new XElement("BusinessMetas"
                , BuildBusinessMetas(OAMetas)
                ));

            Logger.LogInformation($"构建Files");
            //文件节点
            var ele_file = new XElement("Files");

            await foreach (var itemfile in buildFile(itemDocs)
                .WithCancellation(token))
            {
                ele_file.Add(itemfile);
            }

            ele_data.Add(ele_file);

            Logger.LogInformation($"构建Files完毕");

            Logger.LogDebug($"metas结构:{Environment.NewLine}{ele_data}");

            XDocument document = new XDocument(new XDeclaration("1.0", "utf-8", "yes"), ele_data);

            return document;

            //构建Metas中的Files块,并将相应文件上传至OSS
            //本地方法(异步流)
            async IAsyncEnumerable<XElement> buildFile(IEnumerable<AMS_Conver_Doc> _Docs)
            {
                foreach (AMS_Conver_Doc item in _Docs)
                {
                    //item.Path;
                    string objectKey = Guid.NewGuid().ToString();
                    string doc_name = Regex.Replace(Path.GetFileName(item.Doc_Name),"\\a|\\t","");//移除制表符等不可见字符

                    string _directory = item switch
                    {
                        { Type: 6 } when Regex.IsMatch(item.Doc_Name, ".*(?i)(html).*\\.(?i)(pdf)") =>  "办理单",
                        { Type: 6 } => "正文",
                        _ => "原始文件"
                    };

                    (Stream filestream, long doc_size) = await DownLoadFile(item.Path);

                    //签名
                    Logger.LogInformation($"对文件【{item.Doc_Name}】进行签名");
                    string signCode = await FileSign(filestream);

                    Logger.LogDebug($"文件【{item.Doc_Name}】的签名:【{signCode}】");

                    Logger.LogInformation($"文件【{item.Doc_Name}】上传OSS");

                    //上传OSS
                    var task_upload = FileUploadToOSS(filestream, doc_name, objectKey)
                        .ContinueWith(t => {
                            if (t.IsCompleted)
                            {
                                Logger.LogInformation($"文件【{item.Doc_Name}】上传OSS完成");
                            }
                            else if(t.IsCanceled)
                            {
                                Logger.LogInformation($"文件【{item.Doc_Name}】取消上传OSS");
                                //任务取消
                                throw new OperationCanceledException("取消上传OSS");
                            }
                            else
                            {
                                Logger.LogError(t.Exception,$"文件【{item.Doc_Name}】上传OSS异常");
                                //任务失败
                                throw t.Exception;
                            }
                        });

                    var fileMetas = new XElement("File"
                        , new XAttribute("FileID", objectKey)
                        , new XAttribute("FileName", doc_name)
                        , new XAttribute("Digest", "")
                        , new XAttribute("Sign", signCode) //签名
                        , new XAttribute("SignTime", DateTime.Now.ToString("yyyy-MM-dd"))
                        , new XAttribute("Certificate", "")
                        , new XAttribute("FileType", Path.GetExtension(doc_name))
                        , new XAttribute("FileSize", doc_size)
                        , new XAttribute("CreateTime", DateTime.Now.ToString("yyyy-MM-dd"))
                        , new XAttribute("UpdateTime", DateTime.Now.ToString("yyyy-MM-dd"))
                        , new XAttribute("FileFormationMode", _directory)
                        , new XAttribute("Directory", _directory)
                        , new XElement("FileNo")
                        , new XElement("Path"));

                    await task_upload;

                    Logger.LogInformation($"{fileMetas}");
                    yield return fileMetas;
                }
            }           
        }

        private IEnumerable<XElement> BuildBusinessMetas(XElement metas)
        {
            //读取对照
            //从配置文件中读取对照规则
            List<ArchiveCastItem> archiveCastList = new List<ArchiveCastItem>();

            Configuration.GetSection("EAMSOAArchive:OACastList")
                .Bind(archiveCastList);

            return buildBusinessMetas();

            //
            IEnumerable<XElement> buildBusinessMetas()
            {
                foreach (ArchiveCastItem archiveCastItem in archiveCastList)
                {
                    var value = archiveCastItem.IsBase64 
                        ? metas.XPathSelectElement(archiveCastItem.OAXPath)?.Value?.DecodeBase64() 
                        : metas.XPathSelectElement(archiveCastItem.OAXPath)?.Value;

                    Logger.LogInformation($"【{archiveCastItem.EAMSProptyName}】-【{value}】");

                    var businessMeta = new XElement("BusinessMeta"
                                    , new XAttribute("Name", archiveCastItem.EAMSProptyName)
                                    , new XAttribute("value", value)
                                    , new XAttribute("type", "")
                                    , new XAttribute("must", archiveCastItem.IsMust ? "Y":"N"));

                    Logger.LogInformation($"【BusinessMeta】:{businessMeta}");

                    yield return businessMeta;
                }
            }
        }


        private bool OAArchive(string packageID,string businessID,string metasObjKey,string metasSign)
        {
            string _bucketName , _recordType;

            var requestInfo = new ArchiveRequestRequest
            {
                PackageID = packageID,
                BusinessID = businessID,
                XMLFileID = metasObjKey,
                Sign = metasSign,
                Flag = 1,
                BucketName = !string.IsNullOrWhiteSpace(_bucketName = Configuration["EAMSOAArchive:BucketName"]) ? _bucketName
                                : throw new ArgumentNullException("BucketName", "配置节点中的BucketName配置为空"),
                RecordType = !string.IsNullOrWhiteSpace(_recordType = Configuration["EAMSOAArchive:RecordType"]) ? _recordType
                                : throw new ArgumentNullException("RecordType", "配置节点中的EAMSOAArchive:RecordType配置为空"),
                SystemID = int.TryParse(Configuration["EAMSOAArchive:SystemID"], out int _systemId) ? _systemId
                                : throw new ArgumentException("SystemID", "配置节点中的SystemID配置不是整形数字")
            };

            var binding = new BasicHttpBinding()
            {
                MaxReceivedMessageSize = 1024 * 1024 * 1024
            };


            var ArchiveWSAddress = new EndpointAddress(Configuration["OAConfiguration:WriteArchiveServiceUrl"]);

            //忽略SSL证书
            //System.Net.ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, error) =>
            //{
            //    switch (error)
            //    {
            //        case System.Net.Security.SslPolicyErrors.None:
            //        case System.Net.Security.SslPolicyErrors.RemoteCertificateNameMismatch:
            //        case System.Net.Security.SslPolicyErrors.RemoteCertificateNotAvailable:
            //            return true;
            //        default:
            //            return false;
            //    }
            //};

            WriteArchiveInfoClient client = null;

            try
            {
                client = new WriteArchiveInfoClient(binding, ArchiveWSAddress);

                Logger.LogInformation($"向档案系统发送归档请求-【{System.Text.Json.JsonSerializer.Serialize(requestInfo)}】");
                var response = client.PutArchiveInfo(requestInfo);

                switch (response.ResultCode)
                {
                    case ArchiveRequestResultCode.Success:
                        {
                            Logger.LogInformation($"批次 {businessID} === 成功!");

                            return true;
                        }
                    default:
                        {
                            Logger.LogInformation($"批次 {businessID} 电子档案反馈错误信息===={response.Msg}");

                            return false;
                        }
                }
            }
            finally
            {
                client?.CloseAsync();
            }
            
        }

        //申领归档任务
        protected async Task<string> GetArchiveTask()
        {
            /*
            AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);

            //忽略证书
            using var httpClientHandler = new System.Net.Http.HttpClientHandler
            {
                //忽略证书
                ServerCertificateCustomValidationCallback = System.Net.Http.HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
            };
            using var httpClient = new System.Net.Http.HttpClient(httpClientHandler);

            using var channel = GrpcChannel.ForAddress(Configuration["DocumentTaskServiceUrl"]
                , new GrpcChannelOptions { HttpClient = httpClient });//Grpc路径

            var client = new DocumentTask.DocumentTaskClient(channel);

            //申领转换任务
            var response = await client.GetArchiveBatchIdAsync(new ArchiveTaskRequest()
            { 
               
            });
            return response?.BatchId;
            */

            return await Task.FromResult(GetBatch());

        }

        static readonly object sync_batch_obj = new object();
        private string GetBatch()
        {
            lock (sync_batch_obj)
            {
                while (true)
                {
                    string batchid = Configuration["Test:TestBatchid"];

                    var item = AMS_Convert_DAL.GetArchiveBatch();

                    if (item == null) return string.Empty;

                    if (AMS_Convert_DAL.SetBatchStatus_Archiving(item.Batch_ID) < 1)
                    {
                        continue;
                    }

                    return item?.Batch_ID ?? string.Empty;
                }
            }
        }
    }

    public class ArchiveException : Exception
    {
        public ArchiveException() : base()
        {

        }

        public ArchiveException(string message) 
            : base(message)
        {

        }
    }
}
View Code
2、
2.返回顶部
 
3.返回顶部
 
4.返回顶部
 
5.返回顶部
 
 
6.返回顶部
 
warn 作者:ylbtech
出处:http://ylbtech.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
posted on 2020-08-02 13:04  storebook  阅读(14)  评论(0编辑  收藏