@horizon-modules/property-model-v3
Version:
Modelo de propriedades imobiliárias v3 - Sistema de atributos dinâmicos
344 lines (277 loc) • 8 kB
Markdown
# Property Customizer
Sistema de transformação de dados PropertyV3 baseado em regras configuráveis.
## 🎯 Propósito
O Property Customizer permite aplicar transformações complexas em dados PropertyV3 através de regras declarativas, sem necessidade de escrever código customizado para cada transformação.
## 🚀 Instalação
```bash
pnpm add @horizon-modules/property-model-v3
```
## 📦 Importação
```typescript
import {
PropertyCustomizer,
propertyV3CustomMapper,
type MapRules,
type PropertyV3
} from '@horizon-modules/property-model-v3'
```
## 🔧 Uso Básico
### Usando a Classe
```typescript
const mapRules: MapRules = {
attributesRules: [
{
key: "tipo",
condition: { eq: "Casa" },
rules: [
{ fn: "upsertAttr", key: "tipo", value: "Casa/Sobrado" },
{ fn: "upsertAttr", key: "subtipo", value: "Casa" },
]
}
]
}
const customizer = new PropertyCustomizer(mapRules)
const result = customizer.transform(propertyV3Data)
```
### Usando a Função Helper
```typescript
const result = propertyV3CustomMapper(propertyV3Data, mapRules)
```
## 📋 Estrutura das Regras
### Reference Rules
Aplicam transformações baseadas na referência do imóvel:
```typescript
const mapRules: MapRules = {
referenceRules: {
'REF-123': [
{ fn: 'upsertAttr', key: 'destaque', value: true },
{ fn: 'addToArray', key: 'tags', value: 'premium' }
],
'REF-456': [
{ fn: 'upsertAttr', key: 'promocao', value: true }
]
}
}
```
### Attributes Rules
Aplicam transformações baseadas em condições dos atributos:
```typescript
const mapRules: MapRules = {
attributesRules: [
{
key: 'tipo',
condition: { eq: 'Casa de Condomínio' },
rules: [
{ fn: 'upsertAttr', key: 'tipo', value: 'Casa/Sobrado' },
{ fn: 'upsertAttr', key: 'subtipo', value: 'Casa' },
{ fn: 'addToArray', key: 'tags', value: 'em-condominio' }
]
},
{
key: 'dormitorios',
condition: { gte: 3 },
rules: [
{ fn: 'addToArray', key: 'tags', value: 'muitos-quartos' }
]
}
]
}
```
## 🎮 Actions Disponíveis
### upsertAttr
Cria ou atualiza um atributo:
```typescript
{ fn: 'upsertAttr', key: 'piscina', value: true }
```
### removeAttr
Remove um atributo:
```typescript
{ fn: 'removeAttr', key: 'campo_antigo' }
```
### addToArray
Adiciona valor(es) a um array:
```typescript
// Adicionar um valor
{ fn: 'addToArray', key: 'tags', value: 'nova-tag' }
// Adicionar múltiplos valores
{ fn: 'addToArray', key: 'caracteristicas', value: ['item1', 'item2'] }
```
### removeFromArray
Remove um valor de um array:
```typescript
{ fn: 'removeFromArray', key: 'caracteristicas', value: 'Piscina' }
```
## 🎯 Condições Suportadas
### Igualdade
- `eq` - igual a
- `not_eq` - diferente de
### Arrays
- `has` - contém todos os valores
- `has_any` - contém pelo menos um valor
- `not_has` - não contém valor(es)
### Numéricos
- `gt` - maior que
- `gte` - maior ou igual
- `lt` - menor que
- `lte` - menor ou igual
### Texto
- `contains` - contém substring (case insensitive)
- `starts_with` - começa com (case insensitive)
- `ends_with` - termina com (case insensitive)
### Booleanos
- `is_true` - é verdadeiro (true, "true", 1)
- `is_false` - é falso (false, "false", 0)
### Existência
- `exists` - campo existe e não está vazio
- `not_exists` - campo não existe ou está vazio
## 📝 Placeholders
Use `{{value}}` para referenciar o valor do atributo atual:
```typescript
{
key: 'area',
condition: { exists: true },
rules: [
{ fn: 'upsertAttr', key: 'area_formatada', value: '{{value}} m²' }
]
}
```
## 🏷️ Tags de Description
O Property Customizer processa automaticamente tags especiais na description:
### Tags Simples
```typescript
const property: PropertyV3 = {
description: 'Casa linda [[description-en]]Beautiful house[[/description-en]] com vista'
}
// Após processamento:
// property.attributes.description_en = 'Beautiful house'
// property.description = 'Casa linda com vista'
```
### Custom Attributes (JSON)
```typescript
const property: PropertyV3 = {
description: `Imóvel especial
[[custom-attributes]]
{
"energia_solar": true,
"classificacao": "A+"
}
[[/custom-attributes]]
Com tecnologia sustentável`
}
// Após processamento:
// property.attributes.energia_solar = true
// property.attributes.classificacao = 'A+'
// property.description = 'Imóvel especial\nCom tecnologia sustentável'
```
## 💡 Exemplos Práticos
### Transformar Características em Campos Booleanos
```typescript
const mapRules: MapRules = {
attributesRules: [
{
key: 'caracteristicas',
condition: { has_any: ['Piscina'] },
rules: [
{ fn: 'removeFromArray', key: 'caracteristicas', value: 'Piscina' },
{ fn: 'upsertAttr', key: 'piscina', value: true }
]
},
{
key: 'caracteristicas',
condition: { has_any: ['Churrasqueira'] },
rules: [
{ fn: 'removeFromArray', key: 'caracteristicas', value: 'Churrasqueira' },
{ fn: 'upsertAttr', key: 'churrasqueira', value: true }
]
}
]
}
```
### Normalizar Tipos de Imóveis
```typescript
const mapRules: MapRules = {
attributesRules: [
{
key: 'tipo',
condition: { eq: 'Casa de Condomínio' },
rules: [
{ fn: 'upsertAttr', key: 'tipo', value: 'Casa/Sobrado' },
{ fn: 'upsertAttr', key: 'subtipo', value: 'Casa' },
{ fn: 'addToArray', key: 'tags', value: 'em-condominio' }
]
},
{
key: 'tipo',
condition: { eq: 'Apartamento Duplex' },
rules: [
{ fn: 'upsertAttr', key: 'tipo', value: 'Apartamento' },
{ fn: 'upsertAttr', key: 'subtipo', value: 'Duplex' }
]
}
]
}
```
### Adicionar Tags Baseadas em Corretor
```typescript
const mapRules: MapRules = {
attributesRules: [
{
key: 'tags',
condition: { has_any: ['agente-elvis'] },
rules: [
{ fn: 'upsertAttr', key: 'corretor_id', value: 'elvis-ghisi' },
{ fn: 'upsertAttr', key: 'corretor_nome', value: 'Elvis Ghisi' },
{ fn: 'addToArray', key: 'tags', value: 'corretor-premium' }
]
}
]
}
```
## 🧪 Testando
```typescript
import { describe, it, expect } from 'vitest'
import { propertyV3CustomMapper } from '@horizon-modules/property-model-v3'
describe('Minhas regras customizadas', () => {
it('deve transformar corretamente', () => {
const property = {
attributes: {
tipo: 'Casa de Condomínio'
}
}
const result = propertyV3CustomMapper(property, mapRules)
expect(result.attributes.tipo).toBe('Casa/Sobrado')
expect(result.attributes.subtipo).toBe('Casa')
})
})
```
## 📌 Notas Importantes
1. O Property Customizer **não modifica** o objeto original, sempre retorna uma cópia
2. As regras são aplicadas na ordem: description tags → reference rules → attribute rules
3. Para arrays, o `addToArray` automaticamente evita duplicatas usando `union`
4. Condições de texto são case-insensitive
5. Placeholders são resolvidos dinamicamente durante a execução
## 🔄 Migração de Código Legado
Se você tem código customizado para transformações, pode migrar facilmente:
**Antes:**
```typescript
if (property.attributes.tipo === 'Casa de Condomínio') {
property.attributes.tipo = 'Casa/Sobrado'
property.attributes.subtipo = 'Casa'
property.attributes.tags = [...(property.attributes.tags || []), 'em-condominio']
}
```
**Depois:**
```typescript
const mapRules: MapRules = {
attributesRules: [{
key: 'tipo',
condition: { eq: 'Casa de Condomínio' },
rules: [
{ fn: 'upsertAttr', key: 'tipo', value: 'Casa/Sobrado' },
{ fn: 'upsertAttr', key: 'subtipo', value: 'Casa' },
{ fn: 'addToArray', key: 'tags', value: 'em-condominio' }
]
}]
}
const result = propertyV3CustomMapper(property, mapRules)
```