namespace Admin.NET.Plugin.AiDOP.Production; /// /// 产线加班(ResourceOccupancyTime) /// 路由前缀:/api/Production/overtime/... /// [ApiDescriptionSettings(Order = 270, Description = "产线加班")] [Route("api/Production")] [AllowAnonymous] [NonUnify] public class ResourceOvertimeService : IDynamicApiController, ITransient { private readonly ISqlSugarClient _db; private readonly UserManager _userManager; public ResourceOvertimeService(ISqlSugarClient db, UserManager userManager) { _db = db; _userManager = userManager; } [DisplayName("产线加班列表")] [HttpGet("overtime/list")] public async Task GetList([FromQuery] ResourceOvertimeListInput input) { var pars = new List(); var where = "r.IsActive = 1"; if (!string.IsNullOrWhiteSpace(input.Resource)) { where += " AND r.Resource = @Resource"; pars.Add(new SugarParameter("@Resource", input.Resource.Trim())); } if (!string.IsNullOrWhiteSpace(input.StartTimeFrom)) { where += " AND r.StartTime >= @StartTimeFrom"; pars.Add(new SugarParameter("@StartTimeFrom", input.StartTimeFrom.Trim())); } if (!string.IsNullOrWhiteSpace(input.EndTimeFrom)) { where += " AND r.EndTime >= @EndTimeFrom"; pars.Add(new SugarParameter("@EndTimeFrom", input.EndTimeFrom.Trim())); } if (!string.IsNullOrWhiteSpace(input.ResourceType)) { where += " AND r.ResourceType LIKE @ResourceType"; pars.Add(new SugarParameter("@ResourceType", $"%{input.ResourceType.Trim()}%")); } var orderClause = ResolveOrder(input.OrderBy, input.Sort); var offset = (input.Page - 1) * input.PageSize; var baseSql = $""" SELECT r.RecID AS Id, r.`Domain`, r.Site, r.BusinessID, r.ResourceType, r.Resource, r.StartTime, r.EndTime, r.Descr, r.IsRest, r.IsActive, r.IsConfirm, r.IsChanged, r.Ufld1, r.Ufld2, ROUND(CAST(IFNULL(r.Ufld1, 0) AS DECIMAL(20, 10)) * 60, 0) AS Time1, ROUND(CAST(IFNULL(r.Ufld2, 0) AS DECIMAL(20, 10)) * 60, 0) AS Time2, r.CreateUser, r.UpdateUser, r.CreateTime, r.UpdateTime FROM ResourceOccupancyTime r WHERE {where} """; var total = await _db.Ado.GetIntAsync($"SELECT COUNT(*) FROM ({baseSql}) AS t", pars); var list = await _db.Ado.SqlQueryAsync( $"SELECT * FROM ({baseSql}) AS t ORDER BY {orderClause} LIMIT {input.PageSize} OFFSET {offset}", pars); return new { total, page = input.Page, pageSize = input.PageSize, list }; } [DisplayName("产线加班详情")] [HttpGet("overtime/{id:long}")] public async Task GetDetail(long id) { var row = (await _db.Ado.SqlQueryAsync( """ SELECT RecID AS Id, `Domain`, Site, ResourceType, Resource, StartTime, EndTime, Descr, Ufld1, Ufld2, ROUND(CAST(IFNULL(Ufld1, 0) AS DECIMAL(20, 10)) * 60, 0) AS WorkMinutes, ROUND(CAST(IFNULL(Ufld2, 0) AS DECIMAL(20, 10)) * 60, 0) AS RestMinutes FROM ResourceOccupancyTime WHERE RecID = @Id """, new List { new("@Id", id) })).FirstOrDefault() ?? throw Oops.Oh("记录不存在"); return row; } [DisplayName("保存产线加班")] [HttpPost("overtime/save")] public async Task Save([FromBody] ResourceOvertimeSaveInput input) { var account = Truncate(_userManager.Account ?? "system", 8); var now = DateTime.Now; var domain = Truncate(input.Domain.Trim(), 8); var site = string.IsNullOrWhiteSpace(input.Site) ? null : Truncate(input.Site.Trim(), 8); var resource = Truncate(input.Resource.Trim(), 20); var resType = string.IsNullOrWhiteSpace(input.ResourceType) ? "" : Truncate(input.ResourceType.Trim(), 20); var descr = string.IsNullOrWhiteSpace(input.Descr) ? "-" : Truncate(input.Descr.Trim(), 60); var ufld1Hours = input.WorkMinutes.HasValue ? input.WorkMinutes.Value / 60m : (decimal?)null; var ufld2Hours = input.RestMinutes.HasValue ? input.RestMinutes.Value / 60m : (decimal?)null; if (input.Id is null or 0) { await _db.Ado.ExecuteCommandAsync( """ INSERT INTO ResourceOccupancyTime ( `Domain`, Site, BusinessID, ResourceType, Resource, StartTime, EndTime, Descr, IsRest, IsActive, IsConfirm, IsChanged, Ufld1, Ufld2, CreateUser, CreateTime, UpdateUser, UpdateTime ) VALUES ( @Domain, @Site, 0, @ResourceType, @Resource, @StartTime, @EndTime, @Descr, 0, 1, 0, 0, @Ufld1, @Ufld2, @User, @Now, @User, @Now ) """, new List { new("@Domain", domain), new("@Site", (object?)site ?? DBNull.Value), new("@ResourceType", resType), new("@Resource", resource), new("@StartTime", (object?)input.StartTime ?? DBNull.Value), new("@EndTime", (object?)input.EndTime ?? DBNull.Value), new("@Descr", descr), new("@Ufld1", (object?)ufld1Hours ?? DBNull.Value), new("@Ufld2", (object?)ufld2Hours ?? DBNull.Value), new("@User", account), new("@Now", now) }); var newId = await _db.Ado.GetIntAsync("SELECT LAST_INSERT_ID()"); return new { id = (long)newId, message = "新增成功" }; } var n = await _db.Ado.ExecuteCommandAsync( """ UPDATE ResourceOccupancyTime SET `Domain` = @Domain, Site = @Site, ResourceType = @ResourceType, Resource = @Resource, StartTime = @StartTime, EndTime = @EndTime, Descr = @Descr, Ufld1 = @Ufld1, Ufld2 = @Ufld2, UpdateUser = @User, UpdateTime = @Now WHERE RecID = @Id """, new List { new("@Id", input.Id!.Value), new("@Domain", domain), new("@Site", (object?)site ?? DBNull.Value), new("@ResourceType", resType), new("@Resource", resource), new("@StartTime", (object?)input.StartTime ?? DBNull.Value), new("@EndTime", (object?)input.EndTime ?? DBNull.Value), new("@Descr", descr), new("@Ufld1", (object?)ufld1Hours ?? DBNull.Value), new("@Ufld2", (object?)ufld2Hours ?? DBNull.Value), new("@User", account), new("@Now", now) }); if (n == 0) throw Oops.Oh("记录不存在或未变更"); return new { id = input.Id, message = "保存成功" }; } [DisplayName("删除产线加班")] [HttpPost("overtime/delete/{id:long}")] public async Task Delete(long id) { var n = await _db.Ado.ExecuteCommandAsync( "UPDATE ResourceOccupancyTime SET IsActive = 0, UpdateTime = @Now WHERE RecID = @Id", new List { new("@Id", id), new("@Now", DateTime.Now) }); if (n == 0) throw Oops.Oh("记录不存在"); return new { message = "已删除" }; } [DisplayName("生产线列表")] [HttpGet("overtime/lines")] public async Task GetLines([FromQuery] string? domain) { var pars = new List(); var where = "IsActive = 1"; if (!string.IsNullOrWhiteSpace(domain)) { where += " AND `Domain` = @Domain"; pars.Add(new SugarParameter("@Domain", domain.Trim())); } var rows = await _db.Ado.SqlQueryAsync( $"SELECT `Line` AS Value, `Line` AS Label, `Domain` AS Extra FROM LineMaster WHERE {where} ORDER BY `Line`", pars); return new { list = rows }; } private static string Truncate(string s, int maxLen) => s.Length <= maxLen ? s : s[..maxLen]; private static string ResolveOrder(string? orderBy, string? sort) { var desc = string.Equals(sort?.Trim(), "desc", StringComparison.OrdinalIgnoreCase); var dir = desc ? "DESC" : "ASC"; var key = orderBy?.Trim().ToLowerInvariant(); var col = key switch { "resource" => "t.Resource", "starttime" => "t.StartTime", "endtime" => "t.EndTime", "resourcetype" => "t.ResourceType", _ => "t.Id" }; return $"{col} {dir}"; } private sealed class ResourceOvertimeListRow { public long Id { get; set; } public string? Domain { get; set; } public string? Site { get; set; } public long BusinessID { get; set; } public string? ResourceType { get; set; } public string? Resource { get; set; } public DateTime? StartTime { get; set; } public DateTime? EndTime { get; set; } public string? Descr { get; set; } public bool IsRest { get; set; } public bool IsActive { get; set; } public bool IsConfirm { get; set; } public bool IsChanged { get; set; } public string? Ufld1 { get; set; } public string? Ufld2 { get; set; } public decimal? Time1 { get; set; } public decimal? Time2 { get; set; } public string? CreateUser { get; set; } public string? UpdateUser { get; set; } public DateTime? CreateTime { get; set; } public DateTime? UpdateTime { get; set; } } private sealed class ResourceOvertimeDetailRow { public long Id { get; set; } public string? Domain { get; set; } public string? Site { get; set; } public string? ResourceType { get; set; } public string? Resource { get; set; } public DateTime? StartTime { get; set; } public DateTime? EndTime { get; set; } public string? Descr { get; set; } public string? Ufld1 { get; set; } public string? Ufld2 { get; set; } public decimal? WorkMinutes { get; set; } public decimal? RestMinutes { get; set; } } private sealed class LineKvOvertime { public string? Value { get; set; } public string? Label { get; set; } public string? Extra { get; set; } } }