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

- 新增 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,819 @@
using F9WebAiTest.Application.Common;
using F9WebAiTest.Application.Dtos.PayRoll;
using F9WebAiTest.Domain.Common;
using F9WebAiTest.Domain.Entities.Base;
using F9WebAiTest.Domain.Entities.PayRoll;
using F9WebAiTest.Domain.Entities.Prod;
using SysDept = F9WebAiTest.Domain.Entities.SYS.Departments;
using SysEmp = F9WebAiTest.Domain.Entities.SYS.Employees;
using SysWt = F9WebAiTest.Domain.Entities.SYS.WorkTypes;
namespace F9WebAiTest.Application.Services.PayRoll;
/// <summary>
/// 薪资计算-立缫挡车车头(挡车工 / 车头工 产量计算)
/// 对应 F9 WinClient\F9.Business.PayRoll\PAYROLL\CALCLDCCT(Page_CalcLdcct + Page_CalcLdcct_Edit)
/// 数据源:看台记录(挡车/车头)、煮茧日报(今缫桶数)、落丝记录(茧量)、秤大丝(丝量/缫折)、
/// 纤度检验(纤度明细/均方差/总差)、黑板检验(二度/三度/清洁/洁净)、生丝规格、生丝定级指标。
/// 配置:工艺庄口薪资配置、工种薪资配置、挡车岗位薪资配置、挡车纤度奖扣、薪资价格配置。
/// </summary>
public interface ICalcLdcctService
{
Task<PageResult<CalcLdcctListItemDto>> GetPagedAsync(int pageIndex, int pageSize, DateTime start, DateTime end,
long? depId, long? worktypeId, string like);
Task<CalcLdcctResultDto> CalcDcgAsync(CalcLdcctCalcDto dto);
Task<CalcLdcctResultDto> CalcCtgAsync(CalcLdcctCalcDto dto);
Task<int> SaveAsync(CalcLdcctSaveDto dto);
Task DeleteAsync(DateTime start, DateTime end, long depId, long worktypeId);
}
public class CalcLdcctService : ICalcLdcctService
{
private readonly IRepository<CalcLdcct> _repo;
private readonly IRepository<KtjlDcg> _dcgRepo;
private readonly IRepository<KtjlCtg> _ctgRepo;
private readonly IRepository<ZjrbRecord> _zjrbRepo;
private readonly IRepository<ZjrbJizu> _zjrbJizuRepo;
private readonly IRepository<LsjlRecord> _lsjlRepo;
private readonly IRepository<CdsLsjl> _cdsLsjlRepo;
private readonly IRepository<CdsChetai> _cdsChetaiRepo;
private readonly IRepository<XdjyLsjl> _xdjyLsjlRepo;
private readonly IRepository<XdjyRecord> _xdjyRecordRepo;
private readonly IRepository<HbjyRecord> _hbjyRepo;
private readonly IRepository<ConfGyzk> _confGyzkRepo;
private readonly IRepository<ConfGz> _confGzRepo;
private readonly IRepository<ConfDcg> _confDcgRepo;
private readonly IRepository<DangCheXianDuJiangKou> _xdjkRepo;
private readonly IRepository<XinZiJiaGePeiZhi> _xzjgRepo;
private readonly IRepository<ShengsiGuige> _guigeRepo;
private readonly IRepository<ShengsiDingjizhibiao> _ssdjzbRepo;
private readonly IRepository<CraftZhuangKou> _zkRepo;
private readonly IRepository<SysDept.Department> _depRepo;
private readonly IRepository<SysEmp.Employee> _empRepo;
private readonly IRepository<SysWt.WorkType> _wtRepo;
public CalcLdcctService(
IRepository<CalcLdcct> repo,
IRepository<KtjlDcg> dcgRepo,
IRepository<KtjlCtg> ctgRepo,
IRepository<ZjrbRecord> zjrbRepo,
IRepository<ZjrbJizu> zjrbJizuRepo,
IRepository<LsjlRecord> lsjlRepo,
IRepository<CdsLsjl> cdsLsjlRepo,
IRepository<CdsChetai> cdsChetaiRepo,
IRepository<XdjyLsjl> xdjyLsjlRepo,
IRepository<XdjyRecord> xdjyRecordRepo,
IRepository<HbjyRecord> hbjyRepo,
IRepository<ConfGyzk> confGyzkRepo,
IRepository<ConfGz> confGzRepo,
IRepository<ConfDcg> confDcgRepo,
IRepository<DangCheXianDuJiangKou> xdjkRepo,
IRepository<XinZiJiaGePeiZhi> xzjgRepo,
IRepository<ShengsiGuige> guigeRepo,
IRepository<ShengsiDingjizhibiao> ssdjzbRepo,
IRepository<CraftZhuangKou> zkRepo,
IRepository<SysDept.Department> depRepo,
IRepository<SysEmp.Employee> empRepo,
IRepository<SysWt.WorkType> wtRepo)
{
_repo = repo; _dcgRepo = dcgRepo; _ctgRepo = ctgRepo;
_zjrbRepo = zjrbRepo; _zjrbJizuRepo = zjrbJizuRepo; _lsjlRepo = lsjlRepo;
_cdsLsjlRepo = cdsLsjlRepo; _cdsChetaiRepo = cdsChetaiRepo;
_xdjyLsjlRepo = xdjyLsjlRepo; _xdjyRecordRepo = xdjyRecordRepo; _hbjyRepo = hbjyRepo;
_confGyzkRepo = confGyzkRepo; _confGzRepo = confGzRepo; _confDcgRepo = confDcgRepo;
_xdjkRepo = xdjkRepo; _xzjgRepo = xzjgRepo; _guigeRepo = guigeRepo; _ssdjzbRepo = ssdjzbRepo;
_zkRepo = zkRepo; _depRepo = depRepo; _empRepo = empRepo; _wtRepo = wtRepo;
}
// ==================== 常量(对齐 F9 Page_CalcLdcct) ====================
private const decimal XushuPerTai = 20m; // 每台绪数
private const decimal CtgTaiShu = 20m; // 车头工台时按 20 台/组
private const decimal CtgHoursDiv = 9m; // 车头基础单价 /9(标准上班时长)
private const decimal CtgRenShu = 2m; // 车头标准人数 2
private const decimal DengjiStep = 0.1m; // 等级分步长
private const int SaozheDeadZone = 2; // 缫折死区 ±2kg
private const int DcgSaozheCap = 10; // 挡车缫折封顶 ±10
private const int CtgSaozheCap = 3; // 车头缫折封顶 ±3
private const string PriceGroup = "薪资.前缫";
private const string DcgDengjiPriceKey = "挡车工计件等级奖扣单价";
private const string CtgDengjiPriceKey = "车头工计件等级奖扣单价";
// ==================== 查询 ====================
public async Task<PageResult<CalcLdcctListItemDto>> GetPagedAsync(int pageIndex, int pageSize, DateTime start,
DateTime end, long? depId, long? worktypeId, string like)
{
var all = await _repo.GetAllAsync();
var from = start.Date;
var to = end.Date;
var q = all.AsQueryable().Where(x => x.Date.Date >= from && x.Date.Date <= to);
if (depId.HasValue) q = q.Where(x => x.DepId == depId.Value);
if (worktypeId.HasValue) q = q.Where(x => x.WorktypeId == worktypeId.Value);
var deps = await _depRepo.GetAllAsync();
var emps = await _empRepo.GetAllAsync();
var wts = await _wtRepo.GetAllAsync();
var zks = await _zkRepo.GetAllAsync();
var confGyzks = await _confGyzkRepo.GetAllAsync();
var depDict = deps.ToDictionary(d => d.Id, d => d.Name);
var empDict = emps.ToDictionary(e => e.Id, e => e.Name);
var wtDict = wts.ToDictionary(w => w.Id, w => w.WorkName);
var zkDict = zks.ToDictionary(z => z.Id, z => z.ZhuangKouName);
var confDict = confGyzks.GroupBy(c => $"{c.Year}-{c.Month}-{c.GyzkId}")
.ToDictionary(g => g.Key, g => g.OrderByDescending(c => c.Id).First());
if (!string.IsNullOrWhiteSpace(like))
{
var empIds = emps.Where(e => e.Name.Contains(like)).Select(e => e.Id).ToHashSet();
var zkIds = zks.Where(z => z.ZhuangKouName.Contains(like)).Select(z => z.Id).ToHashSet();
q = q.Where(x => empIds.Contains(x.EmployeeId) || zkIds.Contains(x.GyzkId));
}
var total = q.LongCount();
var items = q.OrderBy(x => x.Date).ThenBy(x => x.Jizu).ThenBy(x => x.Banci).ThenBy(x => x.EmployeeId)
.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
return new PageResult<CalcLdcctListItemDto>
{
Items = items.Select(x =>
{
confDict.TryGetValue($"{x.Date.Year}-{x.Date.Month}-{x.GyzkId}", out var cfg);
return new CalcLdcctListItemDto
{
Id = x.Id, Date = x.Date, EmployeeId = x.EmployeeId,
EmployeeName = empDict.GetValueOrDefault(x.EmployeeId, ""),
DepId = x.DepId, DepName = depDict.GetValueOrDefault(x.DepId, ""),
WorktypeId = x.WorktypeId, WorktypeName = wtDict.GetValueOrDefault(x.WorktypeId, ""),
GyzkId = x.GyzkId, GyzkName = zkDict.GetValueOrDefault(x.GyzkId, ""),
Guige = x.Guige, Chetai = x.Chetai, Jizu = x.Jizu, Banci = x.Banci,
BanciName = BanciName(x.Banci),
HoursKantai = x.HoursKantai, Dangcheshu = x.Dangcheshu,
Dengji = x.Dengji, Dengjifen = x.Dengjifen,
GyzkTaichan = cfg?.QsTaichan ?? 0, GyzkSaozhe = cfg?.SaoZhe ?? 0,
ClDanjiaBase = x.ClDanjiaBase, ClDanjiaDengji = x.ClDanjiaDengji,
ClDanjiaActual = x.ClDanjiaActual, ClSiliang = x.ClSiliang,
ZlSaozhe = x.ZlSaozhe, ZlCenterxd = x.ZlCenterxd, ZlYexiannum = x.ZlYexiannum,
ZlQingjie = x.ZlQingjie, ZlJiejing = x.ZlJiejing, ZlErdu = x.ZlErdu, ZlSandu = x.ZlSandu,
JineCl_Original = x.JineCl_Original, Xishu_JineCl = x.Xishu_JineCl, JineCl = x.JineCl,
JineZlSaozhe = x.JineZlSaozhe, JineZlCenterxd = x.JineZlCenterxd, JineZlYexian = x.JineZlYexian,
JineZlQingjie = x.JineZlQingjie, JineZlJiejing = x.JineZlJiejing,
JineZlErdu = x.JineZlErdu, JineZlSandu = x.JineZlSandu,
JineZlTotal = x.JineZlSaozhe + x.JineZlCenterxd + x.JineZlYexian + x.JineZlQingjie
+ x.JineZlJiejing + x.JineZlErdu + x.JineZlSandu
};
}).ToList(),
Total = total, PageIndex = pageIndex, PageSize = pageSize
};
}
// ==================== 一键计算:挡车工 ====================
public async Task<CalcLdcctResultDto> CalcDcgAsync(CalcLdcctCalcDto dto)
{
var ctx = await BuildContextAsync(dto);
var result = new CalcLdcctResultDto();
if (ctx.DcgKtjl.Count == 0)
{
result.Warnings.Add("该账期该部门没有挡车工看台记录");
return result;
}
var rows = new List<CalcLdcct>();
// 按「日 + 机组」组织
var jizuDays = ctx.DcgKtjl.GroupBy(k => (k.Date.Date, k.Jizu));
foreach (var jd in jizuDays)
{
var (date, jizu) = jd.Key;
var ktjls = jd.Where(k => !k.NoChanliang).ToList();
if (ktjls.Count == 0) continue;
// 该机组当日各庄口的今缫桶数(煮茧日报不分班)
var gyzkTongshu = ctx.GyzkTongshuByJizuDay.TryGetValue((date, jizu), out var gt)
? gt : new List<(long GyzkId, string Guige, int Tongshu, int StdLuosi)>();
var tongshuSum = gyzkTongshu.Sum(g => g.Tongshu);
var banciNum = ktjls.Select(k => k.Banci).Distinct().Count();
var half = banciNum >= 2 ? 0.5m : 1m;
foreach (var (gyzkId, guige, tongshu, stdLuosi) in gyzkTongshu)
{
var cfg = ctx.ConfGyzk(gyzkId, date);
if (cfg == null)
{
result.Warnings.Add($"{date:yyyy-MM-dd} 机组 {jizu} 庄口缺少薪资配置,已跳过");
continue;
}
var guigeCfg = ctx.Guige(guige);
// 每桶丝量 = 每车丝量(kg→g) / 标准落丝桶数
if (stdLuosi <= 0 || cfg.MeiCheSiLiang <= 0)
{
result.Warnings.Add($"{date:yyyy-MM-dd} 庄口 {cfg.Guige} 缺少每车丝量/标准落丝桶数,已跳过");
continue;
}
var meitongSiliang = cfg.MeiCheSiLiang * 1000m / stdLuosi;
var jiayiSumSiliang = Math.Round(tongshu * meitongSiliang, 0, MidpointRounding.AwayFromZero);
var rate = tongshuSum > 0 ? (decimal)tongshu / tongshuSum : 0m;
// 按班次拆分丝量:按 台时 = 挡车数 × 时长 加权
var jizuDayTaishi = ktjls.Sum(k => k.DangcheNum * k.Hours);
foreach (var banciGroup in ktjls.GroupBy(k => k.Banci))
{
var banciTaishi = banciGroup.Sum(k => k.DangcheNum * k.Hours);
var banciSiliang = jizuDayTaishi > 0
? Math.Round(jiayiSumSiliang * (banciTaishi / jizuDayTaishi), 0, MidpointRounding.AwayFromZero)
: 0m;
var groupTaishi = banciTaishi;
foreach (var k in banciGroup)
{
// 看台时长:按庄口今缫桶数占比分摊个人工时
var hoursKantai = Math.Round(k.Hours * rate, 1, MidpointRounding.AwayFromZero);
var siliang = groupTaishi > 0
? Math.Round(banciSiliang * (k.DangcheNum * k.Hours / groupTaishi), 0, MidpointRounding.AwayFromZero)
: 0m;
// ---- 质量(按车台看台比例分摊) ----
var quality = CalcDcgQuality(ctx, date, jizu, gyzkId, k, half, cfg, guigeCfg);
// ---- 等级(木桶短板) ----
var (dengji, dengjifen) = CalDengJi(ctx, guige, quality.Junfangcha, quality.Zongcha,
quality.QingjieFen, quality.JiejingFen, quality.Erdu, quality.Sandu, cfg);
// ---- 单价 ----
var xushu = k.DangcheNum * XushuPerTai;
var gangweiBase = GetDcgDanJiaBase(xushu, ctx.ConfDcg);
if (ctx.ConfGz == null || ctx.ConfGz.QqDaysStandard <= 0 || ctx.ConfGz.HoursPerday <= 0
|| cfg.QsTaichan <= 0 || k.DangcheNum <= 0)
{
result.Warnings.Add($"工种薪资配置或庄口台产缺失,{date:yyyy-MM-dd} {k.EmployeeId} 已跳过");
continue;
}
var danjiaBase = Math.Round(gangweiBase / ctx.ConfGz.QqDaysStandard / k.DangcheNum
/ ctx.ConfGz.HoursPerday / cfg.QsTaichan, 7, MidpointRounding.AwayFromZero);
if (danjiaBase == 0) continue;
var danjiaDengji = Math.Round(
(dengjifen - cfg.QsDengjifen) / DengjiStep * ctx.DcgJjDengJiJkPrice / 10m / 1000m,
7, MidpointRounding.AwayFromZero);
var danjiaActual = Math.Round(danjiaBase + danjiaDengji, 7, MidpointRounding.AwayFromZero);
// ---- 金额 ----
var e = new CalcLdcct
{
Date = date, EmployeeId = k.EmployeeId, DepId = dto.DepId, WorktypeId = dto.WorktypeId,
Jizu = jizu, Banci = (int)k.Banci, GyzkId = gyzkId, Guige = guige,
Chetai = k.ChetaiStart == k.ChetaiEnd ? k.ChetaiStart.ToString() : $"{k.ChetaiStart}-{k.ChetaiEnd}",
HoursKantai = hoursKantai, Dangcheshu = k.DangcheNum, Dengji = dengji, Dengjifen = dengjifen,
ClDanjiaBase = danjiaBase, ClDanjiaDengji = danjiaDengji, ClDanjiaActual = danjiaActual,
ClSiliang = siliang,
ZlSaozhe = quality.Saozhe, ZlCenterxd = quality.CenterNum, ZlYexiannum = quality.YexianNum,
ZlQingjie = quality.QingjiePianshu, ZlJiejing = quality.JiejingPianshu,
ZlErdu = quality.Erdu, ZlSandu = quality.Sandu,
Xishu_JineCl = cfg.DcgJijianXishu
};
e.JineCl_Original = Math.Round(danjiaActual * siliang, 2, MidpointRounding.AwayFromZero);
e.JineCl = Math.Round(danjiaActual * siliang * cfg.DcgJijianXishu, 2, MidpointRounding.AwayFromZero);
e.JineZlSaozhe = CalJineSaozhe(cfg.SaoZhe, quality.Saozhe, siliang, DcgSaozheCap, 5m);
e.JineZlCenterxd = Math.Round(quality.CenterNum * 1m, 2, MidpointRounding.AwayFromZero);
e.JineZlYexian = -Math.Round(quality.YexianKouKuan, 2, MidpointRounding.AwayFromZero);
e.JineZlQingjie = -Math.Round(quality.QingjieKouKuan, 2, MidpointRounding.AwayFromZero);
e.JineZlJiejing = -Math.Round(quality.JiejingKouKuan, 2, MidpointRounding.AwayFromZero);
e.JineZlErdu = -Math.Round(quality.Erdu * 2m, 2, MidpointRounding.AwayFromZero);
e.JineZlSandu = -Math.Round(quality.Sandu * 20m, 2, MidpointRounding.AwayFromZero);
rows.Add(e);
}
}
}
}
return await PersistAsync(rows, dto, result);
}
// ==================== 一键计算:车头工 ====================
public async Task<CalcLdcctResultDto> CalcCtgAsync(CalcLdcctCalcDto dto)
{
var ctx = await BuildContextAsync(dto);
var result = new CalcLdcctResultDto();
if (ctx.CtgKtjl.Count == 0)
{
result.Warnings.Add("该账期该部门没有车头工看台记录");
return result;
}
// 车头工强依赖已存的挡车工产量
var dcgRows = (await _repo.GetAllAsync())
.Where(x => x.Date.Date >= dto.StartDate.Date && x.Date.Date <= dto.EndDate.Date
&& x.DepId == dto.DepId && x.WorktypeId == ctx.DcgWorktypeId).ToList();
var rows = new List<CalcLdcct>();
var jizuDays = ctx.CtgKtjl.GroupBy(k => (k.Date.Date, k.Jizu));
foreach (var jd in jizuDays)
{
var (date, jizu) = jd.Key;
var ktjls = jd.Where(k => !k.NoChanliang).ToList();
if (ktjls.Count == 0) continue;
var gyzkTongshu = ctx.GyzkTongshuByJizuDay.TryGetValue((date, jizu), out var gt)
? gt : new List<(long GyzkId, string Guige, int Tongshu, int StdLuosi)>();
var tongshuSum = gyzkTongshu.Sum(g => g.Tongshu);
var banciNum = ktjls.Select(k => k.Banci).Distinct().Count();
var half = banciNum >= 2 ? 0.5m : 1m;
foreach (var (gyzkId, guige, tongshu, stdLuosi) in gyzkTongshu)
{
var cfg = ctx.ConfGyzk(gyzkId, date);
if (cfg == null)
{
result.Warnings.Add($"{date:yyyy-MM-dd} 机组 {jizu} 庄口缺少薪资配置,已跳过");
continue;
}
if (stdLuosi <= 0 || cfg.MeiCheSiLiang <= 0)
{
result.Warnings.Add($"{date:yyyy-MM-dd} 庄口 {cfg.Guige} 缺少每车丝量/标准落丝桶数,已跳过");
continue;
}
var meitongSiliang = cfg.MeiCheSiLiang * 1000m / stdLuosi;
var jiayiSumSiliang = Math.Round(tongshu * meitongSiliang, 0, MidpointRounding.AwayFromZero);
var rate = tongshuSum > 0 ? (decimal)tongshu / tongshuSum : 0m;
// 全组汇总质量(落丝主表,不分摊)
var lsjlIds = ctx.LsjlIdsByJizuDayGyzk.TryGetValue((date, jizu, gyzkId), out var ids)
? ids : new List<long>();
var saozheList = new List<decimal>();
var erduSum = 0m; var sanduSum = 0m;
foreach (var lid in lsjlIds)
{
if (ctx.CdsLsjl.TryGetValue(lid, out var cds) && cds.Siliang > 0 && cds.Jianliang > 0)
saozheList.Add((decimal)cds.Jianliang * 100m / cds.Siliang);
if (ctx.HbjyByLsjl.TryGetValue(lid, out var hb))
{
erduSum += hb.Sum(h => h.YdErdu);
sanduSum += hb.Sum(h => h.YdSandu);
}
}
var zlSaozhe = saozheList.Count > 0
? Math.Round(saozheList.Average(), 0, MidpointRounding.AwayFromZero) : cfg.SaoZhe;
var jizuDayTaishi = ktjls.Sum(k => CtgTaiShu * k.Hours);
foreach (var banciGroup in ktjls.GroupBy(k => k.Banci))
{
var banciTaishi = banciGroup.Sum(k => CtgTaiShu * k.Hours);
var banciSiliang = jizuDayTaishi > 0
? Math.Round(jiayiSumSiliang * (banciTaishi / jizuDayTaishi), 0, MidpointRounding.AwayFromZero)
: 0m;
var maxHours = banciGroup.Max(k => k.Hours);
foreach (var k in banciGroup)
{
var hoursKantai = Math.Round(k.Hours * rate, 1, MidpointRounding.AwayFromZero);
var siliang = maxHours > 0
? Math.Round(banciSiliang * (k.Hours / (maxHours * CtgRenShu)), 0, MidpointRounding.AwayFromZero)
: 0m;
// 等级:按挡车工丝量加权平均等级分再回落取整档
var (dengji, dengjifen) = CalCtgDengji(ctx, dcgRows, date, jizu, gyzkId, guige, cfg);
if (ctx.ConfGz == null || ctx.ConfGz.QqDaysStandard <= 0 || cfg.QsTaichan <= 0)
{
result.Warnings.Add($"工种薪资配置缺失,{date:yyyy-MM-dd} {k.EmployeeId} 已跳过");
continue;
}
var danjiaBase = Math.Round(ctx.ConfDcg.JineCtg / ctx.ConfGz.QqDaysStandard / CtgHoursDiv
/ CtgTaiShu / cfg.QsTaichan * CtgRenShu, 7, MidpointRounding.AwayFromZero);
if (danjiaBase == 0) continue;
var danjiaDengji = Math.Round(
(dengjifen - cfg.QsDengjifen) / DengjiStep * ctx.CtgJjDengJiJkPrice / 10m / 1000m,
7, MidpointRounding.AwayFromZero);
var danjiaActual = Math.Round(danjiaBase + danjiaDengji, 7, MidpointRounding.AwayFromZero);
var e = new CalcLdcct
{
Date = date, EmployeeId = k.EmployeeId, DepId = dto.DepId, WorktypeId = dto.WorktypeId,
Jizu = jizu, Banci = (int)k.Banci, GyzkId = gyzkId, Guige = guige, Chetai = "",
HoursKantai = hoursKantai, Dangcheshu = 0, Dengji = dengji, Dengjifen = dengjifen,
ClDanjiaBase = danjiaBase, ClDanjiaDengji = danjiaDengji, ClDanjiaActual = danjiaActual,
ClSiliang = siliang,
ZlSaozhe = zlSaozhe, ZlCenterxd = 0, ZlYexiannum = 0, ZlQingjie = 0, ZlJiejing = 0,
ZlErdu = Math.Round(erduSum * half, 2, MidpointRounding.AwayFromZero),
ZlSandu = Math.Round(sanduSum * half, 2, MidpointRounding.AwayFromZero),
Xishu_JineCl = cfg.CtgJijianXishu
};
e.JineCl_Original = Math.Round(danjiaActual * siliang, 2, MidpointRounding.AwayFromZero);
e.JineCl = Math.Round(danjiaActual * siliang * cfg.CtgJijianXishu, 2, MidpointRounding.AwayFromZero);
e.JineZlSaozhe = CalJineSaozhe(cfg.SaoZhe, zlSaozhe, siliang, CtgSaozheCap, 1m);
e.JineZlCenterxd = 0; e.JineZlYexian = 0; e.JineZlQingjie = 0; e.JineZlJiejing = 0;
e.JineZlErdu = -Math.Round(e.ZlErdu * 2m, 2, MidpointRounding.AwayFromZero);
e.JineZlSandu = -Math.Round(e.ZlSandu * 20m, 2, MidpointRounding.AwayFromZero);
rows.Add(e);
}
}
}
}
return await PersistAsync(rows, dto, result);
}
// ==================== 保存 / 删除 ====================
public async Task<int> SaveAsync(CalcLdcctSaveDto dto)
{
var olds = (await _repo.GetAllAsync())
.Where(x => x.Date.Date >= dto.StartDate.Date && x.Date.Date <= dto.EndDate.Date
&& x.DepId == dto.DepId && x.WorktypeId == dto.WorktypeId).ToList();
if (olds.Count > 0) await _repo.HardDeleteRangeAsync(olds);
foreach (var it in dto.Items)
{
var e = new CalcLdcct
{
Date = it.Date.Date, EmployeeId = it.EmployeeId, DepId = it.DepId, WorktypeId = it.WorktypeId,
Jizu = it.Jizu, Banci = it.Banci, GyzkId = it.GyzkId, Guige = it.Guige ?? "", Chetai = it.Chetai ?? "",
HoursKantai = it.HoursKantai, Dangcheshu = it.Dangcheshu, Dengji = it.Dengji ?? "", Dengjifen = it.Dengjifen,
ClDanjiaBase = it.ClDanjiaBase, ClDanjiaDengji = it.ClDanjiaDengji, ClDanjiaActual = it.ClDanjiaActual,
ClSiliang = it.ClSiliang,
ZlSaozhe = it.ZlSaozhe, ZlCenterxd = it.ZlCenterxd, ZlYexiannum = it.ZlYexiannum,
ZlQingjie = it.ZlQingjie, ZlJiejing = it.ZlJiejing, ZlErdu = it.ZlErdu, ZlSandu = it.ZlSandu,
JineCl_Original = it.JineCl_Original, Xishu_JineCl = it.Xishu_JineCl, JineCl = it.JineCl,
JineZlSaozhe = it.JineZlSaozhe, JineZlCenterxd = it.JineZlCenterxd, JineZlYexian = it.JineZlYexian,
JineZlQingjie = it.JineZlQingjie, JineZlJiejing = it.JineZlJiejing,
JineZlErdu = it.JineZlErdu, JineZlSandu = it.JineZlSandu
};
await _repo.AddAsync(e);
}
return dto.Items.Count;
}
public async Task DeleteAsync(DateTime start, DateTime end, long depId, long worktypeId)
{
var olds = (await _repo.GetAllAsync())
.Where(x => x.Date.Date >= start.Date && x.Date.Date <= end.Date
&& x.DepId == depId && x.WorktypeId == worktypeId).ToList();
if (olds.Count > 0) await _repo.HardDeleteRangeAsync(olds);
}
// ==================== 内部:上下文 ====================
private class CalcContext
{
public List<KtjlDcg> DcgKtjl = new();
public List<KtjlCtg> CtgKtjl = new();
public long DcgWorktypeId;
public Dictionary<(DateTime, int), List<(long GyzkId, string Guige, int Tongshu, int StdLuosi)>> GyzkTongshuByJizuDay = new();
public Dictionary<(DateTime, int, long), List<long>> LsjlIdsByJizuDayGyzk = new();
public Dictionary<long, CdsLsjl> CdsLsjl = new();
public Dictionary<long, List<CdsChetai>> CdsChetai = new();
public Dictionary<long, List<XdjyRecord>> XdjyByLsjl = new();
public Dictionary<long, List<HbjyRecord>> HbjyByLsjl = new();
public Dictionary<long, List<ConfGyzk>> ConfByGyzk = new();
public Dictionary<string, ShengsiGuige> GuigeDict = new();
public Dictionary<string, List<DangCheXianDuJiangKou>> YeXianJk = new();
public List<ShengsiDingjizhibiao> Djzb = new();
public ConfGz ConfGz = null!;
public ConfDcg ConfDcg = null!;
public decimal DcgJjDengJiJkPrice;
public decimal CtgJjDengJiJkPrice;
public List<string> Warnings = new();
public ConfGyzk ConfGyzk(long gyzkId, DateTime date) =>
ConfByGyzk.TryGetValue(gyzkId, out var list)
? list.FirstOrDefault(c => c.Year == date.Year && c.Month == date.Month)
?? list.OrderByDescending(c => c.Year).ThenByDescending(c => c.Month).FirstOrDefault()
: null;
public ShengsiGuige Guige(string guige) =>
GuigeDict.TryGetValue(guige ?? "", out var g) ? g : null;
public List<DangCheXianDuJiangKou> YeXianJiangKou(string guige) =>
YeXianJk.TryGetValue(guige ?? "", out var l) ? l : new List<DangCheXianDuJiangKou>();
}
private async Task<CalcContext> BuildContextAsync(CalcLdcctCalcDto dto)
{
var ctx = new CalcContext();
var from = dto.StartDate.Date;
var to = dto.EndDate.Date;
ctx.DcgKtjl = (await _dcgRepo.GetAllAsync())
.Where(x => x.Date.Date >= from && x.Date.Date <= to && x.DeptId == dto.DepId).ToList();
ctx.CtgKtjl = (await _ctgRepo.GetAllAsync())
.Where(x => x.Date.Date >= from && x.Date.Date <= to && x.DeptId == dto.DepId).ToList();
// 挡车工工种:取看台记录中出现最多的工种(车头计算需按挡车工工种取已存产量)
ctx.DcgWorktypeId = ctx.DcgKtjl.GroupBy(k => k.WorktypeId).OrderByDescending(g => g.Count())
.Select(g => g.Key).FirstOrDefault();
// 煮茧日报:日 + 机组 → 各庄口今缫桶数(不分班)
var zjrbs = (await _zjrbRepo.GetAllAsync())
.Where(x => x.Date.Date >= from && x.Date.Date <= to && x.DeptId == dto.DepId).ToList();
var zjrbJizus = await _zjrbJizuRepo.GetAllAsync();
foreach (var z in zjrbs)
{
foreach (var jz in zjrbJizus.Where(j => j.ZjrbId == z.Id))
{
var key = (z.Date.Date, jz.Jizu);
if (!ctx.GyzkTongshuByJizuDay.TryGetValue(key, out var list))
ctx.GyzkTongshuByJizuDay[key] = list = new List<(long, string, int, int)>();
list.Add((z.GyzkId, z.Guige, jz.TongshuJinsao, z.StandardLuositongshu));
}
}
// 落丝记录:日 + 机组 + 庄口 → lsjlId 列表
var lsjls = (await _lsjlRepo.GetAllAsync())
.Where(x => x.WorkDate.Date >= from && x.WorkDate.Date <= to && x.DeptId == dto.DepId).ToList();
foreach (var l in lsjls)
{
var key = (l.WorkDate.Date, l.Jizu, l.GyzkId);
if (!ctx.LsjlIdsByJizuDayGyzk.TryGetValue(key, out var list))
ctx.LsjlIdsByJizuDayGyzk[key] = list = new List<long>();
list.Add(l.Id);
}
var allLsjlIds = lsjls.Select(l => l.Id).ToHashSet();
ctx.CdsLsjl = (await _cdsLsjlRepo.GetAllAsync()).Where(x => allLsjlIds.Contains(x.LsjlId))
.GroupBy(x => x.LsjlId).ToDictionary(g => g.Key, g => g.First());
ctx.CdsChetai = (await _cdsChetaiRepo.GetAllAsync()).Where(x => allLsjlIds.Contains(x.LsjlId))
.GroupBy(x => x.LsjlId).ToDictionary(g => g.Key, g => g.ToList());
ctx.XdjyByLsjl = (await _xdjyRecordRepo.GetAllAsync()).Where(x => allLsjlIds.Contains(x.LsjlId))
.GroupBy(x => x.LsjlId).ToDictionary(g => g.Key, g => g.ToList());
ctx.HbjyByLsjl = (await _hbjyRepo.GetAllAsync()).Where(x => allLsjlIds.Contains(x.LsjlId))
.GroupBy(x => x.LsjlId).ToDictionary(g => g.Key, g => g.ToList());
_ = await _xdjyLsjlRepo.GetAllAsync(); // 纤度汇总(等级参考,保留实体引用)
ctx.ConfByGyzk = (await _confGyzkRepo.GetAllAsync())
.GroupBy(c => c.GyzkId).ToDictionary(g => g.Key, g => g.ToList());
ctx.GuigeDict = (await _guigeRepo.GetAllAsync())
.GroupBy(g => g.Guige).ToDictionary(g => g.Key, g => g.First());
ctx.Djzb = (await _ssdjzbRepo.GetAllAsync()).Where(x => x.Standard == 2).ToList(); // 2=个人
ctx.YeXianJk = (await _xdjkRepo.GetAllAsync())
.GroupBy(x => x.GuiGe).ToDictionary(g => g.Key, g => g.OrderBy(x => x.XianDu).ToList());
ctx.ConfGz = await _confGzRepo.FirstOrDefaultAsync(x => x.WorkTypeId == dto.WorktypeId);
ctx.ConfDcg = await _confDcgRepo.FirstOrDefaultAsync(x => x.DepId == dto.DepId)
?? new ConfDcg { DepId = dto.DepId };
ctx.DcgJjDengJiJkPrice = await PriceAsync(DcgDengjiPriceKey);
ctx.CtgJjDengJiJkPrice = await PriceAsync(CtgDengjiPriceKey);
return ctx;
}
private async Task<decimal> PriceAsync(string key)
{
var e = await _xzjgRepo.FirstOrDefaultAsync(x => x.Group == PriceGroup && x.Key == key);
return e?.Price ?? 0m;
}
private async Task<CalcLdcctResultDto> PersistAsync(List<CalcLdcct> rows, CalcLdcctCalcDto dto, CalcLdcctResultDto result)
{
await SaveAsync(new CalcLdcctSaveDto
{
StartDate = dto.StartDate, EndDate = dto.EndDate, DepId = dto.DepId, WorktypeId = dto.WorktypeId,
Items = rows.Select(r => new CalcLdcctSaveItemDto
{
Date = r.Date, EmployeeId = r.EmployeeId, DepId = r.DepId, WorktypeId = r.WorktypeId,
Jizu = r.Jizu, Banci = r.Banci, GyzkId = r.GyzkId, Guige = r.Guige, Chetai = r.Chetai,
HoursKantai = r.HoursKantai, Dangcheshu = r.Dangcheshu, Dengji = r.Dengji, Dengjifen = r.Dengjifen,
ClDanjiaBase = r.ClDanjiaBase, ClDanjiaDengji = r.ClDanjiaDengji, ClDanjiaActual = r.ClDanjiaActual,
ClSiliang = r.ClSiliang,
ZlSaozhe = r.ZlSaozhe, ZlCenterxd = r.ZlCenterxd, ZlYexiannum = r.ZlYexiannum,
ZlQingjie = r.ZlQingjie, ZlJiejing = r.ZlJiejing, ZlErdu = r.ZlErdu, ZlSandu = r.ZlSandu,
JineCl_Original = r.JineCl_Original, Xishu_JineCl = r.Xishu_JineCl, JineCl = r.JineCl,
JineZlSaozhe = r.JineZlSaozhe, JineZlCenterxd = r.JineZlCenterxd, JineZlYexian = r.JineZlYexian,
JineZlQingjie = r.JineZlQingjie, JineZlJiejing = r.JineZlJiejing,
JineZlErdu = r.JineZlErdu, JineZlSandu = r.JineZlSandu
}).ToList()
});
result.Count = rows.Count;
result.SumSiliang = rows.Sum(r => r.ClSiliang);
result.SumJineCl = Math.Round(rows.Sum(r => r.JineCl), 2, MidpointRounding.AwayFromZero);
result.SumJineZl = Math.Round(rows.Sum(r => r.JineZlSaozhe + r.JineZlCenterxd + r.JineZlYexian
+ r.JineZlQingjie + r.JineZlJiejing + r.JineZlErdu + r.JineZlSandu), 2, MidpointRounding.AwayFromZero);
return result;
}
// ==================== 内部:挡车工质量 ====================
private class DcgQuality
{
public decimal Saozhe;
public decimal CenterNum;
public decimal YexianNum;
public decimal YexianKouKuan;
public decimal QingjiePianshu;
public decimal QingjieKouKuan;
public decimal JiejingPianshu;
public decimal JiejingKouKuan;
public decimal Erdu;
public decimal Sandu;
public double Junfangcha;
public double Zongcha;
public decimal QingjieFen;
public decimal JiejingFen;
}
private DcgQuality CalcDcgQuality(CalcContext ctx, DateTime date, int jizu, long gyzkId, KtjlDcg k,
decimal half, ConfGyzk cfg, ShengsiGuige guigeCfg)
{
var q = new DcgQuality();
var lsjlIds = ctx.LsjlIdsByJizuDayGyzk.TryGetValue((date, jizu, gyzkId), out var ids)
? ids : new List<long>();
decimal jianliangNum = 0m, siliangDen = 0m;
double junfangchaSum = 0, zongchaSum = 0;
int junfangchaCnt = 0;
decimal erduSum = 0m, sanduSum = 0m;
decimal qjTdc = 0m, qjDc = 0m, qjCj = 0m;
decimal jj85 = 0m, jj80 = 0m, jj75 = 0m, jj70 = 0m;
decimal pianshuSum = 0m, qingjieDeduct = 0m, jiejingDeduct = 0m;
foreach (var lid in lsjlIds)
{
var chetais = ctx.CdsChetai.TryGetValue(lid, out var ct) ? ct : new List<CdsChetai>();
var cdsSum = ctx.CdsLsjl.TryGetValue(lid, out var cs) ? cs : null;
var xdjy = ctx.XdjyByLsjl.TryGetValue(lid, out var xd) ? xd : new List<XdjyRecord>();
var hbjy = ctx.HbjyByLsjl.TryGetValue(lid, out var hb) ? hb : new List<HbjyRecord>();
foreach (var ctItem in chetais)
{
if (ctItem.Chetaihao < k.ChetaiStart || ctItem.Chetaihao > k.ChetaiEnd) continue;
var rate = ChetaiRate(ctx, date, jizu, ctItem.Chetaihao, k);
// 缫折:茧量只记在落丝层,按「车台丝量占落丝丝量的比例」摊到车台,再看台比例摊到人
var lsjlSiliang = cdsSum?.Siliang ?? 0;
var lsjlJianliang = cdsSum?.Jianliang ?? 0;
var chetaiShare = lsjlSiliang > 0 ? (decimal)ctItem.Siliang / lsjlSiliang : 0m;
jianliangNum += lsjlJianliang * chetaiShare * rate;
siliangDen += ctItem.Siliang * rate;
// 中心纤度 / 野纤(纤度检验明细)
foreach (var x in xdjy.Where(x => x.Chetaihao == ctItem.Chetaihao))
{
var xds = new[] { x.Xd1, x.Xd2, x.Xd3, x.Xd4 }.Where(v => v > 0).ToArray();
if (guigeCfg != null)
{
q.CenterNum += xds.Count(v => Math.Abs(v - guigeCfg.CenterXiandu) < 0.0001) * rate;
q.YexianNum += xds.Count(v => v <= guigeCfg.XiaoyeLolimit || v >= guigeCfg.XiaoyeUplimit) * rate;
}
if (xds.Length > 0)
{
var avg = xds.Average();
var jfc = Math.Sqrt(xds.Sum(v => (v - avg) * (v - avg)) / xds.Length);
junfangchaSum += jfc; junfangchaCnt++;
var n = Math.Max(1, (int)Math.Ceiling(xds.Length * 0.02));
var sorted = xds.OrderBy(v => v).ToArray();
var minAvg = sorted.Take(n).Average();
var maxAvg = sorted.Skip(xds.Length - n).Average();
zongchaSum += Math.Max(Math.Abs(minAvg - avg), Math.Abs(maxAvg - avg));
}
}
// 黑板检验:二度/三度/清洁/洁净
foreach (var h in hbjy.Where(h => h.Chetaihao == ctItem.Chetaihao))
{
erduSum += h.YdErdu * rate;
sanduSum += h.YdSandu * rate;
qjTdc += h.QjZycdTdc * rate;
qjDc += h.QjCycdDc * rate;
qjCj += h.QjPtcdCj * rate;
jj85 += h.Jj85 * rate; jj80 += h.Jj80 * rate;
jj75 += h.Jj75 * rate; jj70 += h.Jj70 * rate;
pianshuSum += h.Pianshu * rate;
qingjieDeduct += (h.QjZycdTdc * 1.0m
+ (h.QjCycdFs + h.QjCycdDc + h.QjCycdNfc + h.QjCycdDcj + h.QjCycdZlx) * 0.4m
+ (h.QjPtcdXc + h.QjPtcdCj + h.QjPtcdLx + h.QjPtcdHjls) * 0.1m) * rate;
var jjCounts = new[] { h.Jj95, h.Jj90, h.Jj85, h.Jj80, h.Jj75, h.Jj70, h.Jj65, h.Jj60 };
for (var i = 0; i < jjCounts.Length; i++) jiejingDeduct += jjCounts[i] * (i + 1) * rate;
}
}
}
q.Saozhe = siliangDen > 0 ? Math.Round(jianliangNum * 100m / siliangDen, 0, MidpointRounding.AwayFromZero)
: cfg.SaoZhe;
q.Erdu = Math.Round(erduSum * half, 2, MidpointRounding.AwayFromZero);
q.Sandu = Math.Round(sanduSum * half, 2, MidpointRounding.AwayFromZero);
q.CenterNum = Math.Round(q.CenterNum * half, 2, MidpointRounding.AwayFromZero);
q.YexianNum = Math.Round(q.YexianNum * half, 2, MidpointRounding.AwayFromZero);
q.QingjiePianshu = Math.Round((qjTdc + qjDc + qjCj) * half, 2, MidpointRounding.AwayFromZero);
q.JiejingPianshu = Math.Round((jj85 + jj80 + jj75 + jj70) * half, 2, MidpointRounding.AwayFromZero);
q.QingjieKouKuan = (qjTdc * 2m + qjDc * 0.5m + qjCj * 2m) * half;
q.JiejingKouKuan = (jj85 * 0.2m + jj80 * 1m + jj75 * 2m + jj70 * 5m) * half;
q.YexianKouKuan = CalYeXianKouKuan(ctx, cfg.Guige, q.YexianNum);
q.Junfangcha = junfangchaCnt > 0 ? Math.Round(junfangchaSum / junfangchaCnt, 2) : 0;
q.Zongcha = junfangchaCnt > 0 ? Math.Round(zongchaSum / junfangchaCnt, 2) : 0;
q.QingjieFen = pianshuSum > 0
? Math.Round((pianshuSum - qingjieDeduct) * 100m / pianshuSum, 2, MidpointRounding.AwayFromZero) : 100m;
q.JiejingFen = pianshuSum > 0
? Math.Round((pianshuSum * 100m - jiejingDeduct * 5m) / pianshuSum, 2, MidpointRounding.AwayFromZero) : 100m;
return q;
}
/// <summary>车台看台比例 = 本人该台工时 / 该台所有看台人工时之和</summary>
private decimal ChetaiRate(CalcContext ctx, DateTime date, int jizu, int chetaihao, KtjlDcg k)
{
var same = ctx.DcgKtjl.Where(x => x.Date.Date == date && x.Jizu == jizu && x.Banci == k.Banci
&& x.ChetaiStart <= chetaihao && x.ChetaiEnd >= chetaihao).ToList();
var sum = same.Sum(x => x.Hours);
return sum > 0 ? k.Hours / sum : 0m;
}
/// <summary>野纤扣款:按规格取挡车纤度奖扣表,≤2 条取扣款最大的 2 条求和</summary>
private decimal CalYeXianKouKuan(CalcContext ctx, string guige, decimal yexianNum)
{
if (yexianNum <= 0) return 0m;
var list = ctx.YeXianJiangKou(guige);
if (list.Count == 0) return 0m;
var items = ExpandCount(yexianNum, list);
if (items.Count >= 2) return items.OrderByDescending(i => i.KouKuan).Take(2).Sum(i => i.KouKuan);
return items.Sum(i => i.KouKuan);
}
/// <summary>按数量展开:整数部分逐条取,小数部分按最后一条比例折算</summary>
private static List<DangCheXianDuJiangKou> ExpandCount(decimal count, List<DangCheXianDuJiangKou> list)
{
var result = new List<DangCheXianDuJiangKou>();
var intPart = (int)Math.Floor(count);
var frac = count - intPart;
for (var i = 0; i < intPart; i++) result.Add(list[i % list.Count]);
if (frac > 0) result.Add(list[intPart % list.Count]);
return result;
}
// ==================== 内部:等级 ====================
/// <summary>挡车工等级:木桶短板法,六项各自定级后取最低分</summary>
private (string Dengji, decimal Dengjifen) CalDengJi(CalcContext ctx, string guige, double piancha,
double dacha, decimal qingjie, decimal jiejing, decimal erdu, decimal sandu, ConfGyzk cfg)
{
var zbs = ctx.Djzb.Where(z => z.Guige == guige).ToList();
if (zbs.Count == 0) return (cfg.QsDengji, cfg.QsDengjifen);
var candidates = new List<ShengsiDingjizhibiao>();
AddBest(candidates, zbs.Where(z => z.Piancha >= piancha));
AddBest(candidates, zbs.Where(z => z.Dacha >= dacha));
AddBest(candidates, zbs.Where(z => z.Qingjie <= (double)qingjie));
AddBest(candidates, zbs.Where(z => z.Jiejing <= (double)jiejing));
AddBest(candidates, zbs.Where(z => z.Erdu >= (double)erdu));
AddBest(candidates, zbs.Where(z => z.Sandu >= (double)sandu));
if (candidates.Count == 0) return (cfg.QsDengji, cfg.QsDengjifen);
var worst = candidates.OrderBy(z => z.GradeScore).First();
return (worst.Grade, (decimal)worst.GradeScore);
}
private static void AddBest(List<ShengsiDingjizhibiao> target, IEnumerable<ShengsiDingjizhibiao> src)
{
var best = src.OrderByDescending(z => z.GradeScore).FirstOrDefault();
if (best != null) target.Add(best);
}
/// <summary>车头工等级:按挡车工丝量加权平均等级分后回落取整档</summary>
private (string Dengji, decimal Dengjifen) CalCtgDengji(CalcContext ctx, List<CalcLdcct> dcgRows,
DateTime date, int jizu, long gyzkId, string guige, ConfGyzk cfg)
{
var related = dcgRows.Where(x => x.Date.Date == date && x.Jizu == jizu && x.GyzkId == gyzkId).ToList();
var siliangSum = related.Sum(x => x.ClSiliang);
if (siliangSum <= 0) return (cfg.QsDengji, cfg.QsDengjifen);
var weighted = related.Sum(x => x.ClSiliang * x.Dengjifen) / siliangSum;
var zbs = ctx.Djzb.Where(z => z.Guige == guige && (decimal)z.GradeScore <= weighted)
.OrderByDescending(z => z.GradeScore).FirstOrDefault();
return zbs != null ? (zbs.Grade, (decimal)zbs.GradeScore) : (cfg.QsDengji, cfg.QsDengjifen);
}
// ==================== 内部:单价 / 金额 ====================
/// <summary>按绪数档取岗位基数金额:&lt;60→50档,&lt;70→60档,&lt;100→70档,否则 100 档</summary>
private static decimal GetDcgDanJiaBase(decimal xushu, ConfDcg conf)
{
if (xushu < 60m) return conf.JineJisuanjishu50;
if (xushu < 70m) return conf.JineJisuanjishu60;
if (xushu < 100m) return conf.JineJisuanjishu70;
return conf.JineJisuanjishu100;
}
/// <summary>缫折考核:±2kg 死区不奖不扣,超出部分按封顶 clamp,每 1kg 每 100kg 丝 奖扣 price 元</summary>
private static decimal CalJineSaozhe(decimal saoZheCfg, decimal saozhe, decimal siliang, int cap, decimal price)
{
if (Math.Abs(saoZheCfg - saozhe) <= SaozheDeadZone) return 0m;
var diff = saoZheCfg - saozhe;
diff = diff < 0 ? diff + SaozheDeadZone : diff - SaozheDeadZone;
if (diff > cap) diff = cap;
if (diff < -cap) diff = -cap;
return Math.Round(diff * (siliang / 1000m / 100m) * price, 2, MidpointRounding.AwayFromZero);
}
private static string BanciName(int banci) => banci switch { 1 => "甲", 2 => "乙", _ => banci.ToString() };
}
@@ -0,0 +1,225 @@
using F9WebAiTest.Application.Common;
using F9WebAiTest.Application.Dtos.PayRoll;
using F9WebAiTest.Domain.Common;
using F9WebAiTest.Domain.Entities.PayRoll;
using SysDept = F9WebAiTest.Domain.Entities.SYS.Departments;
using SysEmp = F9WebAiTest.Domain.Entities.SYS.Employees;
using SysWt = F9WebAiTest.Domain.Entities.SYS.WorkTypes;
namespace F9WebAiTest.Application.Services.PayRoll;
/// <summary>薪资配置-账期(CONFZQ):考勤统计的「前缫出勤天数」来源</summary>
public interface IConfZqService
{
Task<List<ConfZqDto>> GetListAsync(int? year);
Task<ConfZqDto> GetByYearMonthAsync(int year, int month);
Task<long> SaveAsync(ConfZqDto dto);
Task DeleteAsync(long id);
}
/// <summary>薪资配置-职工(CONFEMPLOYEE):法定假小时数 / 是否学工</summary>
public interface IConfEmployeeService
{
Task<List<ConfEmployeeDto>> GetListAsync(long? employeeId, long? worktypeId);
Task<long> SaveAsync(ConfEmployeeDto dto);
Task DeleteAsync(long id);
}
/// <summary>岗位胜任考核:考核结果决定加班费单价档位</summary>
public interface IGwsrkhService
{
Task<List<GwsrkhDto>> GetListAsync(int year, int month, long depId);
Task<long> SaveAsync(GwsrkhDto dto);
Task DeleteAsync(long id);
}
public class ConfZqService : IConfZqService
{
private readonly IRepository<ConfZq> _repo;
public ConfZqService(IRepository<ConfZq> repo) => _repo = repo;
public async Task<List<ConfZqDto>> GetListAsync(int? year)
{
var all = await _repo.GetAllAsync();
var q = all.AsQueryable();
if (year.HasValue) q = q.Where(x => x.Year == year.Value);
return q.ToList().OrderByDescending(x => x.Year).ThenByDescending(x => x.Month)
.Select(x => new ConfZqDto
{
Id = x.Id, Year = x.Year, Month = x.Month,
QsQuanqinDays = x.QsQuanqinDays, Memo = x.Memo ?? ""
}).ToList();
}
public async Task<ConfZqDto> GetByYearMonthAsync(int year, int month)
{
var hit = await _repo.FirstOrDefaultAsync(x => x.Year == year && x.Month == month);
return hit == null
? new ConfZqDto { Year = year, Month = month }
: new ConfZqDto { Id = hit.Id, Year = hit.Year, Month = hit.Month, QsQuanqinDays = hit.QsQuanqinDays, Memo = hit.Memo ?? "" };
}
public async Task<long> SaveAsync(ConfZqDto dto)
{
if (dto.Year <= 0 || dto.Month <= 0) throw new ServiceException("请填写年度与月份");
var exist = await _repo.FirstOrDefaultAsync(x => x.Year == dto.Year && x.Month == dto.Month && x.Id != dto.Id);
if (exist != null) throw new ServiceException($"{dto.Year} 年 {dto.Month} 月的账期薪资配置已存在");
ConfZq entity;
if (dto.Id > 0)
{
entity = await _repo.GetByIdAsync(dto.Id);
if (entity == null) throw new ServiceException("配置不存在", 404);
}
else entity = new ConfZq();
entity.Year = dto.Year;
entity.Month = dto.Month;
entity.QsQuanqinDays = dto.QsQuanqinDays;
entity.Memo = dto.Memo ?? "";
if (dto.Id > 0) await _repo.UpdateAsync(entity); else await _repo.AddAsync(entity);
return entity.Id;
}
public async Task DeleteAsync(long id)
{
var e = await _repo.GetByIdAsync(id);
if (e == null) throw new ServiceException("配置不存在", 404);
await _repo.DeleteAsync(e);
}
}
public class ConfEmployeeService : IConfEmployeeService
{
private readonly IRepository<ConfEmployee> _repo;
private readonly IRepository<SysEmp.Employee> _empRepo;
private readonly IRepository<SysWt.WorkType> _wtRepo;
public ConfEmployeeService(IRepository<ConfEmployee> repo, IRepository<SysEmp.Employee> empRepo,
IRepository<SysWt.WorkType> wtRepo)
{
_repo = repo; _empRepo = empRepo; _wtRepo = wtRepo;
}
public async Task<List<ConfEmployeeDto>> GetListAsync(long? employeeId, long? worktypeId)
{
var all = await _repo.GetAllAsync();
var q = all.AsQueryable();
if (employeeId.HasValue && employeeId.Value > 0) q = q.Where(x => x.EmployeeId == employeeId.Value);
if (worktypeId.HasValue && worktypeId.Value > 0) q = q.Where(x => x.WorktypeId == worktypeId.Value);
var emps = (await _empRepo.GetAllAsync()).ToDictionary(e => e.Id);
var wts = (await _wtRepo.GetAllAsync()).ToDictionary(w => w.Id, w => w.WorkName);
return q.ToList().OrderBy(x => x.EmployeeId).Select(x => new ConfEmployeeDto
{
Id = x.Id,
EmployeeId = x.EmployeeId,
EmployeeCode = emps.TryGetValue(x.EmployeeId, out var e) ? (e.Code ?? "") : "",
EmployeeName = emps.TryGetValue(x.EmployeeId, out var e2) ? (e2.Name ?? "") : "",
WorktypeId = x.WorktypeId,
WorktypeName = wts.TryGetValue(x.WorktypeId, out var wn) ? wn : "",
HoursFadingBz = x.HoursFadingBz,
HoursFadingGz = x.HoursFadingGz,
IsXuegong = x.IsXuegong,
Memo = x.Memo ?? ""
}).ToList();
}
public async Task<long> SaveAsync(ConfEmployeeDto dto)
{
if (dto.EmployeeId <= 0) throw new ServiceException("请选择职工");
var exist = await _repo.FirstOrDefaultAsync(x => x.EmployeeId == dto.EmployeeId && x.WorktypeId == dto.WorktypeId && x.Id != dto.Id);
if (exist != null) throw new ServiceException("该职工该工种的薪资配置已存在");
ConfEmployee entity;
if (dto.Id > 0)
{
entity = await _repo.GetByIdAsync(dto.Id);
if (entity == null) throw new ServiceException("配置不存在", 404);
}
else entity = new ConfEmployee();
entity.EmployeeId = dto.EmployeeId;
entity.WorktypeId = dto.WorktypeId;
entity.HoursFadingBz = dto.HoursFadingBz;
entity.HoursFadingGz = dto.HoursFadingGz;
entity.IsXuegong = dto.IsXuegong;
entity.Memo = dto.Memo ?? "";
if (dto.Id > 0) await _repo.UpdateAsync(entity); else await _repo.AddAsync(entity);
return entity.Id;
}
public async Task DeleteAsync(long id)
{
var e = await _repo.GetByIdAsync(id);
if (e == null) throw new ServiceException("配置不存在", 404);
await _repo.DeleteAsync(e);
}
}
public class GwsrkhService : IGwsrkhService
{
private readonly IRepository<Gwsrkh> _repo;
private readonly IRepository<SysDept.Department> _depRepo;
private readonly IRepository<SysEmp.Employee> _empRepo;
public GwsrkhService(IRepository<Gwsrkh> repo, IRepository<SysDept.Department> depRepo,
IRepository<SysEmp.Employee> empRepo)
{
_repo = repo; _depRepo = depRepo; _empRepo = empRepo;
}
public async Task<List<GwsrkhDto>> GetListAsync(int year, int month, long depId)
{
var all = await _repo.GetAllAsync();
var q = all.AsQueryable().Where(x => x.Year == year && x.Month == month);
if (depId > 0) q = q.Where(x => x.DepId == depId);
var depts = (await _depRepo.GetAllAsync()).ToDictionary(d => d.Id, d => d.Name);
var emps = (await _empRepo.GetAllAsync()).ToDictionary(e => e.Id);
return q.ToList().OrderBy(x => x.EmployeeId).Select(x => new GwsrkhDto
{
Id = x.Id, Year = x.Year, Month = x.Month,
DepId = x.DepId, DepName = depts.TryGetValue(x.DepId, out var dn) ? dn : "",
EmployeeId = x.EmployeeId,
EmployeeCode = emps.TryGetValue(x.EmployeeId, out var e) ? (e.Code ?? "") : "",
EmployeeName = emps.TryGetValue(x.EmployeeId, out var e2) ? (e2.Name ?? "") : "",
KhJieguo = x.KhJieguo ?? "",
Memo = x.Memo ?? ""
}).ToList();
}
public async Task<long> SaveAsync(GwsrkhDto dto)
{
if (dto.EmployeeId <= 0) throw new ServiceException("请选择职工");
if (string.IsNullOrWhiteSpace(dto.KhJieguo)) throw new ServiceException("请选择考核结果");
var exist = await _repo.FirstOrDefaultAsync(x => x.Year == dto.Year && x.Month == dto.Month
&& x.EmployeeId == dto.EmployeeId && x.Id != dto.Id);
if (exist != null) throw new ServiceException("该职工本月考核结果已存在");
Gwsrkh entity;
if (dto.Id > 0)
{
entity = await _repo.GetByIdAsync(dto.Id);
if (entity == null) throw new ServiceException("记录不存在", 404);
}
else entity = new Gwsrkh();
entity.Year = dto.Year;
entity.Month = dto.Month;
entity.DepId = dto.DepId;
entity.EmployeeId = dto.EmployeeId;
entity.KhJieguo = dto.KhJieguo.Trim();
entity.Memo = dto.Memo ?? "";
if (dto.Id > 0) await _repo.UpdateAsync(entity); else await _repo.AddAsync(entity);
return entity.Id;
}
public async Task DeleteAsync(long id)
{
var e = await _repo.GetByIdAsync(id);
if (e == null) throw new ServiceException("记录不存在", 404);
await _repo.DeleteAsync(e);
}
}
@@ -0,0 +1,493 @@
using F9WebAiTest.Application.Common;
using F9WebAiTest.Application.Dtos.PayRoll;
using F9WebAiTest.Domain.Common;
using F9WebAiTest.Domain.Entities.Base;
using F9WebAiTest.Domain.Entities.PayRoll;
using SysDept = F9WebAiTest.Domain.Entities.SYS.Departments;
using SysWt = F9WebAiTest.Domain.Entities.SYS.WorkTypes;
namespace F9WebAiTest.Application.Services.PayRoll;
// 薪资计算依赖的 5 套配置(对应 F9 PAYROLL 下的 CONFGYZK / CONFGZ / CONFDCG / DCXDJK / XinZiJiaGePeiZhi)
// ==================== 薪资配置-工艺庄口 ====================
public interface IConfGyzkService
{
Task<List<ConfGyzkListItemDto>> GetListAsync(int? year, int? month);
Task<ConfGyzkSaveDto> GetForEditAsync(long id);
Task<long> SaveAsync(ConfGyzkSaveDto dto);
Task DeleteAsync(long id);
}
public class ConfGyzkService : IConfGyzkService
{
private readonly IRepository<ConfGyzk> _repo;
private readonly IRepository<CraftZhuangKou> _zkRepo;
public ConfGyzkService(IRepository<ConfGyzk> repo, IRepository<CraftZhuangKou> zkRepo)
{
_repo = repo;
_zkRepo = zkRepo;
}
public async Task<List<ConfGyzkListItemDto>> GetListAsync(int? year, int? month)
{
var all = await _repo.GetAllAsync();
var q = all.AsQueryable();
if (year.HasValue) q = q.Where(x => x.Year == year.Value);
if (month.HasValue) q = q.Where(x => x.Month == month.Value);
var items = q.OrderByDescending(x => x.Year).ThenByDescending(x => x.Month).ThenBy(x => x.GyzkId).ToList();
var zks = await _zkRepo.GetAllAsync();
var zkDict = zks.ToDictionary(z => z.Id, z => z.ZhuangKouName);
return items.Select(x => new ConfGyzkListItemDto
{
Id = x.Id, Year = x.Year, Month = x.Month, GyzkId = x.GyzkId,
GyzkName = zkDict.GetValueOrDefault(x.GyzkId, ""),
Guige = x.Guige, QsTaichan = x.QsTaichan, QsDengji = x.QsDengji, QsDengjifen = x.QsDengjifen,
SaoZhe = x.SaoZhe, MeiTongJianLiang = x.MeiTongJianLiang, LuoSiTongShu = x.LuoSiTongShu,
MeiCheSiLiang = x.MeiCheSiLiang, DcgJijianXishu = x.DcgJijianXishu, CtgJijianXishu = x.CtgJijianXishu,
GyzkLtwxKs = x.GyzkLtwxKs, GyzkLtwxl = x.GyzkLtwxl, XjChanliangPrice = x.XjChanliangPrice
}).ToList();
}
public async Task<ConfGyzkSaveDto> GetForEditAsync(long id)
{
var e = await _repo.GetByIdAsync(id);
if (e == null) throw new ServiceException("工艺庄口薪资配置不存在或已删除", 404);
return new ConfGyzkSaveDto
{
Id = e.Id, Year = e.Year, Month = e.Month, GyzkId = e.GyzkId, Guige = e.Guige,
QsTaichan = e.QsTaichan, QsDengji = e.QsDengji, QsDengjifen = e.QsDengjifen, SaoZhe = e.SaoZhe,
MeiTongJianLiang = e.MeiTongJianLiang, LuoSiTongShu = e.LuoSiTongShu, MeiCheSiLiang = e.MeiCheSiLiang,
DcgJijianXishu = e.DcgJijianXishu, CtgJijianXishu = e.CtgJijianXishu,
GyzkLtwxKs = e.GyzkLtwxKs, GyzkLtwxl = e.GyzkLtwxl, XjChanliangPrice = e.XjChanliangPrice
};
}
public async Task<long> SaveAsync(ConfGyzkSaveDto dto)
{
if (dto.GyzkId == 0) throw new ServiceException("请选择工艺庄口");
if (dto.Year <= 0 || dto.Month <= 0) throw new ServiceException("年度/月份无效");
ConfGyzk e;
if (dto.Id > 0)
{
e = await _repo.GetByIdAsync(dto.Id);
if (e == null) throw new ServiceException("工艺庄口薪资配置不存在或已删除", 404);
var dup = await _repo.FirstOrDefaultAsync(x => x.Id != dto.Id && x.Year == dto.Year
&& x.Month == dto.Month && x.GyzkId == dto.GyzkId);
if (dup != null) throw new ServiceException("该年度月份已存在此庄口的配置!");
}
else
{
var dup = await _repo.AnyAsync(x => x.Year == dto.Year && x.Month == dto.Month && x.GyzkId == dto.GyzkId);
if (dup) throw new ServiceException("该年度月份已存在此庄口的配置!");
e = new ConfGyzk();
await _repo.AddAsync(e);
}
e.Year = dto.Year;
e.Month = dto.Month;
e.GyzkId = dto.GyzkId;
e.Guige = dto.Guige ?? "";
e.QsTaichan = dto.QsTaichan;
e.QsDengji = dto.QsDengji ?? "";
e.QsDengjifen = dto.QsDengjifen;
e.SaoZhe = dto.SaoZhe;
e.MeiTongJianLiang = dto.MeiTongJianLiang;
e.LuoSiTongShu = dto.LuoSiTongShu;
e.MeiCheSiLiang = dto.MeiCheSiLiang;
e.DcgJijianXishu = dto.DcgJijianXishu;
e.CtgJijianXishu = dto.CtgJijianXishu;
e.GyzkLtwxKs = dto.GyzkLtwxKs;
e.GyzkLtwxl = dto.GyzkLtwxl;
e.XjChanliangPrice = dto.XjChanliangPrice;
await _repo.UpdateAsync(e);
return e.Id;
}
public async Task DeleteAsync(long id)
{
var e = await _repo.GetByIdAsync(id);
if (e == null) throw new ServiceException("工艺庄口薪资配置不存在或已删除", 404);
await _repo.HardDeleteAsync(e);
}
}
// ==================== 薪资配置-工种 ====================
public interface IConfGzService
{
Task<List<ConfGzListItemDto>> GetListAsync();
Task<ConfGzSaveDto> GetForEditAsync(long id);
Task<long> SaveAsync(ConfGzSaveDto dto);
Task DeleteAsync(long id);
}
public class ConfGzService : IConfGzService
{
private readonly IRepository<ConfGz> _repo;
private readonly IRepository<SysWt.WorkType> _wtRepo;
public ConfGzService(IRepository<ConfGz> repo, IRepository<SysWt.WorkType> wtRepo)
{
_repo = repo;
_wtRepo = wtRepo;
}
public async Task<List<ConfGzListItemDto>> GetListAsync()
{
var items = await _repo.GetAllAsync();
var wts = await _wtRepo.GetAllAsync();
var wtDict = wts.ToDictionary(w => w.Id, w => w.WorkName);
return items.Select(x => ToDto(x, wtDict)).ToList();
}
public async Task<ConfGzSaveDto> GetForEditAsync(long id)
{
var e = await _repo.GetByIdAsync(id);
if (e == null) throw new ServiceException("工种薪资配置不存在或已删除", 404);
return FromEntity(e);
}
public async Task<long> SaveAsync(ConfGzSaveDto dto)
{
if (dto.WorkTypeId == 0) throw new ServiceException("请选择工种");
ConfGz e;
if (dto.Id > 0)
{
e = await _repo.GetByIdAsync(dto.Id);
if (e == null) throw new ServiceException("工种薪资配置不存在或已删除", 404);
var dup = await _repo.FirstOrDefaultAsync(x => x.Id != dto.Id && x.WorkTypeId == dto.WorkTypeId);
if (dup != null) throw new ServiceException("该工种已存在薪资配置!");
}
else
{
var dup = await _repo.AnyAsync(x => x.WorkTypeId == dto.WorkTypeId);
if (dup) throw new ServiceException("该工种已存在薪资配置!");
e = new ConfGz();
await _repo.AddAsync(e);
}
Apply(e, dto);
await _repo.UpdateAsync(e);
return e.Id;
}
public async Task DeleteAsync(long id)
{
var e = await _repo.GetByIdAsync(id);
if (e == null) throw new ServiceException("工种薪资配置不存在或已删除", 404);
await _repo.HardDeleteAsync(e);
}
private static ConfGzListItemDto ToDto(ConfGz x, Dictionary<long, string> wtDict) => new()
{
Id = x.Id, WorkTypeId = x.WorkTypeId, WorkTypeName = wtDict.GetValueOrDefault(x.WorkTypeId, ""),
HoursPerday = x.HoursPerday, QqDaysStandard = x.QqDaysStandard, QqHoursMaxQj = x.QqHoursMaxQj,
QqHoursDkMinjiaban = x.QqHoursDkMinjiaban, QqHoursDkQingjia = x.QqHoursDkQingjia,
XgJineQuanqin = x.XgJineQuanqin, XgJineGangwei = x.XgJineGangwei, JineNiangong = x.JineNiangong,
JineQuanqin = x.JineQuanqin, JineGangwei = x.JineGangwei, JineYuekhJixiao = x.JineYuekhJixiao,
JineGwkh = x.JineGwkh, JineYueticheng = x.JineYueticheng, JineJtkhYear = x.JineJtkhYear,
JineJisuanjishu = x.JineJisuanjishu, JineYeban = x.JineYeban, JineJiaotongButie = x.JineJiaotongButie,
LiudongJineGuding = x.LiudongJineGuding, JiaqiJineKuangongKk = x.JiaqiJineKuangongKk,
JiaqiJineFadingGz = x.JiaqiJineFadingGz, JiaqiJineFadingBz = x.JiaqiJineFadingBz,
JiaqiJineHunjiaBz = x.JiaqiJineHunjiaBz, JiaqiJineSangjiaBz = x.JiaqiJineSangjiaBz,
JiaqiJineBingjiaBz = x.JiaqiJineBingjiaBz, JiaqiJineLunxiuBz = x.JiaqiJineLunxiuBz,
JiabanJineBz = x.JiabanJineBz, Jiaqi_Jine_TiaoxiuBz = x.Jiaqi_Jine_TiaoxiuBz,
Jiaqi_Jine_NianjiaBz = x.Jiaqi_Jine_NianjiaBz, GwsrkhJineQianhege = x.GwsrkhJineQianhege,
GwsrkhJineQiwang = x.GwsrkhJineQiwang, GwsrkhJineHege = x.GwsrkhJineHege,
GwsrkhJineShengren = x.GwsrkhJineShengren, GwsrkhJineChaoshengren = x.GwsrkhJineChaoshengren,
Jiaban_GwsrkhJineQianhege = x.Jiaban_GwsrkhJineQianhege, Jiaban_GwsrkhJineQiwang = x.Jiaban_GwsrkhJineQiwang,
Jiaban_GwsrkhJineHege = x.Jiaban_GwsrkhJineHege, Jiaban_GwsrkhJineShengren = x.Jiaban_GwsrkhJineShengren,
Jiaban_GwsrkhJineChaoshengren = x.Jiaban_GwsrkhJineChaoshengren
};
private static ConfGzSaveDto FromEntity(ConfGz x) => new()
{
Id = x.Id, WorkTypeId = x.WorkTypeId, HoursPerday = x.HoursPerday, QqDaysStandard = x.QqDaysStandard,
QqHoursMaxQj = x.QqHoursMaxQj, QqHoursDkMinjiaban = x.QqHoursDkMinjiaban, QqHoursDkQingjia = x.QqHoursDkQingjia,
XgJineQuanqin = x.XgJineQuanqin, XgJineGangwei = x.XgJineGangwei, JineNiangong = x.JineNiangong,
JineQuanqin = x.JineQuanqin, JineGangwei = x.JineGangwei, JineYuekhJixiao = x.JineYuekhJixiao,
JineGwkh = x.JineGwkh, JineYueticheng = x.JineYueticheng, JineJtkhYear = x.JineJtkhYear,
JineJisuanjishu = x.JineJisuanjishu, JineYeban = x.JineYeban, JineJiaotongButie = x.JineJiaotongButie,
LiudongJineGuding = x.LiudongJineGuding, JiaqiJineKuangongKk = x.JiaqiJineKuangongKk,
JiaqiJineFadingGz = x.JiaqiJineFadingGz, JiaqiJineFadingBz = x.JiaqiJineFadingBz,
JiaqiJineHunjiaBz = x.JiaqiJineHunjiaBz, JiaqiJineSangjiaBz = x.JiaqiJineSangjiaBz,
JiaqiJineBingjiaBz = x.JiaqiJineBingjiaBz, JiaqiJineLunxiuBz = x.JiaqiJineLunxiuBz,
JiabanJineBz = x.JiabanJineBz, Jiaqi_Jine_TiaoxiuBz = x.Jiaqi_Jine_TiaoxiuBz,
Jiaqi_Jine_NianjiaBz = x.Jiaqi_Jine_NianjiaBz, GwsrkhJineQianhege = x.GwsrkhJineQianhege,
GwsrkhJineQiwang = x.GwsrkhJineQiwang, GwsrkhJineHege = x.GwsrkhJineHege,
GwsrkhJineShengren = x.GwsrkhJineShengren, GwsrkhJineChaoshengren = x.GwsrkhJineChaoshengren,
Jiaban_GwsrkhJineQianhege = x.Jiaban_GwsrkhJineQianhege, Jiaban_GwsrkhJineQiwang = x.Jiaban_GwsrkhJineQiwang,
Jiaban_GwsrkhJineHege = x.Jiaban_GwsrkhJineHege, Jiaban_GwsrkhJineShengren = x.Jiaban_GwsrkhJineShengren,
Jiaban_GwsrkhJineChaoshengren = x.Jiaban_GwsrkhJineChaoshengren
};
private static void Apply(ConfGz e, ConfGzSaveDto d)
{
e.WorkTypeId = d.WorkTypeId;
e.HoursPerday = d.HoursPerday; e.QqDaysStandard = d.QqDaysStandard;
e.QqHoursMaxQj = d.QqHoursMaxQj; e.QqHoursDkMinjiaban = d.QqHoursDkMinjiaban;
e.QqHoursDkQingjia = d.QqHoursDkQingjia; e.XgJineQuanqin = d.XgJineQuanqin;
e.XgJineGangwei = d.XgJineGangwei; e.JineNiangong = d.JineNiangong;
e.JineQuanqin = d.JineQuanqin; e.JineGangwei = d.JineGangwei;
e.JineYuekhJixiao = d.JineYuekhJixiao; e.JineGwkh = d.JineGwkh;
e.JineYueticheng = d.JineYueticheng; e.JineJtkhYear = d.JineJtkhYear;
e.JineJisuanjishu = d.JineJisuanjishu; e.JineYeban = d.JineYeban;
e.JineJiaotongButie = d.JineJiaotongButie; e.LiudongJineGuding = d.LiudongJineGuding;
e.JiaqiJineKuangongKk = d.JiaqiJineKuangongKk; e.JiaqiJineFadingGz = d.JiaqiJineFadingGz;
e.JiaqiJineFadingBz = d.JiaqiJineFadingBz; e.JiaqiJineHunjiaBz = d.JiaqiJineHunjiaBz;
e.JiaqiJineSangjiaBz = d.JiaqiJineSangjiaBz; e.JiaqiJineBingjiaBz = d.JiaqiJineBingjiaBz;
e.JiaqiJineLunxiuBz = d.JiaqiJineLunxiuBz; e.JiabanJineBz = d.JiabanJineBz;
e.Jiaqi_Jine_TiaoxiuBz = d.Jiaqi_Jine_TiaoxiuBz; e.Jiaqi_Jine_NianjiaBz = d.Jiaqi_Jine_NianjiaBz;
e.GwsrkhJineQianhege = d.GwsrkhJineQianhege; e.GwsrkhJineQiwang = d.GwsrkhJineQiwang;
e.GwsrkhJineHege = d.GwsrkhJineHege; e.GwsrkhJineShengren = d.GwsrkhJineShengren;
e.GwsrkhJineChaoshengren = d.GwsrkhJineChaoshengren;
e.Jiaban_GwsrkhJineQianhege = d.Jiaban_GwsrkhJineQianhege;
e.Jiaban_GwsrkhJineQiwang = d.Jiaban_GwsrkhJineQiwang;
e.Jiaban_GwsrkhJineHege = d.Jiaban_GwsrkhJineHege;
e.Jiaban_GwsrkhJineShengren = d.Jiaban_GwsrkhJineShengren;
e.Jiaban_GwsrkhJineChaoshengren = d.Jiaban_GwsrkhJineChaoshengren;
}
}
// ==================== 薪资配置-挡车/车头岗位 ====================
public interface IConfDcgService
{
Task<List<ConfDcgListItemDto>> GetListAsync();
Task<ConfDcgSaveDto> GetForEditAsync(long id);
Task<long> SaveAsync(ConfDcgSaveDto dto);
Task DeleteAsync(long id);
}
public class ConfDcgService : IConfDcgService
{
private readonly IRepository<ConfDcg> _repo;
private readonly IRepository<SysDept.Department> _depRepo;
public ConfDcgService(IRepository<ConfDcg> repo, IRepository<SysDept.Department> depRepo)
{
_repo = repo;
_depRepo = depRepo;
}
public async Task<List<ConfDcgListItemDto>> GetListAsync()
{
var items = await _repo.GetAllAsync();
var deps = await _depRepo.GetAllAsync();
var depDict = deps.ToDictionary(d => d.Id, d => d.Name);
return items.Select(x => new ConfDcgListItemDto
{
Id = x.Id, DepId = x.DepId, DepName = depDict.GetValueOrDefault(x.DepId, ""),
JineJisuanjishu50 = x.JineJisuanjishu50, JineJisuanjishu60 = x.JineJisuanjishu60,
JineJisuanjishu70 = x.JineJisuanjishu70, JineJisuanjishu100 = x.JineJisuanjishu100,
JineCtg = x.JineCtg
}).ToList();
}
public async Task<ConfDcgSaveDto> GetForEditAsync(long id)
{
var e = await _repo.GetByIdAsync(id);
if (e == null) throw new ServiceException("挡车岗位薪资配置不存在或已删除", 404);
return new ConfDcgSaveDto
{
Id = e.Id, DepId = e.DepId, JineJisuanjishu50 = e.JineJisuanjishu50,
JineJisuanjishu60 = e.JineJisuanjishu60, JineJisuanjishu70 = e.JineJisuanjishu70,
JineJisuanjishu100 = e.JineJisuanjishu100, JineCtg = e.JineCtg
};
}
public async Task<long> SaveAsync(ConfDcgSaveDto dto)
{
if (dto.DepId == 0) throw new ServiceException("请选择部门");
ConfDcg e;
if (dto.Id > 0)
{
e = await _repo.GetByIdAsync(dto.Id);
if (e == null) throw new ServiceException("挡车岗位薪资配置不存在或已删除", 404);
var dup = await _repo.FirstOrDefaultAsync(x => x.Id != dto.Id && x.DepId == dto.DepId);
if (dup != null) throw new ServiceException("该部门已存在挡车岗位薪资配置!");
}
else
{
var dup = await _repo.AnyAsync(x => x.DepId == dto.DepId);
if (dup) throw new ServiceException("该部门已存在挡车岗位薪资配置!");
e = new ConfDcg();
await _repo.AddAsync(e);
}
e.DepId = dto.DepId;
e.JineJisuanjishu50 = dto.JineJisuanjishu50;
e.JineJisuanjishu60 = dto.JineJisuanjishu60;
e.JineJisuanjishu70 = dto.JineJisuanjishu70;
e.JineJisuanjishu100 = dto.JineJisuanjishu100;
e.JineCtg = dto.JineCtg;
await _repo.UpdateAsync(e);
return e.Id;
}
public async Task DeleteAsync(long id)
{
var e = await _repo.GetByIdAsync(id);
if (e == null) throw new ServiceException("挡车岗位薪资配置不存在或已删除", 404);
await _repo.HardDeleteAsync(e);
}
}
// ==================== 挡车纤度奖扣 ====================
public interface IDcxdjkService
{
Task<List<DcxdjkListItemDto>> GetListAsync(string guige);
Task<DcxdjkSaveDto> GetForEditAsync(long id);
Task<long> SaveAsync(DcxdjkSaveDto dto);
Task DeleteAsync(long id);
}
public class DcxdjkService : IDcxdjkService
{
private readonly IRepository<DangCheXianDuJiangKou> _repo;
public DcxdjkService(IRepository<DangCheXianDuJiangKou> repo) => _repo = repo;
public async Task<List<DcxdjkListItemDto>> GetListAsync(string guige)
{
var items = await _repo.GetAllAsync();
var q = items.AsQueryable();
if (!string.IsNullOrWhiteSpace(guige)) q = q.Where(x => x.GuiGe == guige);
return q.OrderBy(x => x.GuiGe).ThenBy(x => x.XianDu).ToList()
.Select(x => new DcxdjkListItemDto
{
Id = x.Id, GuiGe = x.GuiGe, XianDu = x.XianDu, JiangLi = x.JiangLi, KouKuan = x.KouKuan
}).ToList();
}
public async Task<DcxdjkSaveDto> GetForEditAsync(long id)
{
var e = await _repo.GetByIdAsync(id);
if (e == null) throw new ServiceException("挡车纤度奖扣不存在或已删除", 404);
return new DcxdjkSaveDto { Id = e.Id, GuiGe = e.GuiGe, XianDu = e.XianDu, JiangLi = e.JiangLi, KouKuan = e.KouKuan };
}
public async Task<long> SaveAsync(DcxdjkSaveDto dto)
{
if (string.IsNullOrWhiteSpace(dto.GuiGe)) throw new ServiceException("请填写规格");
DangCheXianDuJiangKou e;
if (dto.Id > 0)
{
e = await _repo.GetByIdAsync(dto.Id);
if (e == null) throw new ServiceException("挡车纤度奖扣不存在或已删除", 404);
var dup = await _repo.FirstOrDefaultAsync(x => x.Id != dto.Id && x.GuiGe == dto.GuiGe && x.XianDu == dto.XianDu);
if (dup != null) throw new ServiceException("该规格已存在相同纤度的奖扣配置!");
}
else
{
var dup = await _repo.AnyAsync(x => x.GuiGe == dto.GuiGe && x.XianDu == dto.XianDu);
if (dup) throw new ServiceException("该规格已存在相同纤度的奖扣配置!");
e = new DangCheXianDuJiangKou();
await _repo.AddAsync(e);
}
e.GuiGe = dto.GuiGe.Trim();
e.XianDu = dto.XianDu;
e.JiangLi = dto.JiangLi;
e.KouKuan = dto.KouKuan;
await _repo.UpdateAsync(e);
return e.Id;
}
public async Task DeleteAsync(long id)
{
var e = await _repo.GetByIdAsync(id);
if (e == null) throw new ServiceException("挡车纤度奖扣不存在或已删除", 404);
await _repo.HardDeleteAsync(e);
}
}
// ==================== 薪资价格配置 ====================
public interface IXzjgService
{
Task<List<XzjgListItemDto>> GetListAsync(string group);
Task<XzjgSaveDto> GetForEditAsync(long id);
Task<long> SaveAsync(XzjgSaveDto dto);
Task DeleteAsync(long id);
Task<decimal> GetPriceAsync(string group, string key);
}
public class XzjgService : IXzjgService
{
private readonly IRepository<XinZiJiaGePeiZhi> _repo;
public XzjgService(IRepository<XinZiJiaGePeiZhi> repo) => _repo = repo;
public async Task<List<XzjgListItemDto>> GetListAsync(string group)
{
var items = await _repo.GetAllAsync();
var q = items.AsQueryable();
if (!string.IsNullOrWhiteSpace(group)) q = q.Where(x => x.Group == group);
return q.OrderBy(x => x.Group).ThenBy(x => x.Key).ToList()
.Select(x => new XzjgListItemDto
{
Id = x.Id, Group = x.Group, Key = x.Key, Unit = x.Unit, Price = x.Price, Memo = x.Memo
}).ToList();
}
public async Task<XzjgSaveDto> GetForEditAsync(long id)
{
var e = await _repo.GetByIdAsync(id);
if (e == null) throw new ServiceException("薪资价格配置不存在或已删除", 404);
return new XzjgSaveDto { Id = e.Id, Group = e.Group, Key = e.Key, Unit = e.Unit, Price = e.Price, Memo = e.Memo };
}
public async Task<long> SaveAsync(XzjgSaveDto dto)
{
if (string.IsNullOrWhiteSpace(dto.Group)) throw new ServiceException("请填写分组");
if (string.IsNullOrWhiteSpace(dto.Key)) throw new ServiceException("请填写项目");
var grp = dto.Group.Trim();
var key = dto.Key.Trim();
XinZiJiaGePeiZhi e;
if (dto.Id > 0)
{
e = await _repo.GetByIdAsync(dto.Id);
if (e == null) throw new ServiceException("薪资价格配置不存在或已删除", 404);
var dup = await _repo.FirstOrDefaultAsync(x => x.Id != dto.Id && x.Group == grp && x.Key == key);
if (dup != null) throw new ServiceException("该分组已存在相同的项目!");
}
else
{
var dup = await _repo.AnyAsync(x => x.Group == grp && x.Key == key);
if (dup) throw new ServiceException("该分组已存在相同的项目!");
e = new XinZiJiaGePeiZhi();
await _repo.AddAsync(e);
}
e.Group = grp;
e.Key = key;
e.Unit = dto.Unit ?? "";
e.Price = dto.Price;
e.Memo = dto.Memo ?? "";
await _repo.UpdateAsync(e);
return e.Id;
}
public async Task DeleteAsync(long id)
{
var e = await _repo.GetByIdAsync(id);
if (e == null) throw new ServiceException("薪资价格配置不存在或已删除", 404);
await _repo.HardDeleteAsync(e);
}
/// <summary>取价:无配置返回 0</summary>
public async Task<decimal> GetPriceAsync(string group, string key)
{
var e = await _repo.FirstOrDefaultAsync(x => x.Group == group && x.Key == key);
return e?.Price ?? 0m;
}
}