Implementação do Sistema
Agora vamos construir o sistema passo a passo, explicando cada decisão.
Iniciando o Acervo
// Estado global do sistema
let acervo = [];
let proximoId = 1; // Contador para IDs únicos
Conceito aplicado: Usamos let porque essas variáveis serão modificadas. O acervo começa vazio e será populado com livros.
Funções de Validação
Antes de implementar funcionalidades, precisamos garantir que os dados sejam válidos.
/**
* Valida os dados de um livro antes de adicioná-lo ao acervo
* @param {Object} livro - Objeto contendo dados do livro
* @returns {Object} - { valido: boolean, erros: Array }
*/
function validarLivro(livro) {
const erros = [];
// Valida título
if (!livro.titulo || livro.titulo.trim().length < 2) {
erros.push("Título deve ter pelo menos 2 caracteres");
}
// Valida autor
if (!livro.autor || livro.autor.trim().length < 3) {
erros.push("Autor deve ter pelo menos 3 caracteres");
}
// Valida categoria
const categoriasValidas = ["Romance", "Ficção", "Biografia", "Técnico", "História"];
if (!livro.categoria || !categoriasValidas.includes(livro.categoria)) {
erros.push("Categoria inválida. Use: " + categoriasValidas.join(", "));
}
// Valida ISBN (formato básico: 10 ou 13 dígitos)
if (livro.isbn && !/^\d{10}(\d{3})?$/.test(livro.isbn.replace(/-/g, ""))) {
erros.push("ISBN deve ter 10 ou 13 dígitos");
}
// Valida ano
const anoAtual = new Date().getFullYear();
if (livro.ano && (livro.ano < 1000 || livro.ano > anoAtual)) {
erros.push(`Ano deve estar entre 1000 e ${anoAtual}`);
}
return {
valido: erros.length === 0,
erros: erros
};
}
Conceitos aplicados:
- Funções puras: Recebe dados, retorna resultado, sem efeitos colaterais
- Validação múltipla: Coleta todos os erros antes de retornar
- Strings: Métodos como
trim()eincludes() - Condicionais: Verificações complexas com operadores lógicos
- Arrays: Armazenamento de erros e categorias válidas
Funções de Cadastro
Adicionar Livro
/**
* Adiciona um novo livro ao acervo
* @param {Object} dadosLivro - Dados do livro (sem ID)
* @returns {Object} - Resultado da operação
*/
function adicionarLivro(dadosLivro) {
// Validação
const validacao = validarLivro(dadosLivro);
if (!validacao.valido) {
return {
sucesso: false,
mensagem: "Erro ao adicionar livro",
erros: validacao.erros
};
}
// Verifica duplicação por ISBN
if (dadosLivro.isbn) {
const livroExistente = acervo.find(livro => livro.isbn === dadosLivro.isbn);
if (livroExistente) {
return {
sucesso: false,
mensagem: "Já existe um livro com este ISBN no acervo"
};
}
}
// Cria o objeto livro completo
const novoLivro = {
id: proximoId++,
titulo: dadosLivro.titulo.trim(),
autor: dadosLivro.autor.trim(),
categoria: dadosLivro.categoria,
isbn: dadosLivro.isbn || null,
ano: dadosLivro.ano || null,
disponivel: true,
emprestadoPara: null
};
// Adiciona ao acervo
acervo.push(novoLivro);
return {
sucesso: true,
mensagem: "Livro adicionado com sucesso",
livro: novoLivro
};
}
Conceitos aplicados:
- Validação de entrada: Sempre validar antes de processar
- Busca em array: Método
find()para verificar duplicação - Incremento de ID:
proximoId++para garantir unicidade - Objeto de retorno estruturado: Facilita tratamento de resultados
- Valores padrão: Usando
||para propriedades opcionais
Remover Livro
/**
* Remove um livro do acervo
* @param {number} id - ID do livro a ser removido
* @returns {Object} - Resultado da operação
*/
function removerLivro(id) {
const indice = acervo.findIndex(livro => livro.id === id);
if (indice === -1) {
return {
sucesso: false,
mensagem: "Livro não encontrado"
};
}
const livroRemovido = acervo[indice];
// Verifica se o livro está emprestado
if (!livroRemovido.disponivel) {
return {
sucesso: false,
mensagem: "Não é possível remover livro emprestado. Registre a devolução primeiro."
};
}
// Remove do acervo
acervo.splice(indice, 1);
return {
sucesso: true,
mensagem: "Livro removido com sucesso",
livro: livroRemovido
};
}
Conceitos aplicados:
- Busca de índice:
findIndex()para localizar posição - Remoção de array:
splice()para remover elemento específico - Lógica de negócio: Impedir remoção de livros emprestados
- Retorno de informações: Devolver o livro removido para confirmação
Atualizar Livro
/**
* Atualiza informações de um livro existente
* @param {number} id - ID do livro
* @param {Object} novosdados - Dados a serem atualizados
* @returns {Object} - Resultado da operação
*/
function atualizarLivro(id, novosData) {
const livro = acervo.find(l => l.id === id);
if (!livro) {
return {
sucesso: false,
mensagem: "Livro não encontrado"
};
}
// Cria objeto com dados atualizados
const dadosAtualizados = { ...livro, ...novosData, id: livro.id };
// Valida os novos dados
const validacao = validarLivro(dadosAtualizados);
if (!validacao.valido) {
return {
sucesso: false,
mensagem: "Dados inválidos",
erros: validacao.erros
};
}
// Atualiza propriedades permitidas
const propriedadesEditaveis = ["titulo", "autor", "categoria", "isbn", "ano"];
propriedadesEditaveis.forEach(prop => {
if (novosData.hasOwnProperty(prop)) {
livro[prop] = novosData[prop];
}
});
return {
sucesso: true,
mensagem: "Livro atualizado com sucesso",
livro: livro
};
}
Conceitos aplicados:
- Spread operator:
{ ...livro, ...novosData }para mesclar objetos - Proteção de ID: Garante que o ID não seja alterado
- Atualização seletiva: Apenas propriedades editáveis são modificadas
- Validação pós-mesclagem: Valida o objeto final antes de salvar
Funções de Busca
Buscar por Título
/**
* Busca livros por título (busca parcial, case-insensitive)
* @param {string} titulo - Termo de busca
* @returns {Array} - Livros encontrados
*/
function buscarPorTitulo(titulo) {
if (!titulo || titulo.trim().length === 0) {
return [];
}
const termoBusca = titulo.toLowerCase().trim();
return acervo.filter(livro =>
livro.titulo.toLowerCase().includes(termoBusca)
);
}
Conceitos aplicados:
- Busca case-insensitive:
toLowerCase()para ignorar maiúsculas/minúsculas - Busca parcial:
includes()permite encontrar matches parciais - Filtro de array:
filter()retorna todos os livros que correspondem
Buscar por Autor
/**
* Busca livros por autor
* @param {string} autor - Nome do autor
* @returns {Array} - Livros do autor
*/
function buscarPorAutor(autor) {
if (!autor || autor.trim().length === 0) {
return [];
}
const termoBusca = autor.toLowerCase().trim();
return acervo.filter(livro =>
livro.autor.toLowerCase().includes(termoBusca)
);
}
Filtrar por Categoria
/**
* Filtra livros por categoria
* @param {string} categoria - Categoria desejada
* @returns {Array} - Livros da categoria
*/
function filtrarPorCategoria(categoria) {
return acervo.filter(livro => livro.categoria === categoria);
}
Listar Disponíveis
/**
* Lista todos os livros disponíveis para empréstimo
* @returns {Array} - Livros disponíveis
*/
function listarDisponiveis() {
return acervo.filter(livro => livro.disponivel);
}
Conceito fundamental: Todas essas funções usam filter() - um dos métodos mais poderosos de arrays. Ele cria um novo array com todos os elementos que passam no teste (função callback).
Funções de Empréstimo
Emprestar Livro
/**
* Registra empréstimo de um livro
* @param {number} id - ID do livro
* @param {string} nomeUsuario - Nome de quem está pegando emprestado
* @returns {Object} - Resultado da operação
*/
function emprestarLivro(id, nomeUsuario) {
// Validações
if (!nomeUsuario || nomeUsuario.trim().length < 3) {
return {
sucesso: false,
mensagem: "Nome do usuário inválido (mínimo 3 caracteres)"
};
}
const livro = acervo.find(l => l.id === id);
if (!livro) {
return {
sucesso: false,
mensagem: "Livro não encontrado"
};
}
if (!livro.disponivel) {
return {
sucesso: false,
mensagem: `Livro já está emprestado para ${livro.emprestadoPara}`
};
}
// Registra empréstimo
livro.disponivel = false;
livro.emprestadoPara = nomeUsuario.trim();
return {
sucesso: true,
mensagem: `Livro "${livro.titulo}" emprestado para ${nomeUsuario}`,
livro: livro
};
}
Conceitos aplicados:
- Validação de negócio: Verifica disponibilidade antes de emprestar
- Atualização de estado: Modifica propriedades do livro
- Mensagens informativas: Feedback claro sobre o resultado
Devolver Livro
/**
* Registra devolução de um livro
* @param {number} id - ID do livro
* @returns {Object} - Resultado da operação
*/
function devolverLivro(id) {
const livro = acervo.find(l => l.id === id);
if (!livro) {
return {
sucesso: false,
mensagem: "Livro não encontrado"
};
}
if (livro.disponivel) {
return {
sucesso: false,
mensagem: "Este livro não está emprestado"
};
}
const usuarioAnterior = livro.emprestadoPara;
// Registra devolução
livro.disponivel = true;
livro.emprestadoPara = null;
return {
sucesso: true,
mensagem: `Livro "${livro.titulo}" devolvido por ${usuarioAnterior}`,
livro: livro
};
}
Listar Emprestados
/**
* Lista todos os livros atualmente emprestados
* @returns {Array} - Livros emprestados com informações do usuário
*/
function listarEmprestados() {
return acervo
.filter(livro => !livro.disponivel)
.map(livro => ({
id: livro.id,
titulo: livro.titulo,
autor: livro.autor,
emprestadoPara: livro.emprestadoPara
}));
}
Conceito aplicado: Encadeamento de métodos (filter + map) para filtrar e transformar dados em uma única operação.
Funções de Relatório
Contar Total
/**
* Retorna o total de livros no acervo
* @returns {number} - Total de livros
*/
function contarTotal() {
return acervo.length;
}
Contar por Categoria
/**
* Conta livros agrupados por categoria
* @returns {Object} - Objeto com contagem por categoria
*/
function contarPorCategoria() {
return acervo.reduce((contador, livro) => {
contador[livro.categoria] = (contador[livro.categoria] || 0) + 1;
return contador;
}, {});
}
Conceito aplicado: reduce() - o método mais poderoso de arrays, capaz de transformar um array em qualquer estrutura de dados.
Calcular Disponibilidade
/**
* Calcula estatísticas de disponibilidade
* @returns {Object} - Estatísticas do acervo
*/
function calcularDisponibilidade() {
const total = acervo.length;
const disponiveis = acervo.filter(l => l.disponivel).length;
const emprestados = total - disponiveis;
return {
total: total,
disponiveis: disponiveis,
emprestados: emprestados,
taxaDisponibilidade: total > 0 ? ((disponiveis / total) * 100).toFixed(2) + "%" : "0%"
};
}
Listar Autores
/**
* Lista todos os autores únicos no acervo
* @returns {Array} - Array de autores únicos, ordenados
*/
function listarAutores() {
const autoresUnicos = [...new Set(acervo.map(livro => livro.autor))];
return autoresUnicos.sort();
}
Conceito aplicado: Set - estrutura de dados que elimina automaticamente duplicatas. Usamos ... (spread) para converter de volta em array.
Funções de Persistência
Salvar Acervo
/**
* Converte o acervo para JSON para salvar
* @returns {string} - Acervo em formato JSON
*/
function salvarAcervo() {
const dados = {
acervo: acervo,
proximoId: proximoId,
dataBackup: new Date().toISOString()
};
return JSON.stringify(dados, null, 2);
}
Conceito aplicado: JSON.stringify() converte objetos JavaScript em texto JSON. O parâmetro 2 formata com indentação para legibilidade.
Carregar Acervo
/**
* Carrega acervo de string JSON
* @param {string} jsonString - String JSON contendo os dados
* @returns {Object} - Resultado da operação
*/
function carregarAcervo(jsonString) {
try {
const dados = JSON.parse(jsonString);
if (!dados.acervo || !Array.isArray(dados.acervo)) {
return {
sucesso: false,
mensagem: "Formato de dados inválido"
};
}
acervo = dados.acervo;
proximoId = dados.proximoId || acervo.length + 1;
return {
sucesso: true,
mensagem: `Acervo carregado: ${acervo.length} livros`,
quantidadeLivros: acervo.length
};
} catch (erro) {
return {
sucesso: false,
mensagem: "Erro ao processar JSON: " + erro.message
};
}
}
Conceitos aplicados:
- Try-catch: Tratamento de erros para operações que podem falhar
- JSON.parse(): Converte texto JSON em objetos JavaScript
- Validação de estrutura: Verifica se os dados carregados são válidos