funifier-mcp 0.2.26 → 0.2.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/.cursor/rules/funifier.mdc +38 -41
  2. package/.github/copilot-instructions.md +38 -41
  3. package/AGENTS.md +56 -49
  4. package/README.md +40 -22
  5. package/datasource-funifier-docs/.coverage.json +326 -0
  6. package/datasource-funifier-docs/.validation.json +593 -0
  7. package/datasource-funifier-docs/knowledge/guides/aggregates.md +182 -70
  8. package/datasource-funifier-docs/knowledge/guides/database-access.md +174 -88
  9. package/datasource-funifier-docs/knowledge/guides/java-entities.md +294 -204
  10. package/datasource-funifier-docs/knowledge/guides/java-libraries.md +202 -226
  11. package/datasource-funifier-docs/knowledge/guides/java-managers.md +343 -265
  12. package/datasource-funifier-docs/knowledge/guides/trigger-examples.md +180 -236
  13. package/datasource-funifier-docs/knowledge/guides/triggers-guide.md +273 -191
  14. package/datasource-funifier-docs/knowledge/index.md +4 -1
  15. package/datasource-funifier-docs/knowledge/modules/achievement.md +1126 -28
  16. package/datasource-funifier-docs/knowledge/modules/action-log.md +469 -62
  17. package/datasource-funifier-docs/knowledge/modules/action.md +522 -70
  18. package/datasource-funifier-docs/knowledge/modules/auth.md +718 -69
  19. package/datasource-funifier-docs/knowledge/modules/avatar.md +483 -18
  20. package/datasource-funifier-docs/knowledge/modules/backup.md +603 -25
  21. package/datasource-funifier-docs/knowledge/modules/challenge.md +1048 -220
  22. package/datasource-funifier-docs/knowledge/modules/compact.md +469 -26
  23. package/datasource-funifier-docs/knowledge/modules/competition.md +811 -109
  24. package/datasource-funifier-docs/knowledge/modules/crossword.md +504 -28
  25. package/datasource-funifier-docs/knowledge/modules/csv-data.md +645 -20
  26. package/datasource-funifier-docs/knowledge/modules/custom-object.md +701 -36
  27. package/datasource-funifier-docs/knowledge/modules/database.md +730 -164
  28. package/datasource-funifier-docs/knowledge/modules/folder.md +935 -280
  29. package/datasource-funifier-docs/knowledge/modules/kpi-formulas.md +410 -15
  30. package/datasource-funifier-docs/knowledge/modules/lastmile.md +568 -29
  31. package/datasource-funifier-docs/knowledge/modules/leaderboard.md +595 -126
  32. package/datasource-funifier-docs/knowledge/modules/level.md +536 -54
  33. package/datasource-funifier-docs/knowledge/modules/lottery.md +809 -76
  34. package/datasource-funifier-docs/knowledge/modules/marketplace.md +688 -17
  35. package/datasource-funifier-docs/knowledge/modules/mystery.md +662 -52
  36. package/datasource-funifier-docs/knowledge/modules/notification.md +564 -26
  37. package/datasource-funifier-docs/knowledge/modules/patterns.md +519 -814
  38. package/datasource-funifier-docs/knowledge/modules/player.md +773 -73
  39. package/datasource-funifier-docs/knowledge/modules/point.md +380 -83
  40. package/datasource-funifier-docs/knowledge/modules/public.md +508 -178
  41. package/datasource-funifier-docs/knowledge/modules/question.md +619 -99
  42. package/datasource-funifier-docs/knowledge/modules/quiz.md +565 -120
  43. package/datasource-funifier-docs/knowledge/modules/scheduler.md +1092 -39
  44. package/datasource-funifier-docs/knowledge/modules/security.md +674 -112
  45. package/datasource-funifier-docs/knowledge/modules/staging.md +742 -19
  46. package/datasource-funifier-docs/knowledge/modules/story.md +565 -29
  47. package/datasource-funifier-docs/knowledge/modules/studio-page.md +470 -144
  48. package/datasource-funifier-docs/knowledge/modules/swap.md +552 -84
  49. package/datasource-funifier-docs/knowledge/modules/team.md +563 -45
  50. package/datasource-funifier-docs/knowledge/modules/trigger.md +876 -134
  51. package/datasource-funifier-docs/knowledge/modules/upload.md +468 -95
  52. package/datasource-funifier-docs/knowledge/modules/virtual-good.md +510 -63
  53. package/datasource-funifier-docs/knowledge/modules/webhook.md +375 -28
  54. package/datasource-funifier-docs/knowledge/modules/websocket.md +459 -26
  55. package/datasource-funifier-docs/knowledge/modules/widget.md +613 -27
  56. package/dist/cli/init.d.ts.map +1 -1
  57. package/dist/cli/init.js +42 -1
  58. package/dist/cli/init.js.map +1 -1
  59. package/dist/cli/init.test.js +74 -3
  60. package/dist/cli/init.test.js.map +1 -1
  61. package/dist/cli/persona.d.ts +3 -0
  62. package/dist/cli/persona.d.ts.map +1 -0
  63. package/dist/cli/persona.js +25 -0
  64. package/dist/cli/persona.js.map +1 -0
  65. package/dist/mcp/bundle.js +119 -93
  66. package/dist/mcp/check-update.d.ts +5 -0
  67. package/dist/mcp/check-update.d.ts.map +1 -1
  68. package/dist/mcp/check-update.js +21 -10
  69. package/dist/mcp/check-update.js.map +1 -1
  70. package/dist/mcp/check-update.test.d.ts +2 -0
  71. package/dist/mcp/check-update.test.d.ts.map +1 -0
  72. package/dist/mcp/check-update.test.js +33 -0
  73. package/dist/mcp/check-update.test.js.map +1 -0
  74. package/dist/mcp/index.js +2 -2
  75. package/dist/mcp/index.js.map +1 -1
  76. package/dist/mcp/prompts/templates.d.ts.map +1 -1
  77. package/dist/mcp/prompts/templates.js +35 -0
  78. package/dist/mcp/prompts/templates.js.map +1 -1
  79. package/dist/mcp/resources/documentation.d.ts +1 -1
  80. package/dist/mcp/resources/documentation.d.ts.map +1 -1
  81. package/dist/mcp/resources/documentation.js +39 -3
  82. package/dist/mcp/resources/documentation.js.map +1 -1
  83. package/dist/mcp/tools/connect.d.ts.map +1 -1
  84. package/dist/mcp/tools/connect.js +18 -8
  85. package/dist/mcp/tools/connect.js.map +1 -1
  86. package/dist/mcp/tools/database.d.ts.map +1 -1
  87. package/dist/mcp/tools/database.js +59 -47
  88. package/dist/mcp/tools/database.js.map +1 -1
  89. package/dist/mcp/tools/database.test.js +2 -2
  90. package/dist/mcp/tools/database.test.js.map +1 -1
  91. package/dist/mcp/tools/delete.d.ts.map +1 -1
  92. package/dist/mcp/tools/delete.js +13 -3
  93. package/dist/mcp/tools/delete.js.map +1 -1
  94. package/dist/mcp/tools/execute.d.ts.map +1 -1
  95. package/dist/mcp/tools/execute.js +20 -9
  96. package/dist/mcp/tools/execute.js.map +1 -1
  97. package/dist/mcp/tools/folder.d.ts.map +1 -1
  98. package/dist/mcp/tools/folder.js +22 -12
  99. package/dist/mcp/tools/folder.js.map +1 -1
  100. package/dist/mcp/tools/get.d.ts.map +1 -1
  101. package/dist/mcp/tools/get.js +16 -6
  102. package/dist/mcp/tools/get.js.map +1 -1
  103. package/dist/mcp/tools/index.d.ts +1 -1
  104. package/dist/mcp/tools/index.d.ts.map +1 -1
  105. package/dist/mcp/tools/index.js +28 -1
  106. package/dist/mcp/tools/index.js.map +1 -1
  107. package/dist/mcp/tools/list.d.ts.map +1 -1
  108. package/dist/mcp/tools/list.js +38 -14
  109. package/dist/mcp/tools/list.js.map +1 -1
  110. package/dist/mcp/tools/logs.d.ts.map +1 -1
  111. package/dist/mcp/tools/logs.js +15 -5
  112. package/dist/mcp/tools/logs.js.map +1 -1
  113. package/dist/mcp/tools/save.d.ts.map +1 -1
  114. package/dist/mcp/tools/save.js +14 -4
  115. package/dist/mcp/tools/save.js.map +1 -1
  116. package/dist/mcp/tools/save.test.js +3 -3
  117. package/dist/mcp/tools/save.test.js.map +1 -1
  118. package/dist/mcp/tools/search-docs.d.ts +3 -0
  119. package/dist/mcp/tools/search-docs.d.ts.map +1 -0
  120. package/dist/mcp/tools/search-docs.js +102 -0
  121. package/dist/mcp/tools/search-docs.js.map +1 -0
  122. package/package.json +6 -2
  123. package/skills/acquire-funifier-knowledge/SKILL.md +155 -0
  124. package/skills/acquire-funifier-knowledge/assets/templates/CONCERNS.md +25 -0
  125. package/skills/acquire-funifier-knowledge/assets/templates/CUSTOM_ENDPOINTS.md +24 -0
  126. package/skills/acquire-funifier-knowledge/assets/templates/CUSTOM_PAGES.md +24 -0
  127. package/skills/acquire-funifier-knowledge/assets/templates/GAME_MECHANICS.md +35 -0
  128. package/skills/acquire-funifier-knowledge/assets/templates/INTEGRATIONS.md +35 -0
  129. package/skills/acquire-funifier-knowledge/assets/templates/LEADERBOARDS.md +24 -0
  130. package/skills/acquire-funifier-knowledge/assets/templates/OVERVIEW.md +86 -0
  131. package/skills/acquire-funifier-knowledge/assets/templates/PLAYER_MODEL.md +31 -0
  132. package/skills/acquire-funifier-knowledge/assets/templates/SCHEDULERS.md +25 -0
  133. package/skills/acquire-funifier-knowledge/assets/templates/TECHNIQUES_AND_PATTERNS.md +26 -0
  134. package/skills/acquire-funifier-knowledge/assets/templates/TRIGGERS.md +27 -0
  135. package/skills/acquire-funifier-knowledge/references/funifier-inventory-checklist.md +81 -0
  136. package/skills/acquire-funifier-knowledge/references/game-techniques-taxonomy.md +62 -0
  137. package/skills/acquire-funifier-knowledge/references/mcp-call-patterns.md +118 -0
  138. package/skills/funifier/SKILL.md +88 -0
  139. package/skills/funifier/references/configure-security.md +96 -0
  140. package/skills/{funifier-create-action/SKILL.md → funifier/references/create-action.md} +0 -33
  141. package/skills/funifier/references/create-aggregate.md +144 -0
  142. package/skills/funifier/references/create-challenge.md +116 -0
  143. package/skills/funifier/references/create-competition.md +98 -0
  144. package/skills/funifier/references/create-crossword.md +574 -0
  145. package/skills/funifier/references/create-custom-object.md +91 -0
  146. package/skills/funifier/references/create-custom-page.md +135 -0
  147. package/skills/funifier/references/create-folder.md +104 -0
  148. package/skills/funifier/references/create-lastmile.md +643 -0
  149. package/skills/{funifier-create-leaderboard/SKILL.md → funifier/references/create-leaderboard.md} +0 -33
  150. package/skills/funifier/references/create-level.md +94 -0
  151. package/skills/funifier/references/create-lottery.md +913 -0
  152. package/skills/funifier/references/create-mystery.md +769 -0
  153. package/skills/funifier/references/create-notification.md +75 -0
  154. package/skills/{funifier-create-point/SKILL.md → funifier/references/create-point.md} +0 -33
  155. package/skills/funifier/references/create-quiz.md +98 -0
  156. package/skills/funifier/references/create-scheduler.md +141 -0
  157. package/skills/funifier/references/create-story.md +636 -0
  158. package/skills/funifier/references/create-swap.md +95 -0
  159. package/skills/{funifier-create-trigger/SKILL.md → funifier/references/create-trigger.md} +0 -33
  160. package/skills/funifier/references/create-virtual-good.md +96 -0
  161. package/skills/funifier/references/create-webhook.md +72 -0
  162. package/skills/funifier/references/create-websocket.md +71 -0
  163. package/skills/funifier/references/create-widget.md +76 -0
  164. package/skills/funifier/references/debug.md +87 -0
  165. package/skills/funifier/references/help.md +81 -0
  166. package/skills/funifier/references/implement-frontend.md +106 -0
  167. package/skills/funifier/references/import-csv.md +75 -0
  168. package/skills/funifier/references/manage-player.md +82 -0
  169. package/skills/funifier/references/manage-team.md +76 -0
  170. package/skills/funifier/references/upload-file.md +91 -0
  171. package/skills/funifier-create-aggregate/SKILL.md +0 -127
  172. package/skills/funifier-create-challenge/SKILL.md +0 -88
  173. package/skills/funifier-create-custom-page/SKILL.md +0 -127
  174. package/skills/funifier-create-level/SKILL.md +0 -87
  175. package/skills/funifier-create-quiz/SKILL.md +0 -87
  176. package/skills/funifier-create-scheduler/SKILL.md +0 -127
  177. package/skills/funifier-create-virtual-good/SKILL.md +0 -87
  178. package/skills/funifier-debug/SKILL.md +0 -92
  179. package/skills/funifier-help/SKILL.md +0 -86
  180. package/skills/funifier-implement-frontend/SKILL.md +0 -90
  181. package/skills/funifier-index/SKILL.md +0 -58
@@ -1,138 +1,435 @@
1
- # Point (Ponto)
1
+ # `point`
2
2
 
3
3
  **Acesso Studio:** `/studio/point`
4
4
  **API Endpoint:** `/v3/point`
5
+ **Endpoint Legado:** _(não existe — não há controller de point em `rest/engine`)_
6
+ **Coleção MongoDB:** `point_category` (config). Existe ainda uma coleção `point` de saldos por jogador — **legada/inerte** (vide §3.4 e §7).
5
7
 
6
- ## O que é
8
+ ---
9
+
10
+ ## 1. Visão Geral
11
+
12
+ O módulo `point` é o **catálogo de tipos de ponto** de uma gamificação. Cada documento define um "tipo de moeda" do jogo — XP, Karma, Coins, Reputação, etc. — com nome amigável, abreviação, imagem e tags de técnica de jogo.
13
+
14
+ Papel arquitetural (confirmado no código):
15
+
16
+ - O módulo `point` armazena **apenas a configuração/metadados** do tipo de ponto na coleção `point_category` (`Entity.java:228` → `POINT_CATEGORY("point_category", PointCategory.class)`). Ele **não** guarda saldo de jogador.
17
+ - O **saldo de pontos do jogador é derivado da coleção `achievement`** (registros `type=0 / TYPE_POINT`, `item=<_id do ponto>`), somando `total` via aggregation. Esta é a fonte da verdade — vide o módulo `achievement`. O `_id` do `PointCategory` é exatamente o valor usado no campo `item` desses achievements.
18
+ - O `PointCategory` é consumido por outros módulos **só para resolver nome/imagem** a partir do `_id`:
19
+ - `challenge` — `ChallengeManager` (linhas 401, 421, 445) faz `findById(point.category)` para montar a descrição textual da recompensa (`RewardPoint.category` carrega o `_id` do ponto).
20
+ - `achievement` — `AchievementManager.findOptionsByType` (2841-2843) e `findOptionByTypeAndItem` (2903-2907) resolvem `_id → category/image` para dropdowns do Studio e decoração de respostas.
21
+ - `package` — `PackageManager` (301, 380, 438, 488) exporta/importa pontos como parte de um pacote (`findAll`, `findById`, `add`, `delete`).
22
+ - `technique` — `GameTechniqueManager` faz backfill da tag `techniques` e calcula analytics de uso (mapa Octalysis).
23
+
24
+ Problemas que resolve:
25
+
26
+ - Declarar os tipos de ponto que existem na gamificação e dar a eles nome de exibição (`category`), abreviação (`shortName`) e imagem.
27
+ - Servir como tabela de lookup `_id → metadados` para os módulos que geram/consomem pontos (challenge, action, achievement, level, leaderboard, catalog).
28
+
29
+ > ⚠️ **Correção em relação à documentação anterior:** o módulo **não** decide acúmulo, troca ou gasto de pontos. Ele é puramente declarativo. A lógica de crédito/débito/saldo vive em `achievement`. Vide §7 para o detalhamento das funcionalidades que a doc anterior atribuía erroneamente ao campo `techniques`.
30
+
31
+ ---
32
+
33
+ ## 2. Arquitetura e Fluxos
34
+
35
+ ### 2.1 Classes envolvidas
36
+
37
+ | Classe | Arquivo | Papel |
38
+ |---|---|---|
39
+ | `PointCategory` | `engine/point/PointCategory.java` | **Entidade de config** (documento raiz da coleção `point_category`) |
40
+ | `Point` | `engine/point/Point.java` | Entidade de **saldo por jogador** (coleção `point`) — **legada/inerte**, vide §3.4 |
41
+ | `PointDaoMongo` | `engine/point/PointDaoMongo.java` | DAO Jongo das duas coleções |
42
+ | `PointManager` | `engine/point/PointManager.java` | Manager (delega ao DAO; resolvido via `ManagerFactory.getPointManager()`) |
43
+ | `PointRest` | `rest/v3/rest/PointRest.java` | Controller REST v3 (`@Path("v3/point")`) |
44
+ | `Image` / `ImageItem` | `engine/image/` | Sub-entidade de imagem (small/medium/original) |
45
+
46
+ Não há `Repository`/`Service` separado — `PointManager` chama `PointDaoMongo` diretamente, que usa `Jongo` sobre a conexão do tenant (`manager.getJongoConnection()`).
47
+
48
+ ### 2.2 Pipeline — CRUD de tipo de ponto (caminho vivo)
49
+
50
+ Toda chamada REST entra pelo `FrontController.getInstance(authBean.getApiKey())`, que resolve o `ManagerFactory` (e a conexão Mongo) **do tenant dono daquele apiKey**. A partir daí:
51
+
52
+ ```
53
+ GET /v3/point → PointRest.findAll → PointManager.findAll → PointDaoMongo.findAll(point_category) → JsonUtil.toJsonRemoveNullFields(list)
54
+ GET /v3/point/{id} → PointRest.find → PointManager.findById → PointDaoMongo.findById(point_category) → toJsonRemoveNullFields(obj)
55
+ POST /v3/point → PointRest.insert → PointManager.add → PointDaoMongo.add(point_category) → toJsonRemoveNullFields(obj), 201
56
+ DELETE /v3/point/{id} → PointRest.delete → PointManager.delete → PointDaoMongo.delete(point_category) → 204
57
+ ```
58
+
59
+ Algoritmo de `add` (= o que o POST executa) — `PointDaoMongo.java:44-48`:
60
+
61
+ ```
62
+ se point._id é null OU vazio:
63
+ point._id = Guid.newShortGuid() # gera id curto automaticamente
64
+ collection("point_category").save(point) # Jongo save = upsert por _id
65
+ ```
66
+
67
+ Consequência: **POST é create-or-replace por `_id`** (não há PATCH). Se você enviar um `_id` já existente, o documento é **substituído por inteiro** (full replace), não mesclado. Campos omitidos no body somem do documento.
68
+
69
+ > Tudo é **síncrono**, sem transação. Não há disparo de trigger/webhook no CRUD de `point` (o módulo não chama `before_win`/`after_win` nem registra `*_created`).
70
+
71
+ ### 2.3 Como um jogador realmente ganha pontos (integração entre módulos)
72
+
73
+ O módulo `point` só fornece metadados nesse fluxo. O crédito acontece em `achievement`:
74
+
75
+ ### Sequência — jogador ganha pontos
76
+
77
+ ```mermaid
78
+ sequenceDiagram
79
+ participant Client
80
+ participant ActionMgr as ActionManager
81
+ participant AchMgr as AchievementManager
82
+ participant PointCfg as point_category (config)
83
+ participant Ach as achievement (coleção)
84
+
85
+ Client->>ActionMgr: POST /v3/action/log (actionId, userId)
86
+ ActionMgr->>AchMgr: fireAction(actionLog)
87
+ Note over AchMgr: avalia RewardPoints da action/challenge
88
+ AchMgr->>AchMgr: addAchievement(type=0, item=<pointId>, total=N)
89
+ AchMgr->>Ach: insert { player, type:0, item:<pointId>, total:N }
90
+ Note over PointCfg: point_category NÃO é alterado<br/>(usado só p/ nome/imagem em respostas)
91
+ Client->>Ach: saldo = aggregate $match{type:0,item:<pointId>} + $sum:$total
92
+ ```
93
+
94
+ O `point` (config) entra apenas quando a UI precisa exibir o nome/imagem do ponto (`AchievementManager.findOptionByTypeAndItem`, linha 2903). **Mudar um `PointCategory` não recalcula nada** — apenas troca o rótulo exibido dali pra frente.
95
+
96
+ ---
97
+
98
+ ## 3. Estrutura dos Objetos
99
+
100
+ ### 3.1 `PointCategory` — documento raiz (coleção `point_category`)
101
+
102
+ Definição: `engine/point/PointCategory.java`. Anotada com `@JsonIgnoreProperties(ignoreUnknown=true)` (linha 11) — **campos desconhecidos no POST são silenciosamente ignorados**.
103
+
104
+ | Campo | Tipo | Padrão | Obrigatório | Descrição |
105
+ |---|---|---|---|---|
106
+ | `_id` | String | auto (GUID curto se vazio) | não* | Identificador do ponto. Mapeado de `_id` via `@JsonProperty`. Se ausente/vazio no POST, `add()` gera `Guid.newShortGuid()`. |
107
+ | `category` | String | null | recomendado | Nome amigável exibido nas interfaces (ex.: `"Experience Points"`). |
108
+ | `shortName` | String | null | recomendado | Abreviação curta (ex.: `"XP"`). |
109
+ | `image` | `Image` | null | não | Imagem do ponto (3 tamanhos — §3.2). |
110
+ | `extra` | `Map<String,Object>` | `{}` (HashMap vazio) | não | Campos customizados livres. Inicializado como mapa vazio, não null. |
111
+ | `techniques` | `List<String>` | null | não | Tags de técnica de jogo (códigos GT). **Apenas categorização** — vide §3.3 e §7. |
112
+
113
+ \* Nenhum campo é validado como obrigatório no servidor. O `add()` aceita `category`/`shortName` nulos sem erro. **Não há validação de formato do `_id`** (espaços e maiúsculas não são rejeitados pelo backend — "minúsculas sem espaços" é convenção, não regra de código).
114
+
115
+ #### Campos legados / silenciosamente ignorados
116
+
117
+ - **`icon` (String)** — declarado em `PointCategory.java:19`, mas getter/setter estão **comentados** (linhas 62-70) e o campo é package-private. Com a visibilidade padrão do Jackson (apenas getters/setters/atributos públicos), o **REST não desserializa `icon`** mesmo que você o envie no POST — o valor permanece `null`. É um campo morto, resquício de versão antiga (substituído por `image`).
118
+ - **`apiKey`** — havia um campo `apiKey` na entidade (comentado em `PointCategory.java:16,32`). Não existe mais. Se enviado no POST, cai na regra `ignoreUnknown=true` e é descartado.
119
+
120
+ #### Comportamento de serialização da resposta
121
+
122
+ Todas as respostas passam por `JsonUtil.toJsonRemoveNullFields(...)` (`PointRest.java:42,60,93`): **campos `null` são removidos do JSON de resposta**. Logo, um ponto sem `image`/`techniques` retorna sem essas chaves. O `extra` (mapa vazio, não null) tende a aparecer como `{}`.
123
+
124
+ ### 3.2 `Image` / `ImageItem` — sub-entidade de imagem
125
+
126
+ `Image` (`engine/image/Image.java`) tem três variações de tamanho, cada uma um `ImageItem`:
127
+
128
+ | Campo de `Image` | Tipo |
129
+ |---|---|
130
+ | `small` | `ImageItem` |
131
+ | `medium` | `ImageItem` |
132
+ | `original` | `ImageItem` |
133
+
134
+ `ImageItem` (`engine/image/ImageItem.java`):
135
+
136
+ | Campo | Tipo | Observação |
137
+ |---|---|---|
138
+ | `_id` | String | id do arquivo (usado em operações de upload/exclusão) |
139
+ | `type` | String | |
140
+ | `name` | String | |
141
+ | `url` | String | URL pública da imagem |
142
+ | `size` | int | default 0 |
143
+ | `width` | int | default 0 |
144
+ | `height` | int | default 0 |
145
+ | `depth` | int | default 0 |
146
+ | `format` | String | |
147
+
148
+ Na prática basta preencher `url` nos três tamanhos. Os campos numéricos default `0` são aceitos.
149
+
150
+ ### 3.3 Técnicas de jogo (`techniques`)
151
+
152
+ `techniques` é uma `List<String>` livre de **códigos GT** (catálogo Octalysis). O catálogo-mestre fica em `system/technique/GameTechniqueManager.java`. Os códigos relevantes para pontos:
153
+
154
+ | Código | Nome (catálogo) | Core Drive | Descrição (pt) |
155
+ |---|---|---|---|
156
+ | `GT01` | Status Points | CD2 (Desenvolvimento & Realização) | "Unidades de medição do status do jogador. Pontos de consumo podem ser usados para comprar coisas." (`GameTechniqueManager.java:130`) |
157
+ | `GT75` | Exchangeable Points | CD4 (Posse & Propriedade) | "Pontos que podem ser trocados e podem fazer rodar a economia." (`GameTechniqueManager.java:204`) |
158
+
159
+ **Como `techniques` é preenchido (backfill automático):** `engine/technique/GameTechniqueManager.autoConfigureMissingTechniqueFields()` procura documentos em `point_category` **sem techniques** (query `notechniques`, linha 63: `techniques` ausente OU array vazio) e adiciona **apenas `GT01`** (linhas 67, 78-87). **`GT75` nunca é atribuído automaticamente.**
160
+
161
+ **O que `techniques` realmente faz:** serve só de **tag de categorização** para o mapa de técnicas do Studio (`GameTechniqueManager.findConfiguredTechniques` agrega o uso por técnica/core-drive). **Não há nenhum efeito comportamental no módulo `point`** — vide §7.
162
+
163
+ ### 3.4 `Point` — saldo por jogador (coleção `point`) — LEGADO
164
+
165
+ `engine/point/Point.java`. Documento da coleção `point` (constante `PointDaoMongo.COLLECTION = "point"`, linha 10), distinta de `point_category`.
166
+
167
+ | Campo | Tipo | Descrição |
168
+ |---|---|---|
169
+ | `_id` | String | GUID curto (gerado em `creditPoints`/`debitPoints`) |
170
+ | `categoryId` | String | id do `PointCategory` |
171
+ | `category` | String | nome da categoria (copiado de `PointCategory.getCategory()`) |
172
+ | `userId` | String | jogador dono do saldo |
173
+ | `points` | int | pontos obtidos (acumulado) — só incrementado em `creditPoints` |
174
+ | `balance` | int | saldo disponível para troca — incrementado no crédito, decrementado no débito |
175
+
176
+ > **Esta coleção/entidade é inerte no engine atual.** Os métodos que a manipulam (`creditPoints`, `debitPoints`, `transferPoints`, `findByUserId`) **não têm nenhum chamador** (vide §7). O saldo real do jogador vem de `achievement`, não daqui. A classe `Point` é apenas **exposta a scripts server-side** (Groovy/Java) via `import com.funifier.engine.point.Point` injetado pelos runners (`TriggerRunner`, `SchedulerRunner`, `AuthRunner`, `LastMileManager`, `CsvManager`, `PublicManager`, `PreparedManager`, `WebSocketManager`) — mas o core nunca a usa.
177
+
178
+ ---
179
+
180
+ ## 4. Endpoints
181
+
182
+ Controller: `PointRest` (`@Path("v3/point")`, `@Produces(application/json; charset=UTF-8)`). Todos exigem `Authorization: Bearer <token>`.
183
+
184
+ ### `GET /v3/point`
185
+
186
+ | Aspecto | Detalhe |
187
+ |---|---|
188
+ | Finalidade | Lista todos os tipos de ponto (`point_category`) do tenant |
189
+ | Autenticação | Bearer token |
190
+ | Resposta | Array de `PointCategory`, campos null removidos |
191
+ | Código | 200 |
192
+
193
+ ### `GET /v3/point/{id}`
194
+
195
+ | Aspecto | Detalhe |
196
+ |---|---|
197
+ | Finalidade | Busca um tipo de ponto por `_id` |
198
+ | Autenticação | Bearer token |
199
+ | Resposta | Objeto `PointCategory` (null se inexistente) |
200
+ | Código | 200 |
201
+
202
+ **Path params:**
7
203
 
8
- Configuração dos diferentes tipos de pontuação que os jogadores podem conquistar. Os pontos são fundamentais para recompensar ações, mensurar desempenho e alimentar rankings, lojas virtuais e desafios.
204
+ | Param | Tipo | Descrição |
205
+ |---|---|---|
206
+ | `id` | String | `_id` do ponto |
207
+
208
+ > Observação: o apidoc inline desse método está rotulado como "Find Action" (`PointRest.java:48`) — é erro de copy-paste; o método retorna `PointCategory`.
9
209
 
10
- ## Os dois tipos de ponto
210
+ ### `POST /v3/point`
11
211
 
12
- A escolha da técnica (`techniques`) define o comportamento do ponto no sistema:
212
+ | Aspecto | Detalhe |
213
+ |---|---|
214
+ | Finalidade | Cria **ou substitui** um tipo de ponto |
215
+ | Autenticação | Bearer token |
216
+ | Full replace ou patch | **Full replace** por `_id` (Jongo `save`). Não é patch. |
217
+ | Resposta | O `PointCategory` salvo (com `_id` gerado, se aplicável) |
218
+ | Código | 201 CREATED |
13
219
 
14
- ### Ponto Normal — `GT01`
220
+ **Comportamento real:**
221
+ - `_id` ausente/vazio ⇒ GUID curto gerado automaticamente.
222
+ - `_id` presente ⇒ upsert: substitui o documento inteiro. Campos omitidos são apagados.
223
+ - Campos desconhecidos no body ⇒ ignorados (`@JsonIgnoreProperties`).
224
+ - `icon` no body ⇒ ignorado (sem setter público — §3.1).
225
+ - Sem validação de obrigatoriedade ou de formato de `_id`.
15
226
 
16
- Pontos de progressão acumulativa. Usados para XP, score, karma, métricas de desempenho. Ambos os tipos podem ser usados na loja virtual.
227
+ **Exemplo:**
17
228
 
18
229
  ```json
230
+ POST /v3/point
19
231
  {
20
232
  "_id": "xp",
21
233
  "category": "Experience Points",
22
- "shortName": "XP",
23
- "techniques": ["GT01"]
234
+ "shortName": "XP"
24
235
  }
25
236
  ```
26
237
 
27
- Use quando: o ponto representa progresso, conquista ou ranking.
238
+ ### `DELETE /v3/point/{id}`
28
239
 
29
- ### Ponto Trocável entre jogadores — `GT75`
240
+ | Aspecto | Detalhe |
241
+ |---|---|
242
+ | Finalidade | Remove o tipo de ponto da coleção `point_category` |
243
+ | Autenticação | Bearer token |
244
+ | Código | 204 NO_CONTENT |
30
245
 
31
- Pontos que podem ser transferidos entre jogadores (troca/presente entre usuários). Usados para moedas sociais, tokens de reconhecimento entre pares.
246
+ **Comportamento real:** remove apenas o documento de `point_category`. **Não** remove achievements (`type=0, item=<id>`) já gerados, nem referências em `challenge`/`action`/`level`. O histórico/saldo derivado do ponto **continua existindo** em `achievement` após o delete.
32
247
 
33
- ```json
34
- {
35
- "_id": "coin",
36
- "category": "Coins",
37
- "shortName": "Coins",
38
- "techniques": ["GT75"]
39
- }
40
- ```
248
+ > **Não existe `PUT`/`PATCH`.** O método `PointManager.update()` (remove-then-save full replace, `PointDaoMongo.java:50-54`) **não é exposto por nenhum endpoint** e não tem chamadores — é código morto (§7).
41
249
 
42
- Use quando: jogadores podem enviar ou transferir pontos uns para os outros.
250
+ ---
251
+
252
+ ## 5. Regras de Negócio
253
+
254
+ Regras presentes no código mas ausentes do schema:
255
+
256
+ - **`_id` como chave de negócio:** o `_id` do ponto é o identificador usado em todos os outros módulos: `RewardPoint.category` (recompensa de action/challenge), `Requirement.item` (custo na loja), `achievement.item` (lançamento de saldo), `levelConfig.pointCategory` e `level.point` (gatilho de level up). Renomear/excluir um ponto **quebra silenciosamente** essas referências por id.
257
+ - **Saldo é eventual e calculado:** não há campo de saldo no módulo `point`. Qualquer total é uma aggregation sobre `achievement` (consistência eventual; sem cache no módulo).
258
+ - **Multi-tenant rígido por apiKey:** cada requisição opera sobre a base do tenant resolvido por `FrontController.getInstance(apiKey)`. Pontos de um projeto nunca enxergam os de outro. Não há isolamento adicional por usuário dentro do tenant — qualquer token válido do tenant pode listar/criar/excluir pontos (não há checagem de role no `PointRest`).
259
+ - **POST destrutivo:** por ser full-replace, reenviar um ponto sem um campo previamente salvo o remove. Para "editar", releia o objeto, altere e reenvie completo.
260
+ - **`techniques` não é regra de negócio:** apesar de `GT01`/`GT75` sugerirem comportamentos distintos, o módulo trata `techniques` como lista opaca de tags. Não há ramificação de lógica por técnica.
43
261
 
44
262
  ---
45
263
 
46
- **Regra:** se jogadores podem transferir/trocar o ponto entre si, use `GT75`. Para tudo o mais, use `GT01`. Qualquer tipo de ponto pode ser usado na loja virtual.
264
+ ## 6. Comportamentos Automáticos
47
265
 
48
- ## Imagem
266
+ | Comportamento | Trigger | Impacto | Persistência |
267
+ |---|---|---|---|
268
+ | Geração de `_id` | POST com `_id` vazio | `Guid.newShortGuid()` gravado como `_id` | `point_category` |
269
+ | Upsert/full-replace | POST com `_id` existente | Documento substituído por inteiro | `point_category` |
270
+ | Remoção de campos null na resposta | Qualquer GET/POST | `JsonUtil.toJsonRemoveNullFields` | — (só resposta) |
271
+ | Descarte de campos desconhecidos | POST | `@JsonIgnoreProperties(ignoreUnknown=true)` | — |
272
+ | Backfill de `techniques=["GT01"]` | Rotina de manutenção `autoConfigureMissingTechniqueFields()` | Adiciona `GT01` a pontos sem techniques + grava `game_technique_field` | `point_category`, `game_technique_field` |
49
273
 
50
- Points podem ter uma imagem associada via campo `image` com três tamanhos:
274
+ > Não comportamento encadeado de behaviors/triggers no CRUD de point. O backfill de techniques é uma rotina administrativa do sistema, não disparada pelo CRUD.
275
+
276
+ ---
277
+
278
+ ## 7. Suportado vs NÃO Suportado
279
+
280
+ ### ✅ Suportado
281
+
282
+ - CRUD de tipos de ponto via `/v3/point` (list, find, create/replace, delete).
283
+ - Geração automática de `_id` quando omitido.
284
+ - Imagem em 3 tamanhos (`image.small/medium/original`).
285
+ - Campos customizados via `extra`.
286
+ - Tag de técnica `techniques` (categorização para o mapa Octalysis do Studio).
287
+ - Uso do `_id` do ponto como referência por outros módulos (challenge, action, achievement, level, leaderboard, catalog, package).
288
+ - Crédito de pontos ao jogador via `achievement` (feito pelo módulo `achievement`, não por este).
289
+
290
+ ### ❌ NÃO Suportado
291
+
292
+ - **Transferência de pontos entre jogadores (player-to-player).** `PointManager.transferPoints` (linhas 26-38) existe, mas tem **zero chamadores** — não é exposto por REST nem chamado por nenhum manager. É código morto.
293
+ - **Crédito/débito direto de saldo (`creditPoints`/`debitPoints`).** Existem em `PointManager`/`PointDaoMongo` (18-42) mas **sem chamadores no engine**. A chamada que os usaria em compras está **comentada** em `PurchaseManager.java:115` (`//manager.getPointManager().debitPoints(...)`) — evidência de que o sistema de saldo na coleção `point` foi **descontinuado** em favor da aggregation de `achievement`.
294
+ - **A coleção `point` (entidade `Point`) como fonte de saldo.** Inerte no core; apenas importável em scripts customizados.
295
+ - **`PUT`/`PATCH` (edição parcial).** Não há endpoint. POST é full-replace. `PointManager.update()` é código morto (zero chamadores).
296
+ - **Endpoint legado `/2.0.0/point`.** Não existe controller de point em `rest/engine`.
297
+ - **Campo `icon`.** Aceito no schema da classe, mas o REST não o desserializa (sem setter público) — sempre `null`.
298
+ - **Validação de `_id`/campos obrigatórios.** O backend não rejeita `_id` com espaços/maiúsculas nem `category`/`shortName` ausentes.
299
+
300
+ > ### Correção importante sobre `techniques` (GT01 × GT75)
301
+ >
302
+ > A documentação anterior afirmava: _"se jogadores podem transferir/trocar o ponto entre si, use `GT75`; para o resto use `GT01`; qualquer ponto pode ser usado na loja"_. Confrontando com o código:
303
+ >
304
+ > - `GT75` ("Exchangeable Points", CD4) **é uma técnica real do catálogo** (`system/technique/GameTechniqueManager.java:204`) — não foi inventada. Mas **marcar um ponto com `GT75` não habilita transferência alguma**: o mecanismo de troca (`transferPoints`) é código morto.
305
+ > - `techniques` **não altera o comportamento do módulo point**. É só tag de analytics/Octalysis. Não existe ramificação de lógica por GT no engine de pontos.
306
+ > - O **gasto na loja** funciona para **qualquer ponto**, independente de `techniques`, via `Requirement` com `OPERATION_DEDUCT` em `catalog_item` (gera achievement negativo). Não é o `GT75` que "habilita a loja".
307
+ > - O backfill automático só aplica `GT01`. Um ponto sem técnica recebe `GT01` ("Status Points").
308
+ >
309
+ > **Conclusão:** trate `techniques` como rótulo descritivo opcional. Não dependa dele para nenhum comportamento.
310
+
311
+ ---
312
+
313
+ ## 8. Segurança e Permissões
314
+
315
+ - **Autenticação:** `Authorization: Bearer <token>` em todos os métodos (`@BeanParam AuthBean`).
316
+ - **Isolamento por tenant:** `FrontController.getInstance(authBean.getApiKey())` direciona toda operação à base do tenant dono do apiKey. Não há vazamento entre projetos.
317
+ - **Autorização intra-tenant:** o `PointRest` **não** faz checagem de papel/escopo — qualquer token válido do tenant pode criar e **excluir** tipos de ponto. Operações de escrita devem ser restritas a tokens administrativos por convenção operacional (não há enforcement no controller).
318
+ - **Superfície de injeção:** as queries Mongo do módulo são **parametrizadas** via placeholders Jongo (`"{_id:#}"`, `"{userId:#}"`, `"{categoryId:#, userId:#}"`) — sem concatenação de string com input do usuário. Não há superfície de injeção NoSQL no módulo `point` em si. (Atenção: aggregations livres do tenant via `/v3/database/...` são outra superfície, fora deste módulo.)
319
+ - **DELETE não-transacional e sem cascata:** excluir um ponto não limpa referências; pode deixar challenges/levels apontando para um `_id` inexistente (NPE potencial em `ChallengeManager` ao montar descrição — `cat.getCategory()` com `cat == null`).
320
+
321
+ ---
322
+
323
+ ## 9. Observabilidade e Troubleshooting
324
+
325
+ ### Diagnóstico rápido
51
326
 
52
- ```json
53
- "image": {
54
- "small": { "url": "https://...", "size": 0, "width": 0, "height": 0, "depth": 0 },
55
- "medium": { "url": "https://...", "size": 0, "width": 0, "height": 0, "depth": 0 },
56
- "original": { "url": "https://...", "size": 0, "width": 0, "height": 0, "depth": 0 }
57
- }
58
327
  ```
328
+ # O ponto existe?
329
+ GET /v3/point/<id>
59
330
 
60
- Na prática basta preencher `url` nos três tamanhos com a mesma URL se não houver versões distintas.
331
+ # Listar todos os tipos de ponto do projeto
332
+ GET /v3/point
333
+ ```
61
334
 
62
- ## Campos
335
+ ### Saldo do jogador (vem de achievement, não de point)
63
336
 
64
- | Campo | Obrigatório | Descrição |
65
- |---|---|---|
66
- | `_id` | sim | Identificador único — minúsculas, sem espaços (`xp`, `coin`, `karma`) |
67
- | `category` | sim | Nome amigável exibido na interface |
68
- | `shortName` | sim | Abreviação curta exibida em rankings e conquistas |
69
- | `techniques` | sim | `["GT01"]` para normal, `["GT75"]` para trocável |
70
- | `extra` | não | Campos customizados livres |
337
+ ```
338
+ # Total de um ponto para um jogador (fonte da verdade)
339
+ POST /v3/database/achievement/aggregate
340
+ [
341
+ { "$match": { "player": "<userId>", "type": 0, "item": "<pointId>" } },
342
+ { "$group": { "_id": null, "total": { "$sum": "$total" } } }
343
+ ]
344
+ ```
71
345
 
72
- ## Exemplos completos
346
+ ### Investigações úteis
347
+
348
+ ```
349
+ # Quais challenges referenciam este ponto (antes de excluir)?
350
+ POST /v3/database/challenge/aggregate
351
+ [ { "$match": { "points.category": "<pointId>" } } ]
352
+
353
+ # Algum level usa este ponto como gatilho?
354
+ POST /v3/database/level/aggregate
355
+ [ { "$match": { "point": "<pointId>" } } ]
356
+
357
+ # Conferir/limpar a coleção legada de saldos (deveria estar vazia/inerte)
358
+ POST /v3/database/point/aggregate
359
+ [ { "$match": { "categoryId": "<pointId>" } } ]
360
+ ```
361
+
362
+ ### Erros comuns e causas
363
+
364
+ | Sintoma | Causa provável |
365
+ |---|---|
366
+ | "Editei um ponto e sumiram campos" | POST é full-replace — releia, altere, reenvie completo. |
367
+ | "Mandei `icon` e não persistiu" | `icon` é campo morto, não desserializado. Use `image`. |
368
+ | "Marquei `GT75` e os pontos não transferem" | Transferência é código morto; `techniques` não muda comportamento (§7). |
369
+ | "Saldo não bate com o esperado" | Saldo vem de `achievement` (`type:0,item:<id>`), não do módulo point. Verifique os lançamentos. |
370
+ | "Excluí o ponto e o ranking quebrou" | DELETE não faz cascata; challenges/levels ficam apontando para `_id` inexistente. |
371
+ | Resposta sem `techniques`/`image` | Campos null são removidos pelo `toJsonRemoveNullFields`; não significa erro. |
372
+
373
+ ---
374
+
375
+ ## 10. Exemplos Práticos
376
+
377
+ ### Mínimo funcional
73
378
 
74
- **XP (ponto normal):**
75
379
  ```json
380
+ POST /v3/point
76
381
  {
77
382
  "_id": "xp",
78
383
  "category": "Experience Points",
79
- "shortName": "XP",
80
- "techniques": ["GT01"],
81
- "extra": {}
384
+ "shortName": "XP"
82
385
  }
83
386
  ```
84
387
 
85
- **Coin (ponto trocável):**
388
+ ### Avançado (todos os campos relevantes)
389
+
86
390
  ```json
391
+ POST /v3/point
87
392
  {
88
393
  "_id": "coin",
89
394
  "category": "Coins",
90
- "shortName": "Coins",
395
+ "shortName": "C$",
396
+ "image": {
397
+ "small": { "url": "https://cdn.exemplo.com/coin.png" },
398
+ "medium": { "url": "https://cdn.exemplo.com/coin.png" },
399
+ "original": { "url": "https://cdn.exemplo.com/coin.png" }
400
+ },
91
401
  "techniques": ["GT75"],
92
- "extra": {}
402
+ "extra": { "color": "#FFD700", "decimals": 0 }
93
403
  }
94
404
  ```
95
405
 
96
- **Múltiplos pontos num projeto típico:**
97
- ```json
98
- [
99
- { "_id": "xp", "category": "Experience Points", "shortName": "XP", "techniques": ["GT01"] },
100
- { "_id": "coin", "category": "Coins", "shortName": "Coins", "techniques": ["GT75"] },
101
- { "_id": "karma", "category": "Karma", "shortName": "K", "techniques": ["GT01"] }
102
- ]
103
- ```
406
+ > Aqui `techniques: ["GT75"]` é apenas rótulo descritivo ("ponto de economia/troca") para o mapa do Studio. Não habilita transferência nem comportamento especial.
104
407
 
105
- ## API Endpoints
408
+ ### Anti-pattern — o que NÃO fazer
106
409
 
107
- ### Listar Pontos
108
- **Método:** GET
109
- **Endpoint:** `/v3/point`
110
-
111
- ### Criar / Atualizar Ponto
112
- **Método:** POST
113
- **Endpoint:** `/v3/point`
114
-
115
- POST com `_id` existente = update (upsert).
116
-
117
- ### Excluir Ponto
118
- **Método:** DELETE
119
- **Endpoint:** `/v3/point/:id`
120
-
121
- > ⚠ Não delete um ponto referenciado em challenges ativos. Verifique com:
122
- > ```
123
- > funifier_database action=aggregate collection=challenge pipeline='[{"$match": {"points.category": "<point_id>"}}]'
124
- > ```
410
+ ```json
411
+ // ❌ Esperar que GT75 ative troca entre jogadores
412
+ { "_id": "social", "category": "Social Coin", "techniques": ["GT75"] }
413
+ // Não há transferência player-to-player no engine. transferPoints é código morto.
125
414
 
126
- ## Dependências e ordem de criação
415
+ // Tentar "editar parcialmente" via POST
416
+ { "_id": "xp", "shortName": "EXP" }
417
+ // Isto APAGA category/image/etc. do ponto "xp" (full replace). Reenvie o objeto completo.
127
418
 
128
- Points devem ser criados **antes** de challenges e leaderboards que os referenciam.
419
+ // Usar o campo icon
420
+ { "_id": "xp", "category": "XP", "icon": "star" }
421
+ // "icon" é ignorado (campo morto). Use "image".
422
+ ```
129
423
 
130
- Ordem recomendada: `point` → `action` → `challenge` → `leaderboard`
424
+ ---
131
425
 
132
- ## Checklist
426
+ ## Checklist de Configuração
133
427
 
134
- - [ ] `_id` em minúsculas, sem espaços
135
- - [ ] `techniques: ["GT01"]` para ponto normal ou `["GT75"]` para trocável
136
- - [ ] `category` e `shortName` preenchidos
137
- - [ ] Criado antes dos challenges que o referenciam
138
- - [ ] Confirmado com `funifier_list type=point search=<_id>`
428
+ - [ ] `_id` definido (ou omitido de propósito para gerar GUID). Convenção: minúsculas, sem espaços (não é validado pelo backend).
429
+ - [ ] `category` e `shortName` preenchidos (não são obrigatórios no servidor, mas a UI depende deles).
430
+ - [ ] Ponto criado **antes** dos challenges/actions/levels que o referenciam por `_id`.
431
+ - [ ] Para editar, reenviar o objeto **completo** (POST é full-replace) — não envie só o campo alterado.
432
+ - [ ] Usar `image` (não `icon` campo morto).
433
+ - [ ] Não depender de `techniques`/`GT75` para comportamento: é só rótulo. Transferência/saldo não vivem aqui.
434
+ - [ ] Antes de `DELETE`, conferir referências em `challenge.points.category` e `level.point` (delete não faz cascata).
435
+ - [ ] Lembrar que saldo do jogador é calculado de `achievement` (`type:0, item:<_id>`), não do módulo point.