first commit

This commit is contained in:
yanpeng200212
2026-10-08 10:43:59 +08:00
commit 8430ba0452
288 changed files with 29595 additions and 0 deletions
@@ -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;
}