Documentation
¶
Index ¶
- Constants
- Variables
- func AllEventTypes() []string
- func IsSuperAdmin(p *security.Principal) bool
- func PayloadOccurredAt(e DomainEvent) timex.DateTime
- func ValidateBusinessIdentifier(id string) error
- type ActionLog
- type ActionType
- type AddAssigneeType
- type ApprovalMethod
- type ApprovalNodeData
- type AssigneeDefinition
- type AssigneeKind
- type AssigneeService
- type AssigneesAddedEvent
- type AssigneesRemovedEvent
- type BaseNodeData
- type BindingMode
- type BusinessBindingHook
- type CCDefinition
- type CCKind
- type CCNodeData
- type CCNotifiedEvent
- type CCRecord
- type CCTiming
- type CallerContext
- type Condition
- type ConditionBranch
- type ConditionEvaluator
- type ConditionGroup
- type ConditionKind
- type ConditionNodeData
- type ConditionOperator
- type ConsecutiveApproverAction
- type Delegation
- type DomainEvent
- type EdgeDefinition
- type EmptyAssigneeAction
- type EndNodeData
- type EvaluationContext
- type ExecutionType
- type FieldKind
- type FieldOption
- type Flow
- type FlowCategory
- type FlowCreatedEvent
- type FlowDefinition
- type FlowDeployedEvent
- type FlowEdge
- type FlowInitiator
- type FlowNode
- type FlowNodeAssignee
- type FlowNodeCC
- type FlowPublishedEvent
- type FlowToggledEvent
- type FlowUpdatedEvent
- type FlowVersion
- type FormData
- type FormDefinition
- type FormFieldDefinition
- type FormSnapshot
- type HandleNodeData
- type InitiatorKind
- type Instance
- type InstanceBindingFailedEvent
- type InstanceCompletedEvent
- type InstanceCreatedEvent
- type InstanceLifecycleHook
- type InstanceNoGenerator
- type InstanceResubmittedEvent
- type InstanceReturnedEvent
- type InstanceRolledBackEvent
- type InstanceStatus
- type InstanceWithdrawnEvent
- type NodeAutoPassedEvent
- type NodeData
- type NodeDefinition
- type NodeKind
- type OperatorInfo
- type PassRule
- type PassRuleContext
- type PassRuleResult
- type PassRuleStrategy
- type Permission
- type Position
- type PrincipalDepartmentResolver
- type PrincipalTenantResolver
- type ResolvedAssignee
- type RoleMembershipChecker
- type RollbackDataStrategy
- type RollbackType
- type SameApplicantAction
- type StartNodeData
- type StorageMode
- type Task
- type TaskApprovedEvent
- type TaskCanceledEvent
- type TaskCreatedEvent
- type TaskDeadlineWarningEvent
- type TaskHandledEvent
- type TaskNodeData
- type TaskReassignedEvent
- type TaskRejectedEvent
- type TaskStatus
- type TaskTimedOutEvent
- type TaskTransferredEvent
- type TaskUrgedEvent
- type TimeoutAction
- type UrgeRecord
- type UserInfo
- type UserInfoResolver
- type ValidationRule
- type VersionStatus
Constants ¶
const ( EventTypeInstanceCreated = "approval.instance.created" EventTypeInstanceCompleted = "approval.instance.completed" EventTypeInstanceWithdrawn = "approval.instance.withdrawn" EventTypeInstanceRolledBack = "approval.instance.rolled_back" EventTypeInstanceReturned = "approval.instance.returned" EventTypeInstanceResubmitted = "approval.instance.resubmitted" EventTypeInstanceBindingFailed = "approval.instance.binding_failed" EventTypeNodeAutoPassed = "approval.node.auto_passed" EventTypeTaskCreated = "approval.task.created" EventTypeTaskApproved = "approval.task.approved" EventTypeTaskHandled = "approval.task.handled" EventTypeTaskRejected = "approval.task.rejected" EventTypeTaskCanceled = "approval.task.canceled" EventTypeTaskTransferred = "approval.task.transferred" EventTypeTaskReassigned = "approval.task.reassigned" EventTypeTaskTimedOut = "approval.task.timed_out" EventTypeAssigneesAdded = "approval.task.assignees_added" EventTypeAssigneesRemoved = "approval.task.assignees_removed" EventTypeTaskDeadlineWarning = "approval.task.deadline_warning" EventTypeTaskUrged = "approval.task.urged" EventTypeCCNotified = "approval.cc.notified" EventTypeFlowCreated = "approval.flow.created" EventTypeFlowUpdated = "approval.flow.updated" EventTypeFlowDeployed = "approval.flow.deployed" EventTypeFlowToggled = "approval.flow.toggled" EventTypeFlowPublished = "approval.flow.published" )
Approval event type identifiers. Exposed as constants so framework callers (route inspection, subscription filters, metrics labels) can reference them by symbol rather than risking a typo on the wire string.
const ( DefaultExecutionType = ExecutionManual DefaultApprovalMethod = ApprovalParallel DefaultPassRule = PassAll DefaultEmptyAssigneeAction = EmptyAssigneeAutoPass DefaultSameApplicantAction = SameApplicantSelfApprove DefaultConsecutiveApproverAction = ConsecutiveApproverNone DefaultRollbackType = RollbackPrevious DefaultRollbackDataStrategy = RollbackDataKeep DefaultTimeoutAction = TimeoutActionNone DefaultCCTiming = CCTimingAlways // Handle nodes default to sequential execution with the PassAny rule, // since any handler completing the task is sufficient. DefaultHandleApprovalMethod = ApprovalSequential DefaultHandlePassRule = PassAny // DefaultUrgeCooldownMinutes is the urge cooldown applied when a node // leaves UrgeCooldownMinutes at 0. The flow editor surfaces the same // value in its placeholder text; keep the two in lockstep. DefaultUrgeCooldownMinutes = 30 )
Designer-aligned defaults resolved by ApplyTo when a field is omitted from the node data payload. The flow editor displays exactly these values for untouched controls, so resolving them here keeps "what the designer shows" and "what the engine runs" identical by construction.
const DefaultTenantID = "default"
DefaultTenantID is the tenant identifier used when a caller does not carry an explicit tenant — the conventional single-tenant deployment value.
const SuperAdminRole = "approval:super_admin"
SuperAdminRole is the role string that grants cross-tenant access to admin queries and operations. Hosts assign this role to platform-level operators that legitimately need to act across tenants (audit teams, billing, etc.). Without it, admin endpoints reject requests that lack a tenant filter — guarding against accidental cross-tenant data exposure.
Variables ¶
var ( ErrUnknownNodeKind = errors.New("unknown node kind") ErrNodeDataUnmarshal = errors.New("node data unmarshal failed") )
var ErrCrossTenantAccess = errors.New("approval: cross-tenant access denied")
ErrCrossTenantAccess is returned when a non-super-admin caller attempts to act on an entity owned by a different tenant. Resource and command handlers use CallerContext.Authorize to surface it consistently.
var ErrInvalidBusinessIdentifier = errors.New("approval: invalid business identifier (must match ^[A-Za-z_][A-Za-z0-9_]{0,62}$)")
ErrInvalidBusinessIdentifier is returned by ValidateBusinessIdentifier for values that do not match a SQL-safe identifier pattern. Hosts that implement BusinessBindingHook should bubble this up (or wrap it) so admin-side flow CRUD surfaces a meaningful error to operators.
var SystemCaller = CallerContext{IsSystemInternal: true}
SystemCaller is the canonical CallerContext for callers that bypass tenant scoping by carrying IsSystemInternal. In the current tree its only consumers are test fixtures (production system paths operate on trusted, pre-scoped rows without a CallerContext); use it instead of the zero value so the bypass intent is explicit at the call site.
Functions ¶
func AllEventTypes ¶ added in v0.29.0
func AllEventTypes() []string
AllEventTypes returns every approval domain event type identifier. It is the canonical, enumerable registry of the event surface: callers that must stay exhaustive over the event set (e.g. the start-up routing check, which asserts each transactional event resolves to a transactional transport) derive from this list so adding a new event constant cannot silently slip past them. Keep this in sync with the EventType* constants above — the only reason a new constant would be omitted is a deliberate, reviewed decision.
func IsSuperAdmin ¶ added in v0.24.0
IsSuperAdmin reports whether the principal carries the cross-tenant override role. Nil principal returns false.
func PayloadOccurredAt ¶ added in v0.24.0
func PayloadOccurredAt(e DomainEvent) timex.DateTime
PayloadOccurredAt extracts the OccurredTime field from a DomainEvent payload. Every in-tree approval event struct carries this field; publishers use the value to project business time onto Envelope.OccurredAt via event.WithOccurredAt. Returns the zero DateTime for payloads that lack the field (defensive — should never happen for in-tree events).
Exposed as a package-level helper rather than a method on DomainEvent so the interface can stay minimal (EventType only) while still letting transports / behaviors project business time.
func ValidateBusinessIdentifier ¶ added in v0.24.0
ValidateBusinessIdentifier reports whether id is a safe SQL identifier for use as a table or column name in business binding interpolation. Empty / whitespace-only strings pass — the caller decides whether absence is itself an error (see Flow validation paths for the policy).
Types ¶
type ActionLog ¶
type ActionLog struct {
orm.BaseModel `bun:"table:apv_action_log,alias:aal"`
orm.Model
orm.CreationTrackedModel
InstanceID string `json:"instanceId" bun:"instance_id"`
NodeID *string `json:"nodeId" bun:"node_id,nullzero"`
TaskID *string `json:"taskId" bun:"task_id,nullzero"`
Action ActionType `json:"action" bun:"action"`
OperatorID string `json:"operatorId" bun:"operator_id"`
OperatorName string `json:"operatorName" bun:"operator_name"`
OperatorDepartmentID *string `json:"operatorDepartmentId" bun:"operator_department_id,nullzero"`
OperatorDepartmentName *string `json:"operatorDepartmentName" bun:"operator_department_name,nullzero"`
IPAddress *string `json:"ipAddress" bun:"ip_address,nullzero"`
UserAgent *string `json:"userAgent" bun:"user_agent,nullzero"`
Opinion *string `json:"opinion" bun:"opinion,nullzero"`
TransferToID *string `json:"transferToId" bun:"transfer_to_id,nullzero"`
TransferToName *string `json:"transferToName" bun:"transfer_to_name,nullzero"`
RollbackToNodeID *string `json:"rollbackToNodeId" bun:"rollback_to_node_id,nullzero"`
AddAssigneeType *AddAssigneeType `json:"addAssigneeType" bun:"add_assignee_type,nullzero"`
AddedAssigneeIDs []string `json:"addedAssigneeIds" bun:"added_assignee_ids,type:jsonb"`
RemovedAssigneeIDs []string `json:"removedAssigneeIds" bun:"removed_assignee_ids,type:jsonb"`
CCUserIDs []string `json:"ccUserIds" bun:"cc_user_ids,type:jsonb"`
Attachments []string `json:"attachments" bun:"attachments,type:jsonb,nullzero"`
Meta map[string]any `json:"meta" bun:"meta,type:jsonb,nullzero"`
}
ActionLog represents an action log entry.
type ActionType ¶
type ActionType string
ActionType represents the type of action performed by an operator.
const ( ActionSubmit ActionType = "submit" ActionApprove ActionType = "approve" ActionHandle ActionType = "handle" ActionReject ActionType = "reject" ActionTransfer ActionType = "transfer" ActionWithdraw ActionType = "withdraw" ActionCancel ActionType = "cancel" ActionRollback ActionType = "rollback" ActionAddAssignee ActionType = "add_assignee" ActionRemoveAssignee ActionType = "remove_assignee" ActionExecute ActionType = "execute" // System execution action ActionResubmit ActionType = "resubmit" // Resubmit a returned instance ActionReassign ActionType = "reassign" // Admin reassigned task to a different user ActionTerminate ActionType = "terminate" // Admin force-terminated an instance ActionAddCC ActionType = "add_cc" // Participant added CC recipients )
type AddAssigneeType ¶
type AddAssigneeType string
AddAssigneeType represents the type of dynamic assignee addition. It defines how a newly added assignee is positioned relative to the current task.
const ( AddAssigneeBefore AddAssigneeType = "before" // Before: new assignee processes first, original task becomes pending after completion AddAssigneeAfter AddAssigneeType = "after" // After: new assignee processes after the original assignee completes AddAssigneeParallel AddAssigneeType = "parallel" // Parallel: new assignee joins the current parallel group to process together )
func (AddAssigneeType) IsValid ¶
func (t AddAssigneeType) IsValid() bool
IsValid checks if the AddAssigneeType is a valid value.
func (*AddAssigneeType) UnmarshalJSON ¶
func (t *AddAssigneeType) UnmarshalJSON(data []byte) error
UnmarshalJSON validates AddAssigneeType values when decoding JSON payloads.
type ApprovalMethod ¶
type ApprovalMethod string
ApprovalMethod represents the method of approval for a node with multiple assignees. It defines how the approval decision is made when there are multiple approvers.
const ( ApprovalSequential ApprovalMethod = "sequential" // Sequential: approvers process one by one in order, all must approve ApprovalParallel ApprovalMethod = "parallel" // Parallel: approvers process simultaneously, decision based on consensus rules )
func (ApprovalMethod) IsValid ¶ added in v0.29.0
func (m ApprovalMethod) IsValid() bool
IsValid reports whether the approval method is one of the defined values.
type ApprovalNodeData ¶
type ApprovalNodeData struct {
BaseNodeData
TaskNodeData
ApprovalMethod ApprovalMethod `json:"approvalMethod,omitempty"`
PassRule PassRule `json:"passRule,omitempty"`
PassRatio decimal.Decimal `json:"passRatio"`
SameApplicantAction SameApplicantAction `json:"sameApplicantAction,omitempty"`
ConsecutiveApproverAction ConsecutiveApproverAction `json:"consecutiveApproverAction,omitempty"`
RollbackType RollbackType `json:"rollbackType,omitempty"`
RollbackDataStrategy RollbackDataStrategy `json:"rollbackDataStrategy,omitempty"`
RollbackTargetKeys []string `json:"rollbackTargetKeys,omitempty"`
IsRollbackAllowed *bool `json:"isRollbackAllowed,omitempty"`
IsAddAssigneeAllowed *bool `json:"isAddAssigneeAllowed,omitempty"`
AddAssigneeTypes []AddAssigneeType `json:"addAssigneeTypes,omitempty"`
IsRemoveAssigneeAllowed *bool `json:"isRemoveAssigneeAllowed,omitempty"`
IsManualCCAllowed *bool `json:"isManualCcAllowed,omitempty"`
}
ApprovalNodeData contains data specific to approval nodes.
The Is*Allowed permission toggles are pointers because their default is true; see TaskNodeData for the rationale.
func (*ApprovalNodeData) ApplyTo ¶
func (d *ApprovalNodeData) ApplyTo(node *FlowNode)
ApplyTo applies approval node data to a FlowNode, resolving omitted fields to the designer defaults. The target node is assumed to be freshly constructed (zero value); all fields are overwritten unconditionally as a full-snapshot deploy operation.
func (*ApprovalNodeData) Kind ¶
func (*ApprovalNodeData) Kind() NodeKind
Kind returns the node kind.
type AssigneeDefinition ¶
type AssigneeDefinition struct {
Kind AssigneeKind `json:"kind"`
IDs []string `json:"ids,omitempty"`
FormField *string `json:"formField,omitempty"`
SortOrder int `json:"sortOrder"`
}
AssigneeDefinition represents an assignee configuration in the flow definition.
type AssigneeKind ¶
type AssigneeKind string
AssigneeKind represents the kind of assignee.
const ( AssigneeUser AssigneeKind = "user" AssigneeRole AssigneeKind = "role" AssigneeDepartment AssigneeKind = "department" // Department head AssigneeSelf AssigneeKind = "self" // Applicant themselves AssigneeSuperior AssigneeKind = "superior" // Direct superior AssigneeDepartmentLeader AssigneeKind = "department_leader" // Leaders of the applicant's own department (single level) AssigneeFormField AssigneeKind = "form_field" // Based on form field )
func (AssigneeKind) IsValid ¶ added in v0.29.0
func (k AssigneeKind) IsValid() bool
IsValid reports whether the assignee kind is one of the defined values.
type AssigneeService ¶
type AssigneeService interface {
// GetSuperior returns the direct superior's user info for the given user.
GetSuperior(ctx context.Context, userID string) (*UserInfo, error)
// GetDepartmentLeaders returns the leader user info for the given department.
GetDepartmentLeaders(ctx context.Context, departmentID string) ([]UserInfo, error)
// GetRoleUsers returns all user info for users that have the given role.
GetRoleUsers(ctx context.Context, roleID string) ([]UserInfo, error)
}
AssigneeService resolves approval assignees from organizational data (implemented by host app).
type AssigneesAddedEvent ¶
type AssigneesAddedEvent struct {
InstanceID string `json:"instanceId"`
TenantID string `json:"tenantId"`
NodeID string `json:"nodeId"`
TaskID string `json:"taskId"`
AddType AddAssigneeType `json:"addType"`
AssigneeIDs []string `json:"assigneeIds"`
AssigneeNames map[string]string `json:"assigneeNames"`
OccurredTime timex.DateTime `json:"occurredTime"`
}
AssigneesAddedEvent fired when assignees are dynamically added.
func NewAssigneesAddedEvent ¶
func NewAssigneesAddedEvent(instanceID, tenantID, nodeID, taskID string, addType AddAssigneeType, assigneeIDs []string, assigneeNames map[string]string) *AssigneesAddedEvent
func (*AssigneesAddedEvent) EventType ¶ added in v0.24.0
func (*AssigneesAddedEvent) EventType() string
type AssigneesRemovedEvent ¶
type AssigneesRemovedEvent struct {
InstanceID string `json:"instanceId"`
TenantID string `json:"tenantId"`
NodeID string `json:"nodeId"`
TaskID string `json:"taskId"`
AssigneeIDs []string `json:"assigneeIds"`
AssigneeNames map[string]string `json:"assigneeNames"`
OccurredTime timex.DateTime `json:"occurredTime"`
}
AssigneesRemovedEvent fired when assignees are dynamically removed.
func NewAssigneesRemovedEvent ¶
func NewAssigneesRemovedEvent(instanceID, tenantID, nodeID, taskID string, assigneeIDs []string, assigneeNames map[string]string) *AssigneesRemovedEvent
func (*AssigneesRemovedEvent) EventType ¶ added in v0.24.0
func (*AssigneesRemovedEvent) EventType() string
type BaseNodeData ¶
type BaseNodeData struct {
Name string `json:"name,omitempty"`
Description *string `json:"description,omitempty"`
}
BaseNodeData contains common fields shared across all node data types.
func (BaseNodeData) GetDescription ¶
func (d BaseNodeData) GetDescription() *string
GetDescription returns the node description.
type BindingMode ¶
type BindingMode string
BindingMode represents the mode of binding with business data. It defines how the approval workflow stores and associates form data.
const ( BindingStandalone BindingMode = "standalone" // Standalone: form data is stored in the approval workflow's own table BindingBusiness BindingMode = "business" // Business: links to existing business data table )
type BusinessBindingHook ¶ added in v0.24.0
type BusinessBindingHook interface {
// OnInstanceCreated returns the business primary key (BusinessRecordID)
// to persist on the instance. Returning empty string indicates the host
// has nothing to bind (engine stores nil).
OnInstanceCreated(ctx context.Context, db orm.DB, flow *Flow, instance *Instance) (businessRecordID string, err error)
// WriteBackStatus writes the final approval status back to the
// business table. Called asynchronously from the binding Listener
// after InstanceCompletedEvent fires. Implementations should be
// idempotent — the listener may retry through the outbox.
WriteBackStatus(ctx context.Context, db orm.DB, flow *Flow, instance *Instance, finalStatus InstanceStatus) error
}
BusinessBindingHook bridges the approval engine with the host application's business tables when Flow.BindingMode is BindingBusiness. It is narrowly scoped to the "approval row ↔ business row" plumbing; broader lifecycle extension goes through InstanceLifecycleHook instead.
Two lifecycle moments matter:
OnInstanceCreated runs inside the start_instance transaction so the host can resolve / create the business row and return the primary key that the engine stores in Instance.BusinessRecordID. Returning an error rolls back the entire instance creation.
WriteBackStatus runs asynchronously via the binding Listener (which subscribes to InstanceCompletedEvent) so the host can stamp the final approval decision onto its own business table. A non-nil error does NOT roll back the approval — the workflow has already decided. Instead the listener publishes InstanceBindingFailedEvent so the host can retry (saga / outbox compensation).
Hosts override the default implementation by binding their own BusinessBindingHook into the FX container, typically through vef.SupplyBusinessBindingHook.
type CCDefinition ¶
type CCDefinition struct {
Kind CCKind `json:"kind"`
IDs []string `json:"ids,omitempty"`
FormField *string `json:"formField,omitempty"`
Timing CCTiming `json:"timing,omitempty"`
}
CCDefinition represents a CC recipient in node data.
type CCNodeData ¶
type CCNodeData struct {
BaseNodeData
CCs []CCDefinition `json:"ccs,omitempty"`
IsReadConfirmRequired bool `json:"isReadConfirmRequired,omitempty"`
FieldPermissions map[string]Permission `json:"fieldPermissions,omitempty"`
}
CCNodeData contains data specific to CC nodes.
func (*CCNodeData) ApplyTo ¶
func (d *CCNodeData) ApplyTo(node *FlowNode)
ApplyTo applies CC node data to a FlowNode.
func (*CCNodeData) GetCCs ¶
func (d *CCNodeData) GetCCs() []CCDefinition
GetCCs returns the CC definitions from CCNodeData.
type CCNotifiedEvent ¶
type CCNotifiedEvent struct {
InstanceID string `json:"instanceId"`
TenantID string `json:"tenantId"`
NodeID string `json:"nodeId"`
CCUserIDs []string `json:"ccUserIds"`
CCUserNames map[string]string `json:"ccUserNames"`
IsManual bool `json:"isManual"`
OccurredTime timex.DateTime `json:"occurredTime"`
}
CCNotifiedEvent fired when users are carbon-copied.
func NewCCNotifiedEvent ¶
func (*CCNotifiedEvent) EventType ¶ added in v0.24.0
func (*CCNotifiedEvent) EventType() string
type CCRecord ¶
type CCRecord struct {
orm.BaseModel `bun:"table:apv_cc_record,alias:acr"`
orm.Model
orm.CreationTrackedModel
InstanceID string `json:"instanceId" bun:"instance_id"`
NodeID *string `json:"nodeId" bun:"node_id,nullzero"`
TaskID *string `json:"taskId" bun:"task_id,nullzero"`
CCUserID string `json:"ccUserId" bun:"cc_user_id"`
CCUserName string `json:"ccUserName" bun:"cc_user_name"`
IsManual bool `json:"isManual" bun:"is_manual"`
ReadAt *timex.DateTime `json:"readAt" bun:"read_at,nullzero"`
}
CCRecord represents a CC notification record.
type CallerContext ¶ added in v0.24.0
type CallerContext struct {
// TenantID is the caller's resolved tenant. Empty for super-admin or
// for system-internal callers; must be non-empty for every authenticated
// API request.
TenantID string
// IsSuperAdmin grants cross-tenant access regardless of TenantID.
IsSuperAdmin bool
// IsSystemInternal marks a caller without an HTTP / RPC principal whose
// scope is established by other means, so Authorize passes
// unconditionally. Resource paths must NEVER populate this. In the
// current tree only test fixtures set it (the in-tree system paths —
// timeout scanner, binding listener — act directly on already-loaded,
// trusted rows and never construct a CallerContext); it remains the
// intended marker for any host or future in-process system code that
// legitimately needs to bypass tenant scoping.
IsSystemInternal bool
}
CallerContext bundles the tenant authority of a single API call. Resource handlers resolve it from the security principal (via PrincipalTenantResolver + IsSuperAdmin) and pass it into commands / queries through their struct fields so handlers can enforce data ownership without re-parsing principal details.
Production code MUST populate exactly one of TenantID / IsSuperAdmin / IsSystemInternal. A zero-value CallerContext is treated as **unauthorized** so a forgotten resolver call surfaces as a deny rather than silently waving the request through — this is the deliberate fail- closed posture introduced after the security audit caught fail-open regressions in the default principal resolver.
func (CallerContext) Allows ¶ added in v0.24.0
func (c CallerContext) Allows(entityTenantID string) bool
Allows is the bool variant of Authorize. Query handlers use it when a failed authorization must mimic "not found" rather than surface an error — the typical multi-tenant pattern that avoids leaking entity existence across tenants.
func (CallerContext) Authorize ¶ added in v0.24.0
func (c CallerContext) Authorize(entityTenantID string) error
Authorize reports whether the caller is allowed to act on an entity owned by entityTenantID. Super-admin callers always pass; system-internal callers pass too (no HTTP principal exists, scope is enforced upstream). Every other caller must have a non-empty TenantID that matches the entity tenant exactly — zero-value contexts are rejected.
func (CallerContext) ResolveWriteTenant ¶ added in v0.29.0
func (c CallerContext) ResolveWriteTenant(clientTenant string) (string, error)
ResolveWriteTenant returns the tenant a new or mutated entity must be stamped with. Super-admin / system-internal callers may target any tenant, so the client-supplied value is honored verbatim; every other caller is pinned to their own tenant and fails closed with ErrCrossTenantAccess when they have none — preventing a non-privileged caller from writing into another tenant.
func (CallerContext) TenantScopeFilter ¶ added in v0.29.0
func (c CallerContext) TenantScopeFilter(override string) (*string, error)
TenantScopeFilter resolves the tenant filter for a LIST / metrics query, applying the same fail-closed posture as Authorize. A non-nil result scopes the query to that tenant; a nil result means unfiltered cross-tenant access and is only ever returned to super-admin / system-internal callers. An ordinary caller that resolves to an empty tenant fails closed with ErrCrossTenantAccess, so a forgotten or empty PrincipalTenantResolver result can never silently widen a list query to every tenant's rows — mirroring Authorize's rejection of a zero-value context. The override is honored only for privileged callers; ordinary callers are always pinned to their own tenant regardless of what the client sent. This is the single source of truth for list queries — the resource layer must never read params.TenantID directly.
type Condition ¶
type Condition struct {
Kind ConditionKind `json:"kind"`
Subject string `json:"subject"`
Operator ConditionOperator `json:"operator"`
Value any `json:"value"`
Expression string `json:"expression"`
}
Condition represents a branch condition evaluated by condition nodes.
type ConditionBranch ¶
type ConditionBranch struct {
ID string `json:"id"`
Label string `json:"label"`
ConditionGroups []ConditionGroup `json:"conditionGroups,omitempty"`
IsDefault bool `json:"isDefault,omitempty"`
Priority int `json:"priority"`
}
ConditionBranch represents a branch in a condition node. Each branch has its own condition groups and can be linked to an edge via its ID.
type ConditionEvaluator ¶
type ConditionEvaluator interface {
// Kind returns the condition kind this evaluator handles.
Kind() ConditionKind
// Evaluate evaluates a single condition against the given evaluation context.
Evaluate(ctx context.Context, cond Condition, ec *EvaluationContext) (bool, error)
}
ConditionEvaluator evaluates branch conditions.
type ConditionGroup ¶
type ConditionGroup struct {
Conditions []Condition `json:"conditions"`
}
ConditionGroup represents a group of conditions evaluated with AND logic. Multiple groups in a branch are evaluated with OR logic.
type ConditionKind ¶
type ConditionKind string
ConditionKind represents the kind of condition for condition branches.
const ( ConditionField ConditionKind = "field" // Field-based condition ConditionExpression ConditionKind = "expression" // Expression-based condition )
type ConditionNodeData ¶
type ConditionNodeData struct {
BaseNodeData
Branches []ConditionBranch `json:"branches,omitempty"`
}
ConditionNodeData contains data specific to condition nodes.
func (*ConditionNodeData) ApplyTo ¶
func (d *ConditionNodeData) ApplyTo(node *FlowNode)
ApplyTo applies condition node data to a FlowNode.
func (*ConditionNodeData) Kind ¶
func (*ConditionNodeData) Kind() NodeKind
Kind returns the node kind.
type ConditionOperator ¶ added in v0.29.0
type ConditionOperator string
ConditionOperator enumerates the comparison operators a field condition may use. The set is the shared contract between the flow designer (which offers operators per field kind) and the engine's field-condition evaluator; both sides must stay in lockstep with this list.
const ( OperatorEquals ConditionOperator = "eq" OperatorNotEquals ConditionOperator = "ne" OperatorGreater ConditionOperator = "gt" OperatorGreaterOrEq ConditionOperator = "gte" OperatorLess ConditionOperator = "lt" OperatorLessOrEq ConditionOperator = "lte" OperatorIn ConditionOperator = "in" OperatorNotIn ConditionOperator = "not_in" OperatorContains ConditionOperator = "contains" OperatorNotContains ConditionOperator = "not_contains" OperatorStartsWith ConditionOperator = "starts_with" OperatorEndsWith ConditionOperator = "ends_with" OperatorIsEmpty ConditionOperator = "is_empty" OperatorIsNotEmpty ConditionOperator = "is_not_empty" )
func (ConditionOperator) IsValid ¶ added in v0.29.0
func (o ConditionOperator) IsValid() bool
IsValid reports whether the operator is one of the defined values.
type ConsecutiveApproverAction ¶
type ConsecutiveApproverAction string
ConsecutiveApproverAction represents the action when the same approver appears in consecutive approval nodes and approved in the previous node.
const ( ConsecutiveApproverNone ConsecutiveApproverAction = "none" ConsecutiveApproverAutoPass ConsecutiveApproverAction = "auto_pass" )
func (ConsecutiveApproverAction) IsValid ¶ added in v0.29.0
func (a ConsecutiveApproverAction) IsValid() bool
IsValid reports whether the consecutive-approver action is one of the defined values.
type Delegation ¶
type Delegation struct {
orm.BaseModel `bun:"table:apv_delegation,alias:ad"`
orm.FullAuditedModel
DelegatorID string `json:"delegatorId" bun:"delegator_id"`
DelegateeID string `json:"delegateeId" bun:"delegatee_id"`
FlowCategoryID *string `json:"flowCategoryId" bun:"flow_category_id,nullzero"`
FlowID *string `json:"flowId" bun:"flow_id,nullzero"`
StartTime timex.DateTime `json:"startTime" bun:"start_time"`
EndTime timex.DateTime `json:"endTime" bun:"end_time"`
IsActive bool `json:"isActive" bun:"is_active"`
Reason *string `json:"reason" bun:"reason,nullzero"`
}
Delegation represents an approval delegation.
type DomainEvent ¶
type DomainEvent interface {
// EventType returns the unique event identifier (e.g., "approval.instance.created").
EventType() string
}
DomainEvent is the contract every approval domain event satisfies. EventType matches the framework's event.Event surface so domain events can be published through the event Bus without adaptation. Business time is carried as OccurredTime in the concrete payload and projected onto Envelope.OccurredAt via event.WithOccurredAt at publish time.
Tenant scope: every instance/task/node/cc-level event carries TenantID so subscribers can route on tenancy without re-querying. Flow-level events (created/updated/etc.) already include TenantID on the payload.
type EdgeDefinition ¶
type EdgeDefinition struct {
ID string `json:"id"`
Source string `json:"source"`
Target string `json:"target"`
SourceHandle *string `json:"sourceHandle,omitempty"`
Data map[string]any `json:"data,omitempty"`
}
EdgeDefinition represents a connection between nodes.
type EmptyAssigneeAction ¶
type EmptyAssigneeAction string
EmptyAssigneeAction represents the action when no assignee is found.
const ( EmptyAssigneeAutoPass EmptyAssigneeAction = "auto_pass" EmptyAssigneeTransferAdmin EmptyAssigneeAction = "transfer_admin" EmptyAssigneeTransferSuperior EmptyAssigneeAction = "transfer_superior" EmptyAssigneeTransferApplicant EmptyAssigneeAction = "transfer_applicant" EmptyAssigneeTransferSpecified EmptyAssigneeAction = "transfer_specified" )
func (EmptyAssigneeAction) IsValid ¶ added in v0.29.0
func (a EmptyAssigneeAction) IsValid() bool
IsValid reports whether the empty-assignee action is one of the defined values.
type EndNodeData ¶
type EndNodeData struct {
BaseNodeData
}
EndNodeData contains data specific to end nodes.
func (*EndNodeData) ApplyTo ¶
func (d *EndNodeData) ApplyTo(node *FlowNode)
ApplyTo applies end node data to a FlowNode.
type EvaluationContext ¶
type EvaluationContext struct {
FormData FormData
ApplicantID string
ApplicantDepartmentID *string
}
EvaluationContext provides context for condition evaluation.
type ExecutionType ¶
type ExecutionType string
ExecutionType represents how a task node is executed. It determines whether the node waits for human decisions or resolves itself the moment the instance enters it.
const ( ExecutionManual ExecutionType = "manual" // Manual: creates tasks and waits for assignees to act ExecutionAutoPass ExecutionType = "auto_pass" // AutoPass: the node passes immediately on entry, no tasks are created ExecutionAutoReject ExecutionType = "auto_reject" // AutoReject: the instance is rejected immediately on entry )
func (ExecutionType) IsValid ¶ added in v0.29.0
func (t ExecutionType) IsValid() bool
IsValid reports whether the execution type is one of the defined values.
type FieldOption ¶
FieldOption represents a selectable option for select/radio/checkbox fields.
type Flow ¶
type Flow struct {
orm.BaseModel `bun:"table:apv_flow,alias:af"`
orm.FullAuditedModel
TenantID string `json:"tenantId" bun:"tenant_id"`
CategoryID string `json:"categoryId" bun:"category_id"`
Code string `json:"code" bun:"code"`
Name string `json:"name" bun:"name"`
Icon *string `json:"icon" bun:"icon,nullzero"`
Description *string `json:"description" bun:"description,nullzero"`
BindingMode BindingMode `json:"bindingMode" bun:"binding_mode"`
BusinessTable *string `json:"businessTable" bun:"business_table,nullzero"`
BusinessPkField *string `json:"businessPkField" bun:"business_pk_field,nullzero"`
BusinessTitleField *string `json:"businessTitleField" bun:"business_title_field,nullzero"`
BusinessStatusField *string `json:"businessStatusField" bun:"business_status_field,nullzero"`
AdminUserIDs []string `json:"adminUserIds" bun:"admin_user_ids,type:jsonb"`
IsAllInitiationAllowed bool `json:"isAllInitiationAllowed" bun:"is_all_initiation_allowed"`
InstanceTitleTemplate string `json:"instanceTitleTemplate" bun:"instance_title_template"`
IsActive bool `json:"isActive" bun:"is_active"`
CurrentVersion int `json:"currentVersion" bun:"current_version"`
}
Flow represents a flow definition.
type FlowCategory ¶
type FlowCategory struct {
orm.BaseModel `bun:"table:apv_flow_category,alias:afc"`
orm.FullAuditedModel
TenantID string `json:"tenantId" bun:"tenant_id"`
Code string `json:"code" bun:"code"`
Name string `json:"name" bun:"name"`
Icon *string `json:"icon" bun:"icon,nullzero"`
ParentID *string `json:"parentId" bun:"parent_id,nullzero"`
SortOrder int `json:"sortOrder" bun:"sort_order"`
IsActive bool `json:"isActive" bun:"is_active"`
Remark *string `json:"remark" bun:"remark,nullzero"`
Children []FlowCategory `json:"children,omitempty" bun:"-"`
}
FlowCategory represents a category for grouping flows.
type FlowCreatedEvent ¶ added in v0.24.0
type FlowCreatedEvent struct {
FlowID string `json:"flowId"`
TenantID string `json:"tenantId"`
Code string `json:"code"`
Name string `json:"name"`
CategoryID string `json:"categoryId"`
OccurredTime timex.DateTime `json:"occurredTime"`
}
FlowCreatedEvent fires when a new flow definition is created.
func NewFlowCreatedEvent ¶ added in v0.24.0
func NewFlowCreatedEvent(flowID, tenantID, code, name, categoryID string) *FlowCreatedEvent
func (*FlowCreatedEvent) EventType ¶ added in v0.24.0
func (*FlowCreatedEvent) EventType() string
type FlowDefinition ¶
type FlowDefinition struct {
Nodes []NodeDefinition `json:"nodes"`
Edges []EdgeDefinition `json:"edges"`
}
FlowDefinition represents the structure of a flow definition JSON (React Flow compatible).
type FlowDeployedEvent ¶ added in v0.24.0
type FlowDeployedEvent struct {
FlowID string `json:"flowId"`
TenantID string `json:"tenantId"`
VersionID string `json:"versionId"`
Version int `json:"version"`
OccurredTime timex.DateTime `json:"occurredTime"`
}
FlowDeployedEvent fires when a flow's schema is deployed as a new draft version (before it's published).
func NewFlowDeployedEvent ¶ added in v0.24.0
func NewFlowDeployedEvent(flowID, tenantID, versionID string, version int) *FlowDeployedEvent
func (*FlowDeployedEvent) EventType ¶ added in v0.24.0
func (*FlowDeployedEvent) EventType() string
type FlowEdge ¶
type FlowEdge struct {
orm.BaseModel `bun:"table:apv_flow_edge,alias:afe"`
orm.Model
FlowVersionID string `json:"flowVersionId" bun:"flow_version_id"`
Key string `json:"key" bun:"key,nullzero"`
SourceNodeID string `json:"sourceNodeId" bun:"source_node_id"`
SourceNodeKey string `json:"sourceNodeKey" bun:"source_node_key"`
TargetNodeID string `json:"targetNodeId" bun:"target_node_id"`
TargetNodeKey string `json:"targetNodeKey" bun:"target_node_key"`
SourceHandle *string `json:"sourceHandle" bun:"source_handle,nullzero"`
}
FlowEdge represents a directed edge between two flow nodes.
type FlowInitiator ¶
type FlowInitiator struct {
orm.BaseModel `bun:"table:apv_flow_initiator,alias:afi"`
orm.Model
FlowID string `json:"flowId" bun:"flow_id"`
Kind InitiatorKind `json:"kind" bun:"kind"`
IDs []string `json:"ids" bun:"ids,type:jsonb"`
}
FlowInitiator represents a flow initiator configuration.
type FlowNode ¶
type FlowNode struct {
orm.BaseModel `bun:"table:apv_flow_node,alias:afn"`
orm.FullAuditedModel
FlowVersionID string `json:"flowVersionId" bun:"flow_version_id"`
Key string `json:"key" bun:"key"`
Kind NodeKind `json:"kind" bun:"kind"`
Name string `json:"name" bun:"name"`
Description *string `json:"description" bun:"description,nullzero"`
ExecutionType ExecutionType `json:"executionType" bun:"execution_type"`
ApprovalMethod ApprovalMethod `json:"approvalMethod" bun:"approval_method"`
PassRule PassRule `json:"passRule" bun:"pass_rule"`
PassRatio decimal.Decimal `json:"passRatio" bun:"pass_ratio"`
EmptyAssigneeAction EmptyAssigneeAction `json:"emptyAssigneeAction" bun:"empty_assignee_action"`
FallbackUserIDs []string `json:"fallbackUserIds" bun:"fallback_user_ids,type:jsonb"`
AdminUserIDs []string `json:"adminUserIds" bun:"admin_user_ids,type:jsonb"`
SameApplicantAction SameApplicantAction `json:"sameApplicantAction" bun:"same_applicant_action"`
IsRollbackAllowed bool `json:"isRollbackAllowed" bun:"is_rollback_allowed"`
RollbackType RollbackType `json:"rollbackType" bun:"rollback_type"`
RollbackDataStrategy RollbackDataStrategy `json:"rollbackDataStrategy" bun:"rollback_data_strategy"`
RollbackTargetKeys []string `json:"rollbackTargetKeys" bun:"rollback_target_keys,type:jsonb,nullzero"`
IsAddAssigneeAllowed bool `json:"isAddAssigneeAllowed" bun:"is_add_assignee_allowed"`
AddAssigneeTypes []AddAssigneeType `json:"addAssigneeTypes" bun:"add_assignee_types,type:jsonb"`
IsRemoveAssigneeAllowed bool `json:"isRemoveAssigneeAllowed" bun:"is_remove_assignee_allowed"`
FieldPermissions map[string]Permission `json:"fieldPermissions" bun:"field_permissions,type:jsonb"`
IsManualCCAllowed bool `json:"isManualCcAllowed" bun:"is_manual_cc_allowed"`
IsTransferAllowed bool `json:"isTransferAllowed" bun:"is_transfer_allowed"`
IsOpinionRequired bool `json:"isOpinionRequired" bun:"is_opinion_required"`
TimeoutHours int `json:"timeoutHours" bun:"timeout_hours"`
TimeoutAction TimeoutAction `json:"timeoutAction" bun:"timeout_action"`
TimeoutNotifyBeforeHours int `json:"timeoutNotifyBeforeHours" bun:"timeout_notify_before_hours"`
UrgeCooldownMinutes int `json:"urgeCooldownMinutes" bun:"urge_cooldown_minutes"`
ConsecutiveApproverAction ConsecutiveApproverAction `json:"consecutiveApproverAction" bun:"consecutive_approver_action"`
IsReadConfirmRequired bool `json:"isReadConfirmRequired" bun:"is_read_confirm_required"`
Branches []ConditionBranch `json:"branches" bun:"branches,type:jsonb,nullzero"`
}
FlowNode represents a node within a flow version.
type FlowNodeAssignee ¶
type FlowNodeAssignee struct {
orm.BaseModel `bun:"table:apv_flow_node_assignee,alias:afna"`
orm.Model
NodeID string `json:"nodeId" bun:"node_id"`
Kind AssigneeKind `json:"kind" bun:"kind"`
IDs []string `json:"ids" bun:"ids,type:jsonb"`
FormField *string `json:"formField" bun:"form_field,nullzero"`
SortOrder int `json:"sortOrder" bun:"sort_order"`
}
FlowNodeAssignee represents a node assignee configuration.
type FlowNodeCC ¶
type FlowNodeCC struct {
orm.BaseModel `bun:"table:apv_flow_node_cc,alias:afnc"`
orm.Model
NodeID string `json:"nodeId" bun:"node_id"`
Kind CCKind `json:"kind" bun:"kind"`
IDs []string `json:"ids" bun:"ids,type:jsonb"`
FormField *string `json:"formField" bun:"form_field,nullzero"`
Timing CCTiming `json:"timing" bun:"timing"`
}
FlowNodeCC represents a node CC configuration.
type FlowPublishedEvent ¶
type FlowPublishedEvent struct {
FlowID string `json:"flowId"`
TenantID string `json:"tenantId"`
VersionID string `json:"versionId"`
OccurredTime timex.DateTime `json:"occurredTime"`
}
FlowPublishedEvent fired when a flow version is published.
func NewFlowPublishedEvent ¶
func NewFlowPublishedEvent(flowID, tenantID, versionID string) *FlowPublishedEvent
func (*FlowPublishedEvent) EventType ¶ added in v0.24.0
func (*FlowPublishedEvent) EventType() string
type FlowToggledEvent ¶ added in v0.24.0
type FlowToggledEvent struct {
FlowID string `json:"flowId"`
TenantID string `json:"tenantId"`
IsActive bool `json:"isActive"`
OccurredTime timex.DateTime `json:"occurredTime"`
}
FlowToggledEvent fires when a flow is activated or deactivated.
func NewFlowToggledEvent ¶ added in v0.24.0
func NewFlowToggledEvent(flowID, tenantID string, isActive bool) *FlowToggledEvent
func (*FlowToggledEvent) EventType ¶ added in v0.24.0
func (*FlowToggledEvent) EventType() string
type FlowUpdatedEvent ¶ added in v0.24.0
type FlowUpdatedEvent struct {
FlowID string `json:"flowId"`
TenantID string `json:"tenantId"`
OccurredTime timex.DateTime `json:"occurredTime"`
}
FlowUpdatedEvent fires when a flow's metadata (name, description, admins, initiators, etc.) is updated. Version publication has its own event.
func NewFlowUpdatedEvent ¶ added in v0.24.0
func NewFlowUpdatedEvent(flowID, tenantID string) *FlowUpdatedEvent
func (*FlowUpdatedEvent) EventType ¶ added in v0.24.0
func (*FlowUpdatedEvent) EventType() string
type FlowVersion ¶
type FlowVersion struct {
orm.BaseModel `bun:"table:apv_flow_version,alias:afv"`
orm.FullAuditedModel
FlowID string `json:"flowId" bun:"flow_id"`
Version int `json:"version" bun:"version"`
Status VersionStatus `json:"status" bun:"status"`
Description *string `json:"description" bun:"description,nullzero"`
StorageMode StorageMode `json:"storageMode" bun:"storage_mode"`
FlowSchema *FlowDefinition `json:"flowSchema" bun:"flow_schema,type:jsonb,nullzero"`
FormSchema *FormDefinition `json:"formSchema" bun:"form_schema,type:jsonb,nullzero"`
PublishedAt *timex.DateTime `json:"publishedAt" bun:"published_at,nullzero"`
PublishedBy *string `json:"publishedBy" bun:"published_by,nullzero"`
}
FlowVersion represents a versioned snapshot of a flow definition.
type FormData ¶
FormData wraps a map to provide helper methods for form data operations.
func NewFormData ¶
func (FormData) Clone ¶
Clone creates a deep copy via JSON serialization. It returns an error if the map contains values that cannot be marshaled to JSON (e.g. channels, functions). The caller should treat an error as uncloneable data and act accordingly rather than silently losing the original content.
type FormDefinition ¶
type FormDefinition struct {
Fields []FormFieldDefinition `json:"fields"`
}
FormDefinition represents the form schema definition for a flow version.
type FormFieldDefinition ¶
type FormFieldDefinition struct {
// Key is the unique identifier for this field (used in form data keys).
Key string `json:"key"`
// Kind is the field type (e.g., "input", "textarea", "select", "number", "date", "upload").
Kind FieldKind `json:"kind"`
// Label is the display label.
Label string `json:"label"`
// Placeholder is the input placeholder text.
Placeholder string `json:"placeholder,omitempty"`
// DefaultValue is the default value for this field.
DefaultValue any `json:"defaultValue,omitempty"`
// IsRequired indicates whether this field is required.
IsRequired bool `json:"isRequired,omitempty"`
// Options is the list of selectable options (for select, radio, checkbox, etc.).
Options []FieldOption `json:"options,omitempty"`
// Validation contains validation rules.
Validation *ValidationRule `json:"validation,omitempty"`
// Props contains additional component-specific properties.
Props map[string]any `json:"props,omitempty"`
// SortOrder controls the display order.
SortOrder int `json:"sortOrder"`
}
FormFieldDefinition represents a single form field.
type FormSnapshot ¶
type FormSnapshot struct {
orm.BaseModel `bun:"table:apv_form_snapshot,alias:afs"`
orm.Model
orm.CreationTrackedModel
InstanceID string `json:"instanceId" bun:"instance_id"`
NodeID string `json:"nodeId" bun:"node_id"`
FormData map[string]any `json:"formData" bun:"form_data,type:jsonb"`
}
FormSnapshot represents a form snapshot for rollback strategies.
type HandleNodeData ¶
type HandleNodeData struct {
BaseNodeData
TaskNodeData
}
HandleNodeData contains data specific to handle nodes.
func (*HandleNodeData) ApplyTo ¶
func (d *HandleNodeData) ApplyTo(node *FlowNode)
ApplyTo applies handle node data to a FlowNode, resolving omitted fields to the handle defaults (sequential execution, PassAny rule).
type InitiatorKind ¶
type InitiatorKind string
InitiatorKind represents the kind of initiator.
const ( InitiatorUser InitiatorKind = "user" InitiatorRole InitiatorKind = "role" InitiatorDepartment InitiatorKind = "department" )
type Instance ¶
type Instance struct {
orm.BaseModel `bun:"table:apv_instance,alias:ai"`
orm.FullAuditedModel
TenantID string `json:"tenantId" bun:"tenant_id"`
FlowID string `json:"flowId" bun:"flow_id"`
FlowVersionID string `json:"flowVersionId" bun:"flow_version_id"`
Title string `json:"title" bun:"title"`
InstanceNo string `json:"instanceNo" bun:"instance_no"`
ApplicantID string `json:"applicantId" bun:"applicant_id"`
ApplicantName string `json:"applicantName" bun:"applicant_name"`
ApplicantDepartmentID *string `json:"applicantDepartmentId" bun:"applicant_department_id,nullzero"`
ApplicantDepartmentName *string `json:"applicantDepartmentName" bun:"applicant_department_name,nullzero"`
Status InstanceStatus `json:"status" bun:"status"`
CurrentNodeID *string `json:"currentNodeId" bun:"current_node_id,nullzero"`
FinishedAt *timex.DateTime `json:"finishedAt" bun:"finished_at,nullzero"`
BusinessRecordID *string `json:"businessRecordId" bun:"business_record_id,nullzero"`
FormData map[string]any `json:"formData" bun:"form_data,type:jsonb,nullzero"`
}
Instance represents a flow instance.
type InstanceBindingFailedEvent ¶ added in v0.24.0
type InstanceBindingFailedEvent struct {
InstanceID string `json:"instanceId"`
TenantID string `json:"tenantId"`
FlowID string `json:"flowId"`
FinalStatus InstanceStatus `json:"finalStatus"`
BusinessTable string `json:"businessTable"`
ErrorMessage string `json:"errorMessage"`
OccurredTime timex.DateTime `json:"occurredTime"`
}
InstanceBindingFailedEvent fires when business binding (writing the final status back to the host's business table) fails after the approval itself has already committed. Subscribers retry asynchronously; the approval is not rolled back. Operators can grep these events for stuck bindings.
func NewInstanceBindingFailedEvent ¶ added in v0.24.0
func NewInstanceBindingFailedEvent(instanceID, tenantID, flowID string, finalStatus InstanceStatus, businessTable, errorMessage string) *InstanceBindingFailedEvent
func (*InstanceBindingFailedEvent) EventType ¶ added in v0.24.0
func (*InstanceBindingFailedEvent) EventType() string
type InstanceCompletedEvent ¶
type InstanceCompletedEvent struct {
InstanceID string `json:"instanceId"`
TenantID string `json:"tenantId"`
FinalStatus InstanceStatus `json:"finalStatus"`
FinishedAt timex.DateTime `json:"finishedAt"`
OccurredTime timex.DateTime `json:"occurredTime"`
}
InstanceCompletedEvent fired when instance reaches a final status.
func NewInstanceCompletedEvent ¶
func NewInstanceCompletedEvent(instanceID, tenantID string, finalStatus InstanceStatus) *InstanceCompletedEvent
func (*InstanceCompletedEvent) EventType ¶ added in v0.24.0
func (*InstanceCompletedEvent) EventType() string
type InstanceCreatedEvent ¶
type InstanceCreatedEvent struct {
InstanceID string `json:"instanceId"`
TenantID string `json:"tenantId"`
FlowID string `json:"flowId"`
Title string `json:"title"`
ApplicantID string `json:"applicantId"`
ApplicantName string `json:"applicantName"`
OccurredTime timex.DateTime `json:"occurredTime"`
}
InstanceCreatedEvent fired when a new instance is created.
func NewInstanceCreatedEvent ¶
func NewInstanceCreatedEvent(instanceID, tenantID, flowID, title, applicantID, applicantName string) *InstanceCreatedEvent
func (*InstanceCreatedEvent) EventType ¶ added in v0.24.0
func (*InstanceCreatedEvent) EventType() string
type InstanceLifecycleHook ¶ added in v0.24.0
type InstanceLifecycleHook interface {
// OnInstanceCreated runs after the instance row is persisted and the
// initial action log is written, but before the engine advances to
// the first node. Returning an error rolls back start_instance.
OnInstanceCreated(ctx context.Context, db orm.DB, instance *Instance) error
// OnInstanceCompleted runs after the engine applies the final state
// transition (within the same transaction). Returning an error rolls
// back the completion. For at-most-once / fire-and-forget side effects
// (webhooks, notifications), subscribe to InstanceCompletedEvent
// instead so the outbox can guarantee delivery.
OnInstanceCompleted(ctx context.Context, db orm.DB, instance *Instance, finalStatus InstanceStatus) error
}
InstanceLifecycleHook is a synchronous extension point for host applications that need to react to approval lifecycle moments inside the same transaction as the business change. Unlike event subscriptions — which are asynchronous, retryable through outbox, and fire after commit — hooks run *during* the transaction, so a hook that returns an error aborts the surrounding business operation.
Use hooks for invariants that must hold within the transaction (e.g. allocating a business row, writing a tightly-coupled record). Use event subscriptions for everything else (webhooks, notifications, analytics, async integrations).
Multiple implementations are aggregated via FX group `group:"vef:approval:lifecycle_hooks"` and invoked in registration order; any non-nil error stops further hooks and bubbles back to the caller.
type InstanceNoGenerator ¶
type InstanceNoGenerator interface {
// Generate creates a unique instance number for a flow identified by flowCode.
Generate(ctx context.Context, flowCode string) (string, error)
}
InstanceNoGenerator generates unique instance numbers for flow instances.
type InstanceResubmittedEvent ¶
type InstanceResubmittedEvent struct {
InstanceID string `json:"instanceId"`
TenantID string `json:"tenantId"`
OperatorID string `json:"operatorId"`
OccurredTime timex.DateTime `json:"occurredTime"`
}
InstanceResubmittedEvent fired when the initiator resubmits a returned instance.
func NewInstanceResubmittedEvent ¶
func NewInstanceResubmittedEvent(instanceID, tenantID, operatorID string) *InstanceResubmittedEvent
func (*InstanceResubmittedEvent) EventType ¶ added in v0.24.0
func (*InstanceResubmittedEvent) EventType() string
type InstanceReturnedEvent ¶
type InstanceReturnedEvent struct {
InstanceID string `json:"instanceId"`
TenantID string `json:"tenantId"`
FromNodeID string `json:"fromNodeId"`
ToNodeID string `json:"toNodeId"`
OperatorID string `json:"operatorId"`
OccurredTime timex.DateTime `json:"occurredTime"`
}
InstanceReturnedEvent fired when instance is returned to the initiator.
func NewInstanceReturnedEvent ¶
func NewInstanceReturnedEvent(instanceID, tenantID, fromNodeID, toNodeID, operatorID string) *InstanceReturnedEvent
func (*InstanceReturnedEvent) EventType ¶ added in v0.24.0
func (*InstanceReturnedEvent) EventType() string
type InstanceRolledBackEvent ¶
type InstanceRolledBackEvent struct {
InstanceID string `json:"instanceId"`
TenantID string `json:"tenantId"`
FromNodeID string `json:"fromNodeId"`
ToNodeID string `json:"toNodeId"`
OperatorID string `json:"operatorId"`
OccurredTime timex.DateTime `json:"occurredTime"`
}
InstanceRolledBackEvent fired when instance is rolled back.
func NewInstanceRolledBackEvent ¶
func NewInstanceRolledBackEvent(instanceID, tenantID, fromNodeID, toNodeID, operatorID string) *InstanceRolledBackEvent
func (*InstanceRolledBackEvent) EventType ¶ added in v0.24.0
func (*InstanceRolledBackEvent) EventType() string
type InstanceStatus ¶
type InstanceStatus string
InstanceStatus represents the status of a flow instance.
const ( InstanceRunning InstanceStatus = "running" InstanceApproved InstanceStatus = "approved" InstanceRejected InstanceStatus = "rejected" InstanceWithdrawn InstanceStatus = "withdrawn" InstanceReturned InstanceStatus = "returned" InstanceTerminated InstanceStatus = "terminated" )
func (InstanceStatus) IsFinal ¶
func (s InstanceStatus) IsFinal() bool
func (InstanceStatus) String ¶
func (s InstanceStatus) String() string
type InstanceWithdrawnEvent ¶
type InstanceWithdrawnEvent struct {
InstanceID string `json:"instanceId"`
TenantID string `json:"tenantId"`
OperatorID string `json:"operatorId"`
OccurredTime timex.DateTime `json:"occurredTime"`
}
InstanceWithdrawnEvent fired when applicant withdraws the instance.
func NewInstanceWithdrawnEvent ¶
func NewInstanceWithdrawnEvent(instanceID, tenantID, operatorID string) *InstanceWithdrawnEvent
func (*InstanceWithdrawnEvent) EventType ¶ added in v0.24.0
func (*InstanceWithdrawnEvent) EventType() string
type NodeAutoPassedEvent ¶
type NodeAutoPassedEvent struct {
InstanceID string `json:"instanceId"`
TenantID string `json:"tenantId"`
NodeID string `json:"nodeId"`
Reason string `json:"reason"`
OccurredTime timex.DateTime `json:"occurredTime"`
}
NodeAutoPassedEvent fired when a node passes without any human decision: auto-pass execution type, empty-assignee auto-pass, or same-applicant auto-pass. Reason carries which rule produced the pass so audit timelines can render the skipped step.
func NewNodeAutoPassedEvent ¶
func NewNodeAutoPassedEvent(instanceID, tenantID, nodeID, reason string) *NodeAutoPassedEvent
func (*NodeAutoPassedEvent) EventType ¶ added in v0.24.0
func (*NodeAutoPassedEvent) EventType() string
type NodeData ¶
type NodeData interface {
// Kind returns the node kind (start, end, approval, handle, cc, condition).
Kind() NodeKind
// GetName returns the display name of the node.
GetName() string
// GetDescription returns the optional description of the node.
GetDescription() *string
// ApplyTo applies this node data's configuration to the given FlowNode,
// resolving omitted optional fields to their documented defaults so the
// persisted node always carries complete, valid configuration.
ApplyTo(node *FlowNode)
}
NodeData is the interface implemented by all node data types.
type NodeDefinition ¶
type NodeDefinition struct {
ID string `json:"id"`
Kind NodeKind `json:"kind"`
Position Position `json:"position"`
Data json.RawMessage `json:"data,omitempty"`
}
NodeDefinition represents a node in the flow definition.
func (*NodeDefinition) ParseData ¶
func (nd *NodeDefinition) ParseData() (NodeData, error)
ParseData parses Data into the appropriate typed struct based on Kind.
type NodeKind ¶
type NodeKind string
NodeKind represents the kind of a flow node. It defines the different types of nodes that can exist in a workflow.
const ( NodeStart NodeKind = "start" // Start node: the entry point of a workflow NodeApproval NodeKind = "approval" // Approval node: requires approval action from assignees NodeHandle NodeKind = "handle" // Handle node: requires processing/handling action from assignees NodeCondition NodeKind = "condition" // Condition node: branches the flow based on conditions NodeEnd NodeKind = "end" // End node: the terminal point of a workflow NodeCC NodeKind = "cc" // CC node: sends notifications to specified users )
type OperatorInfo ¶
type OperatorInfo struct {
ID string `json:"id"`
Name string `json:"name"`
DepartmentID *string `json:"departmentId,omitempty"`
DepartmentName *string `json:"departmentName,omitempty"`
}
OperatorInfo bundles operator identity for action logging.
func (OperatorInfo) NewActionLog ¶
func (o OperatorInfo) NewActionLog(instanceID string, action ActionType) *ActionLog
NewActionLog creates an ActionLog with the operator fields pre-filled.
type PassRule ¶
type PassRule string
PassRule represents the strategy for passing the node (for Parallel/Or methods).
type PassRuleContext ¶
type PassRuleContext struct {
ApprovedCount int
RejectedCount int
TotalCount int
PassRatio float64
}
PassRuleContext provides context for pass rule evaluation.
type PassRuleResult ¶
type PassRuleResult int
PassRuleResult indicates the outcome of pass rule evaluation.
const ( PassRulePending PassRuleResult = iota // Still waiting for more actions PassRulePassed // Node passed PassRuleRejected // Node rejected )
type PassRuleStrategy ¶
type PassRuleStrategy interface {
// Rule returns the pass rule this strategy handles.
Rule() PassRule
// Evaluate determines the pass/reject/pending outcome based on task approval counts.
Evaluate(ctx PassRuleContext) PassRuleResult
}
PassRuleStrategy evaluates whether a node passes based on task results.
type Permission ¶
type Permission string
Permission represents the permission level.
const ( PermissionVisible Permission = "visible" PermissionEditable Permission = "editable" PermissionHidden Permission = "hidden" PermissionRequired Permission = "required" )
type PrincipalDepartmentResolver ¶ added in v0.20.1
type PrincipalDepartmentResolver interface {
// Resolve extracts the department ID and name from the given security principal.
Resolve(ctx context.Context, principal *security.Principal) (departmentID, departmentName *string, err error)
}
PrincipalDepartmentResolver resolves department info from a security principal. Implemented by host apps since Principal.Details is business-specific.
type PrincipalTenantResolver ¶ added in v0.24.0
type PrincipalTenantResolver interface {
// Resolve returns the tenant identifier for the given principal.
Resolve(ctx context.Context, principal *security.Principal) (string, error)
}
PrincipalTenantResolver extracts the caller's tenant ID from a security principal. Implemented by host applications because Principal.Details is schema-less; the framework cannot know where the host stores tenant affiliation.
Returning an empty string is only meaningful for super-admin or system-internal callers, which bypass tenant scoping. For an ordinary authenticated principal an empty tenant fails closed: CallerContext.Authorize returns ErrCrossTenantAccess and TenantScopeFilter / ResolveWriteTenant likewise reject it, rather than waving the request through.
type ResolvedAssignee ¶
type ResolvedAssignee struct {
UserID string
UserName string
DelegatorID *string
DelegatorName *string
}
ResolvedAssignee represents a resolved assignee with optional delegation info.
type RoleMembershipChecker ¶ added in v0.29.0
type RoleMembershipChecker interface {
// UserHasRole reports whether the user currently holds the role.
UserHasRole(ctx context.Context, userID, roleID string) (bool, error)
}
RoleMembershipChecker is an optional capability of AssigneeService. Hosts that can answer "does this user hold this role" directly should implement it: membership checks (e.g. role-based initiation permission) then skip the GetRoleUsers full-listing fallback, which scales poorly for large roles. Detected via type assertion, so existing implementations keep working unchanged.
type RollbackDataStrategy ¶
type RollbackDataStrategy string
RollbackDataStrategy represents the strategy for handling form data during rollback.
const ( RollbackDataClear RollbackDataStrategy = "clear" // Clear form data RollbackDataKeep RollbackDataStrategy = "keep" // Restore the form snapshot captured when the target node was first entered )
func (RollbackDataStrategy) IsValid ¶ added in v0.29.0
func (s RollbackDataStrategy) IsValid() bool
IsValid reports whether the rollback data strategy is one of the defined values.
type RollbackType ¶
type RollbackType string
RollbackType represents the type of rollback allowed.
const ( RollbackNone RollbackType = "none" RollbackPrevious RollbackType = "previous" // To previous node RollbackStart RollbackType = "start" // To start node (applicant) RollbackAny RollbackType = "any" // To any node RollbackSpecified RollbackType = "specified" // To specified nodes )
func (RollbackType) IsValid ¶ added in v0.29.0
func (t RollbackType) IsValid() bool
IsValid reports whether the rollback type is one of the defined values.
type SameApplicantAction ¶
type SameApplicantAction string
SameApplicantAction represents the action when the assignee is the same as the applicant.
const ( SameApplicantAutoPass SameApplicantAction = "auto_pass" SameApplicantSelfApprove SameApplicantAction = "self_approve" // Default SameApplicantTransferSuperior SameApplicantAction = "transfer_superior" // Transfer to superior )
func (SameApplicantAction) IsValid ¶ added in v0.29.0
func (a SameApplicantAction) IsValid() bool
IsValid reports whether the same-applicant action is one of the defined values.
type StartNodeData ¶
type StartNodeData struct {
BaseNodeData
}
StartNodeData contains data specific to start nodes.
func (*StartNodeData) ApplyTo ¶
func (d *StartNodeData) ApplyTo(node *FlowNode)
ApplyTo applies start node data to a FlowNode.
type StorageMode ¶
type StorageMode string
StorageMode represents the storage mode of form data at the FlowVersion level. It determines the physical storage location and format of form data, and is fixed when a version is published. This is different from BindingMode (Flow-level), which controls how the workflow integrates with business systems.
Usage scenarios:
- JSON mode: Flexible schema, suitable for frequently changing form fields, limited query capabilities
- Table mode: Structured storage, suitable for complex queries and data analysis, requires predefined schema
const ( // StorageJSON stores form data in the apv_instance.form_data JSONB column. // It is currently the only implemented storage mode. StorageJSON StorageMode = "json" )
type Task ¶
type Task struct {
orm.BaseModel `bun:"table:apv_task,alias:at"`
orm.FullAuditedModel
TenantID string `json:"tenantId" bun:"tenant_id"`
InstanceID string `json:"instanceId" bun:"instance_id"`
NodeID string `json:"nodeId" bun:"node_id"`
AssigneeID string `json:"assigneeId" bun:"assignee_id"`
AssigneeName string `json:"assigneeName" bun:"assignee_name"`
DelegatorID *string `json:"delegatorId" bun:"delegator_id,nullzero"`
DelegatorName *string `json:"delegatorName" bun:"delegator_name,nullzero"`
SortOrder int `json:"sortOrder" bun:"sort_order"`
Status TaskStatus `json:"status" bun:"status"`
ReadAt *timex.DateTime `json:"readAt" bun:"read_at,nullzero"`
ParentTaskID *string `json:"parentTaskId" bun:"parent_task_id,nullzero"`
AddAssigneeType *AddAssigneeType `json:"addAssigneeType" bun:"add_assignee_type,nullzero"`
Deadline *timex.DateTime `json:"deadline" bun:"deadline,nullzero"`
IsTimeout bool `json:"isTimeout" bun:"is_timeout"`
IsPreWarningSent bool `json:"isPreWarningSent" bun:"is_pre_warning_sent"`
FinishedAt *timex.DateTime `json:"finishedAt" bun:"finished_at,nullzero"`
}
Task represents an approval task.
type TaskApprovedEvent ¶
type TaskApprovedEvent struct {
TaskID string `json:"taskId"`
TenantID string `json:"tenantId"`
InstanceID string `json:"instanceId"`
NodeID string `json:"nodeId"`
OperatorID string `json:"operatorId"`
Opinion *string `json:"opinion,omitempty"`
OccurredTime timex.DateTime `json:"occurredTime"`
}
TaskApprovedEvent fired when a task is approved.
func NewTaskApprovedEvent ¶
func NewTaskApprovedEvent(taskID, tenantID, instanceID, nodeID, operatorID, opinion string) *TaskApprovedEvent
func (*TaskApprovedEvent) EventType ¶ added in v0.24.0
func (*TaskApprovedEvent) EventType() string
type TaskCanceledEvent ¶ added in v0.29.0
type TaskCanceledEvent struct {
TaskID string `json:"taskId"`
TenantID string `json:"tenantId"`
InstanceID string `json:"instanceId"`
NodeID string `json:"nodeId"`
AssigneeID string `json:"assigneeId"`
AssigneeName string `json:"assigneeName"`
Reason string `json:"reason"`
OccurredTime timex.DateTime `json:"occurredTime"`
}
TaskCanceledEvent fired when the engine cancels a task that no longer needs a decision — its node completed through other votes, or the whole instance was withdrawn, rolled back, or terminated. Subscribers use it to retract pending to-do entries for the canceled assignee. Reason carries the triggering operation.
func NewTaskCanceledEvent ¶ added in v0.29.0
func NewTaskCanceledEvent(taskID, tenantID, instanceID, nodeID, assigneeID, assigneeName, reason string) *TaskCanceledEvent
func (*TaskCanceledEvent) EventType ¶ added in v0.29.0
func (*TaskCanceledEvent) EventType() string
type TaskCreatedEvent ¶
type TaskCreatedEvent struct {
TaskID string `json:"taskId"`
TenantID string `json:"tenantId"`
InstanceID string `json:"instanceId"`
NodeID string `json:"nodeId"`
AssigneeID string `json:"assigneeId"`
AssigneeName string `json:"assigneeName"`
Deadline *timex.DateTime `json:"deadline,omitempty"`
OccurredTime timex.DateTime `json:"occurredTime"`
}
TaskCreatedEvent fires the moment a task row is inserted, not the moment it becomes actionable. Under sequential approval, tasks after the first start with Status=Waiting and a nil Deadline; subscribers should treat a nil Deadline as the cue that the task is queued behind a predecessor and must not yet surface a "new pending task" notification. When the predecessor finishes and the task transitions to Pending, no new event is published — subscribers reading the live task table see the change. If this contract proves insufficient, the right extension is to add a dedicated TaskActivatedEvent rather than overloading TaskCreatedEvent.
func NewTaskCreatedEvent ¶
func NewTaskCreatedEvent(taskID, tenantID, instanceID, nodeID, assigneeID, assigneeName string, deadline *timex.DateTime) *TaskCreatedEvent
func (*TaskCreatedEvent) EventType ¶ added in v0.24.0
func (*TaskCreatedEvent) EventType() string
type TaskDeadlineWarningEvent ¶
type TaskDeadlineWarningEvent struct {
TaskID string `json:"taskId"`
TenantID string `json:"tenantId"`
InstanceID string `json:"instanceId"`
NodeID string `json:"nodeId"`
AssigneeID string `json:"assigneeId"`
AssigneeName string `json:"assigneeName"`
Deadline timex.DateTime `json:"deadline"`
HoursLeft int `json:"hoursLeft"`
OccurredTime timex.DateTime `json:"occurredTime"`
}
TaskDeadlineWarningEvent fired when a task is approaching its deadline.
func NewTaskDeadlineWarningEvent ¶
func NewTaskDeadlineWarningEvent(taskID, tenantID, instanceID, nodeID, assigneeID, assigneeName string, deadline timex.DateTime, hoursLeft int) *TaskDeadlineWarningEvent
func (*TaskDeadlineWarningEvent) EventType ¶ added in v0.24.0
func (*TaskDeadlineWarningEvent) EventType() string
type TaskHandledEvent ¶
type TaskHandledEvent struct {
TaskID string `json:"taskId"`
TenantID string `json:"tenantId"`
InstanceID string `json:"instanceId"`
NodeID string `json:"nodeId"`
OperatorID string `json:"operatorId"`
Opinion *string `json:"opinion,omitempty"`
OccurredTime timex.DateTime `json:"occurredTime"`
}
TaskHandledEvent fired when a handle-type task is completed.
func NewTaskHandledEvent ¶
func NewTaskHandledEvent(taskID, tenantID, instanceID, nodeID, operatorID, opinion string) *TaskHandledEvent
func (*TaskHandledEvent) EventType ¶ added in v0.24.0
func (*TaskHandledEvent) EventType() string
type TaskNodeData ¶
type TaskNodeData struct {
Assignees []AssigneeDefinition `json:"assignees,omitempty"`
ExecutionType ExecutionType `json:"executionType,omitempty"`
EmptyAssigneeAction EmptyAssigneeAction `json:"emptyAssigneeAction,omitempty"`
FallbackUserIDs []string `json:"fallbackUserIds,omitempty"`
AdminUserIDs []string `json:"adminUserIds,omitempty"`
IsTransferAllowed *bool `json:"isTransferAllowed,omitempty"`
IsOpinionRequired bool `json:"isOpinionRequired,omitempty"`
TimeoutHours int `json:"timeoutHours,omitempty"`
TimeoutAction TimeoutAction `json:"timeoutAction,omitempty"`
TimeoutNotifyBeforeHours int `json:"timeoutNotifyBeforeHours,omitempty"`
UrgeCooldownMinutes int `json:"urgeCooldownMinutes,omitempty"`
CCs []CCDefinition `json:"ccs,omitempty"`
FieldPermissions map[string]Permission `json:"fieldPermissions,omitempty"`
}
TaskNodeData contains fields shared by approval and handle nodes.
IsTransferAllowed is a pointer because its default is true: an omitted field must resolve to "allowed", which a plain bool zero value cannot represent.
func (*TaskNodeData) GetAssignees ¶
func (d *TaskNodeData) GetAssignees() []AssigneeDefinition
GetAssignees returns the assignee definitions from TaskNodeData.
func (*TaskNodeData) GetCCs ¶
func (d *TaskNodeData) GetCCs() []CCDefinition
GetCCs returns the CC definitions from TaskNodeData.
type TaskReassignedEvent ¶
type TaskReassignedEvent struct {
TaskID string `json:"taskId"`
TenantID string `json:"tenantId"`
InstanceID string `json:"instanceId"`
NodeID string `json:"nodeId"`
FromUserID string `json:"fromUserId"`
FromUserName string `json:"fromUserName"`
ToUserID string `json:"toUserId"`
ToUserName string `json:"toUserName"`
Reason *string `json:"reason,omitempty"`
OccurredTime timex.DateTime `json:"occurredTime"`
}
TaskReassignedEvent fired when an admin reassigns a task to a different user.
func NewTaskReassignedEvent ¶
func NewTaskReassignedEvent(taskID, tenantID, instanceID, nodeID string, from, to UserInfo, reason string) *TaskReassignedEvent
NewTaskReassignedEvent builds the event for an admin reassigning a task. from/to are UserInfo values so the id↔name pairs cannot be transposed.
func (*TaskReassignedEvent) EventType ¶ added in v0.24.0
func (*TaskReassignedEvent) EventType() string
type TaskRejectedEvent ¶
type TaskRejectedEvent struct {
TaskID string `json:"taskId"`
TenantID string `json:"tenantId"`
InstanceID string `json:"instanceId"`
NodeID string `json:"nodeId"`
OperatorID string `json:"operatorId"`
Opinion *string `json:"opinion,omitempty"`
OccurredTime timex.DateTime `json:"occurredTime"`
}
TaskRejectedEvent fired when a task is rejected.
func NewTaskRejectedEvent ¶
func NewTaskRejectedEvent(taskID, tenantID, instanceID, nodeID, operatorID, opinion string) *TaskRejectedEvent
func (*TaskRejectedEvent) EventType ¶ added in v0.24.0
func (*TaskRejectedEvent) EventType() string
type TaskStatus ¶
type TaskStatus string
TaskStatus represents the status of an approval task.
const ( TaskWaiting TaskStatus = "waiting" TaskPending TaskStatus = "pending" TaskApproved TaskStatus = "approved" TaskRejected TaskStatus = "rejected" TaskHandled TaskStatus = "handled" TaskTransferred TaskStatus = "transferred" TaskRolledBack TaskStatus = "rolled_back" TaskCanceled TaskStatus = "canceled" TaskRemoved TaskStatus = "removed" TaskSkipped TaskStatus = "skipped" )
func (TaskStatus) IsFinal ¶
func (s TaskStatus) IsFinal() bool
func (TaskStatus) String ¶
func (s TaskStatus) String() string
type TaskTimedOutEvent ¶ added in v0.24.0
type TaskTimedOutEvent struct {
TaskID string `json:"taskId"`
TenantID string `json:"tenantId"`
InstanceID string `json:"instanceId"`
NodeID string `json:"nodeId"`
AssigneeID string `json:"assigneeId"`
AssigneeName string `json:"assigneeName"`
Deadline timex.DateTime `json:"deadline"`
OccurredTime timex.DateTime `json:"occurredTime"`
}
TaskTimedOutEvent fired when a task times out.
func NewTaskTimedOutEvent ¶ added in v0.24.0
func NewTaskTimedOutEvent(taskID, tenantID, instanceID, nodeID, assigneeID, assigneeName string, deadline timex.DateTime) *TaskTimedOutEvent
func (*TaskTimedOutEvent) EventType ¶ added in v0.24.0
func (*TaskTimedOutEvent) EventType() string
type TaskTransferredEvent ¶
type TaskTransferredEvent struct {
TaskID string `json:"taskId"`
TenantID string `json:"tenantId"`
InstanceID string `json:"instanceId"`
NodeID string `json:"nodeId"`
FromUserID string `json:"fromUserId"`
FromUserName string `json:"fromUserName"`
ToUserID string `json:"toUserId"`
ToUserName string `json:"toUserName"`
Reason *string `json:"reason,omitempty"`
OccurredTime timex.DateTime `json:"occurredTime"`
}
TaskTransferredEvent fired when a task is transferred.
func NewTaskTransferredEvent ¶
func NewTaskTransferredEvent(taskID, tenantID, instanceID, nodeID string, from, to UserInfo, reason string) *TaskTransferredEvent
NewTaskTransferredEvent builds the event for a task moved from one user to another. The from/to identities are passed as UserInfo values rather than four flat strings so the id↔name pairs cannot be transposed at a call site.
func (*TaskTransferredEvent) EventType ¶ added in v0.24.0
func (*TaskTransferredEvent) EventType() string
type TaskUrgedEvent ¶
type TaskUrgedEvent struct {
InstanceID string `json:"instanceId"`
TenantID string `json:"tenantId"`
NodeID string `json:"nodeId"`
TaskID string `json:"taskId"`
UrgerID string `json:"urgerId"`
UrgerName string `json:"urgerName"`
TargetUserID string `json:"targetUserId"`
TargetUserName string `json:"targetUserName"`
Message *string `json:"message,omitempty"`
OccurredTime timex.DateTime `json:"occurredTime"`
}
TaskUrgedEvent fired when a task assignee is urged/reminded.
func NewTaskUrgedEvent ¶
func NewTaskUrgedEvent(instanceID, tenantID, nodeID, taskID string, urger, target UserInfo, message string) *TaskUrgedEvent
NewTaskUrgedEvent builds the event for an urge/reminder. The urger and target identities are passed as UserInfo values so the id↔name pairs cannot be transposed at a call site.
func (*TaskUrgedEvent) EventType ¶ added in v0.24.0
func (*TaskUrgedEvent) EventType() string
type TimeoutAction ¶
type TimeoutAction string
TimeoutAction represents the action to take when a task times out.
const ( TimeoutActionNone TimeoutAction = "none" // Mark timeout only, no auto action TimeoutActionAutoPass TimeoutAction = "auto_pass" // Automatically approve the task TimeoutActionAutoReject TimeoutAction = "auto_reject" // Automatically reject the task TimeoutActionNotify TimeoutAction = "notify" // Send notification only TimeoutActionTransferAdmin TimeoutAction = "transfer_admin" // Transfer to node admin )
func (TimeoutAction) IsValid ¶ added in v0.29.0
func (a TimeoutAction) IsValid() bool
IsValid reports whether the timeout action is one of the defined values.
type UrgeRecord ¶
type UrgeRecord struct {
orm.BaseModel `bun:"table:apv_urge_record,alias:aur"`
orm.Model
orm.CreationTrackedModel
InstanceID string `json:"instanceId" bun:"instance_id"`
NodeID string `json:"nodeId" bun:"node_id"`
TaskID *string `json:"taskId" bun:"task_id,nullzero"`
UrgerID string `json:"urgerId" bun:"urger_id"`
UrgerName string `json:"urgerName" bun:"urger_name"`
TargetUserID string `json:"targetUserId" bun:"target_user_id"`
TargetUserName string `json:"targetUserName" bun:"target_user_name"`
Message string `json:"message" bun:"message"`
}
UrgeRecord represents an urge/reminder record.
type UserInfoResolver ¶
type UserInfoResolver interface {
// ResolveUsers returns user info for the given IDs.
// Missing IDs should be returned with empty Name (not omitted).
ResolveUsers(ctx context.Context, userIDs []string) (map[string]UserInfo, error)
}
UserInfoResolver resolves user display names by IDs (implemented by host app).
type ValidationRule ¶
type ValidationRule struct {
MinLength *int `json:"minLength,omitempty"`
MaxLength *int `json:"maxLength,omitempty"`
Min *float64 `json:"min,omitempty"`
Max *float64 `json:"max,omitempty"`
Pattern string `json:"pattern,omitempty"`
Message string `json:"message,omitempty"`
}
ValidationRule contains validation constraints for a form field.
type VersionStatus ¶
type VersionStatus string
VersionStatus represents the status of a flow version.
const ( VersionDraft VersionStatus = "draft" VersionPublished VersionStatus = "published" VersionArchived VersionStatus = "archived" )