- 新增 PayRoll(工资)、Wms(仓储)、Prod(车速/缫丝/考勤/丝片检测/丝片台账/纤检日报/质量日报) 模块 - 新增对应 Domain 实体、Application DTO/Service、Api Controller 与前端页面 - 移除旧 Attendance 考勤模块(Controller/Service/Entity/DTO 及 5 个前端页面),由 Prod/Kaoqin 取代 - 调整路由、仪表盘、Lsjl 流丝记录实体与服务 - 更新业务种子数据与 DbSeeder - 补充工作进度文档 14-27
258 lines
10 KiB
C#
258 lines
10 KiB
C#
using F9WebAiTest.Application.Common;
|
||
using F9WebAiTest.Application.Dtos.Prod;
|
||
using F9WebAiTest.Domain.Common;
|
||
using F9WebAiTest.Domain.Entities.Base;
|
||
using F9WebAiTest.Domain.Entities.Prod;
|
||
using SysDept = F9WebAiTest.Domain.Entities.SYS.Departments;
|
||
|
||
namespace F9WebAiTest.Application.Services.Prod;
|
||
|
||
/// <summary>秤大丝:以落丝记录为主表,按车台录入 4 个丝量值并汇总把数/丝量/缫折/台产</summary>
|
||
public interface ICdsService
|
||
{
|
||
Task<PageResult<CdsListItemDto>> GetPagedAsync(int pageIndex, int pageSize, DateTime? date, long? deptId);
|
||
Task<CdsEditDto> GetEditAsync(long lsjlId);
|
||
Task SaveAsync(CdsSaveDto dto);
|
||
Task DeleteAsync(long lsjlId);
|
||
}
|
||
|
||
public class CdsService : ICdsService
|
||
{
|
||
private readonly IRepository<LsjlRecord> _lsjlRepo;
|
||
private readonly IRepository<CdsLsjl> _cdsLsjlRepo;
|
||
private readonly IRepository<CdsChetai> _cdsChetaiRepo;
|
||
private readonly IRepository<SysDept.Department> _deptRepo;
|
||
private readonly IRepository<CraftZhuangKou> _zkRepo;
|
||
|
||
public CdsService(
|
||
IRepository<LsjlRecord> lsjlRepo,
|
||
IRepository<CdsLsjl> cdsLsjlRepo,
|
||
IRepository<CdsChetai> cdsChetaiRepo,
|
||
IRepository<SysDept.Department> deptRepo,
|
||
IRepository<CraftZhuangKou> zkRepo)
|
||
{
|
||
_lsjlRepo = lsjlRepo;
|
||
_cdsLsjlRepo = cdsLsjlRepo;
|
||
_cdsChetaiRepo = cdsChetaiRepo;
|
||
_deptRepo = deptRepo;
|
||
_zkRepo = zkRepo;
|
||
}
|
||
|
||
public async Task<PageResult<CdsListItemDto>> 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 _cdsLsjlRepo.GetAllAsync()).ToDictionary(x => x.LsjlId);
|
||
|
||
var total = q.LongCount();
|
||
var items = q.OrderByDescending(x => x.WorkDate).ThenBy(x => x.Jizu).ThenBy(x => x.Huishu)
|
||
.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
|
||
|
||
return new PageResult<CdsListItemDto>
|
||
{
|
||
Items = items.Select(x =>
|
||
{
|
||
sums.TryGetValue(x.Id, out var sum);
|
||
return new CdsListItemDto
|
||
{
|
||
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,
|
||
Hours = x.Hours,
|
||
Tongshu = x.Tongshu,
|
||
Meitongjianliang = x.Meitongjianliang,
|
||
YingchengNum = YingchengNum(x),
|
||
IsWeighed = sum != null,
|
||
Num = sum?.Num ?? 0,
|
||
Siliang = sum?.Siliang ?? 0,
|
||
Jianliang = sum?.Jianliang ?? 0,
|
||
Saozhe = sum?.Saozhe ?? 0,
|
||
Taichan = sum?.Taichan ?? 0
|
||
};
|
||
}).ToList(),
|
||
Total = total, PageIndex = pageIndex, PageSize = pageSize
|
||
};
|
||
}
|
||
|
||
public async Task<CdsEditDto> 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 records = await _cdsChetaiRepo.FindAsync(x => x.LsjlId == lsjlId);
|
||
List<CdsChetaiDto> chetais;
|
||
if (records.Count > 0)
|
||
{
|
||
chetais = records.OrderBy(r => r.Chetaihao).Select(ToDto).ToList();
|
||
}
|
||
else
|
||
{
|
||
// 首次称重:按车台范围初始化空行
|
||
chetais = new List<CdsChetaiDto>();
|
||
for (var i = lsjl.ChetaiStart; i <= lsjl.ChetaiEnd; i++)
|
||
chetais.Add(new CdsChetaiDto { Chetaihao = i });
|
||
}
|
||
|
||
return new CdsEditDto
|
||
{
|
||
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 = YingchengNum(lsjl),
|
||
Chetais = chetais
|
||
};
|
||
}
|
||
|
||
public async Task SaveAsync(CdsSaveDto dto)
|
||
{
|
||
if (dto.LsjlId == 0) throw new ServiceException("落丝记录无效");
|
||
var lsjl = await _lsjlRepo.GetByIdAsync(dto.LsjlId);
|
||
if (lsjl == null) throw new ServiceException("落丝记录不存在", 404);
|
||
|
||
if (dto.Chetais == null || dto.Chetais.Count == 0) throw new ServiceException("请先录入丝量数据");
|
||
|
||
var invalid = dto.Chetais.Where(i => i.Chetaihao < lsjl.ChetaiStart || i.Chetaihao > lsjl.ChetaiEnd).ToList();
|
||
if (invalid.Count > 0) throw new ServiceException($"车台号超出范围 [{lsjl.ChetaiStart}-{lsjl.ChetaiEnd}]");
|
||
|
||
// 车台明细:个数 = 丝量 > 0 的个数;小计丝量 = 4 个丝量之和
|
||
var items = dto.Chetais.Select(i =>
|
||
{
|
||
var t = new CdsChetaiDto
|
||
{
|
||
Chetaihao = i.Chetaihao,
|
||
Sl1 = Math.Max(0, i.Sl1), Sl2 = Math.Max(0, i.Sl2),
|
||
Sl3 = Math.Max(0, i.Sl3), Sl4 = Math.Max(0, i.Sl4)
|
||
};
|
||
CalChetai(t);
|
||
return t;
|
||
}).OrderBy(i => i.Chetaihao).ToList();
|
||
|
||
var num = items.Sum(x => x.Num);
|
||
if (num == 0) throw new ServiceException("请至少录入一个丝量值");
|
||
|
||
// 车台明细先删后建
|
||
var olds = await _cdsChetaiRepo.FindAsync(x => x.LsjlId == dto.LsjlId);
|
||
if (olds.Count > 0) await _cdsChetaiRepo.HardDeleteRangeAsync(olds);
|
||
|
||
foreach (var t in items)
|
||
{
|
||
await _cdsChetaiRepo.AddAsync(new CdsChetai
|
||
{
|
||
LsjlId = dto.LsjlId,
|
||
Chetaihao = t.Chetaihao,
|
||
Sl1 = t.Sl1, Sl2 = t.Sl2, Sl3 = t.Sl3, Sl4 = t.Sl4,
|
||
Num = t.Num, Siliang = t.Siliang
|
||
});
|
||
}
|
||
|
||
var siliang = items.Sum(x => x.Siliang);
|
||
|
||
// 汇总:茧量 / 缫折 / 实绩台产
|
||
var cdsLsjl = await _cdsLsjlRepo.FirstOrDefaultAsync(x => x.LsjlId == dto.LsjlId);
|
||
if (cdsLsjl == null)
|
||
{
|
||
cdsLsjl = new CdsLsjl { LsjlId = dto.LsjlId };
|
||
await _cdsLsjlRepo.AddAsync(cdsLsjl);
|
||
}
|
||
cdsLsjl.Num = num;
|
||
cdsLsjl.Siliang = siliang;
|
||
CalZhibiao(cdsLsjl, lsjl);
|
||
await _cdsLsjlRepo.UpdateAsync(cdsLsjl);
|
||
}
|
||
|
||
/// <summary>清除称重数据(汇总 + 车台明细物理删除,便于重新称重)</summary>
|
||
public async Task DeleteAsync(long lsjlId)
|
||
{
|
||
if (lsjlId == 0) throw new ServiceException("落丝记录无效");
|
||
var chetais = await _cdsChetaiRepo.FindAsync(x => x.LsjlId == lsjlId);
|
||
if (chetais.Count > 0) await _cdsChetaiRepo.HardDeleteRangeAsync(chetais);
|
||
|
||
var lsjlSum = await _cdsLsjlRepo.FirstOrDefaultAsync(x => x.LsjlId == lsjlId);
|
||
if (lsjlSum != null) await _cdsLsjlRepo.HardDeleteAsync(lsjlSum);
|
||
}
|
||
|
||
// ==================== 计算口径(对齐 F9 Cds_Lsjl_CalZhibiaoCommand) ====================
|
||
|
||
/// <summary>应秤数 = 车台数 × 4 ×(半车 0.5 / 整车 1)</summary>
|
||
private static int YingchengNum(LsjlRecord lsjl)
|
||
=> (int)Math.Round(lsjl.Chetaishu * 4 * (lsjl.Isbanche ? 0.5 : 1), MidpointRounding.AwayFromZero);
|
||
|
||
private static void CalChetai(CdsChetaiDto item)
|
||
{
|
||
item.Num = (item.Sl1 > 0 ? 1 : 0) + (item.Sl2 > 0 ? 1 : 0) + (item.Sl3 > 0 ? 1 : 0) + (item.Sl4 > 0 ? 1 : 0);
|
||
item.Siliang = item.Sl1 + item.Sl2 + item.Sl3 + item.Sl4;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 茧量 = 落丝桶数 × 每桶茧量;缫折 = 茧量 / 丝量 × 100;实绩台产 = 丝量 / 落丝时长 / 车台数。
|
||
/// F9 口径:缫折 ≤ 100 视为异常数据,不落指标。
|
||
/// </summary>
|
||
private static void CalZhibiao(CdsLsjl target, LsjlRecord lsjl)
|
||
{
|
||
if (lsjl.Meitongjianliang == 0 || lsjl.Tongshu == 0)
|
||
{
|
||
target.Jianliang = 0; target.Saozhe = 0; target.Taichan = 0;
|
||
return;
|
||
}
|
||
if (target.Num <= 0 || target.Siliang <= 0 || lsjl.Hours <= 0 || lsjl.Chetaishu <= 0)
|
||
{
|
||
target.Jianliang = 0; target.Saozhe = 0; target.Taichan = 0;
|
||
return;
|
||
}
|
||
|
||
var jianliang = lsjl.Tongshu * lsjl.Meitongjianliang;
|
||
var saozhe = Math.Round((decimal)jianliang / target.Siliang * 100, 2, MidpointRounding.AwayFromZero);
|
||
var taichan = Math.Round((decimal)target.Siliang / (decimal)lsjl.Hours / lsjl.Chetaishu, 2, MidpointRounding.AwayFromZero);
|
||
|
||
if (saozhe > 100)
|
||
{
|
||
target.Jianliang = jianliang;
|
||
target.Saozhe = saozhe;
|
||
target.Taichan = taichan;
|
||
}
|
||
else
|
||
{
|
||
target.Jianliang = jianliang;
|
||
target.Saozhe = 0;
|
||
target.Taichan = 0;
|
||
}
|
||
}
|
||
|
||
private static CdsChetaiDto ToDto(CdsChetai r) => new()
|
||
{
|
||
Chetaihao = r.Chetaihao,
|
||
Sl1 = r.Sl1, Sl2 = r.Sl2, Sl3 = r.Sl3, Sl4 = r.Sl4,
|
||
Num = r.Num, Siliang = r.Siliang
|
||
};
|
||
}
|