新增工资/仓储/生产日报等模块,移除旧考勤模块

- 新增 PayRoll(工资)、Wms(仓储)、Prod(车速/缫丝/考勤/丝片检测/丝片台账/纤检日报/质量日报) 模块
- 新增对应 Domain 实体、Application DTO/Service、Api Controller 与前端页面
- 移除旧 Attendance 考勤模块(Controller/Service/Entity/DTO 及 5 个前端页面),由 Prod/Kaoqin 取代
- 调整路由、仪表盘、Lsjl 流丝记录实体与服务
- 更新业务种子数据与 DbSeeder
- 补充工作进度文档 14-27
This commit is contained in:
yanpeng200212
2026-10-27 09:09:57 +08:00
parent 69c4052a64
commit 1f1b184411
119 changed files with 21576 additions and 1668 deletions
@@ -0,0 +1,496 @@
using F9WebAiTest.Application.Common;
using F9WebAiTest.Application.Dtos.Prod;
using F9WebAiTest.Domain.Common;
using F9WebAiTest.Domain.Entities.Base;
using F9WebAiTest.Domain.Entities.Prod;
namespace F9WebAiTest.Application.Services.Prod;
/// <summary>
/// 选剥茧(对应 F9 生产-选剥茧 XBJ):选茧日报(含盈亏/联统率)、选茧计划及其明细、选茧班组及职工。
/// </summary>
public interface IXjrbService
{
// ---- 选茧日报 ----
Task<PageResult<XjrbListDto>> GetPagedAsync(DateTime? start, DateTime? end, string like, int pageIndex, int pageSize);
Task<XjrbSaveDto> GetByIdAsync(long id);
Task<long> SaveAsync(XjrbSaveDto dto);
Task DeleteAsync(long id);
Task<XjrbYingkuiDto> CountYingkuiAsync(XjrbSaveDto dto);
// ---- 选茧计划 ----
Task<PageResult<XjrbPlanListDto>> GetPlanPagedAsync(DateTime? start, DateTime? end, string like, int pageIndex, int pageSize);
Task<XjrbPlanSaveDto> GetPlanByIdAsync(long id);
Task<long> SavePlanAsync(XjrbPlanSaveDto dto);
Task DeletePlanAsync(long id);
// ---- 选茧计划明细 ----
Task<List<XjrbItemDto>> GetPlanItemsAsync(long planId);
Task<long> SavePlanItemAsync(XjrbItemSaveDto dto);
Task DeletePlanItemAsync(long id);
Task<XjrbYingkuiDto> CountPlanYingkuiAsync(XjrbItemSaveDto dto);
// ---- 选茧班组 ----
Task<PageResult<XjpbBanzuDto>> GetBanzuPagedAsync(string banci, int? zu, int pageIndex, int pageSize);
Task<List<XjpbBanzuZhigongDto>> GetBanzuZhigongAsync(long banzuId);
Task SaveBanzuZhigongAsync(List<XjpbBanzuZhigongSaveDto> dtos);
}
public class XjrbService : IXjrbService
{
private readonly IRepository<XjrbRecord> _xjrbRepo;
private readonly IRepository<XjrbPlan> _planRepo;
private readonly IRepository<XjrbItem> _itemRepo;
private readonly IRepository<XjpbBanzu> _banzuRepo;
private readonly IRepository<XjpbBanzuZhigong> _zgRepo;
private readonly IRepository<CraftZhuangKou> _gyzkRepo;
public XjrbService(
IRepository<XjrbRecord> xjrbRepo, IRepository<XjrbPlan> planRepo, IRepository<XjrbItem> itemRepo,
IRepository<XjpbBanzu> banzuRepo, IRepository<XjpbBanzuZhigong> zgRepo, IRepository<CraftZhuangKou> gyzkRepo)
{
_xjrbRepo = xjrbRepo; _planRepo = planRepo; _itemRepo = itemRepo;
_banzuRepo = banzuRepo; _zgRepo = zgRepo; _gyzkRepo = gyzkRepo;
}
private static decimal Round2(decimal v) => Math.Round(v, 2);
private async Task<Dictionary<long, string>> GyzkDictAsync()
=> (await _gyzkRepo.GetAllAsync()).ToDictionary(x => x.Id, x => x.ZhuangKouName);
// ==================== 选茧日报 ====================
public async Task<PageResult<XjrbListDto>> GetPagedAsync(DateTime? start, DateTime? end, string like, int pageIndex, int pageSize)
{
var all = await _xjrbRepo.GetAllAsync();
var q = all.AsQueryable();
if (start.HasValue) q = q.Where(x => x.Date.Date >= start.Value.Date);
if (end.HasValue) q = q.Where(x => x.Date.Date <= end.Value.Date);
var gyzkDict = await GyzkDictAsync();
if (!string.IsNullOrWhiteSpace(like))
{
var key = like.Trim();
q = q.Where(x => x.GyzkId > 0 && gyzkDict.ContainsKey(x.GyzkId) && gyzkDict[x.GyzkId].Contains(key));
}
var total = q.LongCount();
var items = q.OrderByDescending(x => x.Date).ThenByDescending(x => x.Id)
.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
return new PageResult<XjrbListDto>
{
Items = items.Select(ToDto).ToList(),
Total = total, PageIndex = pageIndex, PageSize = pageSize
};
}
private static XjrbListDto ToDto(XjrbRecord x) => new()
{
Id = x.Id, Date = x.Date, GyzkId = x.GyzkId,
YlLrBaoshu = x.YlLrBaoshu, YlLrZhongliang = x.YlLrZhongliang,
LtjBaoshu = x.LtjBaoshu, LtjZhongliang = x.LtjZhongliang,
HwjjBaoshu = x.HwjjBaoshu, HwjjZhongliang = x.HwjjZhongliang,
LjjBaoshu = x.LjjBaoshu, LjjZhongliang = x.LjjZhongliang,
MjBaoshu = x.MjBaoshu, MjZhongliang = x.MjZhongliang,
SgjBaoshu = x.SgjBaoshu, SgjZhongliang = x.SgjZhongliang,
XjjBaoshu = x.XjjBaoshu, XjjZhongliang = x.XjjZhongliang,
JyBaoshu = x.JyBaoshu, JyZhongliang = x.JyZhongliang,
JhBaoshu = x.JhBaoshu, JhZhongliang = x.JhZhongliang,
TotalZhongliang = x.TotalZhongliang,
ThBaoshu = x.ThBaoshu, ThZhongliang = x.ThZhongliang,
LiantongRate = x.LiantongRate, Yingkui = x.Yingkui
};
public async Task<XjrbSaveDto> GetByIdAsync(long id)
{
var x = await _xjrbRepo.GetByIdAsync(id);
if (x == null) throw new ServiceException("选茧日报不存在", 404);
return new XjrbSaveDto
{
Id = x.Id, Date = x.Date, GyzkId = x.GyzkId,
YlLrBaoshu = x.YlLrBaoshu, YlLrZhongliang = x.YlLrZhongliang,
LtjBaoshu = x.LtjBaoshu, LtjZhongliang = x.LtjZhongliang,
HwjjBaoshu = x.HwjjBaoshu, HwjjZhongliang = x.HwjjZhongliang,
LjjBaoshu = x.LjjBaoshu, LjjZhongliang = x.LjjZhongliang,
MjBaoshu = x.MjBaoshu, MjZhongliang = x.MjZhongliang,
SgjBaoshu = x.SgjBaoshu, SgjZhongliang = x.SgjZhongliang,
XjjBaoshu = x.XjjBaoshu, XjjZhongliang = x.XjjZhongliang,
JyBaoshu = x.JyBaoshu, JyZhongliang = x.JyZhongliang,
JhBaoshu = x.JhBaoshu, JhZhongliang = x.JhZhongliang,
ThBaoshu = x.ThBaoshu, ThZhongliang = x.ThZhongliang,
LiantongRate = x.LiantongRate, Yingkui = x.Yingkui
};
}
public async Task<long> SaveAsync(XjrbSaveDto dto)
{
if (dto.Date == default) throw new ServiceException("请选择选茧日期");
if (dto.GyzkId <= 0) throw new ServiceException("请选择工艺庄口");
XjrbRecord entity;
var isNew = dto.Id == 0;
if (isNew) entity = new XjrbRecord();
else
{
entity = await _xjrbRepo.GetByIdAsync(dto.Id);
if (entity == null) throw new ServiceException("选茧日报不存在", 404);
}
entity.Date = dto.Date.Date;
entity.GyzkId = dto.GyzkId;
entity.YlLrBaoshu = dto.YlLrBaoshu;
entity.YlLrZhongliang = dto.YlLrZhongliang;
entity.LtjBaoshu = dto.LtjBaoshu; entity.LtjZhongliang = dto.LtjZhongliang;
entity.HwjjBaoshu = dto.HwjjBaoshu; entity.HwjjZhongliang = dto.HwjjZhongliang;
entity.LjjBaoshu = dto.LjjBaoshu; entity.LjjZhongliang = dto.LjjZhongliang;
entity.MjBaoshu = dto.MjBaoshu; entity.MjZhongliang = dto.MjZhongliang;
entity.SgjBaoshu = dto.SgjBaoshu; entity.SgjZhongliang = dto.SgjZhongliang;
entity.XjjBaoshu = dto.XjjBaoshu; entity.XjjZhongliang = dto.XjjZhongliang;
entity.JyBaoshu = dto.JyBaoshu; entity.JyZhongliang = dto.JyZhongliang;
entity.JhBaoshu = dto.JhBaoshu; entity.JhZhongliang = dto.JhZhongliang;
entity.ThBaoshu = dto.ThBaoshu; entity.ThZhongliang = dto.ThZhongliang;
entity.LiantongRate = dto.LiantongRate;
entity.Yingkui = dto.Yingkui;
// 合计重量 = 8 类茧重量之和(不含退回、不含领入)
entity.TotalZhongliang = Round2(entity.LtjZhongliang + entity.HwjjZhongliang + entity.LjjZhongliang
+ entity.MjZhongliang + entity.SgjZhongliang + entity.XjjZhongliang + entity.JyZhongliang + entity.JhZhongliang);
if (isNew) await _xjrbRepo.AddAsync(entity); else await _xjrbRepo.UpdateAsync(entity);
return entity.Id;
}
public async Task DeleteAsync(long id)
{
var x = await _xjrbRepo.GetByIdAsync(id);
if (x == null) throw new ServiceException("选茧日报不存在", 404);
await _xjrbRepo.HardDeleteAsync(x);
}
/// <summary>
/// 计算盈亏 / 联统率(对齐 F9 GetXjrbCountYingkuiQuery + GetXjrbLingruStartDateQuery)。
/// 起始日期:由近及远跳过「联统率 = 0(未结算)」的日报,直到碰到已结算的一条为止;
/// 累计区间 = [起始日期, 当前日期];累加当前录入值 + 区间内历史日报(排除自身)。
/// 联统率 = 累计联统茧重量 ÷ 累计干茧领入重量 × 100;盈亏 = 累计选出总重量 − 累计干茧领入重量。
/// </summary>
public async Task<XjrbYingkuiDto> CountYingkuiAsync(XjrbSaveDto dto)
{
if (dto.GyzkId <= 0) throw new ServiceException("请选择工艺庄口");
var date = dto.Date.Date;
var all = await _xjrbRepo.GetAllAsync();
var history = all.Where(x => x.GyzkId == dto.GyzkId && x.Date.Date < date && x.Id != dto.Id)
.OrderByDescending(x => x.Date).ToList();
// 步骤 1:往回跳过未结算(LiantongRate == 0)的记录,确定起始日期
var startDate = date;
foreach (var item in history)
{
if (item.LiantongRate != 0) break;
startDate = item.Date.Date;
}
// 步骤 2:累计区间(闭区间,排除自身)
var range = history.Where(x => x.Date.Date >= startDate && x.Date.Date <= date).ToList();
// 步骤 3:累加(初始值 = 当前表单输入值)
decimal ltj = dto.LtjZhongliang;
decimal yllr = dto.YlLrZhongliang;
decimal allW = dto.LtjZhongliang + dto.HwjjZhongliang + dto.LjjZhongliang + dto.MjZhongliang
+ dto.SgjZhongliang + dto.XjjZhongliang + dto.JyZhongliang + dto.JhZhongliang;
foreach (var item in range)
{
ltj += item.LtjZhongliang;
yllr += item.YlLrZhongliang;
allW += item.LtjZhongliang + item.HwjjZhongliang + item.LjjZhongliang + item.MjZhongliang
+ item.SgjZhongliang + item.XjjZhongliang + item.JyZhongliang + item.JhZhongliang;
}
// 步骤 4:公式
var rate = yllr != 0 ? Round2(ltj / yllr * 100) : 0;
var yingkui = Round2(allW - yllr);
return new XjrbYingkuiDto { LiantongRate = rate, Yingkui = yingkui, StartDate = startDate };
}
// ==================== 选茧计划 ====================
public async Task<PageResult<XjrbPlanListDto>> GetPlanPagedAsync(DateTime? start, DateTime? end, string like, int pageIndex, int pageSize)
{
var all = await _planRepo.GetAllAsync();
var q = all.AsQueryable();
if (start.HasValue) q = q.Where(x => x.Date.Date >= start.Value.Date);
if (end.HasValue) q = q.Where(x => x.Date.Date <= end.Value.Date);
var gyzkDict = await GyzkDictAsync();
if (!string.IsNullOrWhiteSpace(like))
{
var key = like.Trim();
q = q.Where(x => x.GyzkId > 0 && gyzkDict.ContainsKey(x.GyzkId) && gyzkDict[x.GyzkId].Contains(key));
}
var total = q.LongCount();
var items = q.OrderByDescending(x => x.Date).ThenByDescending(x => x.Id)
.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
return new PageResult<XjrbPlanListDto>
{
Items = items.Select(ToPlanDto).ToList(),
Total = total, PageIndex = pageIndex, PageSize = pageSize
};
}
private static XjrbPlanListDto ToPlanDto(XjrbPlan x) => new()
{
Id = x.Id, Date = x.Date, GyzkId = x.GyzkId, YllingliaodanId = x.YllingliaodanId,
YlBaoshu = x.YlBaoshu, YlZhongliang = x.YlZhongliang,
LtjBaoshu = x.LtjBaoshu, LtjZhongliang = x.LtjZhongliang,
HwjjBaoshu = x.HwjjBaoshu, HwjjZhongliang = x.HwjjZhongliang,
WjjBaoshu = x.WjjBaoshu, WjjZhongliang = x.WjjZhongliang,
SgjBaoshu = x.SgjBaoshu, SgjZhongliang = x.SgjZhongliang,
XjjBaoshu = x.XjjBaoshu, XjjZhongliang = x.XjjZhongliang,
JyBaoshu = x.JyBaoshu, JyZhongliang = x.JyZhongliang,
JhBaoshu = x.JhBaoshu, JhZhongliang = x.JhZhongliang,
TotalZhongliang = x.TotalZhongliang, LiantongRate = x.LiantongRate,
Yingkui = x.Yingkui, IsFinish = x.IsFinish
};
public async Task<XjrbPlanSaveDto> GetPlanByIdAsync(long id)
{
var x = await _planRepo.GetByIdAsync(id);
if (x == null) throw new ServiceException("选茧计划不存在", 404);
return new XjrbPlanSaveDto
{
Id = x.Id, Date = x.Date, GyzkId = x.GyzkId, YllingliaodanId = x.YllingliaodanId,
YlBaoshu = x.YlBaoshu, YlZhongliang = x.YlZhongliang,
LtjBaoshu = x.LtjBaoshu, LtjZhongliang = x.LtjZhongliang,
HwjjBaoshu = x.HwjjBaoshu, HwjjZhongliang = x.HwjjZhongliang,
WjjBaoshu = x.WjjBaoshu, WjjZhongliang = x.WjjZhongliang,
SgjBaoshu = x.SgjBaoshu, SgjZhongliang = x.SgjZhongliang,
XjjBaoshu = x.XjjBaoshu, XjjZhongliang = x.XjjZhongliang,
JyBaoshu = x.JyBaoshu, JyZhongliang = x.JyZhongliang,
JhBaoshu = x.JhBaoshu, JhZhongliang = x.JhZhongliang
};
}
public async Task<long> SavePlanAsync(XjrbPlanSaveDto dto)
{
if (dto.Date == default) throw new ServiceException("请选择日期");
if (dto.GyzkId <= 0) throw new ServiceException("请选择工艺庄口");
XjrbPlan entity;
var isNew = dto.Id == 0;
if (isNew) entity = new XjrbPlan();
else
{
entity = await _planRepo.GetByIdAsync(dto.Id);
if (entity == null) throw new ServiceException("选茧计划不存在", 404);
if (entity.IsFinish) throw new ServiceException("该计划已选茧完成,不能修改");
}
entity.Date = dto.Date.Date;
entity.GyzkId = dto.GyzkId;
entity.YllingliaodanId = dto.YllingliaodanId;
entity.YlBaoshu = dto.YlBaoshu; entity.YlZhongliang = dto.YlZhongliang;
entity.LtjBaoshu = dto.LtjBaoshu; entity.LtjZhongliang = dto.LtjZhongliang;
entity.HwjjBaoshu = dto.HwjjBaoshu; entity.HwjjZhongliang = dto.HwjjZhongliang;
entity.WjjBaoshu = dto.WjjBaoshu; entity.WjjZhongliang = dto.WjjZhongliang;
entity.SgjBaoshu = dto.SgjBaoshu; entity.SgjZhongliang = dto.SgjZhongliang;
entity.XjjBaoshu = dto.XjjBaoshu; entity.XjjZhongliang = dto.XjjZhongliang;
entity.JyBaoshu = dto.JyBaoshu; entity.JyZhongliang = dto.JyZhongliang;
entity.JhBaoshu = dto.JhBaoshu; entity.JhZhongliang = dto.JhZhongliang;
entity.TotalZhongliang = Round2(entity.LtjZhongliang + entity.HwjjZhongliang + entity.WjjZhongliang
+ entity.SgjZhongliang + entity.XjjZhongliang + entity.JyZhongliang + entity.JhZhongliang);
if (isNew) await _planRepo.AddAsync(entity); else await _planRepo.UpdateAsync(entity);
return entity.Id;
}
public async Task DeletePlanAsync(long id)
{
var x = await _planRepo.GetByIdAsync(id);
if (x == null) throw new ServiceException("选茧计划不存在", 404);
if (x.IsFinish) throw new ServiceException("该计划已选茧完成,不能删除");
var items = await _itemRepo.FindAsync(i => i.XjrbPlanId == id);
if (items.Count > 0) await _itemRepo.HardDeleteRangeAsync(items);
await _planRepo.HardDeleteAsync(x);
}
// ==================== 选茧计划明细 ====================
public async Task<List<XjrbItemDto>> GetPlanItemsAsync(long planId)
{
var items = await _itemRepo.FindAsync(x => x.XjrbPlanId == planId);
return items.OrderBy(x => x.Date).ThenBy(x => x.Id).Select(x => new XjrbItemDto
{
Id = x.Id, XjrbPlanId = x.XjrbPlanId, Date = x.Date,
LtjBaoshu = x.LtjBaoshu, LtjZhongliang = x.LtjZhongliang,
HwjjBaoshu = x.HwjjBaoshu, HwjjZhongliang = x.HwjjZhongliang,
WjjBaoshu = x.WjjBaoshu, WjjZhongliang = x.WjjZhongliang,
SgjBaoshu = x.SgjBaoshu, SgjZhongliang = x.SgjZhongliang,
XjjBaoshu = x.XjjBaoshu, XjjZhongliang = x.XjjZhongliang,
JyBaoshu = x.JyBaoshu, JyZhongliang = x.JyZhongliang,
JhBaoshu = x.JhBaoshu, JhZhongliang = x.JhZhongliang,
TotalZhongliang = x.TotalZhongliang, IsFinish = x.IsFinish
}).ToList();
}
public async Task<long> SavePlanItemAsync(XjrbItemSaveDto dto)
{
if (dto.XjrbPlanId <= 0) throw new ServiceException("计划无效");
if (dto.Date == default) throw new ServiceException("请选择选茧日期");
var plan = await _planRepo.GetByIdAsync(dto.XjrbPlanId);
if (plan == null) throw new ServiceException("选茧计划不存在", 404);
if (plan.IsFinish) throw new ServiceException("该计划已选茧完成,不能修改明细");
XjrbItem entity;
var isNew = dto.Id == 0;
if (isNew) entity = new XjrbItem { XjrbPlanId = dto.XjrbPlanId };
else
{
entity = await _itemRepo.GetByIdAsync(dto.Id);
if (entity == null) throw new ServiceException("选茧明细不存在", 404);
}
entity.Date = dto.Date.Date;
entity.LtjBaoshu = dto.LtjBaoshu; entity.LtjZhongliang = dto.LtjZhongliang;
entity.HwjjBaoshu = dto.HwjjBaoshu; entity.HwjjZhongliang = dto.HwjjZhongliang;
entity.WjjBaoshu = dto.WjjBaoshu; entity.WjjZhongliang = dto.WjjZhongliang;
entity.SgjBaoshu = dto.SgjBaoshu; entity.SgjZhongliang = dto.SgjZhongliang;
entity.XjjBaoshu = dto.XjjBaoshu; entity.XjjZhongliang = dto.XjjZhongliang;
entity.JyBaoshu = dto.JyBaoshu; entity.JyZhongliang = dto.JyZhongliang;
entity.JhBaoshu = dto.JhBaoshu; entity.JhZhongliang = dto.JhZhongliang;
entity.TotalZhongliang = Round2(entity.LtjZhongliang + entity.HwjjZhongliang + entity.WjjZhongliang
+ entity.SgjZhongliang + entity.XjjZhongliang + entity.JyZhongliang + entity.JhZhongliang);
entity.IsFinish = dto.IsFinish;
if (isNew) await _itemRepo.AddAsync(entity); else await _itemRepo.UpdateAsync(entity);
// 勾选「选茧完成」时把盈亏回写计划(对齐 F9 UpdateXjrbPlanYingkuiCommand)
if (dto.IsFinish)
{
var yk = await CountPlanYingkuiAsync(dto);
plan.TotalZhongliang = Round2(plan.TotalZhongliang + entity.TotalZhongliang);
plan.Yingkui = yk.Yingkui;
plan.LiantongRate = yk.LiantongRate;
plan.IsFinish = true;
await _planRepo.UpdateAsync(plan);
}
return entity.Id;
}
public async Task DeletePlanItemAsync(long id)
{
var x = await _itemRepo.GetByIdAsync(id);
if (x == null) throw new ServiceException("选茧明细不存在", 404);
var plan = await _planRepo.GetByIdAsync(x.XjrbPlanId);
if (plan != null && plan.IsFinish) throw new ServiceException("该计划已选茧完成,不能删除明细");
await _itemRepo.HardDeleteAsync(x);
}
/// <summary>
/// 计划维度盈亏(对齐 F9 GetXjrbPlanYingkuiQuery,口径与日报不同):
/// 联统率按「包数」:联统茧包数 ÷ 各类茧包数之和 × 100;盈亏 = 累计选出总重量 − 计划原料领入重量。
/// 注:老系统仓储里 wjjBaoshu / wjjZhongliang 源字段交叉写反(疑似 bug),此处已按正确字段聚合。
/// </summary>
public async Task<XjrbYingkuiDto> CountPlanYingkuiAsync(XjrbItemSaveDto dto)
{
var plan = await _planRepo.GetByIdAsync(dto.XjrbPlanId);
if (plan == null) throw new ServiceException("选茧计划不存在", 404);
var items = (await _itemRepo.FindAsync(x => x.XjrbPlanId == dto.XjrbPlanId && x.Id != dto.Id)).ToList();
decimal ltjZ = dto.LtjZhongliang, hwjjZ = dto.HwjjZhongliang, wjjZ = dto.WjjZhongliang,
sgjZ = dto.SgjZhongliang, xjjZ = dto.XjjZhongliang, jyZ = dto.JyZhongliang, jhZ = dto.JhZhongliang;
decimal ltjB = dto.LtjBaoshu, hwjjB = dto.HwjjBaoshu, wjjB = dto.WjjBaoshu,
sgjB = dto.SgjBaoshu, xjjB = dto.XjjBaoshu, jyB = dto.JyBaoshu, jhB = dto.JhBaoshu;
foreach (var it in items)
{
ltjZ += it.LtjZhongliang; hwjjZ += it.HwjjZhongliang; wjjZ += it.WjjZhongliang;
sgjZ += it.SgjZhongliang; xjjZ += it.XjjZhongliang; jyZ += it.JyZhongliang; jhZ += it.JhZhongliang;
ltjB += it.LtjBaoshu; hwjjB += it.HwjjBaoshu; wjjB += it.WjjBaoshu;
sgjB += it.SgjBaoshu; xjjB += it.XjjBaoshu; jyB += it.JyBaoshu; jhB += it.JhBaoshu;
}
var totalZhongliang = Round2(ltjZ + hwjjZ + wjjZ + sgjZ + xjjZ + jyZ + jhZ);
var baoshuSum = ltjB + hwjjB + wjjB + sgjB + xjjB + jyB + jhB;
var rate = baoshuSum != 0 ? Round2(ltjB / (decimal)baoshuSum * 100) : 0;
return new XjrbYingkuiDto { LiantongRate = rate, Yingkui = Round2(totalZhongliang - plan.YlZhongliang), StartDate = plan.Date };
}
// ==================== 选茧班组 ====================
public async Task<PageResult<XjpbBanzuDto>> GetBanzuPagedAsync(string banci, int? zu, int pageIndex, int pageSize)
{
var all = await _banzuRepo.GetAllAsync();
var q = all.AsQueryable();
if (!string.IsNullOrWhiteSpace(banci)) q = q.Where(x => x.Banci == banci);
if (zu.HasValue) q = q.Where(x => x.Zu == zu.Value);
var total = q.LongCount();
var items = q.OrderBy(x => x.Banci).ThenBy(x => x.Zu)
.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
return new PageResult<XjpbBanzuDto>
{
Items = items.Select(x => new XjpbBanzuDto { Id = x.Id, Banci = x.Banci, Zu = x.Zu }).ToList(),
Total = total, PageIndex = pageIndex, PageSize = pageSize
};
}
public async Task<List<XjpbBanzuZhigongDto>> GetBanzuZhigongAsync(long banzuId)
{
if (banzuId <= 0) return new List<XjpbBanzuZhigongDto>();
var list = await _zgRepo.FindAsync(x => x.BanzuId == banzuId);
return list.OrderBy(x => x.Sort).ThenBy(x => x.Id).Select(x => new XjpbBanzuZhigongDto
{
Id = x.Id, BanzuId = x.BanzuId, Banci = x.Banci, Zu = x.Zu,
YgId = x.YgId, YgName = x.YgName, YgPhone = x.YgPhone, Sort = x.Sort
}).ToList();
}
/// <summary>
/// 班组「编辑录入」批量保存(对齐 F9 XjpbBanzuZhigong_SaveCommand):
/// 按班次整体覆盖——先清该班次旧职工与班组,再把「已勾选且有组别」的职工写入,并重建班组。
/// </summary>
public async Task SaveBanzuZhigongAsync(List<XjpbBanzuZhigongSaveDto> dtos)
{
var list = dtos ?? new List<XjpbBanzuZhigongSaveDto>();
var banci = list.FirstOrDefault()?.Banci ?? "";
if (string.IsNullOrWhiteSpace(banci)) throw new ServiceException("班次无效");
// 清理该班次旧数据
var oldZg = await _zgRepo.FindAsync(x => x.Banci == banci);
if (oldZg.Count > 0) await _zgRepo.HardDeleteRangeAsync(oldZg);
var oldBanzu = await _banzuRepo.FindAsync(x => x.Banci == banci);
if (oldBanzu.Count > 0) await _banzuRepo.HardDeleteRangeAsync(oldBanzu);
var picked = list.Where(x => x.Selected && x.Zu > 0).ToList();
if (picked.Count == 0) return;
// 重建班组(班次 + 组 去重)
var banzuDict = new Dictionary<int, long>();
foreach (var zu in picked.Select(x => x.Zu).Distinct().OrderBy(x => x))
{
var banzu = new XjpbBanzu { Banci = banci, Zu = zu };
await _banzuRepo.AddAsync(banzu);
banzuDict[zu] = banzu.Id;
}
var sort = 0;
foreach (var p in picked)
{
await _zgRepo.AddAsync(new XjpbBanzuZhigong
{
BanzuId = banzuDict.TryGetValue(p.Zu, out var bid) ? bid : 0,
Banci = banci, Zu = p.Zu,
YgId = p.YgId, YgName = p.YgName ?? "", YgPhone = p.YgPhone ?? "",
Sort = p.Sort > 0 ? p.Sort : ++sort
});
}
}
}