Questa sezione copre il modello di configurazione completo: gerarchie di entità, logica di matching, modalità di assegnazione e gestione del ciclo di vita. È rilevante per chiunque configuri le mission tramite la dashboard o le integri via API. Per una panoramica di alto livello su cosa fanno le mission, vedi i Fondamenti della Gamification. Per i pattern condivisi tra tutti i domini (espressioni JsonLogic, timeframe, entity matching), vedi i Pattern Trasversali.
MissionConfiguration (cosa conta e come contarlo)
│
└──▶ MissionRule (quando, per chi e come assegnare)
│
└──▶ Mission (istanza di tracciamento per utente o per gruppo)
│
└──▶ MissionLog (traccia di audit immutabile dei progressi)| Field | Type | Description |
|---|---|---|
missionConfigurationId | nanoid | Identificatore univoco |
name | string | Riferimento leggibile (es. "Rispondi correttamente a 10 Quiz") |
missionType | INDIVIDUAL | GROUP | Se le mission tracciano un singolo utente o un gruppo |
matchType | INSTANCE | ENTITY | TAG | Come abbinare gli eventi in arrivo |
matchEntity | Activity | Quiz | Tag | Quale tipo di entità monitorare |
matchEntityId | string? | ID specifico dell'entità o del tag (obbligatorio per INSTANCE e TAG) |
matchCondition | JsonLogic | Logica di matching aggiuntiva valutata a runtime |
incrementExpression | JsonLogic | Quanto progresso aggiunge ogni evento corrispondente |
targetAmountExpression | JsonLogic | La soglia di completamento |
origin | CATALOG | CUSTOM | Dove è stata creata questa configurazione |
defaultLang | lang | Codice lingua predefinito |
langs | lang[] | Lingue supportate (1–10) |
INSTANCE: Corrisponde a un'entità specifica. Esempio: completare l'attività abc123. Richiede matchEntityId.ENTITY: Corrisponde a qualsiasi entità di quel tipo. Esempio: completare qualsiasi quiz.TAG: Corrisponde a qualsiasi entità taggata con il tag indicato. Esempio: completare qualsiasi attività taggata sustainability. Richiede matchEntityId (l'ID del tag).matchCondition filtra quali eventi sono idonei. Riceve { mission } come contesto e deve restituire un valore truthy affinché l'evento venga conteggiato. Esempio: contare solo i quiz con outcome SUCCESS.incrementExpression definisce quanto progresso aggiunge ogni evento idoneo. Riceve { user, event } e deve restituire un numero. Un valore statico come 1 significa "aggiungi 1 per evento". Un'espressione condizionale può assegnare importi diversi in base al contesto — ad esempio, assegnare 2 per i quiz difficili e 1 per quelli facili.targetAmountExpression definisce quando la mission è completata. Riceve { user, mission } e deve restituire un numero. Un valore statico 10 significa "completa dopo 10 incrementi". Un'espressione dinamica può impostare obiettivi diversi per utente — ad esempio, un obiettivo più alto per gli utenti premium.Vincolo: Quando un'espressione JsonLogic restituisce null, stringa vuota oNaN, il sistema utilizza il valore predefinito1sia per il calcolo dell'incremento che dell'obiettivo.
| Field | Type | Description |
|---|---|---|
missionRuleId | nanoid | Identificatore univoco |
name | string | Nome leggibile |
missionType | INDIVIDUAL | GROUP | Deve corrispondere alle configurazioni referenziate |
state | PENDING | ACTIVE | ENDED | Stato del ciclo di vita |
assignmentMode | LAZY | EVENT | DISABLED | Come vengono assegnate le mission agli utenti |
usersMatchCondition | JsonLogic? | A quali utenti si applica questa regola (obbligatorio per INDIVIDUAL) |
missionsMatchCondition | JsonLogic | Filtra quali configurazioni istanziare |
missionConfigurationsPool | string[]? | Lista esplicita di ID configurazione da utilizzare (alternativa al matching) |
LAZY: Le mission vengono create on-demand quando un utente esplora le mission disponibili. Se l'utente soddisfa le condizioni della regola, la mission viene generata in tempo reale. Ideale per esperienze guidate dalla scoperta in cui gli utenti scelgono quali mission perseguire.EVENT: Le mission vengono assegnate automaticamente quando si verifica un evento corrispondente — ad esempio, assegnare una mission di follow-up quando l'utente completa un Learning Path. È reattivo e in tempo reale.DISABLED: Non vengono effettuate assegnazioni. Utilizzato per disattivare una regola senza eliminarla.assignmentMode è EVENT, quattro campi aggiuntivi diventano obbligatori:| Field | Type | Description |
|---|---|---|
eventMatchType | INSTANCE | ENTITY | TAG | Come abbinare l'evento scatenante |
eventMatchEntity | Activity | Quiz | Tag | User | Quale tipo di entità attiva l'assegnazione |
eventMatchEntityId | string | ID specifico dell'entità o del tag |
eventMatchCondition | JsonLogic | Filtraggio aggiuntivo sull'evento |
Vincolo: Tutti e quattro i campi eventMatch*sono obbligatori quandoassignmentModeèEVENTe devono essere assenti per le altre modalità.
usersMatchCondition determina quali utenti sono idonei per questa regola. Riceve { user, activeMissions } come contesto — dove activeMissions è la lista delle mission già assegnate all'utente. Questo consente regole come "assegna solo se l'utente non ha già 3 mission attive".Vincolo: usersMatchConditionè obbligatorio per le regoleINDIVIDUALe deve essere assente per le regoleGROUP(l'intero gruppo è idoneo per definizione).
missionsMatchCondition filtra quali Mission Configuration devono essere istanziate. Riceve { user, activeMissions, mission } dove mission è una configurazione candidata. Questo consente regole come "istanzia solo le configurazioni taggate con il dipartimento dell'utente".missionConfigurationsPool è un'alternativa a missionsMatchCondition — una lista esplicita di ID configurazione. Quando presente, vengono considerate solo queste configurazioni.| Field | Type | Description |
|---|---|---|
timeframeType | PERMANENT | RANGE | RECURRING | Se la regola è attiva a tempo indeterminato, una sola volta o in modo ricorrente |
timeframeStartsAt | ISO datetime | Quando la regola inizia |
timeframeEndsAt | ISO datetime? | Quando la regola termina (obbligatorio per RANGE e RECURRING) |
timeframeTimezoneType | FIXED | USER | Se utilizzare un fuso orario fisso o quello di ciascun utente |
timeframeTimezone | timezone? | Il fuso orario fisso (obbligatorio quando FIXED) |
recurrence | DAILY | WEEKLY | MONTHLY | CUSTOM? | Cadenza di reset (obbligatorio per RECURRING) |
scheduleCron | cron? | Espressione cron (obbligatorio quando la ricorrenza è CUSTOM) |
| Field | Type | Description |
|---|---|---|
groupTagId | string? | Il tag che identifica il gruppo (obbligatorio per le regole GROUP) |
Vincolo: Le regole GROUPrichiedonogroupTagId. Le regoleINDIVIDUALnon devono averlo.
| Field | Type | Description |
|---|---|---|
missionId | nanoid | Identificatore univoco |
missionConfigurationId | nanoid | La configurazione su cui si basa questa mission |
missionRuleId | nanoid? | La regola che ha attivato questa assegnazione |
missionType | INDIVIDUAL | GROUP | Ereditato dalla configurazione |
userId | nanoid? | L'utente a cui appartiene questa mission (solo INDIVIDUAL) |
groupTagId | string? | Il gruppo a cui appartiene questa mission (solo GROUP) |
state | PENDING | ACTIVE | ENDED | Stato del ciclo di vita |
isCompleted | boolean? | Se l'obiettivo è stato raggiunto |
completedAt | ISO datetime? | Quando la mission è stata completata |
currentAmount | number | Progresso accumulato |
targetAmount | number | Soglia di completamento congelata |
periodId | string | Chiave di deduplicazione per le mission ricorrenti |
matchType, matchEntity, matchEntityId, matchCondition, incrementExpression, targetAmountExpression) in modo che il tracciamento dei progressi non dipenda dal fatto che la configurazione rimanga invariata.currentAmount >= targetAmount, la mission viene contrassegnata come completata e smette di accettare ulteriori incrementi.currentAmount. A differenza delle mission individuali, le mission di gruppo continuano a contare dopo aver raggiunto l'obiettivo — tracciano il progresso cumulativo del gruppo senza limite.Vincolo: Le mission INDIVIDUAL richiedono userIde non ammettonogroupTagId. Le mission GROUP richiedonogroupTagIde non ammettonouserId.
periodId funge da chiave di deduplicazione che impedisce alla stessa regola di assegnare mission duplicate nello stesso periodo temporale:| Timeframe | periodId Format | Example |
|---|---|---|
PERMANENT | "PERMANENT" | PERMANENT |
RANGE | Orario di inizio della regola in UTC | 2025-01-01T00:00:00 |
RECURRING / DAILY | YYYY-MM-DD | 2025-09-15 |
RECURRING / WEEKLY | YYYY-Www | 2025-W38 |
RECURRING / MONTHLY | YYYY-MM | 2025-09 |
RECURRING / CUSTOM | Orario dell'ultimo trigger cron in UTC | 2025-09-15T06:00:00 |
la regola assegna la mission
│
┌──────▼──────┐
│ PENDING │ ◀── la mission inizia in futuro
└──────┬──────┘
│ startsAt raggiunto
▼
┌─────────────┐ currentAmount >= targetAmount
│ ACTIVE │ ──────────────────────────────────▶ isCompleted = true
└──────┬──────┘ (solo INDIVIDUAL; GROUP continua a contare)
│ endsAt raggiunto
▼
┌─────────────┐
│ ENDED │ ◀── intervallo temporale scaduto
└─────────────┘targetAmount non è ancora calcolato.targetAmount viene calcolato da targetAmountExpression e congelato — modifiche successive all'espressione o al contesto utente non lo influenzano.| Field | Type | Description |
|---|---|---|
missionLogId | nanoid | Identificatore univoco |
missionId | nanoid | La mission che è stata aggiornata |
missionConfigurationId | nanoid | Riferimento alla configurazione |
missionType | INDIVIDUAL | GROUP | Tipo di mission |
userId | nanoid | L'utente che ha attivato il progresso |
groupTagId | nanoid? | Per le mission di gruppo |
amount | number | L'incremento applicato (predefinito: 1) |
additionalData | record? | Contesto aggiuntivo dall'evento sorgente |
userId registra quale utente del gruppo ha contribuito, mentre l'incremento si applica al currentAmount condiviso.ACTIVE con assignmentMode: EVENT che corrispondono al tipo di entità e all'ID dell'evento.eventMatchCondition viene valutata rispetto ai dati dell'evento.usersMatchCondition viene valutata rispetto all'utente (per INDIVIDUAL) o saltata (per GROUP).missionsMatchCondition (o missionConfigurationsPool) determina quali configurazioni istanziare.MissionRuleEvaluation previene assegnazioni duplicate nello stesso periodo.ACTIVE con assignmentMode: LAZY.| Source Event | Maps To |
|---|---|
ActivityLog | Activity |
QuizLog | Quiz |
| Others | Pass through |
ACTIVE e non completate che corrispondono all'evento (per matchType, matchEntity, matchEntityId).matchCondition viene valutata rispetto al contesto dell'evento.incrementExpression viene valutata per determinare quanto aggiungere.currentAmount della mission viene incrementato atomicamente.MissionLog.currentAmount >= targetAmount, la mission viene contrassegnata come completata (isCompleted: true, completedAt impostato).currentAmount della mission viene incrementato una sola volta. Questo viene garantito tramite una chiave di idempotenza basata sull'ID dell'evento.isCompleted: true solo se precedentemente era false, prevenendo il doppio completamento.{
"missionConfigurationId": "mc_quiz_weekly",
"name": "Weekly Quiz Challenge",
"missionType": "INDIVIDUAL",
"matchType": "ENTITY",
"matchEntity": "Quiz",
"matchCondition": { "===": [{ "var": "event.outcome" }, "SUCCESS"] },
"incrementExpression": 1,
"targetAmountExpression": 5,
"defaultLang": "en",
"langs": ["en", "it"]
}{
"missionRuleId": "mr_quiz_weekly",
"name": "Weekly Quiz Rule",
"missionType": "INDIVIDUAL",
"assignmentMode": "LAZY",
"usersMatchCondition": true,
"missionsMatchCondition": true,
"missionConfigurationsPool": ["mc_quiz_weekly"],
"timeframeType": "RECURRING",
"timeframeStartsAt": "2025-01-06T00:00:00Z",
"timeframeEndsAt": "2025-12-31T23:59:59Z",
"timeframeTimezoneType": "USER",
"recurrence": "WEEKLY",
"defaultLang": "en",
"langs": ["en"]
}periodId: "2025-W38", state: "ACTIVE", currentAmount: 0, targetAmount: 5matchCondition è soddisfatta. incrementExpression restituisce 1. La mission diventa currentAmount: 1.matchCondition restituisce falso (l'outcome non è SUCCESS). Nessun incremento.currentAmount: 5, isCompleted: true.2025-W39). La regola LAZY crea una nuova mission con currentAmount: 0.{
"missionRuleId": "mr_team_event",
"name": "Team Onboarding Challenge",
"missionType": "GROUP",
"groupTagId": "department:engineering",
"assignmentMode": "EVENT",
"eventMatchType": "ENTITY",
"eventMatchEntity": "Activity",
"eventMatchEntityId": "activity_onboarding",
"eventMatchCondition": true,
"missionsMatchCondition": true,
"missionConfigurationsPool": ["mc_team_onboarding"],
"timeframeType": "RANGE",
"timeframeStartsAt": "2025-09-01T00:00:00Z",
"timeframeEndsAt": "2025-09-30T23:59:59Z",
"timeframeTimezoneType": "FIXED",
"timeframeTimezone": "Europe/Rome"
}| Concetto | Scopo |
|---|---|
| MissionConfiguration | Definisce quali eventi contano e come misurare i progressi (matching + espressioni) |
| MissionRule | Definisce quando, per chi e come vengono assegnate le mission (intervallo temporale + targeting + modalità) |
| Mission | Istanza di tracciamento per utente o per gruppo con obiettivo congelato e progresso in tempo reale |
| MissionLog | Traccia di audit immutabile di ogni evento di progresso |
| assignmentMode | Come vengono create le mission: LAZY (on-demand), EVENT (reattivo), DISABLED (disattivato) |
| missionType | INDIVIDUAL (un utente, si ferma all'obiettivo) o GROUP (contatore condiviso, continua) |
| matchType | Come vengono abbinati gli eventi: INSTANCE (specifico), ENTITY (qualsiasi del tipo), TAG (per tag) |
| periodId | Chiave di deduplicazione che garantisce una mission per regola per periodo temporale |
| targetAmount | Congelato quando la mission diventa ACTIVE — immune a modifiche successive della configurazione |
| MissionRuleEvaluation | Traccia quali combinazioni regola+periodo sono state valutate, prevenendo duplicati |