model层实现:

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 里)
  • 数据验证

    • 登录、资产导入等需要用 [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();
        }
    }
}
  1. 在 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>(); }); } }

相关推荐
OLong14 分钟前
React Update Queue 源码全链路解析:从 setState 到 DOM 更新
前端·react.js
知识浅谈17 分钟前
OpenLayers与Vue.js结合实现前端地图应用
前端
青云交32 分钟前
Java 大视界 -- 基于 Java 的大数据可视化在能源互联网全景展示与能源调度决策支持中的应用
java·大数据可视化·智能决策·能源互联网·三维渲染·能源调度·nsga-ii
盖世英雄酱5813634 分钟前
国企“高级”程序员写的那些问题代码(六期)
java·后端
藤椒鱼不爱编程37 分钟前
面向对象_类与对象
java
答案answer37 分钟前
three.js 实现几个好看的文本内容效果
前端·webgl·three.js
wanhengidc44 分钟前
企业在使用巨椰云手机进行多开挂机功能的优点有哪些?
运维·服务器·安全·智能手机
Running_C1 小时前
一文读懂跨域
前端·http·面试
南囝coding1 小时前
这个Web新API让任何内容都能画中画!
前端·后端
起这个名字1 小时前
Vue2/3 v-model 使用区别详解,不了解的来看看
前端·javascript·vue.js