diff --git a/IOT_API/Controllers/Asset/EquipmentController.cs b/IOT_API/Controllers/Asset/EquipmentController.cs
new file mode 100644
index 0000000..4e8336f
--- /dev/null
+++ b/IOT_API/Controllers/Asset/EquipmentController.cs
@@ -0,0 +1,141 @@
+using Microsoft.AspNetCore.Mvc;
+using Model;
+using Model.Entity.Asset;
+using ORM;
+using Service.Implement;
+using Service.Interface;
+using SqlSugar;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace IOT_API.Controllers.Asset
+{
+ ///
+ /// 资产管理 - 设备台账控制器
+ ///
+ [Route("api/Equipment")] //资产管理模块下的设备管理接口
+ [ApiController]
+ public class EquipmentController : ControllerBase
+ {
+ private readonly IEquipmentService _equipmentService;
+
+ public EquipmentController()
+ {
+ // 项目未注册 DI 容器,SqlSugarContext.DbContext 为静态单例,直接实例化服务
+ _equipmentService = new EquipmentService(
+ new SqlSugarRepository(),
+ new SqlSugarRepository());
+ }
+
+ ///
+ /// 设备列表(分页查询,支持关键字/分类/状态筛选)
+ ///
+ /// 页码(从1开始,默认1)
+ /// 每页数量(默认10)
+ /// 关键字(模糊匹配设备编号/名称)
+ /// 设备分类Id(0表示不过滤)
+ /// 设备状态(不传表示不过滤)
+ [HttpGet("list")]
+ public async Task>> GetList(int pageIndex = 1, int pageSize = 10,
+ string? keyword = null, long categoryId = 0, EquipmentStatusEnum? status = null)
+ {
+ RefAsync total = 0;
+ var result = await _equipmentService.GetPagedAsync(pageIndex, pageSize, total, keyword, categoryId, status);
+ Response.Headers["X-Total-Count"] = total.Value.ToString();
+ return result;
+ }
+
+ ///
+ /// 查询全部设备(不分页,供下拉选择等场景使用)
+ ///
+ [HttpGet("all")]
+ public async Task>> GetAll()
+ {
+ return await _equipmentService.GetAllAsync();
+ }
+
+ ///
+ /// 设备详情
+ ///
+ /// 设备主键 Id
+ [HttpGet("{id}")]
+ public async Task> GetById(long id)
+ {
+ return await _equipmentService.GetByIdAsync(id);
+ }
+
+ ///
+ /// 新增设备
+ ///
+ /// 设备实体
+ [HttpPost]
+ public async Task> Add([FromBody] EquipmentEntity entity)
+ {
+ return await _equipmentService.InsertAsync(entity);
+ }
+
+ ///
+ /// 修改设备
+ ///
+ /// 设备主键 Id
+ /// 设备实体
+ [HttpPut("{id}")]
+ public async Task> Update(long id, [FromBody] EquipmentEntity entity)
+ {
+ entity.Id = id;
+ return await _equipmentService.UpdateAsync(entity);
+ }
+
+ ///
+ /// 删除设备(软删除)
+ ///
+ /// 设备主键 Id
+ [HttpDelete("{id}")]
+ public async Task> Delete(long id)
+ {
+ return await _equipmentService.DeleteEquipmentAsync(id);
+ }
+
+ ///
+ /// 变更设备状态(同步记录状态变更历史)
+ ///
+ /// 设备主键 Id
+ /// 状态变更请求
+ [HttpPost("{id}/status")]
+ public async Task> ChangeStatus(long id, [FromBody] ChangeStatusRequest request)
+ {
+ return await _equipmentService.ChangeStatusAsync(id, request.Status, request.Operator, request.Remark);
+ }
+
+ ///
+ /// 查询设备状态变更历史记录
+ ///
+ /// 设备主键 Id
+ [HttpGet("{id}/status-records")]
+ public async Task>> GetStatusRecords(long id)
+ {
+ return await _equipmentService.GetStatusRecordsAsync(id);
+ }
+ }
+
+ ///
+ /// 状态变更请求体
+ ///
+ public class ChangeStatusRequest
+ {
+ ///
+ /// 目标状态
+ ///
+ public EquipmentStatusEnum Status { get; set; }
+
+ ///
+ /// 操作人
+ ///
+ public string? Operator { get; set; }
+
+ ///
+ /// 变更原因/备注
+ ///
+ public string? Remark { get; set; }
+ }
+}
diff --git a/IOT_API/Program.cs b/IOT_API/Program.cs
index 4bf7877..9a2a91d 100644
--- a/IOT_API/Program.cs
+++ b/IOT_API/Program.cs
@@ -1,4 +1,5 @@
using Common;
+using ORM;
using System.Text.Json;
namespace WebAPI
@@ -7,33 +8,54 @@ namespace WebAPI
{
public static void Main(string[] args)
{
- // ======= תδ쳣ʱԶ MiniDump =======
+ // ======= ����ת����δ�����쳣ʱ�Զ����� MiniDump =======
AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
{
- //¼dumpļ
+ //��¼dump�ļ�
Exception ex = e.ExceptionObject as Exception;
MiniDump.TryDump($"dumps\\Error_{DateTime.Now:yyyy-MM-dd HH-mm-ss-ms}.dmp", MiniDump.Option.WithFullMemory, ex);
};
- // ======= ת =======
+ // ======= ����ת������ =======
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
- // ======= ÿʼ =======
+ // ======= �������ÿ�ʼ =======
builder.Services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
options.JsonSerializerOptions.NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString;
- // 3. ԱԭǿתΪշ壩
+ // 3. �����������Ա���ԭ������ǿ��תΪ�շ壩
options.JsonSerializerOptions.PropertyNamingPolicy = null;
});
- // ======= ý =======
+ // ======= �������ý��� =======
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
+ // ======= Database init start =======
+ // Npgsql 时间兼容开关:允许向 PostgreSQL 写入本地时间 DateTime(DateTime.Now)
+ AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
+
+ // 从 appsettings.json 读取数据库配置(PostgreSQL)
+ var dbConfig = builder.Configuration.GetSection("Database");
+ DatabaseConfig.InitPostgreSql(
+ dbConfig["Server"] ?? "localhost",
+ int.TryParse(dbConfig["Port"], out var port) ? port : 5432,
+ dbConfig["Database"] ?? "iot",
+ dbConfig["Uid"] ?? "postgres",
+ dbConfig["Pwd"] ?? "");
+
+ // 数据库不存在则创建,并检查连接(租户雪花机器码可按需设置)
+ DatabaseConfig.SetTenant(10001);
+ DatabaseConfig.CreateDatabaseAndCheckConnection(createDatabase: true, checkConnection: true);
+
+ // CodeFirst 自动建表:扫描 Model.dll 中所有以 Entity 结尾的类建表/补列
+ SqlSugarContext.InitDatabase();
+ // ======= Database init end =======
+
var app = builder.Build();
// Configure the HTTP request pipeline.
@@ -43,7 +65,7 @@ namespace WebAPI
app.UseSwaggerUI();
}
- // ע⣺λֻ֧ http ֳʱҪʵע͵ Https ض
+ // ע�⣺�����λ��ֻ֧�� http �����ֳ�����ʱ�������Ҫ����ʵ�����ע�͵��������� Https �ض���
app.UseHttpsRedirection();
app.UseAuthorization();
diff --git a/IOT_API/appsettings.json b/IOT_API/appsettings.json
index 10f68b8..853529b 100644
--- a/IOT_API/appsettings.json
+++ b/IOT_API/appsettings.json
@@ -5,5 +5,12 @@
"Microsoft.AspNetCore": "Warning"
}
},
- "AllowedHosts": "*"
+ "AllowedHosts": "*",
+ "Database": {
+ "Server": "localhost",
+ "Port": 5432,
+ "Database": "iot",
+ "Uid": "postgres",
+ "Pwd": "postgres"
+ }
}
diff --git a/Model/Entity/Asset/EquipmentAttachmentEntity.cs b/Model/Entity/Asset/EquipmentAttachmentEntity.cs
new file mode 100644
index 0000000..e8a528f
--- /dev/null
+++ b/Model/Entity/Asset/EquipmentAttachmentEntity.cs
@@ -0,0 +1,55 @@
+using SqlSugar;
+
+namespace Model.Entity.Asset
+{
+ ///
+ /// 设备附件(资产附件管理:文件上传,支持按文件类型筛选与预览)
+ ///
+ public class EquipmentAttachmentEntity : BaseEntity
+ {
+ ///
+ /// 设备Id(关联 EquipmentEntity.Id)
+ ///
+ public long EquipmentId { get; set; }
+
+ ///
+ /// 文件名称
+ ///
+ [SugarColumn(Length = 200)]
+ public string? FileName { get; set; }
+
+ ///
+ /// 文件类型(使用说明书/合格证明/校准证书/保养卡/合同扫描件/验收照片等)
+ ///
+ public AttachmentTypeEnum FileType { get; set; }
+
+ ///
+ /// 文件扩展名(如 .pdf、.jpg)
+ ///
+ [SugarColumn(Length = 20)]
+ public string? FileExt { get; set; }
+
+ ///
+ /// 文件存储地址(上传后存储的相对路径)
+ ///
+ [SugarColumn(Length = 200)]
+ public string? FileUrl { get; set; }
+
+ ///
+ /// 文件大小(字节)
+ ///
+ public long FileSize { get; set; }
+
+ ///
+ /// 上传人
+ ///
+ [SugarColumn(Length = 50)]
+ public string? Uploader { get; set; }
+
+ ///
+ /// 备注
+ ///
+ [SugarColumn(Length = 500)]
+ public string? Remark { get; set; }
+ }
+}
diff --git a/Model/Entity/Asset/EquipmentCategoryEntity.cs b/Model/Entity/Asset/EquipmentCategoryEntity.cs
new file mode 100644
index 0000000..9c7aaab
--- /dev/null
+++ b/Model/Entity/Asset/EquipmentCategoryEntity.cs
@@ -0,0 +1,43 @@
+using SqlSugar;
+
+namespace Model.Entity.Asset
+{
+ ///
+ /// 设备分类(资产分类:支持按实验室/楼层/部门/位置/产品等多级树形分类,可按类型筛选展示)
+ ///
+ public class EquipmentCategoryEntity : BaseEntity
+ {
+ ///
+ /// 父级分类Id(0表示顶级)
+ ///
+ public long ParentId { get; set; }
+
+ ///
+ /// 分类名称
+ ///
+ [SugarColumn(Length = 100)]
+ public string? Name { get; set; }
+
+ ///
+ /// 分类编码
+ ///
+ [SugarColumn(Length = 50)]
+ public string? Code { get; set; }
+
+ ///
+ /// 层级(1级、2级……,便于树形展示与筛选)
+ ///
+ public int Level { get; set; } = 1;
+
+ ///
+ /// 排序号
+ ///
+ public int Sort { get; set; }
+
+ ///
+ /// 备注
+ ///
+ [SugarColumn(Length = 500)]
+ public string? Remark { get; set; }
+ }
+}
diff --git a/Model/Entity/Asset/EquipmentEntity.cs b/Model/Entity/Asset/EquipmentEntity.cs
new file mode 100644
index 0000000..0c96c3f
--- /dev/null
+++ b/Model/Entity/Asset/EquipmentEntity.cs
@@ -0,0 +1,229 @@
+using SqlSugar;
+
+namespace Model.Entity.Asset
+{
+ ///
+ /// 设备台账(资产台账)
+ ///
+ public class EquipmentEntity : BaseEntity
+ {
+ #region 基础信息(设备信息录入)
+ ///
+ /// 设备编号(唯一)
+ ///
+ [SugarColumn(Length = 50)]
+ public string? Code { get; set; }
+
+ ///
+ /// 设备名称
+ ///
+ [SugarColumn(Length = 100)]
+ public string? Name { get; set; }
+
+ ///
+ /// 设备分类Id(关联 EquipmentCategoryEntity.Id)
+ ///
+ public long CategoryId { get; set; }
+
+ ///
+ /// 设备类型(实验室/楼层/部门/位置/产品/自定义等分类维度下的类型名称,冗余便于展示)
+ ///
+ [SugarColumn(Length = 100)]
+ public string? Type { get; set; }
+
+ ///
+ /// 设备品牌
+ ///
+ [SugarColumn(Length = 100)]
+ public string? Brand { get; set; }
+
+ ///
+ /// 设备型号
+ ///
+ [SugarColumn(Length = 100)]
+ public string? Model { get; set; }
+
+ ///
+ /// 规格参数
+ ///
+ [SugarColumn(Length = 200)]
+ public string? Specifications { get; set; }
+
+ ///
+ /// 制造商(设备制造商)
+ ///
+ [SugarColumn(Length = 100)]
+ public string? Manufacturer { get; set; }
+
+ ///
+ /// 供应商(资产供应商)
+ ///
+ [SugarColumn(Length = 100)]
+ public string? Supplier { get; set; }
+
+ ///
+ /// 设备图片地址(图片上传后存储的相对路径)
+ ///
+ [SugarColumn(Length = 200)]
+ public string? ImageUrl { get; set; }
+
+ ///
+ /// 二维码编号(一物一码)
+ ///
+ [SugarColumn(Length = 50)]
+ public string? QrCode { get; set; }
+
+ ///
+ /// 设备描述
+ ///
+ [SugarColumn(ColumnDataType = "text")]
+ public string? Description { get; set; }
+ #endregion
+
+ #region 权属与使用信息(设备分类管理:实验室/楼层/部门/位置)
+ ///
+ /// 使用部门名称(资产使用部门)
+ ///
+ [SugarColumn(Length = 100)]
+ public string? Department { get; set; }
+
+ ///
+ /// 存放位置(实验室/楼层/房间等位置信息)
+ ///
+ [SugarColumn(Length = 200)]
+ public string? Location { get; set; }
+
+ ///
+ /// 责任人(资产责任人)
+ ///
+ [SugarColumn(Length = 50)]
+ public string? ResponsiblePerson { get; set; }
+
+ ///
+ /// 责任人联系电话(资产责任人电话)
+ ///
+ [SugarColumn(Length = 30)]
+ public string? ContactPhone { get; set; }
+
+ ///
+ /// 保管人
+ ///
+ [SugarColumn(Length = 50)]
+ public string? Custodian { get; set; }
+ #endregion
+
+ #region 购置与财务信息(资产全生命周期:采购阶段)
+ ///
+ /// 购置日期
+ ///
+ public DateTime? PurchaseDate { get; set; }
+
+ ///
+ /// 购置价格(资产购置价格)
+ ///
+ [SugarColumn(DecimalDigits = 2, Length = 18)]
+ public decimal? PurchasePrice { get; set; }
+
+ ///
+ /// 保修到期日期(资产保修到期)
+ ///
+ public DateTime? WarrantyExpireDate { get; set; }
+ #endregion
+
+ #region 生命周期信息(资产全生命周期:验收/使用/报废阶段)
+ ///
+ /// 验收日期(资产验收日期)
+ ///
+ public DateTime? AcceptanceDate { get; set; }
+
+ ///
+ /// 启用日期(资产启用日期)
+ ///
+ public DateTime? EnableDate { get; set; }
+
+ ///
+ /// 使用年限(资产使用年限,单位:年)
+ ///
+ public int? ServiceLifeYears { get; set; }
+
+ ///
+ /// 报废日期(资产报废日期)
+ ///
+ public DateTime? ScrapDate { get; set; }
+ #endregion
+
+ #region 状态信息(设备状态管理:待验收→使用中→已借出→闲置→维修中→已停用→待处置→已处置→入库)
+ ///
+ /// 设备当前状态(状态流转记录见 EquipmentStatusRecordEntity)
+ ///
+ public EquipmentStatusEnum Status { get; set; } = EquipmentStatusEnum.PendingAcceptance;
+ #endregion
+
+ #region 合规信息(设备合规管理:校准/检定/保养)
+ ///
+ /// 校准状态(正常/超期)
+ ///
+ public ComplianceStatusEnum CalibrationStatus { get; set; } = ComplianceStatusEnum.Normal;
+
+ ///
+ /// 上次校准日期(校准日期)
+ ///
+ public DateTime? LastCalibrationDate { get; set; }
+
+ ///
+ /// 下次校准到期日期(用于判断是否超期)
+ ///
+ public DateTime? NextCalibrationDate { get; set; }
+
+ ///
+ /// 检定日期
+ ///
+ public DateTime? InspectionDate { get; set; }
+
+ ///
+ /// 下次检定到期日期(用于判断是否超期)
+ ///
+ public DateTime? NextInspectionDate { get; set; }
+
+ ///
+ /// 保养状态(正常/超期)
+ ///
+ public ComplianceStatusEnum MaintenanceStatus { get; set; } = ComplianceStatusEnum.Normal;
+
+ ///
+ /// 上次保养日期(保养日期)
+ ///
+ public DateTime? LastMaintenanceDate { get; set; }
+
+ ///
+ /// 下次保养到期日期(保养周期到期,用于判断是否超期)
+ ///
+ public DateTime? NextMaintenanceDate { get; set; }
+ #endregion
+
+ #region 审计信息
+ ///
+ /// 备注
+ ///
+ [SugarColumn(Length = 500)]
+ public string? Remark { get; set; }
+
+ ///
+ /// 创建人
+ ///
+ [SugarColumn(Length = 50)]
+ public string? CreateBy { get; set; }
+
+ ///
+ /// 更新人(修改人)
+ ///
+ [SugarColumn(Length = 50)]
+ public string? UpdateBy { get; set; }
+
+ ///
+ /// 更新时间(修改时间)
+ ///
+ public DateTime? UpdateTime { get; set; }
+ #endregion
+ }
+}
diff --git a/Model/Entity/Asset/EquipmentEnums.cs b/Model/Entity/Asset/EquipmentEnums.cs
new file mode 100644
index 0000000..213c33c
--- /dev/null
+++ b/Model/Entity/Asset/EquipmentEnums.cs
@@ -0,0 +1,110 @@
+namespace Model.Entity.Asset
+{
+ ///
+ /// 设备状态(设备状态管理:待验收 → 使用中 → 已借出 → 闲置 → 维修中 → 已停用 → 待处置 → 已处置 → 入库)
+ ///
+ public enum EquipmentStatusEnum
+ {
+ ///
+ /// 待验收
+ ///
+ PendingAcceptance = 1,
+
+ ///
+ /// 使用中
+ ///
+ InUse = 2,
+
+ ///
+ /// 已借出
+ ///
+ Borrowed = 3,
+
+ ///
+ /// 闲置
+ ///
+ Idle = 4,
+
+ ///
+ /// 维修中
+ ///
+ Repairing = 5,
+
+ ///
+ /// 已停用
+ ///
+ Suspended = 6,
+
+ ///
+ /// 待处置
+ ///
+ PendingDisposal = 7,
+
+ ///
+ /// 已处置
+ ///
+ Disposed = 8,
+
+ ///
+ /// 入库
+ ///
+ InStorage = 9
+ }
+
+ ///
+ /// 合规状态(设备合规管理:校准状态、保养状态)
+ ///
+ public enum ComplianceStatusEnum
+ {
+ ///
+ /// 正常
+ ///
+ Normal = 1,
+
+ ///
+ /// 超期
+ ///
+ Overdue = 2
+ }
+
+ ///
+ /// 附件文件类型(设备附件管理:文件类型筛选)
+ ///
+ public enum AttachmentTypeEnum
+ {
+ ///
+ /// 使用说明书
+ ///
+ UserManual = 1,
+
+ ///
+ /// 合格证明
+ ///
+ Certificate = 2,
+
+ ///
+ /// 校准证书
+ ///
+ CalibrationCertificate = 3,
+
+ ///
+ /// 保养卡
+ ///
+ MaintenanceCard = 4,
+
+ ///
+ /// 合同扫描件
+ ///
+ Contract = 5,
+
+ ///
+ /// 验收照片
+ ///
+ AcceptancePhoto = 6,
+
+ ///
+ /// 其他
+ ///
+ Other = 99
+ }
+}
diff --git a/Model/Entity/Asset/EquipmentStatusRecordEntity.cs b/Model/Entity/Asset/EquipmentStatusRecordEntity.cs
new file mode 100644
index 0000000..f752229
--- /dev/null
+++ b/Model/Entity/Asset/EquipmentStatusRecordEntity.cs
@@ -0,0 +1,42 @@
+using SqlSugar;
+
+namespace Model.Entity.Asset
+{
+ ///
+ /// 设备台账状态记录(设备状态管理:状态变更历史记录,并可按时间筛选查询)
+ ///
+ public class EquipmentStatusRecordEntity : BaseEntity
+ {
+ ///
+ /// 设备Id(关联 EquipmentEntity.Id)
+ ///
+ public long EquipmentId { get; set; }
+
+ ///
+ /// 变更前状态
+ ///
+ public EquipmentStatusEnum FromStatus { get; set; }
+
+ ///
+ /// 变更后状态
+ ///
+ public EquipmentStatusEnum ToStatus { get; set; }
+
+ ///
+ /// 状态变更时间
+ ///
+ public DateTime ChangeTime { get; set; }
+
+ ///
+ /// 操作人
+ ///
+ [SugarColumn(Length = 50)]
+ public string? Operator { get; set; }
+
+ ///
+ /// 变更原因/备注
+ ///
+ [SugarColumn(Length = 500)]
+ public string? Remark { get; set; }
+ }
+}
diff --git a/ORM/DatabaseConfig.cs b/ORM/DatabaseConfig.cs
index a790081..9eca36b 100644
--- a/ORM/DatabaseConfig.cs
+++ b/ORM/DatabaseConfig.cs
@@ -74,6 +74,12 @@ namespace ORM
string DBPath = Path.Combine(folder, "SQL.db");
DbConnectionString =$@"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog={DBPath};Integrated Security=True;";
}
+ public static void InitPostgreSql(string Server, int Port, string Database, string Uid, string Pwd)
+ {
+ DbConnectionType = DbType.PostgreSQL;
+ DbConnectionString =
+ $"Host={Server};Port={Port};Database={Database};Username={Uid};Password={Pwd};";
+ }
#endregion
///
/// 设置数据库连接字符串(可手动覆盖)
diff --git a/Service/Implement/EquipmentService.cs b/Service/Implement/EquipmentService.cs
new file mode 100644
index 0000000..025a644
--- /dev/null
+++ b/Service/Implement/EquipmentService.cs
@@ -0,0 +1,266 @@
+using Model;
+using Model.Entity.Asset;
+using ORM;
+using Service.Interface;
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace Service.Implement
+{
+ ///
+ /// 设备台账服务实现(资产管理)
+ ///
+ public class EquipmentService : BaseService, IEquipmentService
+ {
+ private readonly SqlSugarRepository _statusRecordRepository;
+
+ public EquipmentService(SqlSugarRepository repository,
+ SqlSugarRepository statusRecordRepository)
+ : base(repository)
+ {
+ _statusRecordRepository = statusRecordRepository;
+ }
+
+ ///
+ /// 查询全部设备(过滤已删除数据)
+ ///
+ public override async Task>> GetAllAsync()
+ {
+ try
+ {
+ var list = await _repository.Entities
+ .Where(x => x.IsDel == 0)
+ .OrderBy(x => x.CreateTime, OrderByType.Desc)
+ .ToListAsync();
+ return Result>.Success(list);
+ }
+ catch (Exception ex)
+ {
+ return Result>.Error("查询设备台账失败", ex);
+ }
+ }
+
+ ///
+ /// 分页查询设备台账(支持关键字、分类、状态筛选)
+ ///
+ public async Task>> GetPagedAsync(int pageIndex, int pageSize, RefAsync total,
+ string? keyword, long categoryId = 0, EquipmentStatusEnum? status = null)
+ {
+ try
+ {
+ var list = await _repository.Entities
+ .Where(x => x.IsDel == 0)
+ .WhereIF(!string.IsNullOrWhiteSpace(keyword),
+ x => x.Code!.Contains(keyword!) || x.Name!.Contains(keyword!))
+ .WhereIF(categoryId > 0, x => x.CategoryId == categoryId)
+ .WhereIF(status.HasValue, x => x.Status == status!.Value)
+ .OrderBy(x => x.CreateTime, OrderByType.Desc)
+ .ToPageListAsync(pageIndex, pageSize, total);
+ total.Value = (int)Math.Ceiling((double)total.Value / pageSize);
+ return Result>.Success(list);
+ }
+ catch (Exception ex)
+ {
+ return Result>.Error("分页查询设备台账失败", ex);
+ }
+ }
+
+ ///
+ /// 根据 Id 获取设备台账详情
+ ///
+ public async Task> GetByIdAsync(long id)
+ {
+ try
+ {
+ var entity = await _repository.Entities
+ .Where(x => x.Id == id && x.IsDel == 0)
+ .FirstAsync();
+ if (entity == null)
+ {
+ return Result.Error("设备不存在或已被删除");
+ }
+ return Result.Success(entity);
+ }
+ catch (Exception ex)
+ {
+ return Result.Error("查询设备详情失败", ex);
+ }
+ }
+
+ ///
+ /// 新增设备台账(校验编号唯一性,补充创建时间)
+ ///
+ public override async Task> InsertAsync(EquipmentEntity entity)
+ {
+ if (string.IsNullOrWhiteSpace(entity.Code))
+ {
+ return Result.Error("设备编号不能为空");
+ }
+ try
+ {
+ // 设备编号唯一性校验(未删除范围内)
+ bool exists = await _repository.Entities
+ .AnyAsync(x => x.Code == entity.Code && x.IsDel == 0);
+ if (exists)
+ {
+ return Result.Error($"设备编号【{entity.Code}】已存在");
+ }
+
+ entity.CreateTime = DateTime.Now;
+ var result = await _repository.Context.Insertable(entity).ExecuteCommandAsync();
+ return Result.Success(result > 0);
+ }
+ catch (Exception ex)
+ {
+ return Result.Error("新增设备失败", ex);
+ }
+ }
+
+ ///
+ /// 更新设备台账
+ ///
+ public async Task> UpdateAsync(EquipmentEntity entity)
+ {
+ if (entity.Id <= 0)
+ {
+ return Result.Error("设备 Id 无效");
+ }
+ try
+ {
+ var old = await _repository.Entities
+ .Where(x => x.Id == entity.Id && x.IsDel == 0)
+ .FirstAsync();
+ if (old == null)
+ {
+ return Result.Error("设备不存在或已被删除");
+ }
+
+ // 设备编号变更时校验唯一性
+ if (!string.IsNullOrWhiteSpace(entity.Code) && entity.Code != old.Code)
+ {
+ bool exists = await _repository.Entities
+ .AnyAsync(x => x.Code == entity.Code && x.IsDel == 0 && x.Id != entity.Id);
+ if (exists)
+ {
+ return Result.Error($"设备编号【{entity.Code}】已存在");
+ }
+ }
+
+ entity.IsDel = old.IsDel;
+ entity.CreateTime = old.CreateTime;
+ entity.UpdateTime = DateTime.Now;
+ var result = await _repository.Context.Updateable(entity).ExecuteCommandAsync();
+ return Result.Success(result > 0);
+ }
+ catch (Exception ex)
+ {
+ return Result.Error("更新设备失败", ex);
+ }
+ }
+
+ ///
+ /// 软删除设备台账(置 IsDel = 1)
+ ///
+ public async Task> DeleteEquipmentAsync(long id)
+ {
+ if (id <= 0)
+ {
+ return Result.Error("设备 Id 无效");
+ }
+ try
+ {
+ var result = await _repository.Context.Updateable()
+ .SetColumns(x => x.IsDel == 1)
+ .Where(x => x.Id == id)
+ .ExecuteCommandAsync();
+ return Result.Success(result > 0);
+ }
+ catch (Exception ex)
+ {
+ return Result.Error("删除设备失败", ex);
+ }
+ }
+
+ ///
+ /// 变更设备状态,并记录状态变更历史(事务保证一致性)
+ ///
+ public async Task> ChangeStatusAsync(long equipmentId, EquipmentStatusEnum toStatus, string? operatorName, string? remark)
+ {
+ if (equipmentId <= 0)
+ {
+ return Result.Error("设备 Id 无效");
+ }
+ try
+ {
+ var equipment = await _repository.Entities
+ .Where(x => x.Id == equipmentId && x.IsDel == 0)
+ .FirstAsync();
+ if (equipment == null)
+ {
+ return Result.Error("设备不存在或已被删除");
+ }
+ if (equipment.Status == toStatus)
+ {
+ return Result.Error("目标状态与当前状态一致,无需变更");
+ }
+
+ var now = DateTime.Now;
+ var record = new EquipmentStatusRecordEntity
+ {
+ EquipmentId = equipmentId,
+ FromStatus = equipment.Status,
+ ToStatus = toStatus,
+ ChangeTime = now,
+ Operator = operatorName,
+ Remark = remark,
+ CreateTime = now
+ };
+
+ // 事务:更新设备状态 + 插入状态变更记录
+ _repository.BeginTran();
+ try
+ {
+ equipment.Status = toStatus;
+ equipment.UpdateTime = now;
+ await _repository.Context.Updateable(equipment)
+ .UpdateColumns(x => new { x.Status, x.UpdateTime })
+ .ExecuteCommandAsync();
+ await _statusRecordRepository.Context.Insertable(record).ExecuteCommandAsync();
+ _repository.CommitTran();
+ }
+ catch
+ {
+ _repository.RollbackTran();
+ throw;
+ }
+
+ return Result.Success(true);
+ }
+ catch (Exception ex)
+ {
+ return Result.Error("变更设备状态失败", ex);
+ }
+ }
+
+ ///
+ /// 查询指定设备的状态变更历史记录(按变更时间倒序)
+ ///
+ public async Task>> GetStatusRecordsAsync(long equipmentId)
+ {
+ try
+ {
+ var list = await _statusRecordRepository.Entities
+ .Where(x => x.EquipmentId == equipmentId && x.IsDel == 0)
+ .OrderBy(x => x.ChangeTime, OrderByType.Desc)
+ .ToListAsync();
+ return Result>.Success(list);
+ }
+ catch (Exception ex)
+ {
+ return Result>.Error("查询状态变更记录失败", ex);
+ }
+ }
+ }
+}
diff --git a/Service/Interface/IEquipmentService.cs b/Service/Interface/IEquipmentService.cs
new file mode 100644
index 0000000..72b74b1
--- /dev/null
+++ b/Service/Interface/IEquipmentService.cs
@@ -0,0 +1,65 @@
+using Model;
+using Model.Entity.Asset;
+using SqlSugar;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace Service.Interface
+{
+ ///
+ /// 设备台账服务接口(资产管理)
+ ///
+ public interface IEquipmentService : IBaseService
+ {
+ ///
+ /// 分页查询设备台账(支持按关键字、分类、状态筛选)
+ ///
+ /// 页码(从1开始)
+ /// 每页数量
+ /// 总页数(输出参数)
+ /// 关键字(模糊匹配设备编号/名称)
+ /// 设备分类Id(0表示不过滤)
+ /// 设备状态(null表示不过滤)
+ /// 返回包含分页数据的 Result
+ Task>> GetPagedAsync(int pageIndex, int pageSize, RefAsync total,
+ string? keyword, long categoryId = 0, EquipmentStatusEnum? status = null);
+
+ ///
+ /// 根据 Id 获取设备台账详情
+ ///
+ /// 主键 Id
+ /// 返回包含设备详情的 Result
+ Task> GetByIdAsync(long id);
+
+ ///
+ /// 更新设备台账
+ ///
+ /// 设备实体(必须包含 Id)
+ /// 返回操作是否成功的 Result
+ Task> UpdateAsync(EquipmentEntity entity);
+
+ ///
+ /// 软删除设备台账(置 IsDel = 1)
+ ///
+ /// 主键 Id
+ /// 返回操作是否成功的 Result
+ Task> DeleteEquipmentAsync(long id);
+
+ ///
+ /// 变更设备状态,并记录状态变更历史(事务保证一致性)
+ ///
+ /// 设备 Id
+ /// 目标状态
+ /// 操作人
+ /// 变更原因/备注
+ /// 返回操作是否成功的 Result
+ Task> ChangeStatusAsync(long equipmentId, EquipmentStatusEnum toStatus, string? operatorName, string? remark);
+
+ ///
+ /// 查询指定设备的状态变更历史记录(按变更时间倒序)
+ ///
+ /// 设备 Id
+ /// 返回包含状态记录列表的 Result
+ Task>> GetStatusRecordsAsync(long equipmentId);
+ }
+}