- gRPC auth service for token validation - Value comparator system (string, numeric, boolean, datetime, collection) - Condition evaluator with strategy chain - Form definition and data improvements - Workflow instance/task endpoints updated - Seed data and EF design-time factory - Test coverage for comparators and handlers
114 lines
4.8 KiB
Markdown
114 lines
4.8 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
> **Cross-repo rules** — see `/Users/wen/project/rag/CLAUDE.md` for full workspace conventions. Key shared rules:
|
|
> - File-scoped namespaces, primary constructor DI, `record` for DTOs/Commands/Queries
|
|
> - Entity: `BaseEntity + IAuditable/ISoftDelete/IFullAudit` marker interfaces
|
|
> - EF Config: snake_case table, `ValueGeneratedNever()`, `IEntityTypeConfiguration<T>`
|
|
> - Endpoint: `FastEndpoint<TReq, TRes>` + `Permissions("resource:action")`
|
|
> - Middleware: `Cors → GlobalException → ApiResponse → Auth → AuthZ → FastEndpoints`
|
|
> - Response: `{ code: 0, data, message: "ok" }` (auto-wrapped)
|
|
> - JWT shared key: `RagJwtSecretKey2026MustBeAtLeast32CharsLong!`
|
|
> - Other repos: `rag-backend` (5211), `im-system` (5212), `work-flow`, `file-system` (8080 Go), `rag-frontend` (5666 Vue)
|
|
|
|
## Build & Run
|
|
|
|
```bash
|
|
dotnet build
|
|
cd src/Workflow.Api && dotnet run
|
|
cd src/Workflow.Api && dotnet run -- --seed
|
|
cd src/Workflow.Infrastructure && dotnet ef migrations add <Name> --startup-project ../Workflow.Api
|
|
dotnet test # xUnit + FluentAssertions + Moq
|
|
```
|
|
|
|
## Architecture
|
|
|
|
.NET 10, four projects: `Api → Application → Infrastructure → Domain`. Same ABP+FastEndpoints+MediatR stack as rag-backend, but with workflow-specific engine patterns.
|
|
|
|
### Key Difference: Domain-Driven State Machines
|
|
|
|
The core domain logic lives in **stateless state machines**, not in entities. Entities are anemic data holders.
|
|
|
|
Three state machines in `Domain/StateMachine/`:
|
|
- `InstanceStateMachine` — Running ↔ Suspended → Completed/Terminated
|
|
- `TaskStateMachine` — Pending → Approved/Rejected/Transferred/Delegated
|
|
- `TokenStateMachine` — Active → Consumed/Terminated
|
|
|
|
Pattern: tuple switch with context objects:
|
|
```csharp
|
|
return (currentState, operation) switch
|
|
{
|
|
(InstanceStatus.Running, InstanceOperation.Suspend) => InstanceStatus.Suspended,
|
|
(TaskStatus.Pending, TaskOperation.Approve) => TaskStatus.Approved,
|
|
_ => throw new InvalidStateTransitionException(...)
|
|
};
|
|
```
|
|
|
|
### ProcessEngine — Token Propagation
|
|
|
|
`Application/Engine/ProcessEngine.cs` — routes tokens through 7 node types:
|
|
|
|
| Node Type | Behavior |
|
|
|-----------|----------|
|
|
| Start | Consumes token, creates new token on first outgoing edge |
|
|
| End | Consumes token, checks if all instance tokens consumed → Complete |
|
|
| Approval | Creates `WorkflowTask`, token stays Active until human action |
|
|
| Cc | Creates notification tasks, immediately propagates token (fire-and-forget) |
|
|
| Condition | Evaluates edge conditions (first-match-wins), creates one new token |
|
|
| Parallel | Fork: one token per outgoing edge. Join: waits for all incoming tokens |
|
|
| SubProcess | Creates child instance, parent token waits |
|
|
|
|
### Condition Evaluation — Strategy Chain
|
|
|
|
```
|
|
ConditionEvaluator
|
|
→ IValueComparatorRegistry (chain-of-responsibility)
|
|
→ NumericComparator (==, !=, >, <, >=, <=)
|
|
→ DateTimeComparator (==, !=, >, <, >=, <=)
|
|
→ BooleanComparator (==, !=)
|
|
→ CollectionComparator (in)
|
|
→ StringComparator (==, !=, contains, startsWith, endsWith, isEmpty)
|
|
```
|
|
|
|
Two condition syntaxes: simple text (`"amount > 5000"`, regex-parsed) and JSON tree (`{"and": [...], "or": [...]}`).
|
|
|
|
### Node Action Hooks
|
|
|
|
```csharp
|
|
public interface INodeAction { Task ExecuteAsync(NodeActionContext context); }
|
|
```
|
|
Config specifies `"onEnter": "send-notification"`. Engine resolves from DI. Hook failures are swallowed — never block token propagation.
|
|
|
|
## Testing
|
|
|
|
```
|
|
tests/Workflow.Tests/
|
|
Condition/ — 6 test files (one per comparator + evaluator + registry)
|
|
Engine/ — ProcessEngineTests (~775 lines, #region-grouped)
|
|
Form/ — Form definition + data tests with fixture
|
|
Handlers/ — CQRS handler tests with InMemory DB
|
|
StateMachine/ — One test file per state machine (pure unit tests)
|
|
```
|
|
|
|
Stack: xUnit + FluentAssertions + Moq + EF Core InMemory.
|
|
|
|
Handler test pattern:
|
|
```csharp
|
|
await using var db = new WorkflowDbContext(new DbContextOptionsBuilder<WorkflowDbContext>()
|
|
.UseInMemoryDatabase(Guid.NewGuid().ToString()).Options);
|
|
var handler = new CreateXxxCommandHandler(db);
|
|
var result = await handler.Handle(command, CancellationToken.None);
|
|
result.Should().NotBeNull();
|
|
```
|
|
|
|
## Code Patterns
|
|
|
|
Same conventions as rag-backend (see workspace CLAUDE.md), plus:
|
|
|
|
- Table prefix: `wf_` (e.g., `wf_workflow_definitions`, `wf_workflow_tasks`)
|
|
- Request DTOs co-located in endpoint files (not separate DTO files)
|
|
- Endpoints use `AllowAnonymous()` on all routes (auth via gRPC to rag-backend, not local)
|
|
- Entity audit fields are explicit (no base audit class): `Guid CreatedBy`, `DateTime CreatedAt`
|
|
- `AuditInterceptor` registered via `OnConfiguring` override, not DI `AddInterceptors`
|