approval

package
v0.20.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 9, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrUnknownNodeKind   = errors.New("unknown node kind")
	ErrNodeDataUnmarshal = errors.New("node data unmarshal failed")
)

Functions

This section is empty.

Types

type ActionLog

type ActionLog struct {
	orm.BaseModel `bun:"table:apv_action_log,alias:aal"`
	orm.IDModel
	orm.CreatedModel

	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"`
	OperatorDeptID     *string          `json:"operatorDeptId" bun:"operator_dept_id,nullzero"`
	OperatorDeptName   *string          `json:"operatorDeptName" bun:"operator_dept_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
)

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
)

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.

func (*ApprovalNodeData) ApplyTo

func (d *ApprovalNodeData) ApplyTo(node *FlowNode)

ApplyTo applies approval node data to a FlowNode.

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"
	AssigneeDept       AssigneeKind = "dept"        // Department head
	AssigneeSelf       AssigneeKind = "self"        // Applicant themselves
	AssigneeSuperior   AssigneeKind = "superior"    // Direct superior
	AssigneeDeptLeader AssigneeKind = "dept_leader" // Continuous multi-level supervisor
	AssigneeFormField  AssigneeKind = "form_field"  // Based on form field
)

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)
	// GetDeptLeaders returns the leader user info for the given department.
	GetDeptLeaders(ctx context.Context, deptID 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"`
	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, nodeID, taskID string, addType AddAssigneeType, assigneeIDs []string, assigneeNames map[string]string) *AssigneesAddedEvent

func (*AssigneesAddedEvent) EventName

func (*AssigneesAddedEvent) EventName() string

func (*AssigneesAddedEvent) OccurredAt

func (e *AssigneesAddedEvent) OccurredAt() timex.DateTime

type AssigneesRemovedEvent

type AssigneesRemovedEvent struct {
	InstanceID    string            `json:"instanceId"`
	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, nodeID, taskID string, assigneeIDs []string, assigneeNames map[string]string) *AssigneesRemovedEvent

func (*AssigneesRemovedEvent) EventName

func (*AssigneesRemovedEvent) EventName() string

func (*AssigneesRemovedEvent) OccurredAt

func (e *AssigneesRemovedEvent) OccurredAt() timex.DateTime

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.

func (BaseNodeData) GetName

func (d BaseNodeData) GetName() string

GetName returns the node name.

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 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 CCKind

type CCKind string

CCKind represents the kind of CC recipient.

const (
	CCUser      CCKind = "user"
	CCRole      CCKind = "role"
	CCDept      CCKind = "dept"
	CCFormField CCKind = "form_field"
)

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.

func (*CCNodeData) Kind

func (*CCNodeData) Kind() NodeKind

Kind returns the node kind.

type CCNotifiedEvent

type CCNotifiedEvent struct {
	InstanceID   string            `json:"instanceId"`
	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 NewCCNotifiedEvent(instanceID, nodeID string, ccUserIDs []string, ccUserNames map[string]string, isManual bool) *CCNotifiedEvent

func (*CCNotifiedEvent) EventName

func (*CCNotifiedEvent) EventName() string

func (*CCNotifiedEvent) OccurredAt

func (e *CCNotifiedEvent) OccurredAt() timex.DateTime

type CCRecord

type CCRecord struct {
	orm.BaseModel `bun:"table:apv_cc_record,alias:acr"`
	orm.IDModel
	orm.CreatedModel

	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 CCTiming

type CCTiming string

CCTiming represents the timing of CC notification.

const (
	CCTimingAlways    CCTiming = "always"     // Always: send CC regardless of result
	CCTimingOnApprove CCTiming = "on_approve" // OnApprove: send CC only when approved
	CCTimingOnReject  CCTiming = "on_reject"  // OnReject: send CC only when rejected
)

type Condition

type Condition struct {
	Kind       ConditionKind `json:"kind"`
	Subject    string        `json:"subject"`
	Operator   string        `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 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"
)

type Delegation

type Delegation struct {
	orm.BaseModel `bun:"table:apv_delegation,alias:ad"`
	orm.Model

	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 {
	// EventName returns the unique event identifier (e.g., "approval.instance.created").
	EventName() string
	// OccurredAt returns the timestamp when the event occurred.
	OccurredAt() timex.DateTime
}

DomainEvent is the base interface for all approval domain events.

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"
)

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.

func (*EndNodeData) Kind

func (*EndNodeData) Kind() NodeKind

Kind returns the node kind.

type EvaluationContext

type EvaluationContext struct {
	FormData        FormData
	ApplicantID     string
	ApplicantDeptID *string
}

EvaluationContext provides context for condition evaluation.

type EventDispatcher

type EventDispatcher interface {
	// Dispatch sends an outbox event record to the external event system.
	Dispatch(ctx context.Context, record EventOutbox) error
}

EventDispatcher dispatches outbox events to external systems. Default implementation forwards to event.Bus.

type EventOutbox

type EventOutbox struct {
	orm.BaseModel `bun:"table:apv_event_outbox,alias:aeo"`
	orm.IDModel
	orm.CreatedModel

	EventID     string            `json:"eventId" bun:"event_id"`
	EventType   string            `json:"eventType" bun:"event_type"`
	Payload     map[string]any    `json:"payload" bun:"payload,type:jsonb"`
	Status      EventOutboxStatus `json:"status" bun:"status"`
	RetryCount  int               `json:"retryCount" bun:"retry_count"`
	LastError   *string           `json:"lastError" bun:"last_error,nullzero"`
	ProcessedAt *timex.DateTime   `json:"processedAt" bun:"processed_at,nullzero"`
	RetryAfter  *timex.DateTime   `json:"retryAfter" bun:"retry_after,nullzero"`
}

EventOutbox represents an event outbox entry for transactional event publishing.

type EventOutboxStatus

type EventOutboxStatus string

EventOutboxStatus represents the processing status of an event outbox record.

const (
	EventOutboxPending    EventOutboxStatus = "pending"
	EventOutboxProcessing EventOutboxStatus = "processing"
	EventOutboxCompleted  EventOutboxStatus = "completed"
	EventOutboxFailed     EventOutboxStatus = "failed"
)

type ExecutionType

type ExecutionType string

ExecutionType represents how a node is executed. It determines whether the node requires manual intervention or can be processed automatically.

const (
	ExecutionManual     ExecutionType = "manual"      // Manual: requires human intervention to process
	ExecutionAuto       ExecutionType = "auto"        // Auto: automatically executed by the system
	ExecutionAutoPass   ExecutionType = "auto_pass"   // AutoPass: automatically approved when no assignee is found
	ExecutionAutoReject ExecutionType = "auto_reject" // AutoReject: automatically rejected when no assignee is found
)

type FieldKind

type FieldKind string

FieldKind represents the kind of a form field.

const (
	FieldInput    FieldKind = "input"
	FieldTextarea FieldKind = "textarea"
	FieldSelect   FieldKind = "select"
	FieldNumber   FieldKind = "number"
	FieldDate     FieldKind = "date"
	FieldUpload   FieldKind = "upload"
)

type FieldOption

type FieldOption struct {
	Label string `json:"label"`
	Value any    `json:"value"`
}

FieldOption represents a selectable option for select/radio/checkbox fields.

type Flow

type Flow struct {
	orm.BaseModel `bun:"table:apv_flow,alias:af"`
	orm.Model

	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.Model

	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 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 FlowEdge

type FlowEdge struct {
	orm.BaseModel `bun:"table:apv_flow_edge,alias:afe"`
	orm.IDModel

	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 FlowFormField

type FlowFormField struct {
	orm.BaseModel `bun:"table:apv_flow_form_field,alias:afff"`
	orm.IDModel

	FlowVersionID string         `json:"flowVersionId" bun:"flow_version_id"`
	Name          string         `json:"name" bun:"name"`
	Kind          FieldKind      `json:"kind" bun:"kind"`
	Label         string         `json:"label" bun:"label"`
	Placeholder   *string        `json:"placeholder" bun:"placeholder,nullzero"`
	DefaultValue  *string        `json:"defaultValue" bun:"default_value,nullzero"`
	IsRequired    *bool          `json:"isRequired" bun:"is_required,nullzero"`
	IsReadonly    *bool          `json:"isReadonly" bun:"is_readonly,nullzero"`
	Validation    map[string]any `json:"validation" bun:"validation,type:jsonb,nullzero"`
	SortOrder     int            `json:"sortOrder" bun:"sort_order"`
	Meta          map[string]any `json:"meta" bun:"meta,type:jsonb,nullzero"`
}

FlowFormField represents a flow form field definition.

type FlowInitiator

type FlowInitiator struct {
	orm.BaseModel `bun:"table:apv_flow_initiator,alias:afi"`
	orm.IDModel

	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.Model

	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.IDModel

	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.IDModel

	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"`
	VersionID    string         `json:"versionId"`
	OccurredTime timex.DateTime `json:"occurredTime"`
}

FlowPublishedEvent fired when a flow version is published.

func NewFlowPublishedEvent

func NewFlowPublishedEvent(flowID, versionID string) *FlowPublishedEvent

func (*FlowPublishedEvent) EventName

func (*FlowPublishedEvent) EventName() string

func (*FlowPublishedEvent) OccurredAt

func (e *FlowPublishedEvent) OccurredAt() timex.DateTime

type FlowVersion

type FlowVersion struct {
	orm.BaseModel `bun:"table:apv_flow_version,alias:afv"`
	orm.Model

	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

type FormData map[string]any

FormData wraps a map to provide helper methods for form data operations.

func NewFormData

func NewFormData(data map[string]any) FormData

func (FormData) Clone

func (f FormData) Clone() FormData

Clone creates a deep copy via JSON serialization.

func (FormData) Get

func (f FormData) Get(key string) any

func (FormData) Set

func (f FormData) Set(key string, val any)

func (FormData) ToMap

func (f FormData) ToMap() map[string]any

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.IDModel
	orm.CreatedModel

	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. Handle nodes default to sequential execution with PassAny rule, since any handler completing the task is sufficient.

func (*HandleNodeData) Kind

func (*HandleNodeData) Kind() NodeKind

Kind returns the node kind.

type InitiatorKind

type InitiatorKind string

InitiatorKind represents the kind of initiator.

const (
	InitiatorUser InitiatorKind = "user"
	InitiatorRole InitiatorKind = "role"
	InitiatorDept InitiatorKind = "dept"
)

type Instance

type Instance struct {
	orm.BaseModel `bun:"table:apv_instance,alias:ai"`
	orm.Model

	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"`
	ApplicantDeptID   *string         `json:"applicantDeptId" bun:"applicant_dept_id,nullzero"`
	ApplicantDeptName *string         `json:"applicantDeptName" bun:"applicant_dept_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 InstanceCompletedEvent

type InstanceCompletedEvent struct {
	InstanceID   string         `json:"instanceId"`
	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 string, finalStatus InstanceStatus) *InstanceCompletedEvent

func (*InstanceCompletedEvent) EventName

func (*InstanceCompletedEvent) EventName() string

func (*InstanceCompletedEvent) OccurredAt

func (e *InstanceCompletedEvent) OccurredAt() timex.DateTime

type InstanceCreatedEvent

type InstanceCreatedEvent struct {
	InstanceID    string         `json:"instanceId"`
	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, flowID, title, applicantID, applicantName string) *InstanceCreatedEvent

func (*InstanceCreatedEvent) EventName

func (*InstanceCreatedEvent) EventName() string

func (*InstanceCreatedEvent) OccurredAt

func (e *InstanceCreatedEvent) OccurredAt() timex.DateTime

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"`
	OperatorID   string         `json:"operatorId"`
	OccurredTime timex.DateTime `json:"occurredTime"`
}

InstanceResubmittedEvent fired when the initiator resubmits a returned instance.

func NewInstanceResubmittedEvent

func NewInstanceResubmittedEvent(instanceID, operatorID string) *InstanceResubmittedEvent

func (*InstanceResubmittedEvent) EventName

func (*InstanceResubmittedEvent) EventName() string

func (*InstanceResubmittedEvent) OccurredAt

func (e *InstanceResubmittedEvent) OccurredAt() timex.DateTime

type InstanceReturnedEvent

type InstanceReturnedEvent struct {
	InstanceID   string         `json:"instanceId"`
	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, fromNodeID, toNodeID, operatorID string) *InstanceReturnedEvent

func (*InstanceReturnedEvent) EventName

func (*InstanceReturnedEvent) EventName() string

func (*InstanceReturnedEvent) OccurredAt

func (e *InstanceReturnedEvent) OccurredAt() timex.DateTime

type InstanceRolledBackEvent

type InstanceRolledBackEvent struct {
	InstanceID   string         `json:"instanceId"`
	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, fromNodeID, toNodeID, operatorID string) *InstanceRolledBackEvent

func (*InstanceRolledBackEvent) EventName

func (*InstanceRolledBackEvent) EventName() string

func (*InstanceRolledBackEvent) OccurredAt

func (e *InstanceRolledBackEvent) OccurredAt() timex.DateTime

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"`
	OperatorID   string         `json:"operatorId"`
	OccurredTime timex.DateTime `json:"occurredTime"`
}

InstanceWithdrawnEvent fired when applicant withdraws the instance.

func NewInstanceWithdrawnEvent

func NewInstanceWithdrawnEvent(instanceID, operatorID string) *InstanceWithdrawnEvent

func (*InstanceWithdrawnEvent) EventName

func (*InstanceWithdrawnEvent) EventName() string

func (*InstanceWithdrawnEvent) OccurredAt

func (e *InstanceWithdrawnEvent) OccurredAt() timex.DateTime

type NodeAutoPassedEvent

type NodeAutoPassedEvent struct {
	InstanceID   string         `json:"instanceId"`
	NodeID       string         `json:"nodeId"`
	Reason       string         `json:"reason"`
	OccurredTime timex.DateTime `json:"occurredTime"`
}

NodeAutoPassedEvent fired when a node is auto-passed.

func NewNodeAutoPassedEvent

func NewNodeAutoPassedEvent(instanceID, nodeID, reason string) *NodeAutoPassedEvent

func (*NodeAutoPassedEvent) EventName

func (*NodeAutoPassedEvent) EventName() string

func (*NodeAutoPassedEvent) OccurredAt

func (e *NodeAutoPassedEvent) OccurredAt() timex.DateTime

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.
	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 NodeEnteredEvent

type NodeEnteredEvent struct {
	InstanceID   string         `json:"instanceId"`
	NodeID       string         `json:"nodeId"`
	NodeName     string         `json:"nodeName"`
	OccurredTime timex.DateTime `json:"occurredTime"`
}

NodeEnteredEvent fired when instance enters a new node.

func NewNodeEnteredEvent

func NewNodeEnteredEvent(instanceID, nodeID, nodeName string) *NodeEnteredEvent

func (*NodeEnteredEvent) EventName

func (*NodeEnteredEvent) EventName() string

func (*NodeEnteredEvent) OccurredAt

func (e *NodeEnteredEvent) OccurredAt() timex.DateTime

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"`
	DeptID   *string `json:"deptId,omitempty"`
	DeptName *string `json:"deptName,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 ParallelJoinedEvent

type ParallelJoinedEvent struct {
	InstanceID   string         `json:"instanceId"`
	NodeID       string         `json:"nodeId"`
	OccurredTime timex.DateTime `json:"occurredTime"`
}

ParallelJoinedEvent fired when parallel branches are joined.

func NewParallelJoinedEvent

func NewParallelJoinedEvent(instanceID, nodeID string) *ParallelJoinedEvent

func (*ParallelJoinedEvent) EventName

func (*ParallelJoinedEvent) EventName() string

func (*ParallelJoinedEvent) OccurredAt

func (e *ParallelJoinedEvent) OccurredAt() timex.DateTime

type ParallelRecord

type ParallelRecord struct {
	orm.BaseModel `bun:"table:apv_parallel_record,alias:apr"`
	orm.IDModel
	orm.CreatedModel

	InstanceID string  `json:"instanceId" bun:"instance_id"`
	NodeID     string  `json:"nodeId" bun:"node_id"`
	TaskID     string  `json:"taskId" bun:"task_id"`
	AssigneeID string  `json:"assigneeId" bun:"assignee_id"`
	Decision   *string `json:"decision" bun:"decision,nullzero"`
	Opinion    *string `json:"opinion" bun:"opinion,nullzero"`
}

ParallelRecord represents a parallel approval decision record.

type PassRule

type PassRule string

PassRule represents the strategy for passing the node (for Parallel/Or methods).

const (
	PassAll       PassRule = "all"        // All assignees must approve
	PassAny       PassRule = "any"        // At least one assignee must approve
	PassRatio     PassRule = "ratio"      // A certain percentage of assignees must approve
	PassAnyReject PassRule = "any_reject" // Any one rejection fails
)

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 Position

type Position struct {
	X float64 `json:"x"`
	Y float64 `json:"y"`
}

Position represents the visual position of a node on the canvas.

type PrincipalDeptResolver

type PrincipalDeptResolver interface {
	// Resolve extracts the department ID and name from the given security principal.
	Resolve(ctx context.Context, principal *security.Principal) (deptID, deptName *string, err error)
}

PrincipalDeptResolver resolves department info from a security principal. Implemented by host apps since Principal.Details is business-specific.

type ResolvedAssignee

type ResolvedAssignee struct {
	UserID        string
	UserName      string
	DelegatorID   *string
	DelegatorName *string
}

ResolvedAssignee represents a resolved assignee with optional delegation info.

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"  // Keep history data
)

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
)

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
)

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.

func (*StartNodeData) Kind

func (*StartNodeData) Kind() NodeKind

Kind returns the node kind.

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  StorageMode = "json"  // JSON: form data stored in apv_instance.form_data (JSONB column)
	StorageTable StorageMode = "table" // Table: form data stored in dynamically created tables (e.g., apv_form_data_{flow_code})
)

type Task

type Task struct {
	orm.BaseModel `bun:"table:apv_task,alias:at"`
	orm.Model

	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"`
	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, instanceID, nodeID, operatorID, opinion string) *TaskApprovedEvent

func (*TaskApprovedEvent) EventName

func (*TaskApprovedEvent) EventName() string

func (*TaskApprovedEvent) OccurredAt

func (e *TaskApprovedEvent) OccurredAt() timex.DateTime

type TaskCreatedEvent

type TaskCreatedEvent struct {
	TaskID       string          `json:"taskId"`
	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 fired when a new task is created.

func NewTaskCreatedEvent

func NewTaskCreatedEvent(taskID, instanceID, nodeID, assigneeID, assigneeName string, deadline *timex.DateTime) *TaskCreatedEvent

func (*TaskCreatedEvent) EventName

func (*TaskCreatedEvent) EventName() string

func (*TaskCreatedEvent) OccurredAt

func (e *TaskCreatedEvent) OccurredAt() timex.DateTime

type TaskDeadlineWarningEvent

type TaskDeadlineWarningEvent struct {
	TaskID       string         `json:"taskId"`
	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, instanceID, nodeID, assigneeID, assigneeName string, deadline timex.DateTime, hoursLeft int) *TaskDeadlineWarningEvent

func (*TaskDeadlineWarningEvent) EventName

func (*TaskDeadlineWarningEvent) EventName() string

func (*TaskDeadlineWarningEvent) OccurredAt

func (e *TaskDeadlineWarningEvent) OccurredAt() timex.DateTime

type TaskHandledEvent

type TaskHandledEvent struct {
	TaskID       string         `json:"taskId"`
	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, instanceID, nodeID, operatorID, opinion string) *TaskHandledEvent

func (*TaskHandledEvent) EventName

func (*TaskHandledEvent) EventName() string

func (*TaskHandledEvent) OccurredAt

func (e *TaskHandledEvent) OccurredAt() timex.DateTime

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.

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"`
	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, instanceID, nodeID, fromUserID, fromUserName, toUserID, toUserName, reason string) *TaskReassignedEvent

func (*TaskReassignedEvent) EventName

func (*TaskReassignedEvent) EventName() string

func (*TaskReassignedEvent) OccurredAt

func (e *TaskReassignedEvent) OccurredAt() timex.DateTime

type TaskRejectedEvent

type TaskRejectedEvent struct {
	TaskID       string         `json:"taskId"`
	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, instanceID, nodeID, operatorID, opinion string) *TaskRejectedEvent

func (*TaskRejectedEvent) EventName

func (*TaskRejectedEvent) EventName() string

func (*TaskRejectedEvent) OccurredAt

func (e *TaskRejectedEvent) OccurredAt() timex.DateTime

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 TaskTimeoutEvent

type TaskTimeoutEvent struct {
	TaskID       string         `json:"taskId"`
	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"`
}

TaskTimeoutEvent fired when a task times out.

func NewTaskTimeoutEvent

func NewTaskTimeoutEvent(taskID, instanceID, nodeID, assigneeID, assigneeName string, deadline timex.DateTime) *TaskTimeoutEvent

func (*TaskTimeoutEvent) EventName

func (*TaskTimeoutEvent) EventName() string

func (*TaskTimeoutEvent) OccurredAt

func (e *TaskTimeoutEvent) OccurredAt() timex.DateTime

type TaskTransferredEvent

type TaskTransferredEvent struct {
	TaskID       string         `json:"taskId"`
	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, instanceID, nodeID, fromUserID, fromUserName, toUserID, toUserName, reason string) *TaskTransferredEvent

func (*TaskTransferredEvent) EventName

func (*TaskTransferredEvent) EventName() string

func (*TaskTransferredEvent) OccurredAt

func (e *TaskTransferredEvent) OccurredAt() timex.DateTime

type TaskUrgedEvent

type TaskUrgedEvent struct {
	InstanceID     string         `json:"instanceId"`
	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, nodeID, taskID, urgerID, urgerName, targetUserID, targetUserName, message string) *TaskUrgedEvent

func (*TaskUrgedEvent) EventName

func (*TaskUrgedEvent) EventName() string

func (*TaskUrgedEvent) OccurredAt

func (e *TaskUrgedEvent) OccurredAt() timex.DateTime

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
)

type UrgeRecord

type UrgeRecord struct {
	orm.BaseModel `bun:"table:apv_urge_record,alias:aur"`
	orm.IDModel
	orm.CreatedModel

	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 UserInfo

type UserInfo struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

UserInfo represents a user identity with ID and display name.

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"
)

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL