系列文章
【C#】最全业务单据号生成(支持定义规则、流水号、传参数)
本文链接:https://blog.csdn.net/youcheng_ge/article/details/129129787
【C#】日期范围生成器(开始日期、结束日期)
本文链接:https://blog.csdn.net/youcheng_ge/article/details/129040663
【C#】组件化开发,调用dll组件方法
本文链接:https://blog.csdn.net/youcheng_ge/article/details/129492112
文章目录
- 系列文章
- 前言
- 一、问题描述
- 二、解决方案
- 三、软件开发(源码)
-
- [3.1 服务端命令行](#3.1 服务端命令行)
- [3.2 Step.MmsNT.DbMigrator](#3.2 Step.MmsNT.DbMigrator)
- [3.3 Step.MmsNT.Domain领域实体](#3.3 Step.MmsNT.Domain领域实体)
- [3.4 Step.MmsNT.EntityFrameworkCore轻量级跨平台EF Core框架](#3.4 Step.MmsNT.EntityFrameworkCore轻量级跨平台EF Core框架)
- [3.5 Step.MmsNT.Application应用服务](#3.5 Step.MmsNT.Application应用服务)
- [3.6 Step.MmsNT.Application.Contracts按钮权限](#3.6 Step.MmsNT.Application.Contracts按钮权限)
- [3.7 Step.MmsNT.Domain.Shared国际化多语言](#3.7 Step.MmsNT.Domain.Shared国际化多语言)
- 四、项目展示
- 五、资源链接
前言
【C# 项目实战】 拒绝枯燥理论,只讲落地干货!
本专栏收纳我在实际开发中总结的成熟解决方案,涵盖各类业务场景与疑难杂症。文章结构调整为「问题描述 -> 项目展示 -> 解决方案」,让你一眼看懂代码能做什么。所有案例均经过生产环境验证(已规避商业机密),旨在提供高复用性的处理逻辑。代码力求精简高效,专栏持续更新,欢迎关注,一起用C#征服复杂业务!
·提示:本专栏为项目实战篇,未接触项目开发的同学可能理解困难,不推荐阅读。

一、问题描述
MmsNT_Server 是后端,基于 .NET 10 + ABP 10.3,负责接口、登录认证、权限、数据库、业务逻辑。
MmsNT_Client 是主要前端,基于 Vue 3 + Vite + TypeScript + Vben Admin + Ant Design Vue,负责后台管理页面。
二、解决方案
遵循 ABP 分层 DDD:
Domain.Shared 放常量、枚举、本地化、ETO;
Domain 放实体、聚合根、领域服务、仓储接口;
Application.Contracts 放 DTO 和服务接口;
Application 放应用服务实现和映射;
EntityFrameworkCore 放 DbContext、仓储实现、迁移;
HttpApi 只在不用 Auto API 时写 Controller;
DbMigrator 负责迁移和种子数据。
三、软件开发(源码)
3.1 服务端命令行
项目初始化,在Developer PowerShell 中
bash
abp install-libs
项目启动项
bash
Step.MmsNT.HttpApi.Host
3.2 Step.MmsNT.DbMigrator
重新生成表结构,需要运行一下,生成初始化默认数据,否则无法登陆。
3.3 Step.MmsNT.Domain领域实体
手动,创建数据库表实体
Step.MmsNT.Domain\BasicData
3.4 Step.MmsNT.EntityFrameworkCore轻量级跨平台EF Core框架
E:\MyProject\mms\mms_server\src\Step.MmsNT.EntityFrameworkCore\Migrations\20260605033026_AddTAX_0009.cs
自动生成数据库CRUD命令
bash
Add-Migration AddTAX_0009
Update-Database
AddTAX_0009
c#
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace Step.MmsNT.Migrations
{
/// <inheritdoc />
public partial class AddTAX_0009 : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "TAX_0009",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false, comment: "主键")
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
C_IS_USING = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false, comment: "有效标识"),
C_MACHINE_CD = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: false, comment: "机台/产线/机台组 码号/身份"),
C_MACHINE_NM = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: false, comment: "机台/产线/机台组 名称"),
C_SIM_MACHINE_CD = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: false, comment: "机台简码"),
C_PLAN_NO = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: false, comment: "作业计划"),
C_FATHER_MACHINE_ID = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: false, comment: "机台父编码"),
N_MACHINE_FIRST_FEW = table.Column<long>(type: "bigint", nullable: false, comment: "第几台"),
N_LAYER_LEVEL = table.Column<long>(type: "bigint", nullable: false, comment: "层级"),
C_PLANT_CD = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: false, comment: "实体工厂"),
C_PROC_CD = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: false, comment: "工序代码"),
C_MACHINE_FORM = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: false, comment: "机台形态"),
N_SEQ_NO = table.Column<long>(type: "bigint", nullable: false, comment: "顺序号"),
C_REMARK = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: true, comment: "备注"),
C_PRO_STATE = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: false, comment: "生产状态"),
C_MANU = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: false, comment: "厂商"),
C_MODEL = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: false, comment: "型号"),
C_MACHINE_TYPE = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: false, comment: "机台类型"),
C_WORKSHOP_NO = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: false, comment: "所属车间编码"),
C_WORK_CENTER = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: false, comment: "生产工作中心编码"),
C_MACHINE_PID = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: false, comment: "机台父主键"),
C_AUTHORITY_GROUP_ID = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: true, comment: "授权用户组ID"),
ExtraProperties = table.Column<string>(type: "text", nullable: false, comment: "扩展属性"),
ConcurrencyStamp = table.Column<string>(type: "character varying(40)", maxLength: 40, nullable: false, comment: "并发标识"),
CreationTime = table.Column<DateTime>(type: "timestamp without time zone", nullable: false, comment: "创建时间"),
CreatorId = table.Column<Guid>(type: "uuid", nullable: true, comment: "创建人ID"),
LastModificationTime = table.Column<DateTime>(type: "timestamp without time zone", nullable: true, comment: "最后修改时间"),
LastModifierId = table.Column<Guid>(type: "uuid", nullable: true, comment: "最后修改人ID"),
IsDeleted = table.Column<bool>(type: "boolean", nullable: false, defaultValue: false, comment: "是否删除"),
DeleterId = table.Column<Guid>(type: "uuid", nullable: true, comment: "删除人ID"),
DeletionTime = table.Column<DateTime>(type: "timestamp without time zone", nullable: true, comment: "删除时间")
},
constraints: table =>
{
table.PrimaryKey("PK_TAX_0009", x => x.Id);
},
comment: "产线机台主数据");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "TAX_0009");
}
}
}
MmsNTDbContext.cs
如果不添加,TAX_2036AutoMapperProfile 会找不到引用
c#
public DbSet<TAX_2036> TAX_2036 { get; set; }
3.5 Step.MmsNT.Application应用服务
E:\MyProject\mms\mms_server\src\Step.MmsNT.Application\TAX_2036Service\TAX_2036Dtos\CreateUpdateTAX_2036Dto.cs
CreateUpdateTAX_2036Dto创建更新(新增修改)DTO
c#
using System.ComponentModel.DataAnnotations;
namespace Step.MmsNT.TAX_2036Service;
public class CreateUpdateTAX_2036Dto
{
[Required]
[StringLength(512)]
public string CPlantId { get; set; } = string.Empty;
[Required]
[StringLength(512)]
public string CStoreHouse { get; set; } = string.Empty;
[Required]
[StringLength(30)]
public string CStoLocation { get; set; } = string.Empty;
[Required]
[StringLength(30)]
public string CStoType { get; set; } = string.Empty;
[StringLength(10)]
public string? CTlDj { get; set; }
[StringLength(30)]
public string? CStoLocationTxt { get; set; }
[StringLength(100)]
public string? CRemark { get; set; }
[Required]
[StringLength(10)]
public string CIsUse { get; set; } = "Y";
}
E:\MyProject\mms\mms_server\src\Step.MmsNT.Application\TAX_2036Service\TAX_2036Dtos\GetTAX_2036ListInput.cs
GetTAX_2036ListInput输入(查询)参数DTO
c#
using Volo.Abp.Application.Dtos;
namespace Step.MmsNT.TAX_2036Service;
public class GetTAX_2036ListInput : PagedAndSortedResultRequestDto
{
public string? CPlantId { get; set; }
public string? CStoreHouse { get; set; }
public string? CStoLocation { get; set; }
public string? CStoType { get; set; }
public string? CStoLocationTxt { get; set; }
public bool IncludeInvalidData { get; set; }
}
E:\MyProject\mms\mms_server\src\Step.MmsNT.Application\TAX_2036Service\TAX_2036Dtos\ITAX_2036AppService.cs
ITAX_2036AppService服务接口
c#
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
namespace Step.MmsNT.TAX_2036Service;
public interface ITAX_2036AppService :
ICrudAppService<
TAX_2036Dto,
int,
GetTAX_2036ListInput,
CreateUpdateTAX_2036Dto>
{
}
E:\MyProject\mms\mms_server\src\Step.MmsNT.Application\TAX_2036Service\TAX_2036Dtos\TAX_2036Dto.cs
TAX_2036Dto输出(结果)参数DTO
c#
using Volo.Abp.Application.Dtos;
namespace Step.MmsNT.TAX_2036Service;
public class TAX_2036Dto : AuditedEntityDto<int>
{
public string CPlantId { get; set; } = string.Empty;
public string CStoreHouse { get; set; } = string.Empty;
public string CStoLocation { get; set; } = string.Empty;
public string CStoType { get; set; } = string.Empty;
public string? CTlDj { get; set; }
public string? CStoLocationTxt { get; set; }
public string? CRemark { get; set; }
public string CIsUse { get; set; } = string.Empty;
}
E:\MyProject\mms\mms_server\src\Step.MmsNT.Application\TAX_2036Service\TAX_2036AppService.cs
TAX_2036AppService服务方法实现
c#
using Microsoft.AspNetCore.Authorization;
using Step.MmsNT.BasicData;
using Step.MmsNT.Permissions;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
namespace Step.MmsNT.TAX_2036Service;
[Authorize(MmsNTPermissions.Mes.BasicData.TAX_2036.Default)]
public class TAX_2036AppService : MmsNTAppService, ITAX_2036AppService
{
private readonly IRepository<TAX_2036, int> _repository;
public TAX_2036AppService(IRepository<TAX_2036, int> repository)
{
_repository = repository;
}
public async Task<TAX_2036Dto> GetAsync(int id)
{
var storageLocation = await _repository.GetAsync(id);
return ObjectMapper.Map<TAX_2036, TAX_2036Dto>(storageLocation);
}
public async Task<PagedResultDto<TAX_2036Dto>> GetListAsync(GetTAX_2036ListInput input)
{
var queryable = await _repository.GetQueryableAsync();
var filteredQuery = queryable
.WhereIf(!string.IsNullOrWhiteSpace(input.CPlantId), storageLocation => storageLocation.CPlantId.Contains(input.CPlantId!))
.WhereIf(!string.IsNullOrWhiteSpace(input.CStoreHouse), storageLocation => storageLocation.CStoreHouse.Contains(input.CStoreHouse!))
.WhereIf(!string.IsNullOrWhiteSpace(input.CStoLocation), storageLocation => storageLocation.CStoLocation.Contains(input.CStoLocation!))
.WhereIf(!string.IsNullOrWhiteSpace(input.CStoType), storageLocation => storageLocation.CStoType.Contains(input.CStoType!))
.WhereIf(!string.IsNullOrWhiteSpace(input.CStoLocationTxt), storageLocation => storageLocation.CStoLocationTxt != null && storageLocation.CStoLocationTxt.Contains(input.CStoLocationTxt!));
if (!input.IncludeInvalidData)
{
filteredQuery = filteredQuery.Where(storageLocation => storageLocation.CIsUse == "Y");
}
var query = filteredQuery
.OrderBy(string.IsNullOrWhiteSpace(input.Sorting) ? nameof(TAX_2036.CStoLocation) : input.Sorting)
.Skip(input.SkipCount)
.Take(input.MaxResultCount);
var storageLocations = await AsyncExecuter.ToListAsync(query);
var totalCount = await AsyncExecuter.CountAsync(filteredQuery);
return new PagedResultDto<TAX_2036Dto>(
totalCount,
ObjectMapper.Map<List<TAX_2036>, List<TAX_2036Dto>>(storageLocations)
);
}
[Authorize(MmsNTPermissions.Mes.BasicData.TAX_2036.Create)]
public async Task<TAX_2036Dto> CreateAsync(CreateUpdateTAX_2036Dto input)
{
var storageLocation = ObjectMapper.Map<CreateUpdateTAX_2036Dto, TAX_2036>(input);
await _repository.InsertAsync(storageLocation);
return ObjectMapper.Map<TAX_2036, TAX_2036Dto>(storageLocation);
}
[Authorize(MmsNTPermissions.Mes.BasicData.TAX_2036.Edit)]
public async Task<TAX_2036Dto> UpdateAsync(int id, CreateUpdateTAX_2036Dto input)
{
var storageLocation = await _repository.GetAsync(id);
ObjectMapper.Map(input, storageLocation);
await _repository.UpdateAsync(storageLocation);
return ObjectMapper.Map<TAX_2036, TAX_2036Dto>(storageLocation);
}
[Authorize(MmsNTPermissions.Mes.BasicData.TAX_2036.Delete)]
public async Task DeleteAsync(int id)
{
await _repository.DeleteAsync(id);
}
}
TAX_2036AutoMapperProfile.cs
c#
using AutoMapper;
using Step.MmsNT.BasicData;
namespace Step.MmsNT.TAX_2036Service;
public class TAX_2036AutoMapperProfile : Profile
{
public TAX_2036AutoMapperProfile()
{
CreateMap<TAX_2036, TAX_2036Dto>();
CreateMap<CreateUpdateTAX_2036Dto, TAX_2036>();
}
}
3.6 Step.MmsNT.Application.Contracts按钮权限
E:\MyProject\mms\mms_server\src\Step.MmsNT.Application.Contracts\Permissions\MmsNTPermissionDefinitionProvider.cs
MmsNTPermissionDefinitionProvider.cs
c#
var tax2036 = mesBasicData.AddChild(MmsNTPermissions.Mes.BasicData.TAX_2036.Default, L("Permission:TAX_2036"));
tax2036.AddChild(MmsNTPermissions.Mes.BasicData.TAX_2036.Create, L("Permission:TAX_2036.Create"));
tax2036.AddChild(MmsNTPermissions.Mes.BasicData.TAX_2036.Edit, L("Permission:TAX_2036.Edit"));
tax2036.AddChild(MmsNTPermissions.Mes.BasicData.TAX_2036.Delete, L("Permission:TAX_2036.Delete"));
MmsNTPermissions.cs
c#
public static class TAX_2036
{
public const string Default = ModuleName + ".TAX_2036";
public const string Create = Default + ".Create";
public const string Edit = Default + ".Edit";
public const string Delete = Default + ".Delete";
}
3.7 Step.MmsNT.Domain.Shared国际化多语言
E:\MyProject\mms\mms_server\src\Step.MmsNT.Domain.Shared\Localization\MmsNT\en.json
json
"Permission:TAX_2036": "Storage Location Master Data",
"Permission:TAX_2036.Create": "Create Storage Location",
"Permission:TAX_2036.Edit": "Edit Storage Location",
"Permission:TAX_2036.Delete": "Delete Storage Location",
E:\MyProject\mms\mms_server\src\Step.MmsNT.Domain.Shared\Localization\MmsNT\zh-Hans.json
json
"Permission:TAX_2036": "仓位基础数据维护",
"Permission:TAX_2036.Create": "创建仓位",
"Permission:TAX_2036.Edit": "编辑仓位",
"Permission:TAX_2036.Delete": "删除仓位",
四、项目展示



