first commit
This commit is contained in:
@@ -0,0 +1,272 @@
|
||||
using F9WebAiTest.Application.Common;
|
||||
using F9WebAiTest.Application.Dtos;
|
||||
using F9WebAiTest.Application.Dtos.Prod;
|
||||
using F9WebAiTest.Application.Services.Notice;
|
||||
using F9WebAiTest.Domain.Common;
|
||||
using F9WebAiTest.Domain.Entities;
|
||||
using F9WebAiTest.Domain.Entities.Base;
|
||||
using F9WebAiTest.Domain.Entities.Notice;
|
||||
using F9WebAiTest.Domain.Entities.Prod;
|
||||
using SysDept = F9WebAiTest.Domain.Entities.SYS.Departments;
|
||||
using MediatR;
|
||||
|
||||
namespace F9WebAiTest.Application.Services.Prod;
|
||||
|
||||
public interface IHbjyService
|
||||
{
|
||||
Task<PageResult<HbjyListItemDto>> GetPagedAsync(int pageIndex, int pageSize, DateTime? date, long? deptId);
|
||||
Task<HbjyEditDto> GetEditAsync(long lsjlId);
|
||||
Task SaveAsync(HbjySaveDto dto);
|
||||
}
|
||||
|
||||
public class HbjyService : IHbjyService
|
||||
{
|
||||
private readonly IRepository<LsjlRecord> _lsjlRepo;
|
||||
private readonly IRepository<HbjyLsjl> _hbjyLsjlRepo;
|
||||
private readonly IRepository<HbjyRecord> _hbjyRecordRepo;
|
||||
private readonly IRepository<SysDept.Department> _deptRepo;
|
||||
private readonly IRepository<CraftZhuangKou> _zkRepo;
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public HbjyService(
|
||||
IRepository<LsjlRecord> lsjlRepo,
|
||||
IRepository<HbjyLsjl> hbjyLsjlRepo,
|
||||
IRepository<HbjyRecord> hbjyRecordRepo,
|
||||
IRepository<SysDept.Department> deptRepo,
|
||||
IRepository<CraftZhuangKou> zkRepo,
|
||||
IMediator mediator)
|
||||
{
|
||||
_lsjlRepo = lsjlRepo;
|
||||
_hbjyLsjlRepo = hbjyLsjlRepo;
|
||||
_hbjyRecordRepo = hbjyRecordRepo;
|
||||
_deptRepo = deptRepo;
|
||||
_zkRepo = zkRepo;
|
||||
_mediator = mediator;
|
||||
}
|
||||
|
||||
public async Task<PageResult<HbjyListItemDto>> GetPagedAsync(int pageIndex, int pageSize, DateTime? date, long? deptId)
|
||||
{
|
||||
var lsjls = await _lsjlRepo.GetAllAsync();
|
||||
var q = lsjls.AsQueryable();
|
||||
if (date.HasValue) q = q.Where(x => x.WorkDate.Date == date.Value.Date);
|
||||
if (deptId.HasValue) q = q.Where(x => x.DeptId == deptId.Value);
|
||||
|
||||
var depts = await _deptRepo.GetAllAsync();
|
||||
var deptDict = depts.ToDictionary(d => d.Id, d => d.Name);
|
||||
var zks = await _zkRepo.GetAllAsync();
|
||||
var zkDict = zks.ToDictionary(z => z.Id, z => z.ZhuangKouName);
|
||||
|
||||
var sums = (await _hbjyLsjlRepo.GetAllAsync()).ToDictionary(h => h.LsjlId);
|
||||
|
||||
var total = q.LongCount();
|
||||
var items = q.OrderByDescending(x => x.WorkDate).ThenByDescending(x => x.Id)
|
||||
.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
|
||||
|
||||
return new PageResult<HbjyListItemDto>
|
||||
{
|
||||
Items = items.Select(x =>
|
||||
{
|
||||
sums.TryGetValue(x.Id, out var sum);
|
||||
return new HbjyListItemDto
|
||||
{
|
||||
LsjlId = x.Id,
|
||||
WorkDate = x.WorkDate,
|
||||
DeptId = x.DeptId,
|
||||
DeptName = deptDict.GetValueOrDefault(x.DeptId, ""),
|
||||
Jizu = x.Jizu,
|
||||
Huishu = x.Huishu,
|
||||
Guige = x.Guige,
|
||||
ZhuangKouName = zkDict.GetValueOrDefault(x.GyzkId, ""),
|
||||
Yanse = x.Yanse,
|
||||
Isbanche = x.Isbanche,
|
||||
IsbancheName = x.Isbanche ? "半车" : "整车",
|
||||
LuosiTime = x.LuosiTime,
|
||||
Chetaishu = x.Chetaishu,
|
||||
ChetaiStart = x.ChetaiStart,
|
||||
ChetaiEnd = x.ChetaiEnd,
|
||||
YingluPianshu = x.Chetaishu * 20,
|
||||
IsChecked = sum != null,
|
||||
Pianshu = sum?.Pianshu ?? 0,
|
||||
Qingjie = sum?.Qingjie ?? 0,
|
||||
Jiejing = sum?.Jiejing ?? 0,
|
||||
Dengji = sum?.Dengji ?? ""
|
||||
};
|
||||
}).ToList(),
|
||||
Total = total, PageIndex = pageIndex, PageSize = pageSize
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<HbjyEditDto> GetEditAsync(long lsjlId)
|
||||
{
|
||||
var lsjl = await _lsjlRepo.GetByIdAsync(lsjlId);
|
||||
if (lsjl == null) throw new ServiceException("落丝记录不存在", 404);
|
||||
|
||||
var dept = await _deptRepo.GetByIdAsync(lsjl.DeptId);
|
||||
var zk = await _zkRepo.GetByIdAsync(lsjl.GyzkId);
|
||||
|
||||
var sum = await _hbjyLsjlRepo.FirstOrDefaultAsync(h => h.LsjlId == lsjlId);
|
||||
var records = await _hbjyRecordRepo.FindAsync(h => h.LsjlId == lsjlId);
|
||||
|
||||
List<HbjyRecordDto> items;
|
||||
if (records.Count > 0)
|
||||
{
|
||||
items = records.OrderBy(r => r.Chetaihao).Select(ToDto).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
items = new List<HbjyRecordDto>();
|
||||
for (int i = lsjl.ChetaiStart; i <= lsjl.ChetaiEnd; i++)
|
||||
{
|
||||
items.Add(new HbjyRecordDto { Chetaihao = i });
|
||||
}
|
||||
}
|
||||
|
||||
return new HbjyEditDto
|
||||
{
|
||||
LsjlId = lsjl.Id,
|
||||
DeptName = dept?.Name ?? "",
|
||||
Jizu = lsjl.Jizu,
|
||||
Huishu = lsjl.Huishu,
|
||||
ZhuangKouName = zk?.ZhuangKouName ?? "",
|
||||
Yanse = lsjl.Yanse,
|
||||
Isbanche = lsjl.Isbanche,
|
||||
IsbancheName = lsjl.Isbanche ? "半车" : "整车",
|
||||
Guige = lsjl.Guige,
|
||||
ChetaiStart = lsjl.ChetaiStart,
|
||||
ChetaiEnd = lsjl.ChetaiEnd,
|
||||
Chetaishu = lsjl.Chetaishu,
|
||||
YingluPianshu = lsjl.Chetaishu * 20,
|
||||
Dengji = sum?.Dengji ?? "",
|
||||
Records = items
|
||||
};
|
||||
}
|
||||
|
||||
public async Task SaveAsync(HbjySaveDto dto)
|
||||
{
|
||||
if (dto.LsjlId == 0) throw new ServiceException("落丝记录无效");
|
||||
var lsjl = await _lsjlRepo.GetByIdAsync(dto.LsjlId);
|
||||
if (lsjl == null) throw new ServiceException("落丝记录不存在", 404);
|
||||
|
||||
if (dto.Records == null || dto.Records.Count == 0) throw new ServiceException("请先录入检验数据");
|
||||
|
||||
var invalid = dto.Records.Where(i => i.Chetaihao < lsjl.ChetaiStart || i.Chetaihao > lsjl.ChetaiEnd).ToList();
|
||||
if (invalid.Count > 0) throw new ServiceException($"车台号超出范围 [{lsjl.ChetaiStart}-{lsjl.ChetaiEnd}]");
|
||||
|
||||
// 计算每车台片数
|
||||
foreach (var item in dto.Records)
|
||||
{
|
||||
item.Pianshu = CalcPianshu(item);
|
||||
}
|
||||
|
||||
var totalPianshu = dto.Records.Sum(x => x.Pianshu);
|
||||
if (totalPianshu == 0) throw new ServiceException("请至少录入一个洁净等级片数");
|
||||
|
||||
// 物理删除旧明细
|
||||
var oldRecords = await _hbjyRecordRepo.FindAsync(h => h.LsjlId == dto.LsjlId);
|
||||
await _hbjyRecordRepo.HardDeleteRangeAsync(oldRecords);
|
||||
|
||||
foreach (var item in dto.Records)
|
||||
{
|
||||
await _hbjyRecordRepo.AddAsync(new HbjyRecord
|
||||
{
|
||||
LsjlId = dto.LsjlId,
|
||||
Chetaihao = item.Chetaihao,
|
||||
YdYidu = item.YdYidu, YdErdu = item.YdErdu, YdSandu = item.YdSandu,
|
||||
Jj100 = item.Jj100, Jj95 = item.Jj95, Jj90 = item.Jj90, Jj85 = item.Jj85,
|
||||
Jj80 = item.Jj80, Jj75 = item.Jj75, Jj70 = item.Jj70, Jj65 = item.Jj65, Jj60 = item.Jj60,
|
||||
QjZycdTdc = item.QjZycdTdc,
|
||||
QjCycdFs = item.QjCycdFs, QjCycdDc = item.QjCycdDc, QjCycdNfc = item.QjCycdNfc,
|
||||
QjCycdDcj = item.QjCycdDcj, QjCycdZlx = item.QjCycdZlx,
|
||||
QjPtcdXc = item.QjPtcdXc, QjPtcdCj = item.QjPtcdCj, QjPtcdLx = item.QjPtcdLx, QjPtcdHjls = item.QjPtcdHjls,
|
||||
Pianshu = item.Pianshu
|
||||
});
|
||||
}
|
||||
|
||||
// 汇总
|
||||
var sum = CalcSummary(dto.Records);
|
||||
var hbjyLsjl = await _hbjyLsjlRepo.FirstOrDefaultAsync(h => h.LsjlId == dto.LsjlId);
|
||||
if (hbjyLsjl == null)
|
||||
{
|
||||
hbjyLsjl = new HbjyLsjl { LsjlId = dto.LsjlId };
|
||||
await _hbjyLsjlRepo.AddAsync(hbjyLsjl);
|
||||
}
|
||||
hbjyLsjl.Pianshu = sum.Pianshu;
|
||||
hbjyLsjl.Qingjie = sum.Qingjie;
|
||||
hbjyLsjl.Jiejing = sum.Jiejing;
|
||||
hbjyLsjl.YdErdu = sum.YdErdu;
|
||||
hbjyLsjl.YdSandu = sum.YdSandu;
|
||||
hbjyLsjl.Dengji = dto.Dengji ?? "";
|
||||
|
||||
await _hbjyLsjlRepo.UpdateAsync(hbjyLsjl);
|
||||
|
||||
// 业务系统触发消息:发现特大糙时,通过 MediatR 创建"发现特大糙"消息,由 BackgroundService 投递
|
||||
var tdcCount = dto.Records.Sum(x => x.QjZycdTdc);
|
||||
if (tdcCount > 0)
|
||||
{
|
||||
await _mediator.Send(new CreateNotificationCommand
|
||||
{
|
||||
NotificationTypeCode = "TdcFound",
|
||||
BusinessType = "Hbjy",
|
||||
BusinessId = dto.LsjlId.ToString(),
|
||||
Title = $"发现特大糙:{lsjl.Guige} 规格",
|
||||
Content = $"落丝记录(机组 {lsjl.Jizu}、回数 {lsjl.Huishu})黑板检验发现 {tdcCount} 个特大糙,请关注。",
|
||||
Priority = NotificationPriority.Urgent,
|
||||
CreatorId = 0,
|
||||
CreatorName = "系统"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 计算逻辑 ====================
|
||||
|
||||
private static HbjyRecordDto ToDto(HbjyRecord r) => new()
|
||||
{
|
||||
Chetaihao = r.Chetaihao,
|
||||
YdYidu = r.YdYidu, YdErdu = r.YdErdu, YdSandu = r.YdSandu,
|
||||
Jj100 = r.Jj100, Jj95 = r.Jj95, Jj90 = r.Jj90, Jj85 = r.Jj85,
|
||||
Jj80 = r.Jj80, Jj75 = r.Jj75, Jj70 = r.Jj70, Jj65 = r.Jj65, Jj60 = r.Jj60,
|
||||
QjZycdTdc = r.QjZycdTdc,
|
||||
QjCycdFs = r.QjCycdFs, QjCycdDc = r.QjCycdDc, QjCycdNfc = r.QjCycdNfc,
|
||||
QjCycdDcj = r.QjCycdDcj, QjCycdZlx = r.QjCycdZlx,
|
||||
QjPtcdXc = r.QjPtcdXc, QjPtcdCj = r.QjPtcdCj, QjPtcdLx = r.QjPtcdLx, QjPtcdHjls = r.QjPtcdHjls,
|
||||
Pianshu = r.Pianshu
|
||||
};
|
||||
|
||||
// 片数 = 洁净等级各列之和
|
||||
private static int CalcPianshu(HbjyRecordDto item)
|
||||
=> item.Jj100 + item.Jj95 + item.Jj90 + item.Jj85 + item.Jj80 + item.Jj75 + item.Jj70 + item.Jj65 + item.Jj60;
|
||||
|
||||
// 清洁扣分(特大类1分/个,大类0.4分/个,小类0.1分/个)
|
||||
private static decimal CalcQingjieDeduction(HbjyRecordDto item)
|
||||
{
|
||||
decimal teda = item.QjZycdTdc * 1.0M;
|
||||
decimal dalei = (item.QjCycdFs + item.QjCycdDc + item.QjCycdNfc + item.QjCycdDcj + item.QjCycdZlx) * 0.4M;
|
||||
decimal xiaolei = (item.QjPtcdXc + item.QjPtcdCj + item.QjPtcdLx + item.QjPtcdHjls) * 0.1M;
|
||||
return teda + dalei + xiaolei;
|
||||
}
|
||||
|
||||
private static HbjyLsjl CalcSummary(List<HbjyRecordDto> items)
|
||||
{
|
||||
int pianshu = items.Sum(x => x.Pianshu);
|
||||
|
||||
// 清洁分 = (片数 - 总扣分) * 100 / 片数
|
||||
decimal totalDeduction = items.Sum(CalcQingjieDeduction);
|
||||
double qingjie = pianshu == 0 ? 0 : Math.Round((double)((pianshu - totalDeduction) * 100 / pianshu), 2, MidpointRounding.AwayFromZero);
|
||||
|
||||
// 洁净分 = (片数*100 - 扣分总和*5) / 片数
|
||||
int jj95 = items.Sum(x => x.Jj95), jj90 = items.Sum(x => x.Jj90), jj85 = items.Sum(x => x.Jj85);
|
||||
int jj80 = items.Sum(x => x.Jj80), jj75 = items.Sum(x => x.Jj75), jj70 = items.Sum(x => x.Jj70);
|
||||
int jj65 = items.Sum(x => x.Jj65), jj60 = items.Sum(x => x.Jj60);
|
||||
decimal totalJiejingDeduction = 1 * jj95 + 2 * jj90 + 3 * jj85 + 4 * jj80 + 5 * jj75 + 6 * jj70 + 7 * jj65 + 8 * jj60;
|
||||
double jiejing = pianshu == 0 ? 0 : Math.Round((double)((pianshu * 100 - totalJiejingDeduction * 5) / (decimal)pianshu), 2, MidpointRounding.AwayFromZero);
|
||||
|
||||
return new HbjyLsjl
|
||||
{
|
||||
Pianshu = pianshu,
|
||||
Qingjie = qingjie,
|
||||
Jiejing = jiejing,
|
||||
YdErdu = items.Sum(x => x.YdErdu),
|
||||
YdSandu = items.Sum(x => x.YdSandu)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
using F9WebAiTest.Application.Common;
|
||||
using F9WebAiTest.Application.Dtos;
|
||||
using F9WebAiTest.Application.Dtos.Prod;
|
||||
using F9WebAiTest.Domain.Common;
|
||||
using F9WebAiTest.Domain.Entities;
|
||||
using F9WebAiTest.Domain.Entities.Base;
|
||||
using F9WebAiTest.Domain.Entities.Prod;
|
||||
using SysDept = F9WebAiTest.Domain.Entities.SYS.Departments;
|
||||
|
||||
namespace F9WebAiTest.Application.Services.Prod;
|
||||
|
||||
public interface ILsjlService
|
||||
{
|
||||
Task<PageResult<LsjlDto>> GetPagedAsync(int pageIndex, int pageSize, DateTime? date, long? deptId, string keyword);
|
||||
Task<LsjlDto> CreateAsync(LsjlCreateDto dto);
|
||||
Task<LsjlDto> UpdateAsync(long id, LsjlCreateDto dto);
|
||||
Task DeleteAsync(long id);
|
||||
}
|
||||
|
||||
public class LsjlService : ILsjlService
|
||||
{
|
||||
private readonly IRepository<LsjlRecord> _repo;
|
||||
private readonly IRepository<SysDept.Department> _deptRepo;
|
||||
private readonly IRepository<CraftZhuangKou> _zkRepo;
|
||||
|
||||
public LsjlService(IRepository<LsjlRecord> repo, IRepository<SysDept.Department> deptRepo, IRepository<CraftZhuangKou> zkRepo)
|
||||
{
|
||||
_repo = repo;
|
||||
_deptRepo = deptRepo;
|
||||
_zkRepo = zkRepo;
|
||||
}
|
||||
|
||||
public async Task<PageResult<LsjlDto>> GetPagedAsync(int pageIndex, int pageSize, DateTime? date, long? deptId, string keyword)
|
||||
{
|
||||
var all = await _repo.GetAllAsync();
|
||||
var q = all.AsQueryable();
|
||||
if (date.HasValue)
|
||||
q = q.Where(x => x.WorkDate.Date == date.Value.Date);
|
||||
if (deptId.HasValue)
|
||||
q = q.Where(x => x.DeptId == deptId.Value);
|
||||
|
||||
var depts = await _deptRepo.GetAllAsync();
|
||||
var deptDict = depts.ToDictionary(d => d.Id, d => d.Name);
|
||||
var zks = await _zkRepo.GetAllAsync();
|
||||
var zkDict = zks.ToDictionary(z => z.Id, z => z.ZhuangKouName);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(keyword))
|
||||
{
|
||||
var kwDeptIds = depts.Where(d => d.Name.Contains(keyword)).Select(d => d.Id).ToHashSet();
|
||||
var kwZkIds = zks.Where(z => z.ZhuangKouName.Contains(keyword)).Select(z => z.Id).ToHashSet();
|
||||
q = q.Where(x => kwDeptIds.Contains(x.DeptId) || kwZkIds.Contains(x.GyzkId) || x.Guige.Contains(keyword));
|
||||
}
|
||||
|
||||
var total = q.LongCount();
|
||||
var items = q.OrderByDescending(x => x.WorkDate).ThenByDescending(x => x.Id)
|
||||
.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
|
||||
|
||||
return new PageResult<LsjlDto>
|
||||
{
|
||||
Items = items.Select(x => ToDto(x, deptDict, zkDict)).ToList(),
|
||||
Total = total, PageIndex = pageIndex, PageSize = pageSize
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<LsjlDto> CreateAsync(LsjlCreateDto dto)
|
||||
{
|
||||
if (dto.DeptId == 0) throw new ServiceException("请选择车间");
|
||||
if (dto.GyzkId == 0) throw new ServiceException("请选择庄口");
|
||||
var e = Map(new LsjlRecord(), dto);
|
||||
await _repo.AddAsync(e);
|
||||
await _repo.SaveChangesAsync();
|
||||
return await ToDto(e);
|
||||
}
|
||||
|
||||
public async Task<LsjlDto> UpdateAsync(long id, LsjlCreateDto dto)
|
||||
{
|
||||
var e = await _repo.GetByIdAsync(id);
|
||||
if (e == null) throw new ServiceException("记录不存在", 404);
|
||||
Map(e, dto);
|
||||
await _repo.UpdateAsync(e);
|
||||
await _repo.SaveChangesAsync();
|
||||
return await ToDto(e);
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(long id)
|
||||
{
|
||||
var e = await _repo.GetByIdAsync(id);
|
||||
if (e == null) throw new ServiceException("记录不存在", 404);
|
||||
await _repo.DeleteAsync(e);
|
||||
await _repo.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private static LsjlRecord Map(LsjlRecord e, LsjlCreateDto d)
|
||||
{
|
||||
e.WorkDate = d.WorkDate; e.DeptId = d.DeptId; e.GyzkId = d.GyzkId; e.Jizu = d.Jizu;
|
||||
e.LuosiTime = d.LuosiTime; e.Yanse = d.Yanse; e.Huishu = d.Huishu;
|
||||
e.ChetaiStart = d.ChetaiStart; e.ChetaiEnd = d.ChetaiEnd; e.Chetaishu = d.Chetaishu;
|
||||
e.Isbanche = d.Isbanche; e.Hours = d.Hours; e.Guige = d.Guige; e.Chesu = d.Chesu;
|
||||
return e;
|
||||
}
|
||||
|
||||
private async Task<LsjlDto> ToDto(LsjlRecord e)
|
||||
{
|
||||
var dept = await _deptRepo.GetByIdAsync(e.DeptId);
|
||||
var zk = await _zkRepo.GetByIdAsync(e.GyzkId);
|
||||
return new LsjlDto
|
||||
{
|
||||
Id = e.Id, WorkDate = e.WorkDate, DeptId = e.DeptId, DeptName = dept?.Name ?? "",
|
||||
GyzkId = e.GyzkId, ZhuangKouName = zk?.ZhuangKouName ?? "", Jizu = e.Jizu,
|
||||
LuosiTime = e.LuosiTime, Yanse = e.Yanse, Huishu = e.Huishu,
|
||||
ChetaiStart = e.ChetaiStart, ChetaiEnd = e.ChetaiEnd, Chetaishu = e.Chetaishu,
|
||||
Isbanche = e.Isbanche, IsbancheName = e.Isbanche ? "半车" : "整车",
|
||||
Hours = e.Hours, Guige = e.Guige, Chesu = e.Chesu
|
||||
};
|
||||
}
|
||||
|
||||
private static LsjlDto ToDto(LsjlRecord e, Dictionary<long, string> deptDict, Dictionary<long, string> zkDict)
|
||||
{
|
||||
return new LsjlDto
|
||||
{
|
||||
Id = e.Id, WorkDate = e.WorkDate, DeptId = e.DeptId, DeptName = deptDict.GetValueOrDefault(e.DeptId, ""),
|
||||
GyzkId = e.GyzkId, ZhuangKouName = zkDict.GetValueOrDefault(e.GyzkId, ""), Jizu = e.Jizu,
|
||||
LuosiTime = e.LuosiTime, Yanse = e.Yanse, Huishu = e.Huishu,
|
||||
ChetaiStart = e.ChetaiStart, ChetaiEnd = e.ChetaiEnd, Chetaishu = e.Chetaishu,
|
||||
Isbanche = e.Isbanche, IsbancheName = e.Isbanche ? "半车" : "整车",
|
||||
Hours = e.Hours, Guige = e.Guige, Chesu = e.Chesu
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,344 @@
|
||||
using F9WebAiTest.Application.Common;
|
||||
using F9WebAiTest.Application.Dtos;
|
||||
using F9WebAiTest.Application.Dtos.Base;
|
||||
using F9WebAiTest.Application.Dtos.Prod;
|
||||
using F9WebAiTest.Application.Services.Notice;
|
||||
using F9WebAiTest.Domain.Common;
|
||||
using F9WebAiTest.Domain.Entities;
|
||||
using F9WebAiTest.Domain.Entities.Base;
|
||||
using F9WebAiTest.Domain.Entities.Notice;
|
||||
using F9WebAiTest.Domain.Entities.Prod;
|
||||
using SysDept = F9WebAiTest.Domain.Entities.SYS.Departments;
|
||||
using MediatR;
|
||||
|
||||
namespace F9WebAiTest.Application.Services.Prod;
|
||||
|
||||
public interface IXdjyService
|
||||
{
|
||||
Task<PageResult<XdjyListItemDto>> GetPagedAsync(int pageIndex, int pageSize, DateTime? date, long? deptId);
|
||||
Task<XdjyEditDto> GetEditAsync(long lsjlId);
|
||||
Task SaveAsync(XdjySaveDto dto);
|
||||
}
|
||||
|
||||
public class XdjyService : IXdjyService
|
||||
{
|
||||
private readonly IRepository<LsjlRecord> _lsjlRepo;
|
||||
private readonly IRepository<XdjyLsjl> _xdjyLsjlRepo;
|
||||
private readonly IRepository<XdjyRecord> _xdjyRecordRepo;
|
||||
private readonly IRepository<ShengsiGuige> _guigeRepo;
|
||||
private readonly IRepository<SysDept.Department> _deptRepo;
|
||||
private readonly IRepository<CraftZhuangKou> _zkRepo;
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public XdjyService(
|
||||
IRepository<LsjlRecord> lsjlRepo,
|
||||
IRepository<XdjyLsjl> xdjyLsjlRepo,
|
||||
IRepository<XdjyRecord> xdjyRecordRepo,
|
||||
IRepository<ShengsiGuige> guigeRepo,
|
||||
IRepository<SysDept.Department> deptRepo,
|
||||
IRepository<CraftZhuangKou> zkRepo,
|
||||
IMediator mediator)
|
||||
{
|
||||
_lsjlRepo = lsjlRepo;
|
||||
_xdjyLsjlRepo = xdjyLsjlRepo;
|
||||
_xdjyRecordRepo = xdjyRecordRepo;
|
||||
_guigeRepo = guigeRepo;
|
||||
_deptRepo = deptRepo;
|
||||
_zkRepo = zkRepo;
|
||||
_mediator = mediator;
|
||||
}
|
||||
|
||||
public async Task<PageResult<XdjyListItemDto>> GetPagedAsync(int pageIndex, int pageSize, DateTime? date, long? deptId)
|
||||
{
|
||||
var lsjls = await _lsjlRepo.GetAllAsync();
|
||||
var q = lsjls.AsQueryable();
|
||||
if (date.HasValue) q = q.Where(x => x.WorkDate.Date == date.Value.Date);
|
||||
if (deptId.HasValue) q = q.Where(x => x.DeptId == deptId.Value);
|
||||
|
||||
var depts = await _deptRepo.GetAllAsync();
|
||||
var deptDict = depts.ToDictionary(d => d.Id, d => d.Name);
|
||||
var zks = await _zkRepo.GetAllAsync();
|
||||
var zkDict = zks.ToDictionary(z => z.Id, z => z.ZhuangKouName);
|
||||
|
||||
var sums = (await _xdjyLsjlRepo.GetAllAsync()).ToDictionary(x => x.LsjlId);
|
||||
|
||||
var total = q.LongCount();
|
||||
var items = q.OrderByDescending(x => x.WorkDate).ThenByDescending(x => x.Id)
|
||||
.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
|
||||
|
||||
return new PageResult<XdjyListItemDto>
|
||||
{
|
||||
Items = items.Select(x =>
|
||||
{
|
||||
sums.TryGetValue(x.Id, out var sum);
|
||||
var yingcheng = x.Chetaishu * (x.Isbanche ? 2 : 4);
|
||||
return new XdjyListItemDto
|
||||
{
|
||||
LsjlId = x.Id,
|
||||
WorkDate = x.WorkDate,
|
||||
DeptId = x.DeptId,
|
||||
DeptName = deptDict.GetValueOrDefault(x.DeptId, ""),
|
||||
Jizu = x.Jizu,
|
||||
Huishu = x.Huishu,
|
||||
Guige = x.Guige,
|
||||
ZhuangKouName = zkDict.GetValueOrDefault(x.GyzkId, ""),
|
||||
Yanse = x.Yanse,
|
||||
Isbanche = x.Isbanche,
|
||||
IsbancheName = x.Isbanche ? "半车" : "整车",
|
||||
Chetaishu = x.Chetaishu,
|
||||
ChetaiStart = x.ChetaiStart,
|
||||
ChetaiEnd = x.ChetaiEnd,
|
||||
LuosiTime = x.LuosiTime,
|
||||
Yingchengnum = yingcheng,
|
||||
IsChecked = sum != null,
|
||||
Jiaoshu = sum?.Jiaoshu ?? 0,
|
||||
Avgxiandu = sum?.Avgxiandu ?? 0,
|
||||
Junfangcha = sum?.Junfangcha ?? 0,
|
||||
Zongcha = sum?.Zongcha ?? 0,
|
||||
FanxianLv = sum?.FanxianLv ?? 0,
|
||||
FanweiNum = sum?.FanweiNum ?? 0,
|
||||
XiaoyeNum = sum?.XiaoyeNum ?? 0,
|
||||
DayeNum = sum?.DayeNum ?? 0,
|
||||
TeyeNum = sum?.TeyeNum ?? 0,
|
||||
CenterNum = sum?.CenterNum ?? 0,
|
||||
Dengji = sum?.Dengji ?? ""
|
||||
};
|
||||
}).ToList(),
|
||||
Total = total, PageIndex = pageIndex, PageSize = pageSize
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<XdjyEditDto> GetEditAsync(long lsjlId)
|
||||
{
|
||||
var lsjl = await _lsjlRepo.GetByIdAsync(lsjlId);
|
||||
if (lsjl == null) throw new ServiceException("落丝记录不存在", 404);
|
||||
|
||||
var dept = await _deptRepo.GetByIdAsync(lsjl.DeptId);
|
||||
var zk = await _zkRepo.GetByIdAsync(lsjl.GyzkId);
|
||||
var gg = await _guigeRepo.FirstOrDefaultAsync(g => g.Guige == lsjl.Guige);
|
||||
if (gg == null) throw new ServiceException($"未找到规格「{lsjl.Guige}」的生丝规格配置,请先维护生丝规格");
|
||||
|
||||
var sum = await _xdjyLsjlRepo.FirstOrDefaultAsync(x => x.LsjlId == lsjlId);
|
||||
var records = await _xdjyRecordRepo.FindAsync(x => x.LsjlId == lsjlId);
|
||||
|
||||
List<XdjyRecordDto> items;
|
||||
if (records.Count > 0)
|
||||
{
|
||||
// 已检验:加载已有明细
|
||||
items = records.OrderBy(r => r.Chetaihao).Select(ToDto).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 首次检验:按车台范围初始化空明细
|
||||
items = new List<XdjyRecordDto>();
|
||||
for (int i = lsjl.ChetaiStart; i <= lsjl.ChetaiEnd; i++)
|
||||
{
|
||||
items.Add(new XdjyRecordDto { Chetaihao = i });
|
||||
}
|
||||
}
|
||||
|
||||
return new XdjyEditDto
|
||||
{
|
||||
LsjlId = lsjl.Id,
|
||||
DeptName = dept?.Name ?? "",
|
||||
Jizu = lsjl.Jizu,
|
||||
Huishu = lsjl.Huishu,
|
||||
ZhuangKouName = zk?.ZhuangKouName ?? "",
|
||||
Yanse = lsjl.Yanse,
|
||||
Isbanche = lsjl.Isbanche,
|
||||
IsbancheName = lsjl.Isbanche ? "半车" : "整车",
|
||||
Guige = lsjl.Guige,
|
||||
ChetaiStart = lsjl.ChetaiStart,
|
||||
ChetaiEnd = lsjl.ChetaiEnd,
|
||||
Chetaishu = lsjl.Chetaishu,
|
||||
Yingchengnum = lsjl.Chetaishu * (lsjl.Isbanche ? 2 : 4),
|
||||
Dengji = sum?.Dengji ?? "",
|
||||
GuigeConfig = new ShengsiGuigeDto
|
||||
{
|
||||
Id = gg.Id, Guige = gg.Guige,
|
||||
CenterrangeLolimit = gg.CenterrangeLolimit, CenterrangeUplimit = gg.CenterrangeUplimit,
|
||||
CenterXiandu = gg.CenterXiandu,
|
||||
XiaoyeLolimit = gg.XiaoyeLolimit, XiaoyeUplimit = gg.XiaoyeUplimit,
|
||||
DayeLolimit = gg.DayeLolimit, DayeUplimit = gg.DayeUplimit,
|
||||
TeyeLolimit = gg.TeyeLolimit, TeyeUplimit = gg.TeyeUplimit
|
||||
},
|
||||
Xdjy = items
|
||||
};
|
||||
}
|
||||
|
||||
public async Task SaveAsync(XdjySaveDto dto)
|
||||
{
|
||||
if (dto.LsjlId == 0) throw new ServiceException("落丝记录无效");
|
||||
var lsjl = await _lsjlRepo.GetByIdAsync(dto.LsjlId);
|
||||
if (lsjl == null) throw new ServiceException("落丝记录不存在", 404);
|
||||
|
||||
var gg = await _guigeRepo.FirstOrDefaultAsync(g => g.Guige == lsjl.Guige);
|
||||
if (gg == null) throw new ServiceException($"未找到规格「{lsjl.Guige}」的生丝规格配置");
|
||||
|
||||
if (dto.Items == null || dto.Items.Count == 0) throw new ServiceException("请先录入纤度数据");
|
||||
|
||||
// 校验车台号在范围内
|
||||
var invalid = dto.Items.Where(i => i.Chetaihao < lsjl.ChetaiStart || i.Chetaihao > lsjl.ChetaiEnd).ToList();
|
||||
if (invalid.Count > 0) throw new ServiceException($"车台号超出范围 [{lsjl.ChetaiStart}-{lsjl.ChetaiEnd}]");
|
||||
|
||||
// 计算每个车台指标
|
||||
var calcItems = dto.Items.Select(i =>
|
||||
{
|
||||
var r = new XdjyRecordDto { Chetaihao = i.Chetaihao, Xd1 = i.Xd1, Xd2 = i.Xd2, Xd3 = i.Xd3, Xd4 = i.Xd4 };
|
||||
CalcChetai(r, gg);
|
||||
return r;
|
||||
}).ToList();
|
||||
|
||||
var jiaoshu = calcItems.Sum(x => x.XdNum);
|
||||
if (jiaoshu == 0) throw new ServiceException("请至少录入一个纤度值");
|
||||
|
||||
// 物理删除旧明细(避免软删除残留导致唯一索引冲突)
|
||||
var oldRecords = await _xdjyRecordRepo.FindAsync(x => x.LsjlId == dto.LsjlId);
|
||||
await _xdjyRecordRepo.HardDeleteRangeAsync(oldRecords);
|
||||
|
||||
foreach (var item in calcItems)
|
||||
{
|
||||
await _xdjyRecordRepo.AddAsync(new XdjyRecord
|
||||
{
|
||||
LsjlId = dto.LsjlId,
|
||||
Chetaihao = item.Chetaihao,
|
||||
Xd1 = item.Xd1, Xd2 = item.Xd2, Xd3 = item.Xd3, Xd4 = item.Xd4,
|
||||
XdNum = item.XdNum, XdAvg = item.XdAvg,
|
||||
FanweiNum = item.FanweiNum, XiaoyeNum = item.XiaoyeNum,
|
||||
DayeNum = item.DayeNum, TeyeNum = item.TeyeNum, CenterNum = item.CenterNum
|
||||
});
|
||||
}
|
||||
|
||||
// 汇总指标
|
||||
var sum = CalcSummary(calcItems, gg);
|
||||
var xdjyLsjl = await _xdjyLsjlRepo.FirstOrDefaultAsync(x => x.LsjlId == dto.LsjlId);
|
||||
if (xdjyLsjl == null)
|
||||
{
|
||||
xdjyLsjl = new XdjyLsjl { LsjlId = dto.LsjlId };
|
||||
await _xdjyLsjlRepo.AddAsync(xdjyLsjl);
|
||||
}
|
||||
xdjyLsjl.Jiaoshu = sum.Jiaoshu;
|
||||
xdjyLsjl.Avgxiandu = sum.Avgxiandu;
|
||||
xdjyLsjl.Junfangcha = sum.Junfangcha;
|
||||
xdjyLsjl.Zongcha = sum.Zongcha;
|
||||
xdjyLsjl.FanxianLv = sum.FanxianLv;
|
||||
xdjyLsjl.FanweiNum = sum.FanweiNum;
|
||||
xdjyLsjl.XiaoyeNum = sum.XiaoyeNum;
|
||||
xdjyLsjl.DayeNum = sum.DayeNum;
|
||||
xdjyLsjl.TeyeNum = sum.TeyeNum;
|
||||
xdjyLsjl.CenterNum = sum.CenterNum;
|
||||
xdjyLsjl.Dengji = dto.Dengji ?? "";
|
||||
|
||||
await _xdjyLsjlRepo.UpdateAsync(xdjyLsjl);
|
||||
|
||||
// 业务系统触发消息:发现特野时,通过 MediatR 创建"发现特野"消息,由 BackgroundService 投递
|
||||
if (sum.TeyeNum > 0)
|
||||
{
|
||||
await _mediator.Send(new CreateNotificationCommand
|
||||
{
|
||||
NotificationTypeCode = "TeyeFound",
|
||||
BusinessType = "Xdjy",
|
||||
BusinessId = dto.LsjlId.ToString(),
|
||||
Title = $"发现特野:{lsjl.Guige} 规格",
|
||||
Content = $"落丝记录(机组 {lsjl.Jizu}、回数 {lsjl.Huishu})纤度检验发现 {sum.TeyeNum} 个特野值,请关注。",
|
||||
Priority = NotificationPriority.Urgent,
|
||||
CreatorId = 0,
|
||||
CreatorName = "系统"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 计算逻辑(与 WinForms 版保持一致) ====================
|
||||
|
||||
private static XdjyRecordDto ToDto(XdjyRecord r) => new()
|
||||
{
|
||||
Chetaihao = r.Chetaihao,
|
||||
Xd1 = r.Xd1, Xd2 = r.Xd2, Xd3 = r.Xd3, Xd4 = r.Xd4,
|
||||
XdNum = r.XdNum, XdAvg = r.XdAvg,
|
||||
FanweiNum = r.FanweiNum, XiaoyeNum = r.XiaoyeNum,
|
||||
DayeNum = r.DayeNum, TeyeNum = r.TeyeNum, CenterNum = r.CenterNum
|
||||
};
|
||||
|
||||
private static void CalcChetai(XdjyRecordDto item, ShengsiGuige gg)
|
||||
{
|
||||
item.XdNum = (item.Xd1 > 0 ? 1 : 0) + (item.Xd2 > 0 ? 1 : 0) + (item.Xd3 > 0 ? 1 : 0) + (item.Xd4 > 0 ? 1 : 0);
|
||||
if (item.XdNum > 0)
|
||||
{
|
||||
item.XdAvg = Math.Round((item.Xd1 + item.Xd2 + item.Xd3 + item.Xd4) / item.XdNum, 2, MidpointRounding.AwayFromZero);
|
||||
item.FanweiNum = Fanwei(item.Xd1, gg) + Fanwei(item.Xd2, gg) + Fanwei(item.Xd3, gg) + Fanwei(item.Xd4, gg);
|
||||
item.XiaoyeNum = Xiaoye(item.Xd1, gg) + Xiaoye(item.Xd2, gg) + Xiaoye(item.Xd3, gg) + Xiaoye(item.Xd4, gg);
|
||||
item.DayeNum = Daye(item.Xd1, gg) + Daye(item.Xd2, gg) + Daye(item.Xd3, gg) + Daye(item.Xd4, gg);
|
||||
item.TeyeNum = Teye(item.Xd1, gg) + Teye(item.Xd2, gg) + Teye(item.Xd3, gg) + Teye(item.Xd4, gg);
|
||||
item.CenterNum = Center(item.Xd1, gg) + Center(item.Xd2, gg) + Center(item.Xd3, gg) + Center(item.Xd4, gg);
|
||||
}
|
||||
else
|
||||
{
|
||||
item.XdAvg = 0; item.FanweiNum = 0; item.XiaoyeNum = 0; item.DayeNum = 0; item.TeyeNum = 0; item.CenterNum = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static XdjyLsjl CalcSummary(List<XdjyRecordDto> items, ShengsiGuige gg)
|
||||
{
|
||||
var jiaoshu = items.Sum(x => x.XdNum);
|
||||
var avgXiandu = jiaoshu == 0 ? 0 : items.Sum(x => x.Xd1 + x.Xd2 + x.Xd3 + x.Xd4) / jiaoshu;
|
||||
|
||||
// 均方差:sqrt(Σ(xi-avg)² / n)
|
||||
double junfangcha = 0;
|
||||
if (jiaoshu > 0)
|
||||
{
|
||||
double sumDiff = 0;
|
||||
foreach (var it in items)
|
||||
{
|
||||
foreach (var xd in new[] { it.Xd1, it.Xd2, it.Xd3, it.Xd4 })
|
||||
{
|
||||
if (xd > 0) { var d = xd - avgXiandu; sumDiff += d * d; }
|
||||
}
|
||||
}
|
||||
junfangcha = Math.Round(Math.Sqrt(Math.Round(sumDiff / jiaoshu, 2)), 2, MidpointRounding.AwayFromZero);
|
||||
}
|
||||
|
||||
// 总差:抽样绞数 = ceil(n*0.02),最细/最粗各取,与总平均比较取最大差值
|
||||
double zongcha = 0;
|
||||
if (jiaoshu > 0)
|
||||
{
|
||||
var xds = new List<double>();
|
||||
foreach (var it in items)
|
||||
{
|
||||
if (it.Xd1 > 0) xds.Add(it.Xd1);
|
||||
if (it.Xd2 > 0) xds.Add(it.Xd2);
|
||||
if (it.Xd3 > 0) xds.Add(it.Xd3);
|
||||
if (it.Xd4 > 0) xds.Add(it.Xd4);
|
||||
}
|
||||
xds.Sort();
|
||||
var chouyang = Math.Max(1, (int)Math.Ceiling(xds.Count * 0.02));
|
||||
var minAvg = xds.Take(chouyang).Average();
|
||||
var maxAvg = xds.Skip(xds.Count - chouyang).Take(chouyang).Average();
|
||||
var avgR = Math.Round(avgXiandu, 2, MidpointRounding.AwayFromZero);
|
||||
zongcha = Math.Round(Math.Max(Math.Abs(minAvg - avgR), Math.Abs(maxAvg - avgR)), 2, MidpointRounding.AwayFromZero);
|
||||
}
|
||||
|
||||
var fanweiNum = items.Sum(x => x.FanweiNum);
|
||||
var fanxianLv = jiaoshu == 0 ? 0 : Math.Round((double)fanweiNum * 100 / jiaoshu, 0, MidpointRounding.AwayFromZero);
|
||||
|
||||
return new XdjyLsjl
|
||||
{
|
||||
Jiaoshu = jiaoshu,
|
||||
Avgxiandu = Math.Round(avgXiandu, 2, MidpointRounding.AwayFromZero),
|
||||
Junfangcha = junfangcha,
|
||||
Zongcha = zongcha,
|
||||
FanxianLv = fanxianLv,
|
||||
FanweiNum = fanweiNum,
|
||||
XiaoyeNum = items.Sum(x => x.XiaoyeNum),
|
||||
DayeNum = items.Sum(x => x.DayeNum),
|
||||
TeyeNum = items.Sum(x => x.TeyeNum),
|
||||
CenterNum = items.Sum(x => x.CenterNum)
|
||||
};
|
||||
}
|
||||
|
||||
// ---- 判定(纤度 <= 0 视为未称) ----
|
||||
private static int Fanwei(double xd, ShengsiGuige gg) => xd > 0 && gg.CenterrangeLolimit <= xd && gg.CenterrangeUplimit >= xd ? 1 : 0;
|
||||
private static int Xiaoye(double xd, ShengsiGuige gg) => xd > 0 && ((gg.XiaoyeLolimit >= xd && xd > gg.DayeLolimit) || (gg.XiaoyeUplimit <= xd && xd < gg.DayeUplimit)) ? 1 : 0;
|
||||
private static int Daye(double xd, ShengsiGuige gg) => xd > 0 && ((gg.DayeLolimit >= xd && xd > gg.TeyeLolimit) || (gg.DayeUplimit <= xd && xd < gg.TeyeUplimit)) ? 1 : 0;
|
||||
private static int Teye(double xd, ShengsiGuige gg) => xd > 0 && (gg.TeyeLolimit >= xd || gg.TeyeUplimit <= xd) ? 1 : 0;
|
||||
private static int Center(double xd, ShengsiGuige gg) => xd > 0 && gg.CenterXiandu == xd ? 1 : 0;
|
||||
}
|
||||
Reference in New Issue
Block a user