Add domain_events migration and SeaORM entity. Modify EventBus::publish
to persist events before broadcasting (best-effort: DB failure logs
warning but still broadcasts in-memory). Update all 19 publish call
sites across 4 crates to pass db reference.
Add outbox relay background task that polls pending events every 5s
and re-broadcasts them, ensuring no events are lost on server restart.
Store Redis client in AppState instead of discarding it. Create
rate_limit middleware using Redis INCR + EXPIRE for fixed-window
counting. Apply user-based rate limiting (100 req/min) to all
protected routes. Graceful degradation when Redis is unavailable.
Add VersionMismatch error variant and check_version() helper to erp-core.
All 13 mutable entities now enforce version checking on update/delete:
- erp-auth: user, role, organization, department, position
- erp-config: dictionary, dictionary_item, menu, setting, numbering_rule
- erp-workflow: process_definition, process_instance, task
- erp-message: message, message_subscription
Update DTOs to expose version in responses and require version in update
requests. HTTP 409 Conflict returned on version mismatch.
- InstanceMonitor: add '流程图' button that opens ProcessViewer modal
with active node highlighting, loading flow definition via API
- PendingTasks: add '委派' button with delegate modal (UUID input),
wired to the existing delegateTask API function
- Both ProcessViewer component and delegateTask API were previously
dead code (never imported/called)
ServiceTask was accepted by the parser but fell through to the
wildcard branch in the executor, creating an active token that
never progresses. Now returns a clear error so users know the
feature is not yet implemented rather than debugging a stuck flow.
Add doc comments to distinguish the business version field (key-based
revision counter, currently fixed at 1) from the optimistic lock field
(version_field). Renaming requires a DB migration so deferred to later.
The delegate method was accepting any UUID as delegate_to without
verifying the target user belongs to the same tenant. This allowed
cross-tenant task delegation. Added raw SQL check against users table
to avoid cross-module dependency on erp-auth.
Replace hardcoded placeholder values with live data fetched from
/users, /roles, /workflow/instances, and message store unread count.
Uses Promise.allSettled for resilient parallel loading.
- Add POST /config/menus (create single menu)
- Add PUT /config/menus/{id} (update single menu)
- Add DELETE /config/menus/{id} (soft delete single menu)
- Frontend MenuConfig was calling individual CRUD routes that didn't exist,
causing 404 errors on all create/update/delete operations
- Fix frontend to correctly handle nested tree response from backend
- Change all permission codes from colon (`:`) to dot (`.`) separator
to match handler require_permission() calls consistently
- Add missing user.list, role.list, permission.list, organization.list,
department.list, position.list permissions (handlers check for .list
but seeds only had :read)
- Add missing message module permissions (message.list, message.send,
message.template.list, message.template.create)
- Add missing setting.delete, numbering.delete permissions
- Fix workflow handlers: workflow: → workflow.
- Fix message handlers: message: → message.
- Update viewer role READ_PERM_INDICES for new permission list
This fixes a critical runtime bug where ALL permission checks in
erp-auth and erp-config handlers would return 403 Forbidden because
the seed data used colon separators but handlers checked for dots.
- Add missing version column to all message tables (migration + entities)
- Replace N+1 mark_all_read loop with single batch UPDATE query
- Fix NotificationList infinite re-render (extract queryFilter to stable ref)
- Fix NotificationPreferences dynamic import and remove unused Dayjs type
- Add Semaphore (max 8) to event listener for backpressure control
- Add /docs/openapi.json endpoint for API documentation
- Add permission check to unread_count handler
- Add version: Set(1) to all ActiveModel inserts
- Collapse nested if-let in user_service.rs search filter
- Suppress dead_code warning on ApiDoc struct
- Refactor server routing: nest all routes under /api/v1 prefix
- Simplify health check route path
- Improve workflow ProcessDesigner with edit mode and loading states
- Update workflow pages with enhanced UX
- Add Phase 2 implementation plan document
- Update CLAUDE.md architecture snapshot: all phases complete
- Update wiki/index.md: module descriptions and progress table
- All 6 phases of ERP platform base are now implemented
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Security fixes:
- Add startup warning for default JWT secret in config
- Add enum validation for priority, recipient_type, channel fields
- Add pagination size cap (max 100) via safe_page_size()
- Return generic "权限不足" instead of specific permission names
Compilation fixes:
- Fix missing standard fields in ActiveModel for tokens/process_variables
- Fix migration imports for Statement/DatabaseBackend/Uuid
- Add version_field to process_definition ActiveModel
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- CORS: replace permissive() with configurable whitelist (default.toml)
- Auth store: synchronously restore state at creation to eliminate
flash-of-login-page on refresh
- MainLayout: menu highlight now tracks current route via useLocation
- Add extractErrorMessage() utility to reduce repeated error parsing
- Fix all clippy warnings across 4 crates (erp-auth, erp-config,
erp-workflow, erp-message): remove unnecessary casts, use div_ceil,
collapse nested ifs, reduce function arguments with DTOs
Add /api/docs/openapi.json public endpoint returning the OpenAPI spec.
Uses utoipa derive macro with basic API metadata.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add audit_logs database migration with indexes
- Add AuditLog struct in erp-core with builder pattern
- Support old/new value tracking, IP address, user agent
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Message module subscribes to workflow events (process_instance.started)
- Auto-generates notifications when workflows start
- Added started_by to workflow instance event payload
- Event listener runs as background tokio task
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- C-1: Add tenant_id to settings unique index to prevent cross-tenant conflicts
- C-2: Move pg_advisory_xact_lock inside the transaction for correct concurrency
(previously lock was released before the numbering transaction started)
- H-5: Add CORS middleware (permissive for dev, TODO: restrict in production)
Complete Phase 2 identity & authentication module:
- Organization CRUD with tree structure (parent_id + materialized path)
- Department CRUD nested under organizations with tree support
- Position CRUD nested under departments
- User management page with table, create/edit modal, role assignment
- Organization architecture page with 3-panel tree layout
- Frontend API layer for orgs/depts/positions
- Sidebar navigation updated with organization menu item
- Fix parse_ttl edge case for strings ending in 'd' (e.g. "invalid")
- API client with axios interceptors: JWT attach + 401 auto-refresh
- Auth store (Zustand): login/logout/loadFromStorage with localStorage
- Login page: gradient background, Ant Design form, error handling
- Home page: dashboard with statistics cards
- App.tsx: PrivateRoute guard, /login route, auth state restoration
- MainLayout: dynamic user display, logout dropdown, menu navigation
- Users API service: CRUD with pagination support
- seed.rs: creates 21 permissions, admin+viewer roles, admin user with Argon2 password
- AuthConfig added to server config with default password Admin@2026
- Server startup: auto-creates default tenant and seeds auth data if not exists
- Idempotent: checks for existing tenant before seeding
- error.rs: AuthError with proper HTTP status mapping
- service/password.rs: Argon2 hash/verify with tests
- service/token_service.rs: JWT sign/validate, token DB storage with SHA-256 hash
- service/auth_service.rs: login/refresh/logout flows with event publishing
- service/user_service.rs: user CRUD with soft delete and tenant isolation
- Added sha2 dependency to workspace for token hashing
- users with partial unique index on (tenant_id, username) WHERE deleted_at IS NULL
- user_credentials, user_tokens with FK cascade
- roles, permissions with composite unique (tenant_id, code)
- role_permissions, user_roles junction tables
- organizations (self-ref tree), departments (tree + org FK), positions
- All tables include standard fields: id, tenant_id, timestamps, soft delete, version