Files
f9-web-ai-test/backend/F9WebAiTest.Application/Services/Prod/CdsService.cs
T
yanpeng200212 1f1b184411 新增工资/仓储/生产日报等模块,移除旧考勤模块
- 新增 PayRoll(工资)、Wms(仓储)、Prod(车速/缫丝/考勤/丝片检测/丝片台账/纤检日报/质量日报) 模块
- 新增对应 Domain 实体、Application DTO/Service、Api Controller 与前端页面
- 移除旧 Attendance 考勤模块(Controller/Service/Entity/DTO 及 5 个前端页面),由 Prod/Kaoqin 取代
- 调整路由、仪表盘、Lsjl 流丝记录实体与服务
- 更新业务种子数据与 DbSeeder
- 补充工作进度文档 14-27
2026-10-27 09:09:57 +08:00

258 lines
10 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
};
}