work-flow/CLAUDE.md
向宁 fc4ecbbacc feat: add gRPC auth, condition comparators, seed data, EF migrations
- 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
2026-05-20 20:28:35 +08:00

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`