[CLAUDE] App: Contract workflow inner steps DTO + designer mirror PE (Chunk B)
Some checks failed
Deploy SOLUTION_ERP / build-deploy (push) Has been cancelled
Some checks failed
Deploy SOLUTION_ERP / build-deploy (push) Has been cancelled
DTO + Validator + Handler mở rộng cho N-stage admin designer Contract (Mig 20). Mirror PE Mig 18 pattern (PeWorkflowAdminFeatures Chunk B). WorkflowAdminFeatures: - WorkflowStepInnerStepDto record (Id, Order, DeptId, DeptName, PositionLevel, Name, SlaDays, IsRequired) - WorkflowStepDto extend +InnerSteps List - GetWorkflowAdminOverviewQueryHandler — Include InnerSteps OrderBy + resolve DeptNames cho display - CreateWorkflowStepInnerStepInput record - CreateWorkflowStepInput extend +InnerSteps (nullable, default null — backward compat existing test code positional new()) - Validator child rules cho InnerSteps (Order ≥1, DeptId not empty, PositionLevel 1-3, SlaDays ≥0) - CreateWorkflowDefinitionCommandHandler — convert InnerSteps khi build entity (atomic batch insert qua nav collection) Verify: dotnet build 0 error, 89 test pass (no regression). Pending Chunk C: ContractWorkflowService.TransitionAsync N-stage logic + legacy 2-stage fallback mirror PE pattern. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -17,6 +17,17 @@ public record WorkflowStepApproverDto(
|
|||||||
string AssignmentValue,
|
string AssignmentValue,
|
||||||
string? DisplayName); // resolved role label or user fullName
|
string? DisplayName); // resolved role label or user fullName
|
||||||
|
|
||||||
|
// Mig 20 — N-stage approval inner step level con (mirror PE Mig 18)
|
||||||
|
public record WorkflowStepInnerStepDto(
|
||||||
|
Guid Id,
|
||||||
|
int Order,
|
||||||
|
Guid DepartmentId,
|
||||||
|
string? DepartmentName,
|
||||||
|
int PositionLevel, // 1=NV, 2=PP, 3=TP
|
||||||
|
string? Name,
|
||||||
|
int? SlaDays,
|
||||||
|
bool IsRequired);
|
||||||
|
|
||||||
public record WorkflowStepDto(
|
public record WorkflowStepDto(
|
||||||
Guid Id,
|
Guid Id,
|
||||||
int Order,
|
int Order,
|
||||||
@ -24,7 +35,8 @@ public record WorkflowStepDto(
|
|||||||
string PhaseLabel,
|
string PhaseLabel,
|
||||||
string Name,
|
string Name,
|
||||||
int? SlaDays,
|
int? SlaDays,
|
||||||
List<WorkflowStepApproverDto> Approvers);
|
List<WorkflowStepApproverDto> Approvers,
|
||||||
|
List<WorkflowStepInnerStepDto> InnerSteps);
|
||||||
|
|
||||||
public record WorkflowDefinitionDto(
|
public record WorkflowDefinitionDto(
|
||||||
Guid Id,
|
Guid Id,
|
||||||
@ -84,9 +96,21 @@ public class GetWorkflowAdminOverviewQueryHandler(
|
|||||||
var definitions = await db.WorkflowDefinitions.AsNoTracking()
|
var definitions = await db.WorkflowDefinitions.AsNoTracking()
|
||||||
.Include(d => d.Steps.OrderBy(s => s.Order))
|
.Include(d => d.Steps.OrderBy(s => s.Order))
|
||||||
.ThenInclude(s => s.Approvers)
|
.ThenInclude(s => s.Approvers)
|
||||||
|
.Include(d => d.Steps)
|
||||||
|
.ThenInclude(s => s.InnerSteps.OrderBy(i => i.Order))
|
||||||
.OrderByDescending(d => d.Version)
|
.OrderByDescending(d => d.Version)
|
||||||
.ToListAsync(ct);
|
.ToListAsync(ct);
|
||||||
|
|
||||||
|
// Resolve dept names cho InnerStep.DepartmentName display (Mig 20)
|
||||||
|
var deptIds = definitions
|
||||||
|
.SelectMany(d => d.Steps).SelectMany(s => s.InnerSteps)
|
||||||
|
.Select(i => i.DepartmentId).Distinct().ToList();
|
||||||
|
var deptNames = deptIds.Count == 0
|
||||||
|
? new Dictionary<Guid, string>()
|
||||||
|
: await db.Departments.AsNoTracking()
|
||||||
|
.Where(d => deptIds.Contains(d.Id))
|
||||||
|
.ToDictionaryAsync(d => d.Id, d => d.Name, ct);
|
||||||
|
|
||||||
// Resolve user names for User-kind approvers
|
// Resolve user names for User-kind approvers
|
||||||
var userIds = definitions
|
var userIds = definitions
|
||||||
.SelectMany(d => d.Steps)
|
.SelectMany(d => d.Steps)
|
||||||
@ -130,7 +154,16 @@ public class GetWorkflowAdminOverviewQueryHandler(
|
|||||||
s.Approvers.Select(a => new WorkflowStepApproverDto(
|
s.Approvers.Select(a => new WorkflowStepApproverDto(
|
||||||
(int)a.Kind,
|
(int)a.Kind,
|
||||||
a.AssignmentValue,
|
a.AssignmentValue,
|
||||||
ResolveDisplay(a, userNames))).ToList()
|
ResolveDisplay(a, userNames))).ToList(),
|
||||||
|
s.InnerSteps.OrderBy(i => i.Order).Select(i => new WorkflowStepInnerStepDto(
|
||||||
|
i.Id,
|
||||||
|
i.Order,
|
||||||
|
i.DepartmentId,
|
||||||
|
deptNames.GetValueOrDefault(i.DepartmentId),
|
||||||
|
(int)i.PositionLevel,
|
||||||
|
i.Name,
|
||||||
|
i.SlaDays,
|
||||||
|
i.IsRequired)).ToList()
|
||||||
)).ToList());
|
)).ToList());
|
||||||
|
|
||||||
var types = Enum.GetValues<ContractType>()
|
var types = Enum.GetValues<ContractType>()
|
||||||
@ -160,12 +193,23 @@ public class GetWorkflowAdminOverviewQueryHandler(
|
|||||||
|
|
||||||
public record CreateWorkflowStepApproverInput(int Kind, string AssignmentValue);
|
public record CreateWorkflowStepApproverInput(int Kind, string AssignmentValue);
|
||||||
|
|
||||||
|
// Mig 20 — Inner step input cho designer N-stage. InnerSteps optional empty
|
||||||
|
// list → service fallback 2-stage Review/Confirm logic legacy Mig 16.
|
||||||
|
public record CreateWorkflowStepInnerStepInput(
|
||||||
|
int Order,
|
||||||
|
Guid DepartmentId,
|
||||||
|
int PositionLevel, // 1=NV, 2=PP, 3=TP
|
||||||
|
string? Name,
|
||||||
|
int? SlaDays,
|
||||||
|
bool IsRequired);
|
||||||
|
|
||||||
public record CreateWorkflowStepInput(
|
public record CreateWorkflowStepInput(
|
||||||
int Order,
|
int Order,
|
||||||
int Phase,
|
int Phase,
|
||||||
string Name,
|
string Name,
|
||||||
int? SlaDays,
|
int? SlaDays,
|
||||||
List<CreateWorkflowStepApproverInput> Approvers);
|
List<CreateWorkflowStepApproverInput> Approvers,
|
||||||
|
List<CreateWorkflowStepInnerStepInput>? InnerSteps = null);
|
||||||
|
|
||||||
public record CreateWorkflowDefinitionCommand(
|
public record CreateWorkflowDefinitionCommand(
|
||||||
ContractType ContractType,
|
ContractType ContractType,
|
||||||
@ -198,6 +242,16 @@ public class CreateWorkflowDefinitionCommandValidator : AbstractValidator<Create
|
|||||||
app.RuleFor(a => a.Kind).InclusiveBetween(1, 2);
|
app.RuleFor(a => a.Kind).InclusiveBetween(1, 2);
|
||||||
app.RuleFor(a => a.AssignmentValue).NotEmpty().MaximumLength(100);
|
app.RuleFor(a => a.AssignmentValue).NotEmpty().MaximumLength(100);
|
||||||
});
|
});
|
||||||
|
step.RuleForEach(s => s.InnerSteps!).ChildRules(inner =>
|
||||||
|
{
|
||||||
|
inner.RuleFor(i => i.Order).GreaterThanOrEqualTo(1);
|
||||||
|
inner.RuleFor(i => i.DepartmentId).NotEmpty();
|
||||||
|
inner.RuleFor(i => i.PositionLevel).InclusiveBetween(1, 3)
|
||||||
|
.WithMessage("PositionLevel: 1=NV, 2=PP, 3=TP.");
|
||||||
|
inner.RuleFor(i => i.Name).MaximumLength(200);
|
||||||
|
inner.RuleFor(i => i.SlaDays).GreaterThanOrEqualTo(0)
|
||||||
|
.When(i => i.SlaDays != null);
|
||||||
|
}).When(s => s.InnerSteps != null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -242,6 +296,15 @@ public class CreateWorkflowDefinitionCommandHandler(IApplicationDbContext db)
|
|||||||
Kind = (WorkflowApproverKind)a.Kind,
|
Kind = (WorkflowApproverKind)a.Kind,
|
||||||
AssignmentValue = a.AssignmentValue,
|
AssignmentValue = a.AssignmentValue,
|
||||||
}).ToList(),
|
}).ToList(),
|
||||||
|
InnerSteps = (s.InnerSteps ?? new()).OrderBy(i => i.Order).Select(i => new WorkflowStepInnerStep
|
||||||
|
{
|
||||||
|
Order = i.Order,
|
||||||
|
DepartmentId = i.DepartmentId,
|
||||||
|
PositionLevel = (PositionLevel)i.PositionLevel,
|
||||||
|
Name = i.Name,
|
||||||
|
SlaDays = i.SlaDays,
|
||||||
|
IsRequired = i.IsRequired,
|
||||||
|
}).ToList(),
|
||||||
})
|
})
|
||||||
.ToList(),
|
.ToList(),
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user