Baixe o app para aproveitar ainda mais
Prévia do material em texto
Treinamento em C# WebDeveloper Aula 16 Quarta-feira, 05 de Dezembro de 2018 Desenvolvimento Web com Asp.Net MVC Criando a base de dados MDF - Master Database File \Web.config.xml Mapeando a connectionstring do banco de dados <!-- Mapeando a connectionstring --> <connectionStrings> <add name="AULA16" connectionString="Data Source=(LocalDB)\MSSQLLocalDB; AttachDbFilename=C:\Users\COTI\Desktop\Aula_05.12.18\Aula16\ Projeto.Presentation\App_Data\Banco.mdf;Integrated Security=True" /> </connectionStrings> Adicionando no projeto Repositório uma referencia para System.Configuration using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Data.SqlClient; using System.Configuration; namespace Projeto.Repositories { public class Conexao { public SqlConnection Connection { get; set; } public SqlCommand Command { get; set; } public SqlDataReader DataReader { get; set; } public SqlTransaction Transaction { get; set; } public void AbrirConexao() { Connection = new SqlConnection(ConfigurationManager.ConnectionStrings ["AULA16"].ConnectionString); Connection.Open(); } public void FecharConexao() { Connection.Close(); } } } Criando a tabela "Conta" e as procedures na base de dados: create table Conta( IdConta integer identity(1,1), NomeConta nvarchar(50) not null, ValorConta decimal(18,2) not null, DataConta date not null, TipoConta nvarchar(15) not null, primary key(IdConta)) go alter table Conta add constraint CheckTipoConta check(TipoConta in ('Receita', 'Despesa')) go create procedure SpInserirConta @NomeConta nvarchar(50), @ValorConta decimal(18,2), @DataConta date, @TipoConta nvarchar(15) as begin insert into Conta(NomeConta, ValorConta, DataConta, TipoConta) values(@NomeConta, @ValorConta, @DataConta, @TipoConta) end go create procedure SpConsultarContas @DataInicio date, @DataFim date as begin select * from Conta where DataConta between @DataInicio and @DataFim end go Executando a procedure de gravação de conta: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Data; //importando.. using System.Data.SqlClient; //importando.. using Projeto.Entities; //importando.. namespace Projeto.Repositories { public class ContaRepository : Conexao { //método para inserir conta na base de dados.. public void Inserir(Conta conta) { Command = new SqlCommand("SpInserirConta", Connection); Command.CommandType = CommandType.StoredProcedure; Command.Parameters.AddWithValue("@NomeConta", conta.NomeConta); Command.Parameters.AddWithValue("@ValorConta", conta.ValorConta); Command.Parameters.AddWithValue("@DataConta", conta.DataConta); Command.Parameters.AddWithValue("@TipoConta", conta .TipoConta.ToString()); Command.ExecuteNonQuery(); } } } Camada de Regras de Negócio: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Projeto.Entities; //importando.. using Projeto.Repositories; //importando.. namespace Projeto.Business { public class ContaBusiness { public string CadastrarConta(Conta conta) { string resultado = string.Empty; ContaRepository repository = new ContaRepository(); try { repository.AbrirConexao(); repository.Inserir(conta); resultado = $"Conta {conta.NomeConta}, cadastrado com sucesso."; } catch(Exception e) { resultado = "Ocorreu um erro: " + e.Message; } finally { repository.FecharConexao(); } return resultado; } } } Implementando o cadastro em MVC: using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using Projeto.Presentation.Models; using Projeto.Entities; using Projeto.Entities.Types; using Projeto.Business; namespace Projeto.Presentation.Controllers { public class ContaController : Controller { // GET: Conta/Cadastro public ActionResult Cadastro() { return View(); } // GET: Conta/Consulta public ActionResult Consulta() { return View(); } public JsonResult CadastrarConta(ContaCadastroModel model) { //verificando se os dados da model //passaram nas regras de validação.. if(ModelState.IsValid) { Conta conta = new Conta(); conta.NomeConta = model.Nome; conta.DataConta = model.Data; conta.ValorConta = model.Valor; conta.TipoConta = (TipoConta) Enum.Parse (typeof(TipoConta), model.Tipo); ContaBusiness business = new ContaBusiness(); string resultado = business.CadastrarConta(conta); //mensagem.. return Json(resultado); } else { Hashtable erros = new Hashtable(); //percorrer o ModelState foreach(var s in ModelState) { //verificar se o elemento do ModelState //contem erro de validação if(s.Value.Errors.Count > 0) { //armazenar no hashtable.. erros[s.Key] = s.Value.Errors.Select (e => e.ErrorMessage).First(); } } Response.StatusCode = 400; //gerando um erro de servidor return Json(erros); } } public JsonResult ConsultarContas() { //criando uma lista da classe de modelo.. List<ContaConsultaModel> lista = new List<ContaConsultaModel>(); lista.Add(new ContaConsultaModel { IdConta = 1, Nome = "Salário", Valor = "R$ 2.000,00", Data = "03/12/2018", Tipo = "Receita" }); lista.Add(new ContaConsultaModel { IdConta = 2, Nome = "Luz", Valor = "R$ 300,00", Data = "04/12/2018", Tipo = "Despesa" }); lista.Add(new ContaConsultaModel { IdConta = 3, Nome = "Aluguel", Valor = "R$ 1.000,00", Data = "05/12/2018", Tipo = "Despesa" }); //retornando a lista.. return Json(lista); } } } Executando: Implementando a consulta de contas: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Data; //importando.. using System.Data.SqlClient; //importando.. using Projeto.Entities; //importando.. using Projeto.Entities.Types; namespace Projeto.Repositories { public class ContaRepository : Conexao { //método para inserir conta na base de dados.. public void Inserir(Conta conta) { Command = new SqlCommand("SpInserirConta", Connection); Command.CommandType = CommandType.StoredProcedure; Command.Parameters.AddWithValue("@NomeConta", conta.NomeConta); Command.Parameters.AddWithValue("@ValorConta", conta.ValorConta); Command.Parameters.AddWithValue("@DataConta", conta.DataConta); Command.Parameters.AddWithValue("@TipoConta", conta.TipoConta.ToString()); Command.ExecuteNonQuery(); } //método para listar as contas por periodo de datas publicList<Conta> Consultar(DateTime dataIni, DateTime dataFim) { Command = new SqlCommand("SpConsultarContas", Connection); Command.CommandType = CommandType.StoredProcedure; Command.Parameters.AddWithValue("@DataInicio", dataIni); Command.Parameters.AddWithValue("@DataFim", dataFim); DataReader = Command.ExecuteReader(); List<Conta> lista = new List<Conta>(); while(DataReader.Read()) { Conta conta = new Conta(); conta.IdConta = Convert.ToInt32(DataReader["IdConta"]); conta.NomeConta = Convert.ToString(DataReader["NomeConta"]); conta.ValorConta = Convert.ToDecimal(DataReader["ValorConta"]); conta.DataConta = Convert.ToDateTime(DataReader["DataConta"]); string tipoConta = Convert.ToString(DataReader["TipoConta"]); conta.TipoConta = (TipoConta) Enum.Parse(typeof(TipoConta), tipoConta); lista.Add(conta); } return lista; } } } Camada de Regras de Negócio: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Projeto.Entities; //importando.. using Projeto.Repositories; //importando.. using System.Diagnostics; namespace Projeto.Business { public class ContaBusiness { public string CadastrarConta(Conta conta) { string resultado = string.Empty; ContaRepository repository = new ContaRepository(); try { repository.AbrirConexao(); repository.Inserir(conta); resultado = $"Conta {conta.NomeConta}, cadastrado com sucesso."; } catch(Exception e) { resultado = "Ocorreu um erro: " + e.Message; } finally { repository.FecharConexao(); } return resultado; } public List<Conta> ConsultarContas(DateTime dataIni, DateTime dataFim) { List<Conta> lista = new List<Conta>(); ContaRepository repository = new ContaRepository(); try { repository.AbrirConexao(); lista = repository.Consultar(dataIni, dataFim); } catch(Exception e) { Debug.WriteLine("Erro ao consultar contas: " + e.Message); } finally { repository.FecharConexao(); } return lista; } } } Criando uma classe de modelo: using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.ComponentModel.DataAnnotations; namespace Projeto.Presentation.Models { public class ContaFiltroModel { [Required(ErrorMessage = "Por favor, informe a data de início.")] public DateTime DataInicio { get; set; } [Required(ErrorMessage = "Por favor, informe a data de término.")] public DateTime DataFim { get; set; } } } Enviando as datas para o controller na página de consulta: @section javascript{ <script src="http://code.highcharts.com/highcharts.js"></script> <script src="http://code.highcharts.com/modules/exporting.js"></script> <script> //iniciando o jquery na página.. $(document).ready(function () { //criando um evento ao clicar no botão de consulta.. $("#botaoconsulta").click(function () { //criando um JSON.. var model = { DataInicio: $("#datainicio").val(), DataFim: $("#datafim").val() }; //requisição AJAX para o servidor.. $.ajax({ type: "POST", url: "/Conta/ConsultarContas", data: model, success: function (d) { //verificar se o array contem elementos.. if (d.length > 0) { var linhas = ""; //varrendo o retorno do servidor.. $.each(d, function (i, conta) { linhas += "<tr>"; linhas += "<td>" + conta.IdConta + "</td>"; linhas += "<td>" + conta.Nome + "</td>"; linhas += "<td>" + conta.Valor + "</td>"; linhas += "<td>" + conta.Data + "</td>"; linhas += "<td>" + conta.Tipo + "</td>"; linhas += "</tr>"; }); $("#tabela tbody").html(linhas); $("#quantidade").html(d.length); $("#tabela").show(); //gerando os dados gráfico.. var dadosGrafico = []; dadosGrafico.push({ name: "Receitas", data: 2000 }); dadosGrafico.push({ name: "Despesas", data: 1300 }); //configurar o eixo 'y' do gráfico $.each(dadosGrafico, function (i, point) { point.y = point.data; }); $("#grafico").highcharts({ chart: { type: "pie" }, title: { text: "Resumo de Contas" }, series: [ { data : dadosGrafico } ] }); } else { $("#tabela").hide(); } }, error: function (e) { //imprimir o erro no console do navegador.. console.log(e); } }); }); }); </script> } No controller: Tratando as mensagens de validação: using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using Projeto.Presentation.Models; using Projeto.Entities; using Projeto.Entities.Types; using Projeto.Business; namespace Projeto.Presentation.Controllers { public class ContaController : Controller { // GET: Conta/Cadastro public ActionResult Cadastro() { return View(); } // GET: Conta/Consulta public ActionResult Consulta() { return View(); } public JsonResult CadastrarConta(ContaCadastroModel model) { //verificando se os dados da model //passaram nas regras de validação.. if(ModelState.IsValid) { Conta conta = new Conta(); conta.NomeConta = model.Nome; conta.DataConta = model.Data; conta.ValorConta = model.Valor; conta.TipoConta = (TipoConta) Enum.Parse(typeof(TipoConta), model.Tipo); ContaBusiness business = new ContaBusiness(); string resultado = business.CadastrarConta(conta); //mensagem.. return Json(resultado); } else { Hashtable erros = ObterErrosDeValidacao(); Response.StatusCode = 400; //gerando um erro de servidor return Json(erros); } } public JsonResult ConsultarContas(ContaFiltroModel model) { if(ModelState.IsValid) { //criando uma lista da classede modelo.. List<ContaConsultaModel> lista = new List<ContaConsultaModel>(); //retornando a lista.. return Json(lista); } else { Hashtable erros = ObterErrosDeValidacao(); Response.StatusCode = 400; //gerando um erro de servidor return Json(erros); } } private Hashtable ObterErrosDeValidacao() { Hashtable erros = new Hashtable(); //percorrer o ModelState foreach (var s in ModelState) { //verificar se o elemento do ModelState contem erro de validação if (s.Value.Errors.Count > 0) { //armazenar no hashtable.. erros[s.Key] = s.Value.Errors .Select(e => e.ErrorMessage).First(); } } return erros; } } } Na página: Exibindo as mensagens de erro de validação @{ ViewBag.Title = "Consulta"; Layout = "~/Views/Shared/Layout.cshtml"; } @section javascript{ <script src="http://code.highcharts.com/highcharts.js"></script> <script src="http://code.highcharts.com/modules/exporting.js"></script> <script> //iniciando o jquery na página.. $(document).ready(function () { //criando um evento ao clicar no botão de consulta.. $("#botaoconsulta").click(function () { //limpar as mensagens de erro.. $("#erro_datainicio").html(""); $("#erro_datafim").html(""); //criando um JSON.. var model = { DataInicio: $("#datainicio").val(), DataFim: $("#datafim").val() }; //requisição AJAX para o servidor.. $.ajax({ type: "POST", url: "/Conta/ConsultarContas", data: model, success: function (d) { //verificar se o array contem elementos.. if (d.length > 0) { var linhas = ""; //varrendo o retorno do servidor.. $.each(d, function (i, conta) { linhas += "<tr>"; linhas += "<td>" + conta.IdConta + "</td>"; linhas += "<td>" + conta.Nome + "</td>"; linhas += "<td>" + conta.Valor + "</td>"; linhas += "<td>" + conta.Data + "</td>"; linhas += "<td>" + conta.Tipo + "</td>"; linhas += "</tr>"; }); $("#tabela tbody").html(linhas); $("#quantidade").html(d.length); $("#tabela").show(); //gerando os dados gráfico.. var dadosGrafico = []; dadosGrafico.push({ name: "Receitas", data: 2000 }); dadosGrafico.push({ name: "Despesas", data: 1300 }); //configurar o eixo 'y' do gráfico $.each(dadosGrafico, function (i, point) { point.y = point.data; }); $("#grafico").highcharts({ chart: { type: "pie" }, title: { text: "Resumo de Contas" }, series: [ { data : dadosGrafico } ] }); } else { $("#tabela").hide(); } }, error: function (e) { if (e.status == 400){ $("#erro_datainicio").html (e.responseJSON.DataInicio); $("#erro_datafim").html(e.responseJSON.DataFim); } else { //imprimir o erro no console do navegador.. console.log(e); } } }); }); }); </script> } <h4>Consulta de Contas</h4> <a href="/Home/Index">Página inicial</a> <hr /> <div class="row"> <div class="col-md-3"> <label>Data de:</label> <input type="date" id="datainicio" class="form-control"/> <span id="erro_datainicio" class="text-danger"></span> <br/> <label>Data até:</label> <input type="date" id="datafim" class="form-control"/> <span id="erro_datafim" class="text-danger"></span> <br/> <button class="btn btn-primary" id="botaoconsulta"> Consultar Contas </button> <br/> <br/> <div id="mensagem"></div> </div> <div class="col-md-9"> <div id="grafico"></div> <br/> <table id="tabela" class="table table-hover table-bordered table-striped" style="display: none;"> <thead> <tr> <th>Código</th> <th>Nome da Conta</th> <th>Valor</th> <th>Data</th> <th>Tipo</th> </tr> </thead> <tbody> </tbody> <tfoot> <tr> <td colspan="5"> Quantidade de contas: <span id="quantidade"></span> </td> </tr> </tfoot> </table> </div> </div> Executando: Realizando a pesquisa: using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using Projeto.Presentation.Models; using Projeto.Entities; using Projeto.Entities.Types; using Projeto.Business; namespace Projeto.Presentation.Controllers { public class ContaController : Controller { // GET: Conta/Cadastro public ActionResult Cadastro() { return View(); } // GET: Conta/Consulta public ActionResult Consulta() { return View(); } public JsonResult CadastrarConta(ContaCadastroModel model) { //verificando se os dados da model //passaram nas regras de validação.. if(ModelState.IsValid) { Conta conta = new Conta(); conta.NomeConta = model.Nome; conta.DataConta = model.Data; conta.ValorConta = model.Valor; conta.TipoConta = (TipoConta) Enum.Parse(typeof(TipoConta), model.Tipo); ContaBusiness business = new ContaBusiness(); string resultado = business.CadastrarConta(conta); //mensagem.. return Json(resultado); } else { Hashtable erros = ObterErrosDeValidacao(); Response.StatusCode = 400; //gerando um erro de servidor return Json(erros); } } public JsonResult ConsultarContas(ContaFiltroModel model) { if(ModelState.IsValid) { //criando uma lista da classe de modelo.. List<ContaConsultaModel> lista = new List<ContaConsultaModel>(); //classe de negócio.. ContaBusiness business = new ContaBusiness(); foreach(Conta conta in business.ConsultarContas(model.DataInicio, model.DataFim)) { ContaConsultaModel contaModel = new ContaConsultaModel(); contaModel.IdConta = conta.IdConta; contaModel.Nome = conta.NomeConta; contaModel.Data = conta.DataConta.ToString("dd/MM/yyyy"); contaModel.Valor = conta.ValorConta.ToString("c"); contaModel.Tipo = conta.TipoConta.ToString(); lista.Add(contaModel); } //retornando a lista.. return Json(lista); } else { Hashtable erros = ObterErrosDeValidacao(); Response.StatusCode = 400; //gerando um erro de servidor return Json(erros); } } private Hashtable ObterErrosDeValidacao() { Hashtable erros = new Hashtable(); //percorrer o ModelState foreach (var s in ModelState) { //verificar se o elemento do ModelState contem erro de validação if (s.Value.Errors.Count > 0) { //armazenar no hashtable.. erros[s.Key] = s.Value.Errors .Select(e => e.ErrorMessage).First(); } } return erros; } } } Criando uma classe de modelo para gerar o resultado do gráfico: using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace Projeto.Presentation.Models { public class ContaResumoModel { public decimal TotalReceitas { get; set; } public decimal TotalDespesas { get; set; } } } No controller: using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using Projeto.Presentation.Models; using Projeto.Entities; using Projeto.Entities.Types; using Projeto.Business; namespace Projeto.Presentation.Controllers { public class ContaController : Controller { // GET: Conta/Cadastro public ActionResult Cadastro() { return View(); } // GET: Conta/Consulta public ActionResult Consulta() { return View(); } public JsonResult CadastrarConta(ContaCadastroModel model) { //verificando se os dados da model //passaram nas regras de validação.. if(ModelState.IsValid) { Conta conta = new Conta(); conta.NomeConta = model.Nome; conta.DataConta = model.Data; conta.ValorConta = model.Valor; conta.TipoConta = (TipoConta) Enum.Parse (typeof(TipoConta), model.Tipo); ContaBusiness business = new ContaBusiness(); string resultado = business.CadastrarConta(conta); //mensagem.. return Json(resultado); } else { Hashtable erros = ObterErrosDeValidacao(); Response.StatusCode = 400; //gerando um erro de servidor return Json(erros); } } public JsonResult ConsultarContas(ContaFiltroModel model) { if(ModelState.IsValid) { //criando uma lista da classe de modelo.. List<ContaConsultaModel> lista = new List<ContaConsultaModel>(); //classe de negócio.. ContaBusiness business = new ContaBusiness(); foreach(Conta conta in business.ConsultarContas (model.DataInicio, model.DataFim)) { ContaConsultaModel contaModel = new ContaConsultaModel(); contaModel.IdConta = conta.IdConta; contaModel.Nome = conta.NomeConta; contaModel.Data = conta.DataConta.ToString("dd/MM/yyyy"); contaModel.Valor = conta.ValorConta.ToString("c"); contaModel.Tipo = conta.TipoConta.ToString(); lista.Add(contaModel); } //retornando a lista.. return Json(lista); } else { Hashtable erros = ObterErrosDeValidacao(); Response.StatusCode = 400; //gerando um erro de servidor return Json(erros); } } public JsonResult ObterResumoContas(ContaFiltroModel model) { if(ModelState.IsValid) { ContaBusiness business = new ContaBusiness(); List<Conta> lista = business.ConsultarContas (model.DataInicio, model.DataFim); //resumindo.. ContaResumoModel resumo = new ContaResumoModel(); resumo.TotalReceitas = lista .Where(c => c.TipoConta == TipoConta.Receita) .Sum(c => c.ValorConta); resumo.TotalDespesas = lista .Where(c => c.TipoConta == TipoConta.Despesa) .Sum(c => c.ValorConta); return Json(resumo); } else { Hashtable erros = ObterErrosDeValidacao(); Response.StatusCode = 400; //gerando um erro de servidor return Json(erros); } } private Hashtable ObterErrosDeValidacao() { Hashtable erros = new Hashtable(); //percorrer o ModelState foreach (var s in ModelState) { //verificar se o elemento do ModelState contem erro de validação if (s.Value.Errors.Count > 0) { //armazenar no hashtable.. erros[s.Key] = s.Value.Errors .Select(e => e.ErrorMessage).First(); } } return erros; } } } Na página: @{ ViewBag.Title = "Consulta"; Layout = "~/Views/Shared/Layout.cshtml"; } @section javascript{ <script src="http://code.highcharts.com/highcharts.js"></script> <script src="http://code.highcharts.com/modules/exporting.js"></script> <script> //iniciando o jquery na página.. $(document).ready(function () { //criando um evento ao clicar no botão de consulta.. $("#botaoconsulta").click(function () { //limpar as mensagens de erro.. $("#erro_datainicio").html(""); $("#erro_datafim").html(""); //criando um JSON.. var model = { DataInicio: $("#datainicio").val(), DataFim: $("#datafim").val() }; //requisição AJAX para o servidor.. $.ajax({ type: "POST", url: "/Conta/ConsultarContas", data: model, success: function (d) { //verificar se o array contem elementos.. if (d.length > 0) { var linhas = ""; //varrendo o retorno do servidor.. $.each(d, function (i, conta) { linhas += "<tr>"; linhas += "<td>" + conta.IdConta + "</td>"; linhas += "<td>" + conta.Nome + "</td>"; linhas += "<td>" + conta.Valor + "</td>"; linhas += "<td>" + conta.Data + "</td>"; linhas += "<td>" + conta.Tipo + "</td>"; linhas += "</tr>"; }); $("#tabela tbody").html(linhas);$("#quantidade").html(d.length); $("#tabela").show(); $.ajax({ type: "POST", url: "/Conta/ObterResumoContas", data: model, success: function (d) { //gerando os dados gráfico.. var dadosGrafico = []; dadosGrafico.push({ name: "Receitas", data: d.TotalReceitas }); dadosGrafico.push({ name: "Despesas", data: d.TotalDespesas }); //configurar o eixo 'y' do gráfico $.each(dadosGrafico, function (i, point) { point.y = point.data; }); $("#grafico").highcharts({ chart: { type: "pie" }, title: { text: "Resumo de Contas" }, series: [{ data: dadosGrafico }] }); }, error: function (e) { console.log(e); } }); } else { $("#tabela").hide(); } }, error: function (e) { if (e.status == 400){ $("#erro_datainicio") .html(e.responseJSON.DataInicio); $("#erro_datafim") .html(e.responseJSON.DataFim); } else { //imprimir o erro no console do navegador.. console.log(e); } } }); }); }); </script> } <h4>Consulta de Contas</h4> <a href="/Home/Index">Página inicial</a> <hr /> <div class="row"> <div class="col-md-3"> <label>Data de:</label> <input type="date" id="datainicio" class="form-control"/> <span id="erro_datainicio" class="text-danger"></span> <br/> <label>Data até:</label> <input type="date" id="datafim" class="form-control"/> <span id="erro_datafim" class="text-danger"></span> <br/> <button class="btn btn-primary" id="botaoconsulta"> Consultar Contas </button> <br/> <br/> <div id="mensagem"></div> </div> <div class="col-md-9"> <div id="grafico"></div> <br/> <table id="tabela" class="table table-hover table-bordered table-striped" style="display: none;"> <thead> <tr> <th>Código</th> <th>Nome da Conta</th> <th>Valor</th> <th>Data</th> <th>Tipo</th> </tr> </thead> <tbody> </tbody> <tfoot> <tr> <td colspan="5"> Quantidade de contas: <span id="quantidade"></span> </td> </tr> </tfoot> </table> </div> </div> Executando: 24
Compartilhar