DuOrigin v2 - React + NestJS + Prisma + EUDR API Integration

This commit is contained in:
2026-02-09 09:10:15 -03:00
parent cb90bad239
commit 4122dc6f3b
170 changed files with 31333 additions and 200 deletions

View File

@@ -0,0 +1,133 @@
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
enum Role {
ADMIN
OPERADOR
}
enum StatusAvaliacao {
RASCUNHO
EM_ANALISE
APROVADA
REPROVADA
}
enum NivelRisco {
BAIXO
MEDIO
ALTO
CRITICO
}
model User {
id Int @id @default(autoincrement())
email String @unique
password String
nome String
role Role @default(OPERADOR)
ativo Boolean @default(true)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
avaliacoes Avaliacao[]
documentos Documento[]
@@map("users")
}
model Empresa {
id Int @id @default(autoincrement())
razaoSocial String @map("razao_social")
nomeFantasia String? @map("nome_fantasia")
cnpj String @unique
inscricaoEstadual String? @map("inscricao_estadual")
atividadeAgricola String? @map("atividade_agricola")
endereco String?
cidade String?
estado String?
cep String?
telefone String?
email String?
responsavel String?
ativo Boolean @default(true)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
propriedades Propriedade[]
@@map("empresas")
}
model Propriedade {
id Int @id @default(autoincrement())
nome String
empresaId Int @map("empresa_id")
codigoCar String? @map("codigo_car")
areaHa Float? @map("area_ha")
latitude Float?
longitude Float?
bioma String?
endereco String?
cidade String?
estado String?
nivelRisco NivelRisco @default(BAIXO) @map("nivel_risco")
flagDesmatamento String? @map("flag_desmatamento")
geojson String? @db.Text
ativo Boolean @default(true)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
empresa Empresa @relation(fields: [empresaId], references: [id], onDelete: Cascade)
avaliacoes Avaliacao[]
documentos Documento[]
@@map("propriedades")
}
model Avaliacao {
id Int @id @default(autoincrement())
propriedadeId Int @map("propriedade_id")
userId Int @map("user_id")
referencia String @unique
status StatusAvaliacao @default(RASCUNHO)
nivelRisco NivelRisco @default(BAIXO) @map("nivel_risco")
dataAvaliacao DateTime? @map("data_avaliacao")
dataSubmissao DateTime? @map("data_submissao")
observacoes String? @db.Text
resultadoDds String? @map("resultado_dds") @db.Text
referenciaEu String? @map("referencia_eu")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
propriedade Propriedade @relation(fields: [propriedadeId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id])
documentos Documento[]
@@map("avaliacoes")
}
model Documento {
id Int @id @default(autoincrement())
nome String
tipo String?
tamanho Int?
mimeType String? @map("mime_type")
path String
propriedadeId Int? @map("propriedade_id")
avaliacaoId Int? @map("avaliacao_id")
userId Int @map("user_id")
createdAt DateTime @default(now()) @map("created_at")
propriedade Propriedade? @relation(fields: [propriedadeId], references: [id], onDelete: Cascade)
avaliacao Avaliacao? @relation(fields: [avaliacaoId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id])
@@map("documentos")
}

328
backend/prisma/seed.ts Normal file
View File

@@ -0,0 +1,328 @@
import { PrismaClient, Role, NivelRisco, StatusAvaliacao } from '@prisma/client';
import * as bcrypt from 'bcrypt';
const prisma = new PrismaClient();
async function main() {
console.log('🌱 Seeding database...');
// Create users
const adminPassword = await bcrypt.hash('DuoDemo2026', 10);
const operadorPassword = await bcrypt.hash('DuoDemo2026', 10);
const admin = await prisma.user.upsert({
where: { email: 'demo@duorigin.com' },
update: {},
create: {
email: 'demo@duorigin.com',
password: adminPassword,
nome: 'Administrador Demo',
role: Role.ADMIN,
ativo: true,
},
});
const operador = await prisma.user.upsert({
where: { email: 'operador@duorigin.com' },
update: {},
create: {
email: 'operador@duorigin.com',
password: operadorPassword,
nome: 'Operador Demo',
role: Role.OPERADOR,
ativo: true,
},
});
console.log('✅ Users created:', { admin: admin.email, operador: operador.email });
// Create empresas
const empresas = await Promise.all([
prisma.empresa.upsert({
where: { cnpj: '12.345.678/0001-90' },
update: {},
create: {
razaoSocial: 'Fazenda Bom Sucesso Ltda',
nomeFantasia: 'Fazenda Bom Sucesso',
cnpj: '12.345.678/0001-90',
inscricaoEstadual: '123.456.789.012',
atividadeAgricola: 'Soja, Milho',
endereco: 'Rodovia BR-050, Km 45',
cidade: 'Uberlândia',
estado: 'MG',
cep: '38400-000',
telefone: '(34) 99999-0001',
email: 'contato@bomsucesso.com.br',
responsavel: 'João Silva',
ativo: true,
},
}),
prisma.empresa.upsert({
where: { cnpj: '23.456.789/0001-01' },
update: {},
create: {
razaoSocial: 'Agropecuária São José S.A.',
nomeFantasia: 'São José Agro',
cnpj: '23.456.789/0001-01',
inscricaoEstadual: '234.567.890.123',
atividadeAgricola: 'Café, Pecuária',
endereco: 'Estrada Municipal SP-123, Km 10',
cidade: 'Ribeirão Preto',
estado: 'SP',
cep: '14000-000',
telefone: '(16) 99999-0002',
email: 'contato@saojoseagro.com.br',
responsavel: 'Maria Santos',
ativo: true,
},
}),
prisma.empresa.upsert({
where: { cnpj: '34.567.890/0001-12' },
update: {},
create: {
razaoSocial: 'Cooperativa Agrícola Vale Verde',
nomeFantasia: 'Coop Vale Verde',
cnpj: '34.567.890/0001-12',
inscricaoEstadual: '345.678.901.234',
atividadeAgricola: 'Soja, Algodão, Milho',
endereco: 'Av. Principal, 1000',
cidade: 'Rondonópolis',
estado: 'MT',
cep: '78700-000',
telefone: '(66) 99999-0003',
email: 'contato@valeverde.coop.br',
responsavel: 'Carlos Oliveira',
ativo: true,
},
}),
]);
console.log('✅ Empresas created:', empresas.length);
// Create propriedades
const propriedades = await Promise.all([
prisma.propriedade.create({
data: {
nome: 'Fazenda Primavera',
empresaId: empresas[0].id,
codigoCar: 'MG-3170206-A1B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6',
areaHa: 1500.5,
latitude: -18.9186,
longitude: -48.2772,
bioma: 'Cerrado',
endereco: 'Zona Rural, Lote 45',
cidade: 'Uberlândia',
estado: 'MG',
nivelRisco: NivelRisco.BAIXO,
flagDesmatamento: 'clean',
ativo: true,
},
}),
prisma.propriedade.create({
data: {
nome: 'Sítio Esperança',
empresaId: empresas[0].id,
codigoCar: 'MG-3170206-B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6Q7',
areaHa: 320.0,
latitude: -18.8500,
longitude: -48.3000,
bioma: 'Cerrado',
cidade: 'Uberlândia',
estado: 'MG',
nivelRisco: NivelRisco.BAIXO,
ativo: true,
},
}),
prisma.propriedade.create({
data: {
nome: 'Fazenda São José',
empresaId: empresas[1].id,
codigoCar: 'SP-3543402-C3D4E5F6G7H8I9J0K1L2M3N4O5P6Q7R8',
areaHa: 850.75,
latitude: -21.1767,
longitude: -47.8108,
bioma: 'Mata Atlântica',
cidade: 'Ribeirão Preto',
estado: 'SP',
nivelRisco: NivelRisco.MEDIO,
flagDesmatamento: 'under_review',
ativo: true,
},
}),
prisma.propriedade.create({
data: {
nome: 'Fazenda Pioneira',
empresaId: empresas[2].id,
codigoCar: 'MT-5107602-D4E5F6G7H8I9J0K1L2M3N4O5P6Q7R8S9',
areaHa: 5200.0,
latitude: -16.4673,
longitude: -54.6372,
bioma: 'Cerrado',
cidade: 'Rondonópolis',
estado: 'MT',
nivelRisco: NivelRisco.BAIXO,
ativo: true,
},
}),
prisma.propriedade.create({
data: {
nome: 'Fazenda Horizonte',
empresaId: empresas[2].id,
codigoCar: 'MT-5107602-E5F6G7H8I9J0K1L2M3N4O5P6Q7R8S9T0',
areaHa: 3800.25,
latitude: -16.5000,
longitude: -54.7000,
bioma: 'Cerrado',
cidade: 'Rondonópolis',
estado: 'MT',
nivelRisco: NivelRisco.ALTO,
flagDesmatamento: 'flagged',
ativo: true,
},
}),
]);
console.log('✅ Propriedades created:', propriedades.length);
// Create avaliacoes
const generateRef = () => {
const timestamp = Date.now().toString(36).toUpperCase();
const random = Math.random().toString(36).substring(2, 6).toUpperCase();
return `DUO-${timestamp}-${random}`;
};
const avaliacoes = await Promise.all([
prisma.avaliacao.create({
data: {
propriedadeId: propriedades[0].id,
userId: admin.id,
referencia: generateRef(),
status: StatusAvaliacao.APROVADA,
nivelRisco: NivelRisco.BAIXO,
dataAvaliacao: new Date('2024-01-15'),
dataSubmissao: new Date('2024-01-20'),
observacoes: 'Propriedade em conformidade com EUDR.',
referenciaEu: 'EU-ABC123',
},
}),
prisma.avaliacao.create({
data: {
propriedadeId: propriedades[0].id,
userId: operador.id,
referencia: generateRef(),
status: StatusAvaliacao.EM_ANALISE,
nivelRisco: NivelRisco.BAIXO,
dataAvaliacao: new Date('2024-06-01'),
observacoes: 'Aguardando documentação adicional.',
},
}),
prisma.avaliacao.create({
data: {
propriedadeId: propriedades[1].id,
userId: admin.id,
referencia: generateRef(),
status: StatusAvaliacao.RASCUNHO,
nivelRisco: NivelRisco.BAIXO,
dataAvaliacao: new Date(),
},
}),
prisma.avaliacao.create({
data: {
propriedadeId: propriedades[2].id,
userId: operador.id,
referencia: generateRef(),
status: StatusAvaliacao.EM_ANALISE,
nivelRisco: NivelRisco.MEDIO,
dataAvaliacao: new Date('2024-05-10'),
dataSubmissao: new Date('2024-05-15'),
observacoes: 'Área com histórico de desmatamento em análise.',
},
}),
prisma.avaliacao.create({
data: {
propriedadeId: propriedades[2].id,
userId: admin.id,
referencia: generateRef(),
status: StatusAvaliacao.REPROVADA,
nivelRisco: NivelRisco.ALTO,
dataAvaliacao: new Date('2024-03-01'),
dataSubmissao: new Date('2024-03-10'),
observacoes: 'Documentação irregular. Necessário revisão.',
},
}),
prisma.avaliacao.create({
data: {
propriedadeId: propriedades[3].id,
userId: admin.id,
referencia: generateRef(),
status: StatusAvaliacao.APROVADA,
nivelRisco: NivelRisco.BAIXO,
dataAvaliacao: new Date('2024-02-20'),
dataSubmissao: new Date('2024-02-25'),
referenciaEu: 'EU-DEF456',
},
}),
prisma.avaliacao.create({
data: {
propriedadeId: propriedades[3].id,
userId: operador.id,
referencia: generateRef(),
status: StatusAvaliacao.RASCUNHO,
nivelRisco: NivelRisco.BAIXO,
dataAvaliacao: new Date(),
},
}),
prisma.avaliacao.create({
data: {
propriedadeId: propriedades[4].id,
userId: admin.id,
referencia: generateRef(),
status: StatusAvaliacao.EM_ANALISE,
nivelRisco: NivelRisco.ALTO,
dataAvaliacao: new Date('2024-06-05'),
dataSubmissao: new Date('2024-06-10'),
observacoes: 'Área com risco alto de desmatamento identificado.',
},
}),
prisma.avaliacao.create({
data: {
propriedadeId: propriedades[4].id,
userId: operador.id,
referencia: generateRef(),
status: StatusAvaliacao.REPROVADA,
nivelRisco: NivelRisco.CRITICO,
dataAvaliacao: new Date('2024-04-01'),
dataSubmissao: new Date('2024-04-05'),
observacoes: 'Desmatamento ilegal detectado. Propriedade bloqueada.',
},
}),
prisma.avaliacao.create({
data: {
propriedadeId: propriedades[1].id,
userId: operador.id,
referencia: generateRef(),
status: StatusAvaliacao.APROVADA,
nivelRisco: NivelRisco.BAIXO,
dataAvaliacao: new Date('2024-01-05'),
dataSubmissao: new Date('2024-01-10'),
referenciaEu: 'EU-GHI789',
},
}),
]);
console.log('✅ Avaliacoes created:', avaliacoes.length);
console.log('🎉 Seed completed successfully!');
console.log('\n📝 Login credentials:');
console.log(' Admin: demo@duorigin.com / DuoDemo2026');
console.log(' Operador: operador@duorigin.com / DuoDemo2026');
}
main()
.catch((e) => {
console.error('❌ Seed error:', e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});