简介:一套基于C#的ASP.NET供应商管理系统完整源码,面向计算机相关专业学生、初级开发者以及需要快速搭建供应链管理后台的人员。系统围绕供应商信息的全生命周期管理展开,支持按类型、地区、产品或服务等维度分类,并提供供应商资料的录入、更新与查询,同时包含合作评价、留言沟通和用户权限分配等配套功能,能够帮助企业或开发者规范供应商档案、辅助采购决策。资源包共386个文件,压缩包大小约1.77MB,主要文件类型涵盖aspx动态页面、cs后端代码、js脚本、css样式表、gif图片及数据库文件(如mdf/ldf)等,基本覆盖了页面呈现、业务逻辑与数据存储三层结构。读者通过解压运行,可以直观看到登录验证、供应商列表、评价管理、留言反馈等典型模块的代码实现,并借鉴其中基于ASP.NET MVC的控制器与视图组织方式。已有386人学习下载,适合作为毕业设计参考或企业内部系统开发的起始模板。
1. 供应商管理系统,为什么 ASP.NET 这把老枪依然好使
接到“ASP.NET供应商管理系统”这类标题,大多数 .NET 从业者的第一反应是“又是这套成熟业务”。实际动手后才发现,业务越传统,坑越深。供应商主数据怎么建模、资质证照怎么管有效期、评审流程怎么落地,每一步都在考验架构基本功。ASP.NET 的生态成熟、招人容易、坑都有前人踩过,反而是这类系统最稳的底座。这篇文章不聊概念,直接讲我用这套技术栈做供应商管理系统的完整方案:技术选型、表结构、状态机、踩坑记录,以及最后的评分扩展。适合正要动手做供应商模块、或者想把旧系统从 Excel 和邮件里解放出来的团队参考。
2. 技术选型与架构分层:先把方案立住再动手
2.1 从 Web Forms 到 MVC 再到 Core:选哪一代才不翻车
“ASP.NET供应商管理系统”这个标题本身没有限定具体版本,但选型直接决定后面三个月是顺畅开发还是持续填坑。目前存量系统里 Web Forms 还有不少,新项目用 MVC 或 Core 居多。我的常见做法是:新系统直接用 ASP.NET Core,目标框架选 .NET 6 或 .NET 8 这类 LTS 版本;如果公司服务端还是 Windows Server 2012 且运维不愿意装运行时,那就退回 ASP.NET MVC 5,跑在 .NET Framework 4.8 上。
选择依据很简单:供应商管理系统是典型的“表单密集 + 审批流 + 报表导出”业务,对性能要求不高,对开发效率和生态依赖高。Core 的依赖注入、EF Core、自带的身份认证都省事;MVC 5 则胜在兼容老服务器、不用额外部署运行时。至于 Web Forms,除非是接手存量项目,否则我不建议新写——ViewState 和事件模型在复杂业务下维护成本偏高。
有个容易翻车的点:很多人以为“ASP.NET”就等于“.NET Framework”,其实 ASP.NET Core 是跨平台的,能跑 Linux。如果公司云主机是 Linux,选 Core 可以直接省掉 Windows 授权费。我经手的一个模拟项目X就是在 Linux + Nginx 上部署的,除了首次配置反向代理时折腾了一会儿,整体很顺。
2.2 常见分层与项目结构:按业务拆还是按技术拆
供应商管理系统的业务边界其实很清晰:供应商档案、资质证照、评审记录、合格名录、询报价记录。分层我一般按“技术分层 + 业务模块”混合来拆,既照顾分层架构的通用性,又避免所有 Controller 堆在一个项目里。
一个我常用的最小可落地结构:
src/ Supplier.Web // ASP.NET Core MVC 项目:Controller、View、Model Supplier.Application // 应用服务层:用例编排、DTO、接口 Supplier.Domain // 领域层:实体、枚举、领域服务 Supplier.Infrastructure // 数据访问、EF Core DbContext、仓储实现、Excel导出dotnet new mvc -n Supplier.Web dotnet new classlib -n Supplier.Application dotnet new classlib -n Supplier.Domain dotnet new classlib -n Supplier.Infrastructure这个结构的逻辑是:Controller 层只做参数绑定和结果返回,不写业务;Application 层编排业务流程,比如“供应商注册时先校验信用代码唯一,再创建档案”;Domain 层放核心实体和状态枚举;Infrastructure 层管数据库和文件导出。
参数层面有两个点值得注意。第一,项目引用方向必须单向:Web 引用 Application 和 Infrastructure,Application 引用 Domain,Domain 不引用任何其他项目。第二,EF Core 的 DbContext 放在 Infrastructure 层,但实体类放在 Domain 层,这样领域层不会被 EF Core 的 NuGet 包污染,后续换 ORM 不用动业务代码。
2.3 数据访问选 EF Core 还是 Dapper:查询惯性决定开发速度
这个话题在供应商管理系统里尤其现实。系统里有大量“按公司名模糊查”“按资质类型过滤”“按有效期区间查”的查询,而且查询条件经常组合。EF Core 的 LINQ 表达式在这种场景下写起来最快,改条件也就是加一个Where的事情;Dapper 写 SQL 更灵活,但每加一个条件就得改 SQL 字符串,时间长了拼接逻辑容易出 bug。
我的选型原则是:主流程用 EF Core,报表和复杂统计用 Dapper 或者直接写 SQL 映射到视图。EF Core 在列表页配合IQueryable延迟执行,分页和排序都顺畅;报表场景往往涉及多表聚合,EF Core 生成的 SQL 可读性差,不如直接写原生 SQL。这里有个性能教训:EF Core 千万不要在循环里查询数据库,比如查 100 个供应商再逐个查资质,就是 101 条 SQL;用Include或者手动 join 一次查出来。
如果首次跑通代码,还需要在 Program.cs 里注册 DbContext:
builder.Services.AddDbContext<SupplierDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("SupplierDb")));注册字符串里注意Max Pool Size参数,默认 100 连接池上限一般够用,但供应商系统上线初期并发不高,反而要小心连接未释放的问题。我遇到过症状是“系统用一会儿就卡死,重启 IIS 就好”,后来发现是有个查询用了AsEnumerable()导致连接没释放,改成ToListAsync()并加上using或await using就稳定了。
3. 供应商主数据模型:把表结构和实体类一次性定对
3.1 核心表设计:供应商基础信息、资质与行业分类
供应商管理系统的地基是主数据表。这块设计错了,后面所有功能都在打补丁。我给出的常见做法是拆五张核心表:供应商主表、行业分类表、供应商分类关联表、资质证照表、评审记录表。
供应商主表的字段设计尤其要注意“唯一性”和“变更留痕”。唯一性指统一社会信用代码这个自然键,必须在数据库层面加唯一索引,不能只靠业务逻辑判断,否则并发注册时两条重复数据就进来了。变更留痕指联系人、银行账户这类字段要用独立的历史记录表存变更前后值,方便审计。供应商主表本身只存当前值。
| 字段 | 类型 | 约束/说明 |
|---|---|---|
| Id | bigint | 主键,自增 |
| SupplierCode | nvarchar(32) | 业务编号,如 SUP20240001 |
| CompanyName | nvarchar(200) | 公司全称 |
| CreditCode | nvarchar(18) | 统一社会信用代码,唯一索引 |
| Status | int | 0草稿 1审核中 2合格 3黑名单 |
| ContactName | nvarchar(50) | 当前联系人 |
| ContactPhone | nvarchar(20) | 联系电话 |
| CreatedAt | datetime2 | 创建时间,UTC存储 |
| UpdatedAt | datetime2 | 更新时间,UTC存储 |
资质证照表是另一个关键设计点。供应商的资质有营业执照、ISO 认证、行业许可证等,每类证照的有效期不同。我的做法是统一用一张表存,用CertType字段区分,ExpiryDate单独建索引,方便跑“即将过期”的定时提醒任务。
CREATE TABLE SupplierCertifications ( Id BIGINT IDENTITY(1,1) PRIMARY KEY, SupplierId BIGINT NOT NULL, CertType NVARCHAR(50) NOT NULL, CertName NVARCHAR(200) NOT NULL, CertNo NVARCHAR(100) NULL, ExpiryDate DATE NULL, FilePath NVARCHAR(500) NULL, CreatedAt DATETIME2 DEFAULT SYSUTCDATETIME() ); CREATE INDEX IX_SupplierCert_Expiry ON SupplierCertifications(ExpiryDate);通用字段设计这里有个非常重要的点:ExpiryDate一定要允许为 NULL。有些资质是长期有效的,比如营业执照有的地区长期有效,强行要求填日期会导致录入人员乱填 2099 年做变通,反而脏了数据。查询“过期资质”时用(ExpiryDate IS NOT NULL AND ExpiryDate < @today)条件过滤即可。
3.2 用 EF Core Code First 落地实体与 DbContext 配置
表结构定好了,代码侧我用 EF Core Code First 方式把实体类写出来,好处是数据库迁移和实体定义同步,改字段不用手写 SQL。供应商实体的核心属性如下:
public class Supplier { public long Id { get; set; } public string SupplierCode { get; set; } = string.Empty; public string CompanyName { get; set; } = string.Empty; public string CreditCode { get; set; } = string.Empty; public SupplierStatus Status { get; set; } public string ContactName { get; set; } = string.Empty; public string ContactPhone { get; set; } = string.Empty; public DateTime CreatedAt { get; set; } public DateTime UpdatedAt { get; set; } public ICollection<SupplierCertification> Certifications { get; set; } = new List<SupplierCertification>(); }DbContext 里用 Fluent API 配置唯一索引和字段长度,比 DataAnnotation 写在实体类里更集中,字段校验规则变动时只改一处。唯一索引配置如下:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Supplier>(entity => { entity.ToTable("Suppliers"); entity.HasIndex(e => e.CreditCode).IsUnique(); entity.Property(e => e.CompanyName).HasMaxLength(200).IsRequired(); entity.Property(e => e.ContactPhone).HasMaxLength(20); entity.HasMany(e => e.Certifications) .WithOne() .HasForeignKey(e => e.SupplierId); }); }逻辑说明:HasIndex(...).IsUnique()把统一社会信用代码的唯一约束下沉到数据库,这比在服务层用AnyAsync检查更可靠。HasMaxLength限制字符串长度,防止用户把一个 2000 字的备注粘到公司名字段里。HasMany配置了供应商和资质表的一对多关系,这样 EF Core 做Include查询时能自动关联。
参数说明:迁移命令建议用dotnet ef migrations add InitSchema生成迁移文件,再用dotnet ef database update建库。这里有个暗坑,dotnet ef工具版本必须和项目引用的Microsoft.EntityFrameworkCore.Design包版本一致,不一致会出现“无法加载程序集”的报错。我第一次遇到时查了半天,最后发现是 .NET SDK 全局工具版本和新项目的 EF Core 包版本差了一个大版本。
3.3 两个必调参数:级联删除与字段长度校验
级联删除是供应商系统里的火药桶。供应商有资质、评审、询报价记录,这些子表都外键引用供应商主表。如果删除供应商时数据库默认的NO ACTION被触发,删除会因外键约束失败;如果改成CASCADE,删一个供应商可能连资质文件和历史评审一起删掉,这在审计上是灾难。
我的做法是“逻辑删除代替物理删除”。在供应商表加IsDeleted位字段,删除操作只是把它置为 1,所有查询默认过滤掉。这样既能保证外键关系不炸,又能保留历史记录。对应的 DbContext 配置:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Supplier>().HasQueryFilter(e => !e.IsDeleted); }注意:HasQueryFilter是 EF Core 的全局查询过滤器,它会自动附加到所有针对该实体的 LINQ 查询上。这样即使代码里漏写了Where(e => !e.IsDeleted),也不会查出已删除数据。但有个副作用:Include关联查询时,如果子表也有类似过滤器,要小心出现预期外的数据缺失。解决方法是关联表不设全局过滤器,而是用显式条件查询。
字段长度校验是另一个必调点。供应商系统经常要对接 Excel 导入,Excel 里一个字段可能写了 500 个字符,数据库字段却只有 200,导入时 EF Core 报String or binary data would be truncated错误。这个报错在 SQL Server 里属于常见异常,但信息量大,调试还行;Oracle 数据库则直接报 ORA-12899,更直白。我的建议是所有导入操作的入口统一做一个长度预校验:
if (input.CompanyName?.Length > 200) { throw new ValidationException($"公司全称超过最大长度200,当前{input.CompanyName.Length}"); }这个校验放在 Application 层做,而不是让数据库兜底。好处是用户能立刻看到是哪个字段超长,坏处是校验规则需要维护两份。为了省事,我一般用DataAnnotations的MaxLength特性,配合 ASP.NET MVC 模型验证自动执行,页面上的校验和数据库约束至少能对齐一部分。
4. 关键业务流实现:审核、转正与合格名录
4.1 供应商注册申请到审核流转的代码实现
供应商系统的核心流程是:供应商提交注册申请、采购员初审、部门负责人复审、通过后进入合格名录。这个流程用状态机描述比用一堆 if-else 清晰得多。常见的状态枚举如下:
public enum SupplierStatus { Draft = 0, // 草稿 PendingReview = 1, // 待审核 Approved = 2, // 审核通过,合格供应商 Rejected = 3, // 已驳回 Blacklisted = 4, // 黑名单 Frozen = 5 // 冻结,临时暂停合作 }状态流转规则:Draft 提交后变 PendingReview;PendingReview 审核通过变 Approved,驳回则回 Draft 或直接 Rejected;Approved 供应商出现质量问题可转 Frozen 或 Blacklisted。代码里我一般用一个独立的领域服务方法来确保状态流转不越级:
public async Task<Result> ReviewSupplierAsync(long supplierId, bool isApproved, string comment) { var supplier = await _dbContext.Suppliers .Include(s => s.Certifications) .SingleOrDefaultAsync(s => s.Id == supplierId); if (supplier == null) return Result.Fail("供应商不存在"); if (supplier.Status != SupplierStatus.PendingReview) return Result.Fail("当前状态不可审核"); if (isApproved) { // 校验关键资质是否齐全且未过期 var hasValidCert = supplier.Certifications.Any(c => c.CertType == "BusinessLicense" && (c.ExpiryDate == null || c.ExpiryDate >= DateTime.UtcNow.Date)); if (!hasValidCert) return Result.Fail("营业执照缺失或已过期,不能审核通过"); supplier.Status = SupplierStatus.Approved; supplier.ApprovedAt = DateTime.UtcNow; } else { supplier.Status = SupplierStatus.Rejected; } supplier.UpdatedAt = DateTime.UtcNow; _dbContext.ReviewHistories.Add(new ReviewHistory { SupplierId = supplierId, IsApproved = isApproved, Comment = comment, ReviewedAt = DateTime.UtcNow }); await _dbContext.SaveChangesAsync(); return Result.Ok(); }这段代码的逻辑说明:先查供应商并Include资质列表,避免后续访问Certifications属性时触发懒加载产生额外查询。审核动作前的状态判断是关键,把状态机约束落在代码里,而不是由调用方保证。审核通过前检查营业执照是否存在且未过期,这是供应商管理系统的硬约束,比“上游判断好了再调接口”更安全。
参数说明:DateTime.UtcNow.Date是为了拿到 UTC 当天日期,避免 SQL Server 的datetime2数据类型出现时间部分干扰日期比较。审核历史单独存表,方便日后追溯“谁在什么时候审核的、结论是什么”,这条在供应商审计场景几乎是必须的。
4.2 供应商合格名录与状态变更的批量操作
审核通过的供应商进入合格名录后,后续要支持“批量冻结”“批量转黑名单”这类操作。最常见的场景是:某类原材料出现质量事故,需要把所有该品类供应商冻结。用循环逐条更新会有性能问题,而且事务边界太大。我的做法是用 EF Core 的ExecuteUpdate直接发一条 SQL 更新语句:
await _dbContext.Suppliers .Where(s => s.CategoryId == categoryId && s.Status == SupplierStatus.Approved) .ExecuteUpdateAsync(setters => setters .SetProperty(s => s.Status, SupplierStatus.Frozen) .SetProperty(s => s.UpdatedAt, DateTime.UtcNow));ExecuteUpdate是 EF Core 7 提供的高效更新 API,直接生成UPDATE语句,不查实体也不走变更追踪,特别适合这种批量状态变更场景。需要注意两点:第一,ExecuteUpdate不会触发 EF Core 的 SaveChanges 拦截器,所以审计日志需要手动记录;第二,ExecuteUpdate不能更新导航属性,只能更新标量属性,所以这个动作在 Application 层补一条黑名单操作日志。
批量状态变更后,合格名录页面要能实时反映变化。我用的是IQueryable分页查询,好处是筛选条件和分页可以组合成一条 SQL,不用把全表数据拉到内存再分页:
var query = _dbContext.Suppliers .Where(s => s.Status == SupplierStatus.Approved) .AsNoTracking(); if (!string.IsNullOrEmpty(keyword)) { query = query.Where(s => s.CompanyName.Contains(keyword)); } var pageResult = await query .OrderByDescending(s => s.ApprovedAt) .Skip((pageIndex - 1) * pageSize) .Take(pageSize) .ToListAsync();逻辑说明:条件先组合到IQueryable,最后才调用ToListAsync执行 SQL。AsNoTracking()是查询优化关键,纯展示场景不需要 EF Core 跟踪实体状态,可以减少内存占用和快照开销。OrderByDescending之后再做Skip/Take,确保分页顺序稳定。这里有个排序注坑:如果排序字段是DateTime?,空值会排在最前面或最后面,不同数据库行为不一致,所以在建表时把ApprovedAt设为非空、默认值SYSUTCDATETIME()可以规避。
4.3 资质到期提醒与定时任务的落地写法
供应商系统的加固功能是资质到期提前预警。采购员不可能每天人工翻证照有效期,所以需要后台定时任务扫描。ASP.NET Core 里有现成的BackgroundService可以做。我写过一个最小可用的到期提醒服务:
public class CertExpiryCheckService : BackgroundService { private readonly IServiceScopeFactory _scopeFactory; public CertExpiryCheckService(IServiceScopeFactory scopeFactory) { _scopeFactory = scopeFactory; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { await CheckAndNotifyAsync(); await Task.Delay(TimeSpan.FromHours(24), stoppingToken); } } private async Task CheckAndNotifyAsync() { using var scope = _scopeFactory.CreateScope(); var dbContext = scope.ServiceProvider.GetRequiredService<SupplierDbContext>(); var today = DateTime.UtcNow.Date; var expiring = await dbContext.SupplierCertifications .Where(c => c.ExpiryDate != null && c.ExpiryDate >= today && c.ExpiryDate <= today.AddDays(30)) .ToListAsync(); // 按供应商分组,生成提醒文案,发邮件或站内消息 } }关键点有两个。第一,BackgroundService里不能直接用构造函数注入SupplierDbContext,因为它是单例生命周期,而 DbContext 是 Scoped;必须用IServiceScopeFactory创建作用域,否则拿到的 DbContext 生命周期混乱,搞不好出现并发访问问题。第二,Task.Delay的间隔时间是“执行完一轮后等 24 小时”而不是“固定间隔 24 小时执行”,这样如果查询慢,不会造成任务叠加。
定时任务是供应商系统里最容易出玄学问题的地方。我的血泪经验是:定时任务代码要记录运行日志,至少要有“开始时间、扫描行数、异常信息”。没有日志的定时任务出问题时,你只能干瞪眼,完全不知道它是没跑还是跑了没查到期数据。
5. ASP.NET 供应商管理系统的 5 条踩坑记录
5.1 现象:导入 Excel 时重复供应商数据成倍增加
原因:导入逻辑只按“公司名称”判断是否已存在,两个系统录入的名称一个带“有限公司”,一个带“(中国)有限公司”,就被当成两条。再加上未在数据库层加唯一约束,重复数据直接入库。
解决:统一社会信用代码是唯一可靠的自然键,导入前先按CreditCode去重。Excel 里没有信用代码的,用“公司名全称 + 注册地”组合去重,并且用脚本把已有数据清洗一遍再导入。清洗脚本要在事务里跑,先查出重复数据,再决定保留哪一条、合并哪一条。
5.2 现象:审核通过后,供应商列表里却看不到记录
原因:我用了HasQueryFilter(e => !e.IsDeleted)全局过滤,但审核通过的逻辑里没有给IsDeleted赋值,而新供应商实体创建时没显式初始化该字段,数据库默认值设成了 1。
解决:建表时所有可空或非可空的字段都要显式给出默认值。IsDeleted字段的默认值必须设为0,并且在实体构造函数里初始化:
public bool IsDeleted { get; set; } public Supplier() { IsDeleted = false; }这项排查花了一下午,最后是直接查数据库发现IsDeleted全是 1 才定位到问题。所以建实体类时,bool类型属性千万别依赖 CLR 默认值false,一定要在构造函数或字段声明里写清楚。
5.3 现象:服务器时区设成 UTC+8,到期提醒总是提前 8 小时触发
原因:代码里用DateTime.UtcNow存时间,但查询条件里的today.AddDays(30)用的是服务器本地时间。服务器时区为 UTC+8,导致“今天”的实际含义和个人电脑不一致。
解决:统一时间基准,所有时间计算都用 UTC,展示层才转本地时间。SQL 查询里也写成SYSUTCDATETIME()而非GETDATE()。这个坑在供应商管理系统尤其致命,因为证照过期时间是业务硬约束,差一天可能就是准入和禁止的区别。另外提醒邮件里要标注“北京时间”还是“UTC 时间”,否则采购员会看迷糊。
5.4 现象:导出供应商台账时,页面超时或内存溢出
原因:用 EF Core 直接查全量数据到内存,再循环生成内存中的 Excel。供应商数据超过 1 万条后,内存占用翻了几倍,页面卡死。
解决:改成流式导出,用StreamWriter逐行写 CSV,或者用 Excel 模板逐行写入。查询也要分批,每 500 条一批ToList,而不是一次性拉全量。如果导出条件是“某品类下所有供应商”且数据量大,建议直接走后台任务,生成文件后回写下载地址,别在 HTTP 请求里同步导出。
5.5 现象:部署到 IIS 后界面样式加载不出来,URL 路由全部 404
原因:ASP.NET Core 部署到 IIS 时需要 ASP.NET Core Module 运行时,且web.config里的托管方式要配置正确。最常见的是忘了装 Hosting Bundle,或者把应用池的“托管管道模式”设置成了“经典模式”,导致请求没有被转发给 Kestrel。
解决:装 .NET Core Hosting Bundle;web.config里的aspNetCore节点用默认配置即可。应用池“托管管道模式”设为“集成模式”,“启用 32 位应用程序”设为 False。这个坑几乎每个部署的人都会踩一次,经验是部署前先跑一下命令行判断运行是否正常:
dotnet Supplier.Web.dll如果命令行能起来而 IIS 不行,问题基本都在模块或应用池上。写完这个排查思路,基本能覆盖绝大多数部署後 404 的问题。
6. 进阶:给系统加一个供应商风险评分模块
供应商管理系统跑顺之后,业务方一定会提一个新需求:“能不能自动评估供应商的风险等级?”我一般是这么做的:建一张SupplierRiskScore表,字段有供应商ID、总分、风险等级、最后评分时间。评分规则是:资质过期未更新扣 40 分;最近 6 个月到货及时率低于 90% 扣 20 分;有质量退货记录每次扣 10 分;黑名单供应商直接一票否决设为 0 分。计算逻辑写在 Application 层的一个独立服务里,每天凌晨跑一次:
public async Task ComputeRiskScoreAsync() { var suppliers = await _dbContext.Suppliers .Where(s => s.Status == SupplierStatus.Approved) .Include(s => s.Certifications) .Include(s => s.DeliveryRecords) .ToListAsync(); foreach (var supplier in suppliers) { var score = 100; var expiredCert = supplier.Certifications.Any(c => c.ExpiryDate < DateTime.UtcNow.Date); if (expiredCert) score -= 40; var lateRate = GetLateRate(supplier.DeliveryRecords); if (lateRate < 90) score -= 20; supplier.RiskScore = Math.Max(score, 0); supplier.RiskLevel = score >= 80 ? "Low" : score >= 60 ? "Medium" : "High"; } await _dbContext.SaveChangesAsync(); }评分模块的价值在于让“合格供应商”不再是拍脑袋的状态,而是有一个动态调整的依据。实际部署时,我把评分结果和供应商列表页做了联动,列表默认按风险等级排序,采购员第一眼就能看到哪些供应商需要重点盯。这套扩展的验收办法很简单:拿过去三个月的供应商历史数据人工打分,和系统跑出来的结果对比,一致率到 90% 以上再放开给业务方用。
我做过三次类似的风险评分模块,第一次把评分逻辑写在页面点击事件里,第二次用定时任务但没加日志,第三次才改成现在的形态:定时任务独立跑、结果落表、页面只读展示。每一次都有新教训,比如“供应商的到货及时率数据没接进来前,评分永远都是 100 分”。这个模块的扩展方向还可以接合同履约数据、财务付款周期,本质上就是把供应商关系管理的判断规则数字化。希望这篇笔记里的选型和踩坑记录能帮到你,少走几步弯路。
本文还有配套的精品资源,点击获取