新增质量模块(工艺执行书 / 样茧试缫)与报表中心
质量模块: - 11 张工艺执行书表:封面、试产、选剥茧(含混茧明细)、试缫与大样、煮茧(含分车间配温、副煮车间)、自动缫(含大样)、后缫 - 10 张样茧试缫表:原料小样/综合样主表 + 选茧、解舒、茧幅、切剖、清洁洁净、茧丝百回纤度 - 服务与接口、主控指针回写(列表 √ 标记)、封面编号自增、报表数据包 - 前端 8 张报表打印版式、7 类工艺单据与 6 类调查录入弹窗、字段驱动表单/表格组件 - 菜单种子新增「质量」目录,样茧子表增加 YjssType 判别列,避免原料小样与综合样自增 Id 重号串数据 报表中心: - 报表模板管理、锐浪 grhtml5 加载与预览、全屏设计面板、数据源 Provider(落丝记录、职工花名册) 其他:同步工作进度 1-11 文档、开发数据库快照、.gitignore 忽略运行日志目录
This commit is contained in:
+11
@@ -5,6 +5,7 @@ using F9WebAiTest.Domain.Common;
|
||||
using F9WebAiTest.Domain.Entities.SYS.RBAC;
|
||||
using F9WebAiTest.Domain.Enums.SYS.RBAC;
|
||||
using NotificationTypeEntity = F9WebAiTest.Domain.Entities.Notice.NotificationType;
|
||||
using MenuEntity = F9WebAiTest.Domain.Entities.SYS.RBAC.Menu;
|
||||
using F9WebAiTest.Domain.Interfaces.SYS.RBAC.Permissions;
|
||||
using F9WebAiTest.Domain.Interfaces.SYS.RBAC.Roles;
|
||||
using MediatR;
|
||||
@@ -21,6 +22,7 @@ public class AssignRolePermissionCommandHandler(
|
||||
IRolePermissionWriteRepository rolePermissionRep,
|
||||
IPermissionWriteRepository permissionWriteRepository,
|
||||
IRepository<NotificationTypeEntity> notificationTypeRepository,
|
||||
IRepository<MenuEntity> menuRepository,
|
||||
ISnowflakeIdGenerator snowflakeIdGenerator,
|
||||
IPublisher eventBus) : IRequestHandler<RolePermission_AssignCommand, long>
|
||||
{
|
||||
@@ -46,6 +48,15 @@ public class AssignRolePermissionCommandHandler(
|
||||
command.ResourceIdentity,
|
||||
command.ResourceId);
|
||||
|
||||
// 菜单/操作权限:Code 对齐菜单上声明的权限标识(如 sys:user:add),
|
||||
// 否则 v-permission 之类按业务权限码判断的地方永远匹配不上
|
||||
if (command.ResourceType is Resource_Type_Enum.菜单 or Resource_Type_Enum.操作)
|
||||
{
|
||||
var menu = await menuRepository.GetByIdAsync(command.ResourceId);
|
||||
if (menu != null && !string.IsNullOrEmpty(menu.Permission))
|
||||
dbPermission.SetCode(menu.Permission);
|
||||
}
|
||||
|
||||
// 消息订阅权限:Code 对齐消息类型的 RequiredPermission(业务权限码),保证订阅校验一致
|
||||
if (command.ResourceType == Resource_Type_Enum.消息订阅)
|
||||
{
|
||||
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
using F9WebAiTest.Application.Dtos.SYS.Reports;
|
||||
using F9WebAiTest.Application.Interfaces.Repositories.SYS.Employees;
|
||||
using MediatR;
|
||||
|
||||
namespace F9WebAiTest.Application.CQRS.SYS.Reports.Queries;
|
||||
|
||||
/// <summary>职工花名册报表数据查询(示例,对应 DsName = RepDt_Sys_Users)</summary>
|
||||
public record GetEmployeeDirectoryRepQuery(long DepId, string Like) : IRequest<ReportDto_DataSource>;
|
||||
|
||||
public class GetEmployeeDirectoryRepQueryHandler : IRequestHandler<GetEmployeeDirectoryRepQuery, ReportDto_DataSource>
|
||||
{
|
||||
private readonly IEmployeeReadRepository _readRepository;
|
||||
|
||||
public GetEmployeeDirectoryRepQueryHandler(IEmployeeReadRepository readRepository)
|
||||
{
|
||||
_readRepository = readRepository;
|
||||
}
|
||||
|
||||
public async Task<ReportDto_DataSource> Handle(GetEmployeeDirectoryRepQuery query, CancellationToken cancellationToken)
|
||||
{
|
||||
var dto = new ReportDto_DataSource
|
||||
{
|
||||
Report_Data = new Dictionary<string, ReportDataDto_Data>()
|
||||
};
|
||||
|
||||
// 1. 通过仓储查询职工数据(部门含子部门)
|
||||
var page = await _readRepository.GetPagedAsync(query.DepId, 0, null, null,
|
||||
string.IsNullOrWhiteSpace(query.Like) ? "" : query.Like, 1, 1000);
|
||||
|
||||
// 2. 部门名称(查询条件展示用)
|
||||
var depName = query.DepId > 0 ? await _readRepository.GetDepNameAsync(query.DepId) : "";
|
||||
|
||||
// 3. 明细数据:属性名须与 .grf 模板 DetailGrid.Recordset.Field 一致
|
||||
var rows = new List<object>();
|
||||
foreach (var e in page.Items)
|
||||
{
|
||||
rows.Add(new
|
||||
{
|
||||
EmployeeCode = e.Code ?? "",
|
||||
EmployeeName = e.Name ?? "",
|
||||
Gender = e.Gender ?? "",
|
||||
DepName = e.DepName ?? "",
|
||||
WorktypeName = e.WorktypeName ?? "",
|
||||
Phone = e.Phone ?? "",
|
||||
RuzhiDate = e.RuzhiDate?.ToString("yyyy-MM-dd") ?? ""
|
||||
});
|
||||
}
|
||||
|
||||
// 4. 报表参数(模板以 StaticBox 的 Parameter 属性引用)
|
||||
var mainData = new ReportDataDto_Data { Field_Data = rows };
|
||||
mainData.Parameter_Data = new
|
||||
{
|
||||
部门 = string.IsNullOrWhiteSpace(depName) ? "(全部)" : depName,
|
||||
关键字 = string.IsNullOrWhiteSpace(query.Like) ? "(全部)" : query.Like,
|
||||
记录数 = page.Total,
|
||||
查询时间 = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
|
||||
};
|
||||
|
||||
dto.Report_Data.Add("main", mainData);
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
using F9WebAiTest.Application.Dtos.SYS.Reports;
|
||||
using F9WebAiTest.Domain.Common;
|
||||
using F9WebAiTest.Domain.Entities.Base;
|
||||
using F9WebAiTest.Domain.Entities.Prod;
|
||||
using MediatR;
|
||||
using SysDept = F9WebAiTest.Domain.Entities.SYS.Departments;
|
||||
|
||||
namespace F9WebAiTest.Application.CQRS.SYS.Reports.Queries;
|
||||
|
||||
/// <summary>
|
||||
/// 落丝记录报表查询(对应 DsName = RepDt_Prod_Lsjl)。
|
||||
/// 参数:日期范围 / 车间(含子车间)/ 机组 / 庄口 / 关键字(庄口、规格、颜色)。
|
||||
/// 输出字段名与 F9 落丝记录报表模板一致(中文列名)。
|
||||
/// </summary>
|
||||
public record GetLsjlRepQuery(
|
||||
DateTime? DateStart,
|
||||
DateTime? DateEnd,
|
||||
long DepId,
|
||||
int Jizu,
|
||||
long GyzkId,
|
||||
string Like) : IRequest<ReportDto_DataSource>;
|
||||
|
||||
public class GetLsjlRepQueryHandler : IRequestHandler<GetLsjlRepQuery, ReportDto_DataSource>
|
||||
{
|
||||
private readonly IRepository<LsjlRecord> _repo;
|
||||
private readonly IRepository<SysDept.Department> _deptRepo;
|
||||
private readonly IRepository<CraftZhuangKou> _zkRepo;
|
||||
|
||||
public GetLsjlRepQueryHandler(
|
||||
IRepository<LsjlRecord> repo,
|
||||
IRepository<SysDept.Department> deptRepo,
|
||||
IRepository<CraftZhuangKou> zkRepo)
|
||||
{
|
||||
_repo = repo;
|
||||
_deptRepo = deptRepo;
|
||||
_zkRepo = zkRepo;
|
||||
}
|
||||
|
||||
public async Task<ReportDto_DataSource> Handle(GetLsjlRepQuery query, CancellationToken cancellationToken)
|
||||
{
|
||||
var all = await _repo.GetAllAsync();
|
||||
var depts = await _deptRepo.GetAllAsync();
|
||||
var zks = await _zkRepo.GetAllAsync();
|
||||
|
||||
var deptDict = depts.GroupBy(d => d.Id).ToDictionary(g => g.Key, g => g.First().Name);
|
||||
var zkDict = zks.GroupBy(z => z.Id).ToDictionary(g => g.Key, g => g.First().ZhuangKouName);
|
||||
|
||||
var depIds = ResolveDepIds(depts, query.DepId);
|
||||
|
||||
IEnumerable<LsjlRecord> rows = all;
|
||||
if (query.DateStart.HasValue)
|
||||
{
|
||||
var start = query.DateStart.Value.Date;
|
||||
rows = rows.Where(x => x.WorkDate.Date >= start);
|
||||
}
|
||||
if (query.DateEnd.HasValue)
|
||||
{
|
||||
var end = query.DateEnd.Value.Date;
|
||||
rows = rows.Where(x => x.WorkDate.Date <= end);
|
||||
}
|
||||
if (depIds != null)
|
||||
rows = rows.Where(x => depIds.Contains(x.DeptId));
|
||||
if (query.Jizu > 0)
|
||||
rows = rows.Where(x => x.Jizu == query.Jizu);
|
||||
if (query.GyzkId > 0)
|
||||
rows = rows.Where(x => x.GyzkId == query.GyzkId);
|
||||
if (!string.IsNullOrWhiteSpace(query.Like))
|
||||
{
|
||||
var kw = query.Like.Trim();
|
||||
rows = rows.Where(x =>
|
||||
deptDict.GetValueOrDefault(x.DeptId, "").Contains(kw) ||
|
||||
zkDict.GetValueOrDefault(x.GyzkId, "").Contains(kw) ||
|
||||
(x.Guige ?? "").Contains(kw) ||
|
||||
(x.Yanse ?? "").Contains(kw));
|
||||
}
|
||||
|
||||
var list = rows
|
||||
.OrderBy(x => x.WorkDate)
|
||||
.ThenBy(x => deptDict.GetValueOrDefault(x.DeptId, ""))
|
||||
.ThenBy(x => x.Jizu)
|
||||
.ThenBy(x => x.LuosiTime)
|
||||
.ToList();
|
||||
|
||||
// 明细行:键名与 .grf 模板 DetailGrid.Recordset.Field 完全一致(中文列名)
|
||||
var fieldRows = new List<object>(list.Count);
|
||||
foreach (var x in list)
|
||||
{
|
||||
var depName = deptDict.GetValueOrDefault(x.DeptId, "");
|
||||
var zkName = zkDict.GetValueOrDefault(x.GyzkId, "");
|
||||
fieldRows.Add(new Dictionary<string, object>(StringComparer.Ordinal)
|
||||
{
|
||||
["日期"] = x.WorkDate.ToString("yyyy-MM-dd"),
|
||||
["车间"] = depName,
|
||||
["机组"] = x.Jizu,
|
||||
["庄口"] = zkName,
|
||||
["回数"] = x.Huishu,
|
||||
["颜色"] = x.Yanse ?? "",
|
||||
["半车"] = x.Isbanche ? "半车" : "整车",
|
||||
["落丝时间"] = x.LuosiTime ?? "",
|
||||
["缫丝桶数"] = x.Chetaishu,
|
||||
// 以下是备选列(模板未使用时不显示,可用于自定义模板)
|
||||
["规格"] = x.Guige ?? "",
|
||||
["车台范围"] = x.ChetaiStart > 0 ? $"{x.ChetaiStart}-{x.ChetaiEnd}" : "",
|
||||
["缫丝时长"] = Math.Round(x.Hours, 2),
|
||||
["车速"] = Math.Round(x.Chesu, 2),
|
||||
});
|
||||
}
|
||||
|
||||
var depText = query.DepId > 0 ? deptDict.GetValueOrDefault(query.DepId, "") : "(全部)";
|
||||
var zkText = query.GyzkId > 0 ? zkDict.GetValueOrDefault(query.GyzkId, "") : "(全部)";
|
||||
var paramData = new ReportDataDto_Data { Field_Data = fieldRows };
|
||||
paramData.Parameter_Data = new
|
||||
{
|
||||
开始日期 = query.DateStart?.ToString("yyyy-MM-dd") ?? "",
|
||||
结束日期 = query.DateEnd?.ToString("yyyy-MM-dd") ?? "",
|
||||
车间 = string.IsNullOrWhiteSpace(depText) ? "(全部)" : depText,
|
||||
机组 = query.Jizu > 0 ? query.Jizu.ToString() : "(全部)",
|
||||
庄口 = string.IsNullOrWhiteSpace(zkText) ? "(全部)" : zkText,
|
||||
记录数 = fieldRows.Count,
|
||||
查询时间 = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
|
||||
};
|
||||
|
||||
var dto = new ReportDto_DataSource();
|
||||
dto.Report_Data.Add("main", paramData);
|
||||
return dto;
|
||||
}
|
||||
|
||||
/// <summary>车间 Id → 自身 + 全部子孙车间 Id(null 表示不限)</summary>
|
||||
private static HashSet<long>? ResolveDepIds(List<SysDept.Department> depts, long depId)
|
||||
{
|
||||
if (depId <= 0) return null;
|
||||
|
||||
var children = depts
|
||||
.GroupBy(d => d.ParentId)
|
||||
.ToDictionary(g => g.Key, g => g.Select(d => d.Id).ToList());
|
||||
|
||||
var result = new HashSet<long> { depId };
|
||||
var stack = new Stack<long>();
|
||||
stack.Push(depId);
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
var cur = stack.Pop();
|
||||
if (!children.TryGetValue(cur, out var kids)) continue;
|
||||
foreach (var k in kids)
|
||||
if (result.Add(k)) stack.Push(k);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user