zoukankan      html  css  js  c++  java
  • MailKit和MimeKit 收发邮件

    新建项目,引用MailKit和MimeKit NuGet包

    using CommonTool.MailKit;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ProjectDemo.ConsoleApp
    {
        class Program
        {
            static void Main(string[] args)
            {
                try
                {
                    var ccList = new List<string>();
                    ccList.Add("*****@qq.com");
                    var recipients = new List<string>();
                    recipients.Add("*****@qq.com");
                    var mailBodyEntity = new MailBodyEntity()
                    {
                        Body = "这是一个测试邮件body内容<a href='http://www.baidu.com'>123</a>",
                        Cc = ccList,
                        //MailBodyType = "html",
                        MailFiles = new List<MailFile>() {
                            new MailFile { MailFilePath = @"D:文档File20180807165402.png", MailFileSubType = "png", MailFileType = "image" },
                            new MailFile { MailFilePath = @"D:文档FileTIM截图20180807165402.png", MailFileSubType = "png", MailFileType = "image" }
                        },
                        Recipients = recipients,
                        Sender = "邮件的发件人",
                        SenderAddress = "111111@qq.com",
                        Subject = "测试邮件是否可以发送的标题",
                    };
                    var sendServerConfiguration = new SendServerConfigurationEntity()
                    {
                        SenderPassword = "123456",
                        SmtpPort = 465,
                        IsSsl = true,
                        MailEncoding = "utf-8",
                        SenderAccount = "11111@qq.com",
                        SmtpHost = "smtp.qq.com",
    
                    };
    
                    var result=SeedMailHelper.SendMail(mailBodyEntity, sendServerConfiguration);
                    ReceiveEmailHelper.ReceiveEmail();
                    ReceiveEmailHelper.DownloadBodyParts();
                    Console.WriteLine("成功!");
                    Console.ReadLine();
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                }
            }
        }
    }

    发送邮件

    using MailKit;
    using MailKit.Net.Smtp;
    using MailKit.Security;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace CommonTool.MailKit
    {
        /// <summary>
        /// 发送邮件
        /// </summary>
        public static class SeedMailHelper
        {
            /// <summary>
            /// 发送邮件
            /// </summary>
            /// <param name="mailBodyEntity">邮件基础信息</param>
            /// <param name="sendServerConfiguration">发件人基础信息</param>
            public static SendResultEntity SendMail(MailBodyEntity mailBodyEntity,
                SendServerConfigurationEntity sendServerConfiguration)
            {
                if (mailBodyEntity == null)
                {
                    throw new ArgumentNullException();
                }
    
                if (sendServerConfiguration == null)
                {
                    throw new ArgumentNullException();
                }
    
                var sendResultEntity = new SendResultEntity();
    
                using (var client = new SmtpClient(new ProtocolLogger(MailMessage.CreateMailLog())))
                {
                    client.ServerCertificateValidationCallback = (s, c, h, e) => true;
    
                    Connection(mailBodyEntity, sendServerConfiguration, client, sendResultEntity);
    
                    if (sendResultEntity.ResultStatus == false)
                    {
                        return sendResultEntity;
                    }
    
                    SmtpClientBaseMessage(client);
    
                    // Note: since we don't have an OAuth2 token, disable
                    // the XOAUTH2 authentication mechanism.
                    client.AuthenticationMechanisms.Remove("XOAUTH2");
    
                    Authenticate(mailBodyEntity, sendServerConfiguration, client, sendResultEntity);
    
                    if (sendResultEntity.ResultStatus == false)
                    {
                        return sendResultEntity;
                    }
    
                    Send(mailBodyEntity, sendServerConfiguration, client, sendResultEntity);
    
                    if (sendResultEntity.ResultStatus == false)
                    {
                        return sendResultEntity;
                    }
                    client.Disconnect(true);
                }
                return sendResultEntity;
            }
    
    
            /// <summary>
            /// 连接服务器
            /// </summary>
            /// <param name="mailBodyEntity">邮件内容</param>
            /// <param name="sendServerConfiguration">发送配置</param>
            /// <param name="client">客户端对象</param>
            /// <param name="sendResultEntity">发送结果</param>
            public static void Connection(MailBodyEntity mailBodyEntity, SendServerConfigurationEntity sendServerConfiguration,
                SmtpClient client, SendResultEntity sendResultEntity)
            {
                try
                {
                    client.Connect(sendServerConfiguration.SmtpHost, sendServerConfiguration.SmtpPort);
                }
                catch (SmtpCommandException ex)
                {
                    sendResultEntity.ResultInformation = $"尝试连接时出错:{0}" + ex.Message;
                    sendResultEntity.ResultStatus = false;
                }
                catch (SmtpProtocolException ex)
                {
                    sendResultEntity.ResultInformation = $"尝试连接时的协议错误:{0}" + ex.Message;
                    sendResultEntity.ResultStatus = false;
                }
                catch (Exception ex)
                {
                    sendResultEntity.ResultInformation = $"服务器连接错误:{0}" + ex.Message;
                    sendResultEntity.ResultStatus = false;
                }
            }
    
            /// <summary>
            /// 账户认证
            /// </summary>
            /// <param name="mailBodyEntity">邮件内容</param>
            /// <param name="sendServerConfiguration">发送配置</param>
            /// <param name="client">客户端对象</param>
            /// <param name="sendResultEntity">发送结果</param>
            public static void Authenticate(MailBodyEntity mailBodyEntity, SendServerConfigurationEntity sendServerConfiguration,
                SmtpClient client, SendResultEntity sendResultEntity)
            {
                try
                {
                    client.Authenticate(sendServerConfiguration.SenderAccount, sendServerConfiguration.SenderPassword);
                }
                catch (AuthenticationException ex)
                {
                    sendResultEntity.ResultInformation = $"无效的用户名或密码:{0}" + ex.Message;
                    sendResultEntity.ResultStatus = false;
                }
                catch (SmtpCommandException ex)
                {
                    sendResultEntity.ResultInformation = $"尝试验证错误:{0}" + ex.Message;
                    sendResultEntity.ResultStatus = false;
                }
                catch (SmtpProtocolException ex)
                {
                    sendResultEntity.ResultInformation = $"尝试验证时的协议错误:{0}" + ex.Message;
                    sendResultEntity.ResultStatus = false;
                }
                catch (Exception ex)
                {
                    sendResultEntity.ResultInformation = $"账户认证错误:{0}" + ex.Message;
                    sendResultEntity.ResultStatus = false;
                }
            }
    
            /// <summary>
            /// 发送邮件
            /// </summary>
            /// <param name="mailBodyEntity">邮件内容</param>
            /// <param name="sendServerConfiguration">发送配置</param>
            /// <param name="client">客户端对象</param>
            /// <param name="sendResultEntity">发送结果</param>
            public static void Send(MailBodyEntity mailBodyEntity, SendServerConfigurationEntity sendServerConfiguration,
                SmtpClient client, SendResultEntity sendResultEntity)
            {
                try
                {
                    client.Send(MailMessage.AssemblyMailMessage(mailBodyEntity));
                }
                catch (SmtpCommandException ex)
                {
                    switch (ex.ErrorCode)
                    {
                        case SmtpErrorCode.RecipientNotAccepted:
                            sendResultEntity.ResultInformation = $"收件人未被接受:{ex.Message}";
                            break;
                        case SmtpErrorCode.SenderNotAccepted:
                            sendResultEntity.ResultInformation = $"发件人未被接受:{ex.Message}";
                            break;
                        case SmtpErrorCode.MessageNotAccepted:
                            sendResultEntity.ResultInformation = $"消息未被接受:{ex.Message}";
                            break;
                    }
                    sendResultEntity.ResultStatus = false;
                }
                catch (SmtpProtocolException ex)
                {
                    sendResultEntity.ResultInformation = $"发送消息时的协议错误:{ex.Message}";
                    sendResultEntity.ResultStatus = false;
                }
                catch (Exception ex)
                {
                    sendResultEntity.ResultInformation = $"邮件接收失败:{ex.Message}";
                    sendResultEntity.ResultStatus = false;
                }
            }
    
            /// <summary>
            /// 获取SMTP基础信息
            /// </summary>
            /// <param name="client">客户端对象</param>
            /// <returns></returns>
            public static MailServerInformation SmtpClientBaseMessage(SmtpClient client)
            {
                var mailServerInformation = new MailServerInformation
                {
                    Authentication = client.Capabilities.HasFlag(SmtpCapabilities.Authentication),
                    BinaryMime = client.Capabilities.HasFlag(SmtpCapabilities.BinaryMime),
                    Dsn = client.Capabilities.HasFlag(SmtpCapabilities.Dsn),
                    EightBitMime = client.Capabilities.HasFlag(SmtpCapabilities.EightBitMime),
                    Size = client.MaxSize
                };
    
                return mailServerInformation;
            }
        }
    }

    基础类

    using MimeKit;
    using MimeKit.Text;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace CommonTool.MailKit
    {
        /// <summary>
        /// 邮件信息
        /// </summary>
        public static class MailMessage
        {
            /// <summary>
            /// 组装邮件文本/附件邮件信息
            /// </summary>
            /// <param name="mailBodyEntity">邮件消息实体</param>
            /// <returns></returns>
            public static MimeMessage AssemblyMailMessage(MailBodyEntity mailBodyEntity)
            {
                if (mailBodyEntity == null)
                {
                    throw new ArgumentNullException(nameof(mailBodyEntity));
                }
                var message = new MimeMessage();
    
                //设置邮件基本信息
                SetMailBaseMessage(message, mailBodyEntity);
    
                var multipart = new Multipart("mixed");
    
                //插入文本消息
                if (!string.IsNullOrEmpty(mailBodyEntity.Body))
                {
                    var alternative = new MultipartAlternative
                    {
                        AssemblyMailTextMessage(mailBodyEntity.Body, mailBodyEntity.MailBodyType)
                     };
                    multipart.Add(alternative);
                }
    
                //插入附件
                foreach (var mailFile in mailBodyEntity.MailFiles)
                {
                    if (mailFile.MailFilePath != null && File.Exists(mailFile.MailFilePath))
                    {
                        var mimePart = AssemblyMailAttachmentMessage(mailFile.MailFileType, mailFile.MailFileSubType,
                             mailFile.MailFilePath);
                        multipart.Add(mimePart);
                    }
                }
    
                //组合邮件内容
                message.Body = multipart;
                return message;
            }
    
            /// <summary>
            /// 设置邮件基础信息
            /// </summary>
            /// <param name="minMessag"></param>
            /// <param name="mailBodyEntity"></param>
            /// <returns></returns>
            public static MimeMessage SetMailBaseMessage(MimeMessage minMessag, MailBodyEntity mailBodyEntity)
            {
                if (minMessag == null)
                {
                    throw new ArgumentNullException();
                }
                if (mailBodyEntity == null)
                {
                    throw new ArgumentNullException();
                }
    
                //插入发件人
                minMessag.From.Add(new MailboxAddress(mailBodyEntity.Sender, mailBodyEntity.SenderAddress));
    
                //插入收件人
                if(mailBodyEntity.Recipients.Any())
                {
                    foreach (var recipients in mailBodyEntity.Recipients)
                    {
                        minMessag.To.Add(new MailboxAddress(recipients));
                    }
                }
    
                //插入抄送人
                if (mailBodyEntity.Cc!=null&&mailBodyEntity.Cc.Any())
                {
                    foreach (var cC in mailBodyEntity.Cc)
                    {
                        minMessag.Cc.Add(new MailboxAddress(cC));
                    }
                }
    
                //插入密送人
                if (mailBodyEntity.Bcc != null && mailBodyEntity.Bcc.Any())
                {
                    foreach (var bcc in mailBodyEntity.Bcc)
                    {
                        minMessag.Bcc.Add(new MailboxAddress(bcc));
                    }
                }
    
                //插入主题
                minMessag.Subject = mailBodyEntity.Subject;
                return minMessag;
            }
    
            /// <summary>
            /// 组装邮件文本信息
            /// </summary>
            /// <param name="mailBody">邮件内容</param>
            /// <param name="textPartType">邮件类型(plain,html,rtf,xml)</param>
            /// <returns></returns>
            public static TextPart AssemblyMailTextMessage(string mailBody, TextFormat textPartType)
            {
                if (string.IsNullOrEmpty(mailBody))
                {
                    throw new ArgumentNullException();
                }
                //var textBody = new TextPart(textPartType)
                //{
                //    Text = mailBody,
                //};
    
                //处理查看源文件有乱码问题
                var textBody = new TextPart(textPartType);
                textBody.SetText(Encoding.Default, mailBody);
                return textBody;
            }
    
            /// <summary>
            /// 组装邮件附件信息
            /// </summary>
            /// <param name="fileAttachmentType">附件类型(image,application)</param>
            /// <param name="fileAttachmentSubType">附件子类型 </param>
            /// <param name="fileAttachmentPath">附件路径</param>
            /// <returns></returns>
            public static MimePart AssemblyMailAttachmentMessage(string fileAttachmentType, string fileAttachmentSubType, string fileAttachmentPath)
            {
                if (string.IsNullOrEmpty(fileAttachmentSubType))
                {
                    throw new ArgumentNullException();
                }
                if (string.IsNullOrEmpty(fileAttachmentType))
                {
                    throw new ArgumentNullException();
                }
                if (string.IsNullOrEmpty(fileAttachmentPath))
                {
                    throw new ArgumentNullException();
                }
                var fileName = Path.GetFileName(fileAttachmentPath);
                var attachment = new MimePart(fileAttachmentType, fileAttachmentSubType)
                {
                    Content = new MimeContent(File.OpenRead(fileAttachmentPath)),
                    ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),
                    ContentTransferEncoding = ContentEncoding.Base64,
                    //FileName = fileName,
                };
    
                //qq邮箱附件文件名中文乱码问题
                //var charset = "GB18030";
                attachment.ContentType.Parameters.Add(Encoding.Default, "name", fileName);
                attachment.ContentDisposition.Parameters.Add(Encoding.Default, "filename", fileName);
    
                //处理文件名过长
                foreach (var param in attachment.ContentDisposition.Parameters)
                    param.EncodingMethod = ParameterEncodingMethod.Rfc2047;
                foreach (var param in attachment.ContentType.Parameters)
                    param.EncodingMethod = ParameterEncodingMethod.Rfc2047;
    
                return attachment;
            }
    
            /// <summary>
            /// 创建邮件日志文件
            /// </summary>
            /// <returns></returns>
            public static string CreateMailLog()
            {
                var logPath = AppDomain.CurrentDomain.BaseDirectory + "/DocumentLog/" +
                    Guid.NewGuid() + ".txt";
    
                if (File.Exists(logPath)) return logPath;
                var fs = File.Create(logPath);
                fs.Close();
                return logPath;
    
            }
        }
    }

    实体

    using MimeKit.Text;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace CommonTool.MailKit
    {
        /// <summary>
        /// 邮件内容实体
        /// </summary>
        public class MailBodyEntity
        {
            ///// <summary>
            ///// 邮件文本内容
            ///// </summary>
            //public string MailTextBody { get; set; }
    
            /// <summary>
            /// 邮件内容类型
            /// </summary>
            public TextFormat MailBodyType { get; set; } = TextFormat.Html;
    
            /// <summary>
            /// 邮件附件集合
            /// </summary>
            public List<MailFile> MailFiles { get; set; }
    
            /// <summary>
            /// 收件人
            /// </summary>
            public List<string> Recipients { get; set; }
    
            /// <summary>
            /// 抄送
            /// </summary>
            public List<string> Cc { get; set; }
    
            /// <summary>
            /// 密送
            /// </summary>
            public List<string> Bcc { get; set; }
    
            /// <summary>
            /// 发件人
            /// </summary>
            public string Sender { get; set; }
    
            /// <summary>
            /// 发件人地址
            /// </summary>
            public string SenderAddress { get; set; }
    
            /// <summary>
            /// 邮件主题
            /// </summary>
            public string Subject { get; set; }
    
            /// <summary>
            /// 邮件内容
            /// </summary>
            public string Body { get; set; }
        }
    
        public class MailFile
        {
            /// <summary>
            /// 邮件附件文件类型 例如:图片 MailFileType="image"
            /// </summary>
            public string MailFileType { get; set; }
    
            /// <summary>
            /// 邮件附件文件子类型 例如:图片 MailFileSubType="png"
            /// </summary>
            public string MailFileSubType { get; set; }
    
            /// <summary>
            /// 邮件附件文件路径  例如:图片 MailFilePath=@"C:Files123.png"
            /// </summary>
            public string MailFilePath { get; set; }
        }
    
        /// <summary>
        /// 邮件服务器基础信息
        /// </summary>
        public class MailServerInformation
        {
            /// <summary>
            /// SMTP服务器支持SASL机制类型
            /// </summary>
            public bool Authentication { get; set; }
    
            /// <summary>
            /// SMTP服务器对消息的大小
            /// </summary>
            public uint Size { get; set; }
    
            /// <summary>
            /// SMTP服务器支持传递状态通知
            /// </summary>
            public bool Dsn { get; set; }
    
            /// <summary>
            /// SMTP服务器支持Content-Transfer-Encoding
            /// </summary>
            public bool EightBitMime { get; set; }
    
            /// <summary>
            /// SMTP服务器支持Content-Transfer-Encoding
            /// </summary>
            public bool BinaryMime { get; set; }
    
            /// <summary>
            /// SMTP服务器在消息头中支持UTF-8
            /// </summary>
            public string UTF8 { get; set; }
        }
    
        /// <summary>
        /// 邮件发送结果
        /// </summary>
        public class SendResultEntity
        {
            /// <summary>
            /// 结果信息
            /// </summary>
            public string ResultInformation { get; set; } = "发送成功!";
    
            /// <summary>
            /// 结果状态
            /// </summary>
            public bool ResultStatus { get; set; } = true;
        }
    
        /// <summary>
        /// 邮件发送服务器配置
        /// </summary>
        public class SendServerConfigurationEntity
        {
            /// <summary>
            /// 邮箱SMTP服务器地址
            /// </summary>
            public string SmtpHost { get; set; }
    
            /// <summary>
            /// 邮箱SMTP服务器端口
            /// </summary>
            public int SmtpPort { get; set; }
    
            /// <summary>
            /// 是否启用IsSsl
            /// </summary>
            public bool IsSsl { get; set; }
    
            /// <summary>
            /// 邮件编码
            /// </summary>
            public string MailEncoding { get; set; }
    
            /// <summary>
            /// 邮箱账号
            /// </summary>
            public string SenderAccount { get; set; }
    
            /// <summary>
            /// 邮箱密码
            /// </summary>
            public string SenderPassword { get; set; }
    
        }
    }

    接收

    using MailKit;
    using MailKit.Net.Imap;
    using MailKit.Search;
    using MailKit.Security;
    using MimeKit;
    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace CommonTool.MailKit
    {
        /// <summary>
        /// 跟投邮件服务API
        /// </summary>
        public static class ReceiveEmailHelper
        {
            /// <summary>
            /// 设置发件人信息
            /// </summary>
            /// <returns></returns>
            public static SendServerConfigurationEntity SetSendMessage()
            {
                var sendServerConfiguration = new SendServerConfigurationEntity
                {
                    SmtpHost = ConfigurationManager.AppSettings["SmtpServer"],
                    SmtpPort = int.Parse(ConfigurationManager.AppSettings["SmtpPort"]),
                    IsSsl = Convert.ToBoolean(ConfigurationManager.AppSettings["IsSsl"]),
                    MailEncoding = ConfigurationManager.AppSettings["MailEncoding"],
                    SenderAccount = ConfigurationManager.AppSettings["SenderAccount"],
                    SenderPassword = ConfigurationManager.AppSettings["SenderPassword"]
                };
                return sendServerConfiguration;
            }
    
            /// <summary>
            /// 接收邮件
            /// </summary>
            public static void ReceiveEmail()
            {
                var sendServerConfiguration = SetSendMessage();
    
                if (sendServerConfiguration == null)
                {
                    throw new ArgumentNullException();
                }
    
                using (var client = new ImapClient(new ProtocolLogger(MailMessage.CreateMailLog())))
                {
                    client.Connect(sendServerConfiguration.SmtpHost, sendServerConfiguration.SmtpPort,
                        SecureSocketOptions.SslOnConnect);
                    client.Authenticate(sendServerConfiguration.SenderAccount, sendServerConfiguration.SenderPassword);
                    client.Inbox.Open(FolderAccess.ReadOnly);
                    var uids = client.Inbox.Search(SearchQuery.All);
                    foreach (var uid in uids)
                    {
                        var message = client.Inbox.GetMessage(uid);
                        message.WriteTo($"{uid}.eml");
                    }
    
                    client.Disconnect(true);
                }
            }
    
            /// <summary>
            /// 下载邮件内容
            /// </summary>
            public static void DownloadBodyParts()
            {
                var sendServerConfiguration = SetSendMessage();
    
                using (var client = new ImapClient())
                {
                    client.Connect(sendServerConfiguration.SmtpHost, sendServerConfiguration.SmtpPort,
                        SecureSocketOptions.SslOnConnect);
                    client.Authenticate(sendServerConfiguration.SenderAccount, sendServerConfiguration.SenderPassword);
                    client.Inbox.Open(FolderAccess.ReadOnly);
    
                    // 搜索Subject标题包含“MimeKit”或“MailKit”的邮件
                    var query = SearchQuery.SubjectContains("MimeKit").Or(SearchQuery.SubjectContains("MailKit"));
                    var uids = client.Inbox.Search(query);
    
                    // 获取搜索结果的摘要信息(我们需要UID和BODYSTRUCTURE每条消息,以便我们可以提取文本正文和附件)
                    var items = client.Inbox.Fetch(uids, MessageSummaryItems.UniqueId | MessageSummaryItems.BodyStructure);
    
                    foreach (var item in items)
                    {
                        // 确定一个目录来保存内容
                        var directory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "/MailBody", item.UniqueId.ToString());
    
                        Directory.CreateDirectory(directory);
    
                        // IMessageSummary.TextBody是一个便利的属性,可以为我们找到“文本/纯文本”的正文部分
                        var bodyPart = item.TextBody;
    
                        // 下载'text / plain'正文部分
                        var body = (TextPart)client.Inbox.GetBodyPart(item.UniqueId, bodyPart);
    
                        // TextPart.Text是一个便利的属性,它解码内容并将结果转换为我们的字符串
                        var text = body.Text;
    
                        File.WriteAllText(Path.Combine(directory, "body.txt"), text);
    
                        // 现在遍历所有附件并将其保存到磁盘
                        foreach (var attachment in item.Attachments)
                        {
                            // 像我们对内容所做的那样下载附件
                            var entity = client.Inbox.GetBodyPart(item.UniqueId, attachment);
    
                            // 附件可以是message / rfc822部件或常规MIME部件
                            var messagePart = entity as MessagePart;
                            if (messagePart != null)
                            {
                                var rfc822 = messagePart;
    
                                var path = Path.Combine(directory, attachment.PartSpecifier + ".eml");
    
                                rfc822.Message.WriteTo(path);
                            }
                            else
                            {
                                var part = (MimePart)entity;
    
                                // 注意:这可能是空的,但大多数会指定一个文件名
                                var fileName = part.FileName;
    
                                var path = Path.Combine(directory, fileName);
    
                                // decode and save the content to a file
                                using (var stream = File.Create(path))
                                    part.Content.DecodeTo(stream);
                            }
                        }
                    }
    
                    client.Disconnect(true);
                }
            }
        }
    }

    https://blog.csdn.net/sd7o95o/article/details/79493045

  • 相关阅读:
    触发Full GC执行的情况 以及其它补充信息
    HotSpot垃圾收集器GC的种类
    JVM学习之GC常用算法
    十大经典排序算法
    Java transient关键字使用
    Java并发编程:volatile关键字解析
    Linux Centos7.2 编译安装PHP7.0.2
    深入理解分布式事务及高并发下分布式事务的解决方案
    Windows及Linux环境搭建Redis集群
    软件项目进度管理(含敏捷项目管理)
  • 原文地址:https://www.cnblogs.com/sanday/p/9482024.html
Copyright © 2011-2022 走看看