LoginRequest.cs:
using System.ComponentModel.DataAnnotations;
namespace FixedAssetInventory.Models
{
/// <summary>
/// 登录请求 DTO
/// </summary>
public class LoginRequest
{
/// <summary>
/// 登录用户名
/// </summary>
[Required(ErrorMessage = "用户名不能为空")]
public string UserName { get; set; }
/// <summary>
/// 登录密码
/// </summary>
[Required(ErrorMessage = "密码不能为空")]
public string Password { get; set; }
}
}
InventoryResultDto.cs
using System;
namespace FixedAssetInventory.Models
{
/// <summary>
/// 盘点结果 DTO
/// </summary>
public class InventoryResultDto
{
/// <summary>
/// 资产编号
/// </summary>
public string AssetCode { get; set; }
/// <summary>
/// 盘点状态(已盘点/未盘点/异常)
/// </summary>
public string Status { get; set; }
/// <summary>
/// 异常说明
/// </summary>
public string ExceptionRemark { get; set; }
/// <summary>
/// 盘点时间
/// </summary>
public DateTime CheckedAt { get; set; }
/// <summary>
/// 盘点人
/// </summary>
public string CheckedBy { get; set; }
}
}
AssetDto.cs:
using System;
namespace FixedAssetInventory.Models
{
/// <summary>
/// 固定资产 DTO
/// </summary>
public class AssetDto
{
public long Id { get; set; }
/// <summary>
/// 资产编号
/// </summary>
public string AssetCode { get; set; }
/// <summary>
/// 资产名称
/// </summary>
public string AssetName { get; set; }
/// <summary>
/// 所属部门
/// </summary>
public string Department { get; set; }
/// <summary>
/// 使用人
/// </summary>
public string UserName { get; set; }
/// <summary>
/// 存放位置
/// </summary>
public string Location { get; set; }
/// <summary>
/// 状态
/// </summary>
public string Status { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreatedAt { get; set; }
}
}
TaskDto.cs
using System;
namespace FixedAssetInventory.Models
{
/// <summary>
/// 盘点任务 DTO
/// </summary>
public class TaskDto
{
public long Id { get; set; }
/// <summary>
/// 任务名称
/// </summary>
public string TaskName { get; set; }
/// <summary>
/// 任务状态(进行中/已完成/已取消)
/// </summary>
public string Status { get; set; }
/// <summary>
/// 创建人姓名
/// </summary>
public string CreatedBy { get; set; }
/// <summary>
/// 执行人姓名
/// </summary>
public string AssignedTo { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreatedAt { get; set; }
/// <summary>
/// 预计完成时间
/// </summary>
public DateTime? DueDate { get; set; }
}
}
ReportDto.cs
using System;
using System.Collections.Generic;
namespace FixedAssetInventory.Models
{
/// <summary>
/// 盘点统计报表 DTO
/// </summary>
public class ReportDto
{
/// <summary>
/// 任务名称
/// </summary>
public string TaskName { get; set; }
/// <summary>
/// 总资产数
/// </summary>
public int TotalAssets { get; set; }
/// <summary>
/// 已盘点资产数
/// </summary>
public int CheckedAssets { get; set; }
/// <summary>
/// 异常资产数
/// </summary>
public int ExceptionAssets { get; set; }
/// <summary>
/// 完成率(百分比)
/// </summary>
public double CompletionRate
{
get
{
if (TotalAssets == 0) return 0;
return Math.Round((double)CheckedAssets / TotalAssets * 100, 2);
}
}
/// <summary>
/// 异常率(百分比)
/// </summary>
public double ExceptionRate
{
get
{
if (TotalAssets == 0) return 0;
return Math.Round((double)ExceptionAssets / TotalAssets * 100, 2);
}
}
/// <summary>
/// 图表数据
/// </summary>
public List<ChartDataItem> ChartData { get; set; } = new List<ChartDataItem>();
}
/// <summary>
/// 图表数据项 DTO
/// </summary>
public class ChartDataItem
{
public string Label { get; set; }
public int Value { get; set; }
}
}
-
严格区分 DTO 与 Entity
- DTO 只暴露业务需要的字段,防止直接返回数据库字段(比如
PasswordHash
不会出现在 DTO 里)
- DTO 只暴露业务需要的字段,防止直接返回数据库字段(比如
-
数据验证
- 登录、资产导入等需要用
[Required]
、[StringLength]
等注解
- 登录、资产导入等需要用
-
计算属性
ReportDto
里的完成率、异常率是后端自动算的,前端不用重复计算
-
扩展性
ChartData
用于可视化(前端 Blazor 或 ECharts 直接绑定)AutoMapper 配置文件
AutoMapper 配置文件:
我们在 FixedAssetInventory.Core
下新建 Mapping/AutoMapperProfile.cs
using AutoMapper;
using FixedAssetInventory.Core.Entities;
using FixedAssetInventory.Models;
namespace FixedAssetInventory.Core.Mapping
{
/// <summary>
/// AutoMapper 映射配置
/// </summary>
public class AutoMapperProfile : Profile
{
public AutoMapperProfile()
{
// User <-> LoginRequest
CreateMap<User, LoginRequest>().ReverseMap();
// Asset <-> AssetDto
CreateMap<Asset, AssetDto>().ReverseMap();
// InventoryRecord -> InventoryResultDto
CreateMap<InventoryRecord, InventoryResultDto>()
.ForMember(dest => dest.AssetCode, opt => opt.MapFrom(src => src.AssetCode))
.ForMember(dest => dest.CheckedAt, opt => opt.MapFrom(src => src.CheckedAt))
.ForMember(dest => dest.CheckedBy, opt => opt.MapFrom(src => src.CheckedBy))
.ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.Status))
.ForMember(dest => dest.ExceptionRemark, opt => opt.MapFrom(src => src.Remark))
.ReverseMap();
// Task <-> TaskDto
CreateMap<InventoryTask, TaskDto>()
.ForMember(dest => dest.CreatedBy, opt => opt.MapFrom(src => src.CreatedByName))
.ForMember(dest => dest.AssignedTo, opt => opt.MapFrom(src => src.AssignedToName))
.ReverseMap();
// Report 数据映射
CreateMap<ReportDto, ReportDto>().ReverseMap();
}
}
}
-
在 Startup.cs 注册 AutoMapper
using AutoMapper;
using FixedAssetInventory.Core.Mapping;public class Startup
{
public IConfiguration Configuration { get; }public Startup(IConfiguration configuration) { Configuration = configuration; } public void ConfigureServices(IServiceCollection services) { // 注册 AutoMapper services.AddAutoMapper(typeof(AutoMapperProfile)); // 注册控制器 + JSON 配置 services.AddControllers() .AddNewtonsoftJson(options => { options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; }); // 注册自定义服务(依赖注入) services.AddScoped<IAuthService, AuthService>(); services.AddScoped<IInventoryService, InventoryService>(); services.AddScoped<IAssetService, AssetService>(); services.AddScoped<ITaskService, TaskService>(); services.AddScoped<IReportService, ReportService>(); // 注册 FreeSql 连接 Oracle services.AddSingleton<IFreeSql>(sp => { return new FreeSql.FreeSqlBuilder() .UseConnectionString(FreeSql.DataType.Oracle, Configuration.GetConnectionString("OracleConnection")) .UseMonitorCommand(cmd => Console.WriteLine($"SQL: {cmd.CommandText}")) .Build(); }); // 添加跨域支持 services.AddCors(options => { options.AddPolicy("AllowAll", builder => { builder.AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader(); }); }); // 添加Swagger services.AddSwaggerGen(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseSwagger(); app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "FixedAssetInventory API V1"); }); } app.UseRouting(); app.UseCors("AllowAll"); app.UseMiddleware<AuthMiddleware>(); app.UseMiddleware<LoggingMiddleware>(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
}
3. 配置文件 appsettings.json
确保 appsettings.json
里有连接字符串和 OCR API URL:
json
复制编辑
{ "ConnectionStrings": { "OracleConnection": "Data Source=127.0.0.1:1521/ORCL;User Id=asset_user;Password=123456;" }, "OcrApi": { "BaseUrl": "http://127.0.0.1:5001/api/ocr" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*" }
4. Program.cs
csharp
复制编辑
using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; namespace FixedAssetInventory { public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); } }