Compare commits
3
Commits
01f51fa3b4
...
24d67baf65
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
24d67baf65 | ||
|
|
2306fbd36b | ||
|
|
a027bf5d9e |
@@ -366,3 +366,4 @@ MigrationBackup/
|
|||||||
# Fody - auto-generated XML schema
|
# Fody - auto-generated XML schema
|
||||||
FodyWeavers.xsd
|
FodyWeavers.xsd
|
||||||
/IOT_API/appsettings.json
|
/IOT_API/appsettings.json
|
||||||
|
/IOT_API/wwwroot
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Model;
|
using Model;
|
||||||
|
using Model.Dto.Asset;
|
||||||
using Model.Entity.Asset;
|
using Model.Entity.Asset;
|
||||||
|
using Model.Mapper;
|
||||||
using Service.Interface;
|
using Service.Interface;
|
||||||
using SqlSugar;
|
using SqlSugar;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace IOT_API.Controllers.Asset
|
namespace IOT_API.Controllers.Asset
|
||||||
@@ -11,16 +14,21 @@ namespace IOT_API.Controllers.Asset
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 资产管理 - 设备台账控制器
|
/// 资产管理 - 设备台账控制器
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Route("api/Equipment")] //资产管理模块下的设备管理接口
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/asset/equipment")]
|
[Route("api/asset/equipment")] //资产管理模块下的设备管理接口
|
||||||
public class EquipmentController : ControllerBase
|
public class EquipmentController : ControllerBase
|
||||||
{
|
{
|
||||||
private readonly IEquipmentService _equipmentService;
|
private readonly IEquipmentService _equipmentService;
|
||||||
|
private readonly IEquipmentAttachmentService _attachmentService;
|
||||||
|
private readonly IWebHostEnvironment _webHostEnvironment;
|
||||||
|
|
||||||
public EquipmentController(IEquipmentService equipmentService)
|
public EquipmentController(IEquipmentService equipmentService,
|
||||||
|
IEquipmentAttachmentService attachmentService,
|
||||||
|
IWebHostEnvironment webHostEnvironment)
|
||||||
{
|
{
|
||||||
_equipmentService = equipmentService;
|
_equipmentService = equipmentService;
|
||||||
|
_attachmentService = attachmentService;
|
||||||
|
_webHostEnvironment = webHostEnvironment;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -32,22 +40,27 @@ namespace IOT_API.Controllers.Asset
|
|||||||
/// <param name="categoryId">设备分类Id(0表示不过滤)</param>
|
/// <param name="categoryId">设备分类Id(0表示不过滤)</param>
|
||||||
/// <param name="status">设备状态(不传表示不过滤)</param>
|
/// <param name="status">设备状态(不传表示不过滤)</param>
|
||||||
[HttpGet("list")]
|
[HttpGet("list")]
|
||||||
public async Task<Result<List<EquipmentEntity>>> GetList(int pageIndex = 1, int pageSize = 10,
|
public async Task<Result<List<EquipmentDto>>> GetList(int pageIndex = 1, int pageSize = 10,
|
||||||
string? keyword = null, long categoryId = 0, EquipmentStatusEnum? status = null)
|
string? keyword = null, long categoryId = 0, EquipmentStatusEnum? status = null)
|
||||||
{
|
{
|
||||||
RefAsync<int> total = 0;
|
RefAsync<int> total = 0;
|
||||||
var result = await _equipmentService.GetPagedAsync(pageIndex, pageSize, total, keyword, categoryId, status);
|
var result = await _equipmentService.GetPagedAsync(pageIndex, pageSize, total, keyword, categoryId, status);
|
||||||
Response.Headers["X-Total-Count"] = total.Value.ToString();
|
Response.Headers["X-Total-Count"] = total.Value.ToString();
|
||||||
return result;
|
return result.IsSuccess
|
||||||
|
? Result<List<EquipmentDto>>.Success(result.Data.ToDtoList())
|
||||||
|
: Result<List<EquipmentDto>>.Error(result.Msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查询全部设备(不分页,供下拉选择等场景使用)
|
/// 查询全部设备(不分页,供下拉选择等场景使用)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpGet("all")]
|
[HttpGet("all")]
|
||||||
public async Task<Result<List<EquipmentEntity>>> GetAll()
|
public async Task<Result<List<EquipmentDto>>> GetAll()
|
||||||
{
|
{
|
||||||
return await _equipmentService.GetAllAsync();
|
var result = await _equipmentService.GetAllAsync();
|
||||||
|
return result.IsSuccess
|
||||||
|
? Result<List<EquipmentDto>>.Success(result.Data.ToDtoList())
|
||||||
|
: Result<List<EquipmentDto>>.Error(result.Msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -55,29 +68,33 @@ namespace IOT_API.Controllers.Asset
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">设备主键 Id</param>
|
/// <param name="id">设备主键 Id</param>
|
||||||
[HttpGet("{id}")]
|
[HttpGet("{id}")]
|
||||||
public async Task<Result<EquipmentEntity>> GetById(long id)
|
public async Task<Result<EquipmentDto>> GetById(long id)
|
||||||
{
|
{
|
||||||
return await _equipmentService.GetByIdAsync(id);
|
var res = await _equipmentService.GetByIdAsync(id);
|
||||||
|
return res.IsSuccess
|
||||||
|
? Result<EquipmentDto>.Success(res.Data.ToDto())
|
||||||
|
: Result<EquipmentDto>.Error(res.Msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 新增设备
|
/// 新增设备
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="entity">设备实体</param>
|
/// <param name="dto">设备 DTO(IsDel/CreateTime 等服务端字段不接受入参)</param>
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<Result<bool>> Add([FromBody] EquipmentEntity entity)
|
public async Task<Result<bool>> Add([FromBody] EquipmentDto dto)
|
||||||
{
|
{
|
||||||
return await _equipmentService.InsertAsync(entity);
|
return await _equipmentService.InsertAsync(dto.ToEntity());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 修改设备
|
/// 修改设备
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">设备主键 Id</param>
|
/// <param name="id">设备主键 Id(以路由参数为准,忽略请求体中的 Id)</param>
|
||||||
/// <param name="entity">设备实体</param>
|
/// <param name="dto">设备 DTO(IsDel/CreateTime 等服务端字段不接受入参)</param>
|
||||||
[HttpPut("{id}")]
|
[HttpPut("{id}")]
|
||||||
public async Task<Result<bool>> Update(long id, [FromBody] EquipmentEntity entity)
|
public async Task<Result<bool>> Update(long id, [FromBody] EquipmentDto dto)
|
||||||
{
|
{
|
||||||
|
var entity = dto.ToEntity();
|
||||||
entity.Id = id;
|
entity.Id = id;
|
||||||
return await _equipmentService.UpdateAsync(entity);
|
return await _equipmentService.UpdateAsync(entity);
|
||||||
}
|
}
|
||||||
@@ -96,11 +113,11 @@ namespace IOT_API.Controllers.Asset
|
|||||||
/// 变更设备状态(同步记录状态变更历史)
|
/// 变更设备状态(同步记录状态变更历史)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">设备主键 Id</param>
|
/// <param name="id">设备主键 Id</param>
|
||||||
/// <param name="request">状态变更请求</param>
|
/// <param name="dto">状态变更请求(目标状态/操作人/备注)</param>
|
||||||
[HttpPost("{id}/status")]
|
[HttpPost("{id}/status")]
|
||||||
public async Task<Result<bool>> ChangeStatus(long id, [FromBody] ChangeStatusRequest request)
|
public async Task<Result<bool>> ChangeStatus(long id, [FromBody] EquipmentStatusChangeDto dto)
|
||||||
{
|
{
|
||||||
return await _equipmentService.ChangeStatusAsync(id, request.Status, request.Operator, request.Remark);
|
return await _equipmentService.ChangeStatusAsync(id, dto.Status, dto.Operator, dto.Remark);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -108,30 +125,65 @@ namespace IOT_API.Controllers.Asset
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">设备主键 Id</param>
|
/// <param name="id">设备主键 Id</param>
|
||||||
[HttpGet("{id}/status-records")]
|
[HttpGet("{id}/status-records")]
|
||||||
public async Task<Result<List<EquipmentStatusRecordEntity>>> GetStatusRecords(long id)
|
public async Task<Result<List<EquipmentStatusRecordDto>>> GetStatusRecords(long id)
|
||||||
{
|
{
|
||||||
return await _equipmentService.GetStatusRecordsAsync(id);
|
var res = await _equipmentService.GetStatusRecordsAsync(id);
|
||||||
}
|
return res.IsSuccess
|
||||||
|
? Result<List<EquipmentStatusRecordDto>>.Success(res.Data.ToDtoList())
|
||||||
|
: Result<List<EquipmentStatusRecordDto>>.Error(res.Msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 状态变更请求体
|
/// 查询设备附件列表(资产附件管理)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ChangeStatusRequest
|
/// <param name="id">设备主键 Id</param>
|
||||||
|
[HttpGet("{id}/attachments")]
|
||||||
|
public async Task<Result<List<EquipmentAttachmentDto>>> GetAttachments(long id)
|
||||||
{
|
{
|
||||||
/// <summary>
|
var res = await _attachmentService.GetByEquipmentAsync(id);
|
||||||
/// 目标状态
|
return res.IsSuccess
|
||||||
/// </summary>
|
? Result<List<EquipmentAttachmentDto>>.Success(res.Data.ToDtoList())
|
||||||
public EquipmentStatusEnum Status { get; set; }
|
: Result<List<EquipmentAttachmentDto>>.Error(res.Msg);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 操作人
|
/// 上传设备附件(multipart/form-data,文件字段名 file)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? Operator { get; set; }
|
/// <param name="id">设备主键 Id</param>
|
||||||
|
/// <param name="file">上传的文件</param>
|
||||||
|
/// <param name="fileType">文件类型(1使用说明书/2合格证明/3校准证书/4保养卡/5合同扫描件/6验收照片/99其他)</param>
|
||||||
|
/// <param name="uploader">上传人</param>
|
||||||
|
/// <param name="remark">备注</param>
|
||||||
|
[HttpPost("{id}/attachments")]
|
||||||
|
[RequestSizeLimit(50 * 1024 * 1024)]
|
||||||
|
public async Task<Result<EquipmentAttachmentDto>> UploadAttachment(long id, IFormFile file,
|
||||||
|
[FromForm] AttachmentTypeEnum fileType = AttachmentTypeEnum.Other,
|
||||||
|
[FromForm] string? uploader = null, [FromForm] string? remark = null)
|
||||||
|
{
|
||||||
|
if (file == null || file.Length == 0)
|
||||||
|
{
|
||||||
|
return Result<EquipmentAttachmentDto>.Error("请选择要上传的文件");
|
||||||
|
}
|
||||||
|
|
||||||
|
// wwwroot 不存在时退回运行目录下的 wwwroot(上传时会自动创建)
|
||||||
|
string webRootPath = _webHostEnvironment.WebRootPath
|
||||||
|
?? Path.Combine(_webHostEnvironment.ContentRootPath, "wwwroot");
|
||||||
|
|
||||||
|
using var stream = file.OpenReadStream();
|
||||||
|
var res = await _attachmentService.UploadAsync(id, stream, file.FileName, file.Length, fileType, uploader, remark, webRootPath);
|
||||||
|
return res.IsSuccess
|
||||||
|
? Result<EquipmentAttachmentDto>.Success(res.Data.ToDto())
|
||||||
|
: Result<EquipmentAttachmentDto>.Error(res.Msg);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 变更原因/备注
|
/// 删除设备附件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? Remark { get; set; }
|
/// <param name="attachmentId">附件主键 Id</param>
|
||||||
|
[HttpDelete("attachments/{attachmentId}")]
|
||||||
|
public async Task<Result<bool>> DeleteAttachment(long attachmentId)
|
||||||
|
{
|
||||||
|
return await _attachmentService.DeleteAsync(attachmentId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,4 +17,8 @@
|
|||||||
<ProjectReference Include="..\Service\Service.csproj" />
|
<ProjectReference Include="..\Service\Service.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Converters\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ namespace WebAPI
|
|||||||
options.JsonSerializerOptions.NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString;
|
options.JsonSerializerOptions.NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString;
|
||||||
|
|
||||||
options.JsonSerializerOptions.PropertyNamingPolicy = null;
|
options.JsonSerializerOptions.PropertyNamingPolicy = null;
|
||||||
|
|
||||||
});
|
});
|
||||||
// 自动注册业务服务(Service.Interface -> Service.Implement)
|
// 自动注册业务服务(Service.Interface -> Service.Implement)
|
||||||
builder.Services.AddBusinessServices();
|
builder.Services.AddBusinessServices();
|
||||||
@@ -63,6 +64,9 @@ namespace WebAPI
|
|||||||
|
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
// 静态文件:设备附件上传后通过 /uploads/... 访问(存储于 wwwroot)
|
||||||
|
app.UseStaticFiles();
|
||||||
|
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ namespace Model.Dto.Asset
|
|||||||
/// <summary>创建时间</summary>
|
/// <summary>创建时间</summary>
|
||||||
public DateTime? CreateTime { get; set; }
|
public DateTime? CreateTime { get; set; }
|
||||||
|
|
||||||
/// <summary>设备Id(关联 EquipmentEntity.Id)</summary>
|
/// <summary>设备Id(关联 EquipmentEntity.Id,雪花 Id 转 string 防精度丢失)</summary>
|
||||||
public long EquipmentId { get; set; }
|
public string EquipmentId { get; set; }
|
||||||
|
|
||||||
/// <summary>文件名称</summary>
|
/// <summary>文件名称</summary>
|
||||||
public string? FileName { get; set; }
|
public string? FileName { get; set; }
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ namespace Model.Dto.Asset
|
|||||||
/// <summary>创建时间</summary>
|
/// <summary>创建时间</summary>
|
||||||
public DateTime? CreateTime { get; set; }
|
public DateTime? CreateTime { get; set; }
|
||||||
|
|
||||||
/// <summary>父级分类Id(0表示顶级)</summary>
|
/// <summary>父级分类Id(0表示顶级,雪花 Id 转 string 防精度丢失)</summary>
|
||||||
public long ParentId { get; set; }
|
public string ParentId { get; set; }
|
||||||
|
|
||||||
/// <summary>分类名称</summary>
|
/// <summary>分类名称</summary>
|
||||||
public string? Name { get; set; }
|
public string? Name { get; set; }
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ namespace Model.Dto.Asset
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class EquipmentDto
|
public class EquipmentDto
|
||||||
{
|
{
|
||||||
/// <summary>主键 Id(long → string)</summary>
|
/// <summary>主键 Id(long → string;入参可空,新增时无需传递)</summary>
|
||||||
public string Id { get; set; }
|
public string? Id { get; set; }
|
||||||
|
|
||||||
/// <summary>删除状态(0、未删除;1、已删除)</summary>
|
/// <summary>删除状态(0、未删除;1、已删除)</summary>
|
||||||
public byte IsDel { get; set; }
|
public byte IsDel { get; set; }
|
||||||
@@ -22,8 +22,8 @@ namespace Model.Dto.Asset
|
|||||||
/// <summary>设备名称</summary>
|
/// <summary>设备名称</summary>
|
||||||
public string? Name { get; set; }
|
public string? Name { get; set; }
|
||||||
|
|
||||||
/// <summary>设备分类Id(关联 EquipmentCategoryEntity.Id)</summary>
|
/// <summary>设备分类Id(关联 EquipmentCategoryEntity.Id,雪花 Id 转 string 防精度丢失;可空,未分类时为 "0" 或不传)</summary>
|
||||||
public long CategoryId { get; set; }
|
public string? CategoryId { get; set; }
|
||||||
|
|
||||||
/// <summary>设备类型</summary>
|
/// <summary>设备类型</summary>
|
||||||
public string? Type { get; set; }
|
public string? Type { get; set; }
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using Model.Entity.Asset;
|
||||||
|
|
||||||
|
namespace Model.Dto.Asset
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 设备状态变更请求 DTO(POST api/asset/equipment/{id}/status 入参)
|
||||||
|
/// </summary>
|
||||||
|
public class EquipmentStatusChangeDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 目标状态(必填)
|
||||||
|
/// </summary>
|
||||||
|
public EquipmentStatusEnum Status { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 操作人
|
||||||
|
/// </summary>
|
||||||
|
public string? Operator { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 变更原因/备注
|
||||||
|
/// </summary>
|
||||||
|
public string? Remark { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,8 +16,8 @@ namespace Model.Dto.Asset
|
|||||||
/// <summary>创建时间</summary>
|
/// <summary>创建时间</summary>
|
||||||
public DateTime? CreateTime { get; set; }
|
public DateTime? CreateTime { get; set; }
|
||||||
|
|
||||||
/// <summary>设备Id(关联 EquipmentEntity.Id)</summary>
|
/// <summary>设备Id(关联 EquipmentEntity.Id,雪花 Id 转 string 防精度丢失)</summary>
|
||||||
public long EquipmentId { get; set; }
|
public string EquipmentId { get; set; }
|
||||||
|
|
||||||
/// <summary>变更前状态</summary>
|
/// <summary>变更前状态</summary>
|
||||||
public EquipmentStatusEnum FromStatus { get; set; }
|
public EquipmentStatusEnum FromStatus { get; set; }
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace Model.Entity.Asset
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 文件名称
|
/// 文件名称
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length = 200)]
|
[SugarColumn(Length = 200, IsNullable = true)]
|
||||||
public string? FileName { get; set; }
|
public string? FileName { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -26,13 +26,13 @@ namespace Model.Entity.Asset
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 文件扩展名(如 .pdf、.jpg)
|
/// 文件扩展名(如 .pdf、.jpg)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length = 20)]
|
[SugarColumn(Length = 20, IsNullable = true)]
|
||||||
public string? FileExt { get; set; }
|
public string? FileExt { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 文件存储地址(上传后存储的相对路径)
|
/// 文件存储地址(上传后存储的相对路径)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length = 200)]
|
[SugarColumn(Length = 200, IsNullable = true)]
|
||||||
public string? FileUrl { get; set; }
|
public string? FileUrl { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -43,13 +43,13 @@ namespace Model.Entity.Asset
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 上传人
|
/// 上传人
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length = 50)]
|
[SugarColumn(Length = 50, IsNullable = true)]
|
||||||
public string? Uploader { get; set; }
|
public string? Uploader { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 备注
|
/// 备注
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length = 500)]
|
[SugarColumn(Length = 500, IsNullable = true)]
|
||||||
public string? Remark { get; set; }
|
public string? Remark { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,13 +15,13 @@ namespace Model.Entity.Asset
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 分类名称
|
/// 分类名称
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length = 100)]
|
[SugarColumn(Length = 100, IsNullable = true)]
|
||||||
public string? Name { get; set; }
|
public string? Name { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 分类编码
|
/// 分类编码
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length = 50)]
|
[SugarColumn(Length = 50, IsNullable = true)]
|
||||||
public string? Code { get; set; }
|
public string? Code { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -37,7 +37,7 @@ namespace Model.Entity.Asset
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 备注
|
/// 备注
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length = 500)]
|
[SugarColumn(Length = 500, IsNullable = true)]
|
||||||
public string? Remark { get; set; }
|
public string? Remark { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ namespace Model.Entity.Asset
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 设备编号(唯一)
|
/// 设备编号(唯一)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length = 50)]
|
[SugarColumn(Length = 50, IsNullable = true)]
|
||||||
public string? Code { get; set; }
|
public string? Code { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 设备名称
|
/// 设备名称
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length = 100)]
|
[SugarColumn(Length = 100, IsNullable = true)]
|
||||||
public string? Name { get; set; }
|
public string? Name { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -28,55 +28,55 @@ namespace Model.Entity.Asset
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 设备类型(实验室/楼层/部门/位置/产品/自定义等分类维度下的类型名称,冗余便于展示)
|
/// 设备类型(实验室/楼层/部门/位置/产品/自定义等分类维度下的类型名称,冗余便于展示)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length = 100)]
|
[SugarColumn(Length = 100, IsNullable = true)]
|
||||||
public string? Type { get; set; }
|
public string? Type { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 设备品牌
|
/// 设备品牌
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length = 100)]
|
[SugarColumn(Length = 100, IsNullable = true)]
|
||||||
public string? Brand { get; set; }
|
public string? Brand { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 设备型号
|
/// 设备型号
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length = 100)]
|
[SugarColumn(Length = 100, IsNullable = true)]
|
||||||
public string? Model { get; set; }
|
public string? Model { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 规格参数
|
/// 规格参数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length = 200)]
|
[SugarColumn(Length = 200, IsNullable = true)]
|
||||||
public string? Specifications { get; set; }
|
public string? Specifications { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 制造商(设备制造商)
|
/// 制造商(设备制造商)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length = 100)]
|
[SugarColumn(Length = 100, IsNullable = true)]
|
||||||
public string? Manufacturer { get; set; }
|
public string? Manufacturer { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 供应商(资产供应商)
|
/// 供应商(资产供应商)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length = 100)]
|
[SugarColumn(Length = 100, IsNullable = true)]
|
||||||
public string? Supplier { get; set; }
|
public string? Supplier { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 设备图片地址(图片上传后存储的相对路径)
|
/// 设备图片地址(图片上传后存储的相对路径)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length = 200)]
|
[SugarColumn(Length = 200, IsNullable = true)]
|
||||||
public string? ImageUrl { get; set; }
|
public string? ImageUrl { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 二维码编号(一物一码)
|
/// 二维码编号(一物一码)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length = 50)]
|
[SugarColumn(Length = 50, IsNullable = true)]
|
||||||
public string? QrCode { get; set; }
|
public string? QrCode { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 设备描述
|
/// 设备描述
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(ColumnDataType = "text")]
|
[SugarColumn(ColumnDataType = "text", IsNullable = true)]
|
||||||
public string? Description { get; set; }
|
public string? Description { get; set; }
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -84,31 +84,31 @@ namespace Model.Entity.Asset
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 使用部门名称(资产使用部门)
|
/// 使用部门名称(资产使用部门)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length = 100)]
|
[SugarColumn(Length = 100, IsNullable = true)]
|
||||||
public string? Department { get; set; }
|
public string? Department { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 存放位置(实验室/楼层/房间等位置信息)
|
/// 存放位置(实验室/楼层/房间等位置信息)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length = 200)]
|
[SugarColumn(Length = 200, IsNullable = true)]
|
||||||
public string? Location { get; set; }
|
public string? Location { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 责任人(资产责任人)
|
/// 责任人(资产责任人)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length = 50)]
|
[SugarColumn(Length = 50, IsNullable = true)]
|
||||||
public string? ResponsiblePerson { get; set; }
|
public string? ResponsiblePerson { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 责任人联系电话(资产责任人电话)
|
/// 责任人联系电话(资产责任人电话)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length = 30)]
|
[SugarColumn(Length = 30, IsNullable = true)]
|
||||||
public string? ContactPhone { get; set; }
|
public string? ContactPhone { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 保管人
|
/// 保管人
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length = 50)]
|
[SugarColumn(Length = 50, IsNullable = true)]
|
||||||
public string? Custodian { get; set; }
|
public string? Custodian { get; set; }
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -116,17 +116,19 @@ namespace Model.Entity.Asset
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 购置日期
|
/// 购置日期
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[SugarColumn(IsNullable = true)]
|
||||||
public DateTime? PurchaseDate { get; set; }
|
public DateTime? PurchaseDate { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 购置价格(资产购置价格)
|
/// 购置价格(资产购置价格)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(DecimalDigits = 2, Length = 18)]
|
[SugarColumn(DecimalDigits = 2, Length = 18, IsNullable = true)]
|
||||||
public decimal? PurchasePrice { get; set; }
|
public decimal? PurchasePrice { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 保修到期日期(资产保修到期)
|
/// 保修到期日期(资产保修到期)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[SugarColumn(IsNullable = true)]
|
||||||
public DateTime? WarrantyExpireDate { get; set; }
|
public DateTime? WarrantyExpireDate { get; set; }
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -134,21 +136,25 @@ namespace Model.Entity.Asset
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 验收日期(资产验收日期)
|
/// 验收日期(资产验收日期)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[SugarColumn(IsNullable = true)]
|
||||||
public DateTime? AcceptanceDate { get; set; }
|
public DateTime? AcceptanceDate { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 启用日期(资产启用日期)
|
/// 启用日期(资产启用日期)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[SugarColumn(IsNullable = true)]
|
||||||
public DateTime? EnableDate { get; set; }
|
public DateTime? EnableDate { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 使用年限(资产使用年限,单位:年)
|
/// 使用年限(资产使用年限,单位:年)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[SugarColumn(IsNullable = true)]
|
||||||
public int? ServiceLifeYears { get; set; }
|
public int? ServiceLifeYears { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 报废日期(资产报废日期)
|
/// 报废日期(资产报废日期)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[SugarColumn(IsNullable = true)]
|
||||||
public DateTime? ScrapDate { get; set; }
|
public DateTime? ScrapDate { get; set; }
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -168,21 +174,25 @@ namespace Model.Entity.Asset
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 上次校准日期(校准日期)
|
/// 上次校准日期(校准日期)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[SugarColumn(IsNullable = true)]
|
||||||
public DateTime? LastCalibrationDate { get; set; }
|
public DateTime? LastCalibrationDate { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 下次校准到期日期(用于判断是否超期)
|
/// 下次校准到期日期(用于判断是否超期)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[SugarColumn(IsNullable = true)]
|
||||||
public DateTime? NextCalibrationDate { get; set; }
|
public DateTime? NextCalibrationDate { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 检定日期
|
/// 检定日期
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[SugarColumn(IsNullable = true)]
|
||||||
public DateTime? InspectionDate { get; set; }
|
public DateTime? InspectionDate { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 下次检定到期日期(用于判断是否超期)
|
/// 下次检定到期日期(用于判断是否超期)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[SugarColumn(IsNullable = true)]
|
||||||
public DateTime? NextInspectionDate { get; set; }
|
public DateTime? NextInspectionDate { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -193,11 +203,13 @@ namespace Model.Entity.Asset
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 上次保养日期(保养日期)
|
/// 上次保养日期(保养日期)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[SugarColumn(IsNullable = true)]
|
||||||
public DateTime? LastMaintenanceDate { get; set; }
|
public DateTime? LastMaintenanceDate { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 下次保养到期日期(保养周期到期,用于判断是否超期)
|
/// 下次保养到期日期(保养周期到期,用于判断是否超期)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[SugarColumn(IsNullable = true)]
|
||||||
public DateTime? NextMaintenanceDate { get; set; }
|
public DateTime? NextMaintenanceDate { get; set; }
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -205,24 +217,25 @@ namespace Model.Entity.Asset
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 备注
|
/// 备注
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length = 500)]
|
[SugarColumn(Length = 500, IsNullable = true)]
|
||||||
public string? Remark { get; set; }
|
public string? Remark { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 创建人
|
/// 创建人
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length = 50)]
|
[SugarColumn(Length = 50, IsNullable = true)]
|
||||||
public string? CreateBy { get; set; }
|
public string? CreateBy { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 更新人(修改人)
|
/// 更新人(修改人)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length = 50)]
|
[SugarColumn(Length = 50, IsNullable = true)]
|
||||||
public string? UpdateBy { get; set; }
|
public string? UpdateBy { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 更新时间(修改时间)
|
/// 更新时间(修改时间)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[SugarColumn(IsNullable = true)]
|
||||||
public DateTime? UpdateTime { get; set; }
|
public DateTime? UpdateTime { get; set; }
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,13 +30,13 @@ namespace Model.Entity.Asset
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 操作人
|
/// 操作人
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length = 50)]
|
[SugarColumn(Length = 50, IsNullable = true)]
|
||||||
public string? Operator { get; set; }
|
public string? Operator { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 变更原因/备注
|
/// 变更原因/备注
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length = 500)]
|
[SugarColumn(Length = 500, IsNullable = true)]
|
||||||
public string? Remark { get; set; }
|
public string? Remark { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ namespace Model.Mapper
|
|||||||
CreateTime = entity.CreateTime,
|
CreateTime = entity.CreateTime,
|
||||||
Code = entity.Code,
|
Code = entity.Code,
|
||||||
Name = entity.Name,
|
Name = entity.Name,
|
||||||
CategoryId = entity.CategoryId,
|
CategoryId = entity.CategoryId.ToString(),
|
||||||
Type = entity.Type,
|
Type = entity.Type,
|
||||||
Brand = entity.Brand,
|
Brand = entity.Brand,
|
||||||
Model = entity.Model,
|
Model = entity.Model,
|
||||||
@@ -71,6 +71,54 @@ namespace Model.Mapper
|
|||||||
{
|
{
|
||||||
return entities?.Select(e => e.ToDto()).ToList() ?? new List<EquipmentDto>();
|
return entities?.Select(e => e.ToDto()).ToList() ?? new List<EquipmentDto>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// EquipmentDto → EquipmentEntity(入参映射,用于新增/修改)
|
||||||
|
/// 安全约定:IsDel/CreateTime/UpdateTime 等服务端管控字段刻意不映射,防止过度绑定(over-binding);
|
||||||
|
/// Id 解析失败时为 0(新增时由雪花算法自动生成)
|
||||||
|
/// </summary>
|
||||||
|
public static EquipmentEntity ToEntity(this EquipmentDto dto)
|
||||||
|
{
|
||||||
|
if (dto == null) return null;
|
||||||
|
return new EquipmentEntity
|
||||||
|
{
|
||||||
|
Id = long.TryParse(dto.Id, out var id) ? id : 0,
|
||||||
|
Code = dto.Code,
|
||||||
|
Name = dto.Name,
|
||||||
|
CategoryId = long.TryParse(dto.CategoryId, out var categoryId) ? categoryId : 0,
|
||||||
|
Type = dto.Type,
|
||||||
|
Brand = dto.Brand,
|
||||||
|
Model = dto.Model,
|
||||||
|
Specifications = dto.Specifications,
|
||||||
|
Manufacturer = dto.Manufacturer,
|
||||||
|
Supplier = dto.Supplier,
|
||||||
|
ImageUrl = dto.ImageUrl,
|
||||||
|
QrCode = dto.QrCode,
|
||||||
|
Description = dto.Description,
|
||||||
|
Department = dto.Department,
|
||||||
|
Location = dto.Location,
|
||||||
|
ResponsiblePerson = dto.ResponsiblePerson,
|
||||||
|
ContactPhone = dto.ContactPhone,
|
||||||
|
Custodian = dto.Custodian,
|
||||||
|
PurchaseDate = dto.PurchaseDate,
|
||||||
|
PurchasePrice = dto.PurchasePrice,
|
||||||
|
WarrantyExpireDate = dto.WarrantyExpireDate,
|
||||||
|
AcceptanceDate = dto.AcceptanceDate,
|
||||||
|
EnableDate = dto.EnableDate,
|
||||||
|
ServiceLifeYears = dto.ServiceLifeYears,
|
||||||
|
ScrapDate = dto.ScrapDate,
|
||||||
|
Status = dto.Status,
|
||||||
|
CalibrationStatus = dto.CalibrationStatus,
|
||||||
|
LastCalibrationDate = dto.LastCalibrationDate,
|
||||||
|
NextCalibrationDate = dto.NextCalibrationDate,
|
||||||
|
InspectionDate = dto.InspectionDate,
|
||||||
|
NextInspectionDate = dto.NextInspectionDate,
|
||||||
|
MaintenanceStatus = dto.MaintenanceStatus,
|
||||||
|
LastMaintenanceDate = dto.LastMaintenanceDate,
|
||||||
|
NextMaintenanceDate = dto.NextMaintenanceDate,
|
||||||
|
Remark = dto.Remark
|
||||||
|
};
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 设备附件
|
#region 设备附件
|
||||||
@@ -85,7 +133,7 @@ namespace Model.Mapper
|
|||||||
Id = entity.Id.ToString(),
|
Id = entity.Id.ToString(),
|
||||||
IsDel = entity.IsDel,
|
IsDel = entity.IsDel,
|
||||||
CreateTime = entity.CreateTime,
|
CreateTime = entity.CreateTime,
|
||||||
EquipmentId = entity.EquipmentId,
|
EquipmentId = entity.EquipmentId.ToString(),
|
||||||
FileName = entity.FileName,
|
FileName = entity.FileName,
|
||||||
FileType = entity.FileType,
|
FileType = entity.FileType,
|
||||||
FileExt = entity.FileExt,
|
FileExt = entity.FileExt,
|
||||||
@@ -117,7 +165,7 @@ namespace Model.Mapper
|
|||||||
Id = entity.Id.ToString(),
|
Id = entity.Id.ToString(),
|
||||||
IsDel = entity.IsDel,
|
IsDel = entity.IsDel,
|
||||||
CreateTime = entity.CreateTime,
|
CreateTime = entity.CreateTime,
|
||||||
ParentId = entity.ParentId,
|
ParentId = entity.ParentId.ToString(),
|
||||||
Name = entity.Name,
|
Name = entity.Name,
|
||||||
Code = entity.Code,
|
Code = entity.Code,
|
||||||
Level = entity.Level,
|
Level = entity.Level,
|
||||||
@@ -147,7 +195,7 @@ namespace Model.Mapper
|
|||||||
Id = entity.Id.ToString(),
|
Id = entity.Id.ToString(),
|
||||||
IsDel = entity.IsDel,
|
IsDel = entity.IsDel,
|
||||||
CreateTime = entity.CreateTime,
|
CreateTime = entity.CreateTime,
|
||||||
EquipmentId = entity.EquipmentId,
|
EquipmentId = entity.EquipmentId.ToString(),
|
||||||
FromStatus = entity.FromStatus,
|
FromStatus = entity.FromStatus,
|
||||||
ToStatus = entity.ToStatus,
|
ToStatus = entity.ToStatus,
|
||||||
ChangeTime = entity.ChangeTime,
|
ChangeTime = entity.ChangeTime,
|
||||||
|
|||||||
@@ -0,0 +1,148 @@
|
|||||||
|
using Model;
|
||||||
|
using Model.Entity.Asset;
|
||||||
|
using ORM;
|
||||||
|
using Service.Interface;
|
||||||
|
using SqlSugar;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Service.Implement
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 设备附件服务实现(资产附件管理)
|
||||||
|
/// </summary>
|
||||||
|
public class EquipmentAttachmentService : IEquipmentAttachmentService
|
||||||
|
{
|
||||||
|
private readonly SqlSugarRepository<EquipmentAttachmentEntity> _repository;
|
||||||
|
|
||||||
|
public EquipmentAttachmentService(SqlSugarRepository<EquipmentAttachmentEntity> repository)
|
||||||
|
{
|
||||||
|
_repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查询指定设备的附件列表(按上传时间倒序)
|
||||||
|
/// </summary>
|
||||||
|
public async Task<Result<List<EquipmentAttachmentEntity>>> GetByEquipmentAsync(long equipmentId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var list = await _repository.Entities
|
||||||
|
.Where(x => x.EquipmentId == equipmentId && x.IsDel == 0)
|
||||||
|
.OrderBy(x => x.CreateTime, OrderByType.Desc)
|
||||||
|
.ToListAsync();
|
||||||
|
return Result<List<EquipmentAttachmentEntity>>.Success(list);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return Result<List<EquipmentAttachmentEntity>>.Error("查询附件列表失败", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 上传附件(保存文件到 uploads 目录并写入附件记录)
|
||||||
|
/// </summary>
|
||||||
|
public async Task<Result<EquipmentAttachmentEntity>> UploadAsync(long equipmentId, Stream stream, string originalFileName, long fileSize,
|
||||||
|
AttachmentTypeEnum fileType, string? uploader, string? remark, string webRootPath)
|
||||||
|
{
|
||||||
|
if (equipmentId <= 0)
|
||||||
|
{
|
||||||
|
return Result<EquipmentAttachmentEntity>.Error("设备 Id 无效");
|
||||||
|
}
|
||||||
|
if (stream == null || fileSize <= 0)
|
||||||
|
{
|
||||||
|
return Result<EquipmentAttachmentEntity>.Error("上传文件为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 按设备分目录存储:uploads/equipment/{equipmentId}/
|
||||||
|
string relativeDir = Path.Combine("uploads", "equipment", equipmentId.ToString());
|
||||||
|
string physicalDir = Path.Combine(webRootPath, relativeDir);
|
||||||
|
if (!Directory.Exists(physicalDir))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(physicalDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 存储文件名:时间戳 + 原扩展名,避免重名与中文路径问题
|
||||||
|
string ext = Path.GetExtension(originalFileName);
|
||||||
|
string storedName = $"{DateTime.Now:yyyyMMddHHmmssfff}{ext}";
|
||||||
|
string physicalPath = Path.Combine(physicalDir, storedName);
|
||||||
|
|
||||||
|
using (var fileStream = new FileStream(physicalPath, FileMode.Create))
|
||||||
|
{
|
||||||
|
await stream.CopyToAsync(fileStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
var entity = new EquipmentAttachmentEntity
|
||||||
|
{
|
||||||
|
EquipmentId = equipmentId,
|
||||||
|
FileName = originalFileName,
|
||||||
|
FileType = fileType,
|
||||||
|
FileExt = ext,
|
||||||
|
FileUrl = $"/{relativeDir.Replace('\\', '/')}/{storedName}",
|
||||||
|
FileSize = fileSize,
|
||||||
|
Uploader = uploader,
|
||||||
|
Remark = remark,
|
||||||
|
CreateTime = DateTime.Now
|
||||||
|
};
|
||||||
|
|
||||||
|
await _repository.Context.Insertable(entity).ExecuteCommandAsync();
|
||||||
|
return Result<EquipmentAttachmentEntity>.Success(entity);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return Result<EquipmentAttachmentEntity>.Error("上传附件失败", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除附件(软删除记录并删除物理文件)
|
||||||
|
/// </summary>
|
||||||
|
public async Task<Result<bool>> DeleteAsync(long id)
|
||||||
|
{
|
||||||
|
if (id <= 0)
|
||||||
|
{
|
||||||
|
return Result<bool>.Error("附件 Id 无效");
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var attachment = await _repository.Entities
|
||||||
|
.Where(x => x.Id == id && x.IsDel == 0)
|
||||||
|
.FirstAsync();
|
||||||
|
if (attachment == null)
|
||||||
|
{
|
||||||
|
return Result<bool>.Error("附件不存在或已被删除");
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = await _repository.Context.Updateable<EquipmentAttachmentEntity>()
|
||||||
|
.SetColumns(x => x.IsDel == 1)
|
||||||
|
.Where(x => x.Id == id)
|
||||||
|
.ExecuteCommandAsync();
|
||||||
|
|
||||||
|
// 删除物理文件(失败不影响记录删除结果)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string webRoot = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "wwwroot");
|
||||||
|
string physicalPath = Path.Combine(webRoot, (attachment.FileUrl ?? string.Empty).TrimStart('/').Replace('/', Path.DirectorySeparatorChar));
|
||||||
|
if (File.Exists(physicalPath))
|
||||||
|
{
|
||||||
|
File.Delete(physicalPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// 物理文件删除失败忽略
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result<bool>.Success(result > 0);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return Result<bool>.Error("删除附件失败", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
using Model;
|
||||||
|
using Model.Entity.Asset;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Service.Interface
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 设备附件服务接口(资产附件管理:分类存储、在线预览)
|
||||||
|
/// </summary>
|
||||||
|
public interface IEquipmentAttachmentService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 查询指定设备的附件列表(按上传时间倒序)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="equipmentId">设备 Id</param>
|
||||||
|
/// <returns>返回包含附件列表的 Result</returns>
|
||||||
|
Task<Result<List<EquipmentAttachmentEntity>>> GetByEquipmentAsync(long equipmentId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 上传附件(保存文件到 uploads 目录并写入附件记录)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="equipmentId">设备 Id</param>
|
||||||
|
/// <param name="stream">文件流</param>
|
||||||
|
/// <param name="originalFileName">原始文件名</param>
|
||||||
|
/// <param name="fileSize">文件大小(字节)</param>
|
||||||
|
/// <param name="fileType">文件类型(说明书/合格证/校准证书等)</param>
|
||||||
|
/// <param name="uploader">上传人</param>
|
||||||
|
/// <param name="remark">备注</param>
|
||||||
|
/// <param name="webRootPath">站点静态文件根目录物理路径</param>
|
||||||
|
/// <returns>返回新增附件记录的 Result</returns>
|
||||||
|
Task<Result<EquipmentAttachmentEntity>> UploadAsync(long equipmentId, Stream stream, string originalFileName, long fileSize,
|
||||||
|
AttachmentTypeEnum fileType, string? uploader, string? remark, string webRootPath);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除附件(软删除记录并删除物理文件)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">附件 Id</param>
|
||||||
|
/// <returns>返回操作是否成功的 Result</returns>
|
||||||
|
Task<Result<bool>> DeleteAsync(long id);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user