
简介这是一份基于C#和ASP.NET框架的供应商管理系统毕业设计资源面向需要完成Web管理系统课题的高校学生与ASP.NET入门开发者覆盖供应商分类、信息管理、评价、留言及用户权限等典型业务模块。压缩包共386个文件大小仅1.77MB其中包含173个gif界面截图、90个js脚本、51个html页面、26个css样式、12个cs源文件及7个aspx页面源码与页面资源齐全便于对照学习前端交互和后端处理逻辑。已有384人学习适合作为课程设计或毕业设计的参考蓝本。除核心代码外资源还包含数据库文件和系统配置文件可帮助读者快速还原运行环境整体目录按功能模块组织能直观看到供应商列表、用户管理、登录验证等页面的实现方式并借助评价管理、留言管理等案例理解ASP.NET MVC的分层设计与业务逻辑组织为二次开发和答辩演示提供完整样例。 真正让我下决心动手做这套ASP.NET供应商管理系统不是领导安排的任务而是2018年年底一次非常普通的年终对账。采购部同事抱着一摞各家供应商的报价单来找我说ERP里的单价跟手上的纸质报价对不上查了一下午才弄清楚是有一家供应商在年中调过一次价邮件回复了但没人同步更新系统。那时候公司两千多家供应商的资质、联系方式、报价记录全都散落在Excel、邮箱甚至微信聊天记录里谁是一级供应商、谁该淘汰、谁的证书快过期基本靠采购员的人肉记忆。做这套系统的目标很明确把供应商从准入、报价、下订单到考核评估的整个生命周期管起来让数据有一个唯一的、可信的归宿。这篇文章就和你聊聊这套系统的技术选型、核心表设计、权限与文件上传等实战细节既适合正要自研供应商管理系统的同行也适合想了解ASP.NET MVC在内部管理系统里怎么落地的朋友。1. 自研供应商管理系统到底要解决什么问题从需求到边界的梳理1.1 内部管理系统的真正痛点是数据口径不一致市面上其实有不少成熟的采购管理系统和SRM产品我也去调研过两三家。但说实话对于一家内部流程特殊、预算有限、又希望能按自己习惯定制的中型制造企业来说买一套通用系统往往意味着要反过来改自己的流程来适应产品光是供应商分类编码规则、报价审批流程、考核指标这三项就谈不到一起。最后我们评估下来自研这套系统并不是要去拼功能完整度而是要解决一个核心问题让供应商主数据从源头统一让所有下游模块共用同一个数据口径。所以我在需求梳理阶段就给自己划了一条边界不做大而全的ERP不做复杂的财务结算只围绕供应商本身的生命周期做闭环管理。范围是供应商档案管理、资质证照管理、询价报价协同、订单确认、供应商业绩评估这五个核心域。这样划分之后整个系统的技术复杂度其实下降了一个量级也让后面选型的时候我心里更有底。1.2 主要用户角色和核心业务流程系统使用方主要有三类采购员是日常操作量最大的用户负责供应商开发、询价、报价比价和下订单业务审批人负责准入审批、调价审批和超额订单审批供应商本身也开放了一个外部账号可以自己维护档案资料、在线报价、确认订单。整体业务流程是潜在供应商申请入驻提交资质资料经采购初筛和业务主管审批后转为合格供应商然后进入询比价和下单环节最后按季度进行绩效评分不合格的暂停合作或移入黑名单。这个流程听起来不复杂但真正落到系统里就会发现每一步都牵扯到单据状态流转、数据校验和历史留痕。所以我一直认为内部管理系统开发的难点从来不在某个单个功能而在于把业务规则表达清楚并且让系统在异常情况下还能保持数据一致。2. 技术选型复盘在ASP.NET的这个分支上我为什么没有盲目追新2.1 对外部环境和团队现状的冷静评估当时是2019年初.NET Core已经出到2.x了社区里到处是跨平台云原生的声音。但我的团队情况是现有的其他几个内部系统都跑在Windows Server 2012上加IIS数据库全是SQL Server团队多数人写了五六年的.NET Framework对微软自家的传统技术栈非常熟。作为开发者我也很想上新东西但作为要为系统持续负责的人我更看重的是这个技术选型能不能在现有环境里稳定运行团队遇到问题能不能快速解决。评估下来我最终选择了ASP.NET MVC 5而不是.NET Core。核心原因有三个一是目标服务器和运维习惯都不具备容器化条件跨平台的优势对我们没有实际收益二是原有的许多公共类库、报表组件和第三方控件在.NET Framework环境下最稳妥三是内部系统强调快速交付和长期稳定没必要让团队为一个新框架的兼容性问题买单。我的观点一直是技术选型没有绝对的先进只有合不合适具体场景。2.2 系统分层和项目结构设计项目最终分成了五个程序集Web层、业务逻辑层、数据访问层、实体模型层和公共工具层。实体模型层放数据库表映射和DTO数据访问层直接用Entity Framework 6业务层做校验、状态流转和事务控制Web层只用Controller和View来承接请求。大致目录结构是这样的K36.Supplier.Web // MVC项目Controllers、Views、Scripts K36.Supplier.Business // 业务逻辑供应商服务、询报价服务、订单服务 K36.Supplier.DataAccess // EF6 数据上下文、仓储、迁移脚本 K36.Supplier.Models // 实体类、枚举、DTO K36.Supplier.Common // 扩展方法、日志、Excel导出、通用返回结果这里有个设计细节我觉得值得说所有业务方法统一返回一个Result对象里面包含成功标志、错误码、提示信息和返回数据。这个习惯救了我很多次尤其是做对接和批量导入的时候每个接口的调用方都能拿到统一的错误结构处理异常逻辑比到处抛异常要清晰得多。2.3 为什么没有采用前后端分离我承认前后端分离是趋势但对这个项目而言用Razor视图加少量jQuery和Ajax就完全足够了。系统内部使用并发量不大交互复杂度和电商类网站差着几个量级。采用服务端MVC渲染的一个好处是开发效率极高一个功能从页面到Controller到业务层链路短、好调试、权限控制也简单。我也见过很多内部系统强行上Vue加Web API结果是维护成本反而比原来的MVC高了不少。技术没有优劣关键是别杀鸡用牛刀。3. 核心数据模型供应商全生命周期撑在四张表上3.1 供应商主档表状态机是整套数据的灵魂供应商主档表是整个系统最核心的一张表它承载的不仅是公司名称和联系人更是每一个供应商在业务中的身份状态。我设计的字段包括主键、供应商编码、供应商全称、简称、统一社会信用代码做唯一性校验、法定代表人、注册地址、联系人、联系电话、合作状态、供应商分类、来源渠道、创建人、创建时间、更新时间、软删除标记。合作状态是这套系统的关键。我把整个生命周期分成六个状态潜在、申请中、合格、暂停、黑名单、淘汰。状态之间的流转是严格禁止跳级和回退的比如不合格的供应商只能从申请中回到潜在不能直接变成合格或暂停。3.2 资质证照表到期预警不是一个小功能很多系统的资质管理就是传几张图片就完事但真正做过采购业务的都知道供应商的ISO证书、特种设备许可证、安全生产标准化证书过期一天都可能导致整个采购合同无效。资质证照表我单独设计了一个子表字段包含供应商ID、证书类型编码、证书编号、发证机关、发证日期、有效期至、附件路径、是否强制资质、审核状态。证书到期预警用一条定时任务去扫这张表。提前90天、60天、30天三个节点分别给采购员发站内信和邮件提醒。结果上线三个月光这一项就让采购部提前处理了四十多次即将到期的资质更新之前那种到用的时候才发现资质过期的窘境再也没出现过。3.3 物料报价、订单关联与业绩评估将日常业务串起来物料报价和订单数据不能和主档脱节。我设计了供应商物料报价表字段包括供应商ID、物料编码、物料名称、规格型号、单位、含税单价、币种、报价有效期、报价状态。这个表是询比价的结果也是下订单时的价格依据。每次报价单审批通过后自动更新该供应商对应物料的最新有效价形成询价—报价—定价的闭环。业绩评估表按季度记录供应商的交付评分包括及时交付率、来料合格率、配合度评分、整改响应速度等。评估结果反过来影响主档的合作状态连续两个季度评分低于60分的系统自动触发暂停流需要重新评审才能恢复。这样做的好处是淘汰供应商不再是采购员的主观决定而是系统里有数据支撑的客观结果。3.4 编码规则、软删除和并发控制供应商编码用的是固定的规则SUP加四位年份加三位流水号比如SUP202100137。这个规则看起来简单但在生成时必须做并发控制我用的是数据库中的一张编码序列表每次生成编码时开启一个独立事务先更新序列号再取出编码避免并发情况下出现重复编号。软删除标记在每个业务表的查询里都默认过滤供历史订单追溯用。内部系统千万不要物理删除供应商数据因为历史订单、报价记录、对账单都关联着主档一旦删除整条业务链就断了。4. 询比价和订单协同把Excel工作流搬到线上问题的关键在状态流转4.1 询价单与报价单的灵活设计询价单我用主从表结构主表记录询价单号和需求部门从表记录每一行物料。供应商在外部账号里只能看到自己被邀请询价的物料输入含税报价、交期和起订量提交后不能修改。报价单提交后系统会记录报价日志如果供应商发现报错了只能重新报价不能覆盖历史记录这样每次比价都有据可查。设计这个问题的时候我自己踩过一个坑最初报价单是允许供应商多次修改覆盖的结果有一次两家供应商的关键物料报价几乎一样审计的时候说不清楚最终价是哪个时点确认的。后来改成每次报价存一条版本记录、参与比价取最新版本整个留痕就清晰了。历史版本永不修改新版本通过状态字段来控制生效这个设计对所有涉及价格的单据都适用。4.2 比价逻辑与单据状态机比价逻辑我区分了两种方式单一物料按最低价成交多物料、多供应商的打包单则按供应商总价和交期综合评分。页面展示时会同时列出各个供应商的单项价、总价、交付周期和上次合作评分采购员可以根据权重系数来自动生成推荐结果但仍然保留人工调整的权限。单据的关键状态机设计如下public enum RfqStatus { Draft 0, // 草稿 Published 1, // 已发布 Quoting 2, // 报价中 Closed 3, // 报价截止 Awarded 4, // 已定标 Cancelled 5 // 已取消 }定标之后系统自动生成采购订单草稿经过内部审批后推送给供应商确认供应商确认后单据状态变成已确认此时订单才允许进入送货和收货流程。整个状态机使用一个统一的状态流转服务来更新任何非法状态跳转都会被拦截。4.3 订单确认环节的防丢单设计订单推送给供应商之后如果供应商72小时没有确认系统会自动提醒采购员电话跟进。如果供应商在系统里点了交期异议订单会回到采购员那边重新确认交期不能直接挂着不处理。这个逻辑看起来简单但当初如果不做状态超时扫描线上订单很容易变成发了但没人管的失控状态。这里我特别想强调一点任何线上协同流程一定要设计无响应后的升级机制。内部系统最怕的不是流程跑不通而是流程停在某个节点上没人知道。所以我在所有关键节点都设置了超时提醒或者升级审批让每一张卡住的单据都能及时暴露出来而不是默默躺在数据库里。5. 三个高频实战坑读取请求体、IIS上传限制、按钮权限控制5.1 StreamReader读取HttpContext.Request.Body的细节系统里有一个场景供应商平台会通过接口回调把物流状态推送到我们的供应商管理系统。很多初学者拿到这种需求第一反应是用模型绑定直接把JSON反序列化成实体类但实际上回调的报文格式经常不稳定或者需要做签名验证和原始报文留痕这个时候直接读Request.Body反而是更可靠的做法。我处理回调的代码写得很直接public async TaskActionResult ReceiveDeliveryCallback() { string rawBody string.Empty; using (var reader new StreamReader(HttpContext.Request.Body, Encoding.UTF8)) { rawBody await reader.ReadToEndAsync(); } if (string.IsNullOrWhiteSpace(rawBody)) { return Content(empty); } // 1. 先做签名校验防止伪造请求 // 2. 留痕原始报文到日志表 // 3. 再反序列化成具体DTO做业务处理 var dto JsonConvert.DeserializeObjectDeliveryCallbackDto(rawBody); // 业务处理... return Content(ok); }这里有几个关键细节一定要记住第一Request.Body是一个前向读取的流只能读一次如果你在Filter里不小心读过了Controller里再用模型绑定就读不到数据了所以在Filter里读取后必须把流位置重置为0第二一定要用using保证读取后流被释放避免连接池和资源泄露第三接收回调的接口必须做签名校验或者IP白名单否则别人抓包模拟请求就能往你的系统里推假数据。这套做法在后续对接物流接口、对接电子签章平台时都反复用到了属于一次写对、长期复用的典型代码。5.2 IIS对于文件上传的限制要改两个地方供应商系统里采购员要上传供应商的资质文件PDF和图片居多单个文件经常超过10MB。我最初开发的时候本地一切正常部署到IIS服务器以后一直报404.13错误查了半天才发现是IIS的请求过滤模块默认限制上传文件不超过30MB而且这个限制和ASP.NET自身的限制是两套体系必须同时修改。web.config里需要修改的两个位置是system.web httpRuntime targetFramework4.7.2 maxRequestLength102400 executionTimeout300 / /system.web system.webServer security requestFiltering requestLimits maxAllowedContentLength104857600 / /requestFiltering /security /system.webServermaxRequestLength的单位是KBmaxAllowedContentLength的单位是字节这两个常常被搞混。而且改了之后IIS的Web.config缓存会让修改不立即生效最好重启一下应用程序池不然验证的时候很容易得出改了没效果的错误结论。另外图片入库之后最好做一次压缩和缩略图处理否则数据库和文件服务器都容易被大图片拖垮。5.3 按钮级权限用自定义AuthorizeAttribute落地内部门户系统有严格的权限要求采购员能看到全部供应商资料但只能编辑自己负责的供应商财务角色只能导出价格数据供应商账号只能看自己相关的内容。我直接使用ASP.NET MVC的Filter机制来实现按钮级权限控制。权限表里保存了类似Supplier.Edit、Quote.Approve这样的权限码每个Action配置对应的权限码页面渲染按钮时也通过一个扩展方法来判断是否显示。核心思路是重写AuthorizeAttributepublic class PermissionAuthorizeAttribute : AuthorizeAttribute { public string PermissionCode { get; set; } protected override bool AuthorizeCore(HttpContextBase httpContext) { if (!base.AuthorizeCore(httpContext)) { return false; } var user (UserPrincipal)httpContext.User; return user.Permissions ! null user.Permissions.Contains(PermissionCode); } protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { if (filterContext.HttpContext.Request.IsAjaxRequest()) { filterContext.Result new JsonResult { Data new { success false, message 没有操作权限 }, JsonRequestBehavior JsonRequestBehavior.AllowGet }; } else { base.HandleUnauthorizedRequest(filterContext); } } }这样做的好处是权限判断和业务逻辑完全解耦新增加一个操作只需要在Controller方法上加一行特性标签页面按钮再用同一个权限码控制显隐。权限验证在MVC管线里已经过滤了一遍页面隐藏只是用户体验层面的兜底真正安全的是服务端的这一层校验这个思路在后来扩展系统模块时帮我省了很多事。6. 定时任务与最后落地让系统真正运转起来的细节6.1 用Hangfire做到期提醒但要注意IIS应用池回收资质证照到期提醒、订单超时自动关闭、供应商季度考核自动打分这些后台任务我选了Hangfire来做。选它的原因很简单不用额外部署Windows服务直接在ASP.NET应用里注册后台任务可以集成到站点仪表盘里查看执行日志。但这里有一个坑我必须提醒在IIS里部署的ASP.NET应用默认情况下应用池空闲超过20分钟会被回收一旦回收托管在进程里的Hangfire后台任务就停掉了。解决方法是把应用池的闲置超时设为0同时开启启动模式为AlwaysRunning让站点进程常驻。除此之外我在世界时间每天凌晨3点定时执行一个自检任务检查所有定时任务的执行记录如果有连续三天没跑成功的任务会自动发告警邮件。后台任务这种东西跑了很久突然静默失效是最可怕的没有监控就等于没有定时任务。6.2 上线前的数据迁移Excel导入的编码坑和脏数据清洗公司原有的供应商资料都散在Excel里导入成了上线前最琐碎的一步。采购部的朋友发过来的Excel文件经常是多个sheet混合了供应商档案、联系人、报价三种数据格式还不统一。我写了一个专用的导入页面先在页面上预览清洗后的数据、核对错误行列再分批入库绝不在导入页面上直接执行整表入库。导出用NPOI导入时用NPOI读取Excel但要注意编码问题老版本的Excel文件用NPOI读取通常没问题csv文件反而容易踩GBK和UTF-8编码的坑。如果非要用StreamReader读csv记得用Encoding.GetEncoding(GBK)而不是UTF8。这个看起来很小的细节在数据处理那一周差点让人崩溃。6.3 我的一点体会内部系统开发稳定比惊艳重要这套系统从需求梳理到上线大概用了四个月现在运行了快三年期间大大小小改过三十多个版本没有出现过一次需要业务停摆的严重故障。我最大的体会是内部管理系统的开发很多时候不是在追求技术上的炫耀而是要在复杂多变的业务里保持克制把数据模型和状态流转设计扎实把权限和审计留痕做规范。最后再分享一个小技巧当初我在所有业务表都加了created_at、updated_at、created_by这几个字段当时采购部觉得多此一举后来每一次数据出现争议、每一次审计要求追责这几个字段都成了救命稻草。我强烈建议做同类型系统的朋友千万别为了省地方把审计字段去掉下次再有人问这条报价谁改的你就知道这个决定有多值钱了。本文还有配套的精品资源点击获取