Practica 1

parent 37c2574b
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DBNavigator.Project.DatabaseFileManager">
<open-files />
</component>
<component name="DBNavigator.Project.Settings">
<connections />
<browser-settings>
<general>
<display-mode value="TABBED" />
<navigation-history-size value="100" />
<show-object-details value="false" />
</general>
<filters>
<object-type-filter>
<object-type name="SCHEMA" enabled="true" />
<object-type name="USER" enabled="true" />
<object-type name="ROLE" enabled="true" />
<object-type name="PRIVILEGE" enabled="true" />
<object-type name="CHARSET" enabled="true" />
<object-type name="TABLE" enabled="true" />
<object-type name="VIEW" enabled="true" />
<object-type name="MATERIALIZED_VIEW" enabled="true" />
<object-type name="NESTED_TABLE" enabled="true" />
<object-type name="COLUMN" enabled="true" />
<object-type name="INDEX" enabled="true" />
<object-type name="CONSTRAINT" enabled="true" />
<object-type name="DATASET_TRIGGER" enabled="true" />
<object-type name="DATABASE_TRIGGER" enabled="true" />
<object-type name="SYNONYM" enabled="true" />
<object-type name="SEQUENCE" enabled="true" />
<object-type name="PROCEDURE" enabled="true" />
<object-type name="FUNCTION" enabled="true" />
<object-type name="PACKAGE" enabled="true" />
<object-type name="TYPE" enabled="true" />
<object-type name="TYPE_ATTRIBUTE" enabled="true" />
<object-type name="ARGUMENT" enabled="true" />
<object-type name="DIMENSION" enabled="true" />
<object-type name="CLUSTER" enabled="true" />
<object-type name="DBLINK" enabled="true" />
</object-type-filter>
</filters>
<sorting>
<object-type name="COLUMN" sorting-type="NAME" />
<object-type name="FUNCTION" sorting-type="NAME" />
<object-type name="PROCEDURE" sorting-type="NAME" />
<object-type name="ARGUMENT" sorting-type="POSITION" />
<object-type name="TYPE ATTRIBUTE" sorting-type="POSITION" />
</sorting>
<default-editors>
<object-type name="VIEW" editor-type="SELECTION" />
<object-type name="PACKAGE" editor-type="SELECTION" />
<object-type name="TYPE" editor-type="SELECTION" />
</default-editors>
</browser-settings>
<navigation-settings>
<lookup-filters>
<lookup-objects>
<object-type name="SCHEMA" enabled="true" />
<object-type name="USER" enabled="false" />
<object-type name="ROLE" enabled="false" />
<object-type name="PRIVILEGE" enabled="false" />
<object-type name="CHARSET" enabled="false" />
<object-type name="TABLE" enabled="true" />
<object-type name="VIEW" enabled="true" />
<object-type name="MATERIALIZED VIEW" enabled="true" />
<object-type name="INDEX" enabled="true" />
<object-type name="CONSTRAINT" enabled="true" />
<object-type name="DATASET TRIGGER" enabled="true" />
<object-type name="DATABASE TRIGGER" enabled="true" />
<object-type name="SYNONYM" enabled="false" />
<object-type name="SEQUENCE" enabled="true" />
<object-type name="PROCEDURE" enabled="true" />
<object-type name="FUNCTION" enabled="true" />
<object-type name="PACKAGE" enabled="true" />
<object-type name="TYPE" enabled="true" />
<object-type name="DIMENSION" enabled="false" />
<object-type name="CLUSTER" enabled="false" />
<object-type name="DBLINK" enabled="true" />
</lookup-objects>
<force-database-load value="false" />
<prompt-connection-selection value="true" />
<prompt-schema-selection value="true" />
</lookup-filters>
</navigation-settings>
<dataset-grid-settings>
<general>
<enable-zooming value="true" />
<enable-column-tooltip value="true" />
</general>
<sorting>
<nulls-first value="true" />
<max-sorting-columns value="4" />
</sorting>
<audit-columns>
<column-names value="" />
<visible value="true" />
<editable value="false" />
</audit-columns>
</dataset-grid-settings>
<dataset-editor-settings>
<text-editor-popup>
<active value="false" />
<active-if-empty value="false" />
<data-length-threshold value="100" />
<popup-delay value="1000" />
</text-editor-popup>
<values-actions-popup>
<show-popup-button value="true" />
<element-count-threshold value="1000" />
<data-length-threshold value="250" />
</values-actions-popup>
<general>
<fetch-block-size value="100" />
<fetch-timeout value="30" />
<trim-whitespaces value="true" />
<convert-empty-strings-to-null value="true" />
<select-content-on-cell-edit value="true" />
<large-value-preview-active value="true" />
</general>
<filters>
<prompt-filter-dialog value="true" />
<default-filter-type value="BASIC" />
</filters>
<qualified-text-editor text-length-threshold="300">
<content-types>
<content-type name="Text" enabled="true" />
<content-type name="Properties" enabled="true" />
<content-type name="XML" enabled="true" />
<content-type name="DTD" enabled="true" />
<content-type name="HTML" enabled="true" />
<content-type name="XHTML" enabled="true" />
<content-type name="CSS" enabled="true" />
<content-type name="Java" enabled="true" />
<content-type name="SQL" enabled="true" />
<content-type name="PL/SQL" enabled="true" />
<content-type name="JavaScript" enabled="true" />
<content-type name="JSON" enabled="true" />
<content-type name="JSON5" enabled="true" />
<content-type name="JSP" enabled="true" />
<content-type name="JSPx" enabled="true" />
<content-type name="Groovy" enabled="true" />
<content-type name="FTL" enabled="true" />
<content-type name="VTL" enabled="true" />
<content-type name="YAML" enabled="true" />
<content-type name="Manifest" enabled="true" />
</content-types>
</qualified-text-editor>
<record-navigation>
<navigation-target value="VIEWER" />
</record-navigation>
</dataset-editor-settings>
<code-editor-settings>
<general>
<show-object-navigation-gutter value="false" />
<show-spec-declaration-navigation-gutter value="true" />
<enable-spellchecking value="true" />
<enable-reference-spellchecking value="false" />
</general>
<confirmations>
<save-changes value="false" />
<revert-changes value="true" />
<exit-on-changes value="ASK" />
</confirmations>
</code-editor-settings>
<code-completion-settings>
<filters>
<basic-filter>
<filter-element type="RESERVED_WORD" id="keyword" selected="true" />
<filter-element type="RESERVED_WORD" id="function" selected="true" />
<filter-element type="RESERVED_WORD" id="parameter" selected="true" />
<filter-element type="RESERVED_WORD" id="datatype" selected="true" />
<filter-element type="RESERVED_WORD" id="exception" selected="true" />
<filter-element type="OBJECT" id="schema" selected="true" />
<filter-element type="OBJECT" id="role" selected="true" />
<filter-element type="OBJECT" id="user" selected="true" />
<filter-element type="OBJECT" id="privilege" selected="true" />
<user-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="false" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</user-schema>
<public-schema>
<filter-element type="OBJECT" id="table" selected="false" />
<filter-element type="OBJECT" id="view" selected="false" />
<filter-element type="OBJECT" id="materialized view" selected="false" />
<filter-element type="OBJECT" id="index" selected="false" />
<filter-element type="OBJECT" id="constraint" selected="false" />
<filter-element type="OBJECT" id="trigger" selected="false" />
<filter-element type="OBJECT" id="synonym" selected="false" />
<filter-element type="OBJECT" id="sequence" selected="false" />
<filter-element type="OBJECT" id="procedure" selected="false" />
<filter-element type="OBJECT" id="function" selected="false" />
<filter-element type="OBJECT" id="package" selected="false" />
<filter-element type="OBJECT" id="type" selected="false" />
<filter-element type="OBJECT" id="dimension" selected="false" />
<filter-element type="OBJECT" id="cluster" selected="false" />
<filter-element type="OBJECT" id="dblink" selected="false" />
</public-schema>
<any-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</any-schema>
</basic-filter>
<extended-filter>
<filter-element type="RESERVED_WORD" id="keyword" selected="true" />
<filter-element type="RESERVED_WORD" id="function" selected="true" />
<filter-element type="RESERVED_WORD" id="parameter" selected="true" />
<filter-element type="RESERVED_WORD" id="datatype" selected="true" />
<filter-element type="RESERVED_WORD" id="exception" selected="true" />
<filter-element type="OBJECT" id="schema" selected="true" />
<filter-element type="OBJECT" id="user" selected="true" />
<filter-element type="OBJECT" id="role" selected="true" />
<filter-element type="OBJECT" id="privilege" selected="true" />
<user-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</user-schema>
<public-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</public-schema>
<any-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</any-schema>
</extended-filter>
</filters>
<sorting enabled="true">
<sorting-element type="RESERVED_WORD" id="keyword" />
<sorting-element type="RESERVED_WORD" id="datatype" />
<sorting-element type="OBJECT" id="column" />
<sorting-element type="OBJECT" id="table" />
<sorting-element type="OBJECT" id="view" />
<sorting-element type="OBJECT" id="materialized view" />
<sorting-element type="OBJECT" id="index" />
<sorting-element type="OBJECT" id="constraint" />
<sorting-element type="OBJECT" id="trigger" />
<sorting-element type="OBJECT" id="synonym" />
<sorting-element type="OBJECT" id="sequence" />
<sorting-element type="OBJECT" id="procedure" />
<sorting-element type="OBJECT" id="function" />
<sorting-element type="OBJECT" id="package" />
<sorting-element type="OBJECT" id="type" />
<sorting-element type="OBJECT" id="dimension" />
<sorting-element type="OBJECT" id="cluster" />
<sorting-element type="OBJECT" id="dblink" />
<sorting-element type="OBJECT" id="schema" />
<sorting-element type="OBJECT" id="role" />
<sorting-element type="OBJECT" id="user" />
<sorting-element type="RESERVED_WORD" id="function" />
<sorting-element type="RESERVED_WORD" id="parameter" />
</sorting>
<format>
<enforce-code-style-case value="true" />
</format>
</code-completion-settings>
<execution-engine-settings>
<statement-execution>
<fetch-block-size value="100" />
<execution-timeout value="20" />
<debug-execution-timeout value="600" />
<focus-result value="false" />
<prompt-execution value="false" />
</statement-execution>
<script-execution>
<command-line-interfaces />
<execution-timeout value="300" />
</script-execution>
<method-execution>
<execution-timeout value="30" />
<debug-execution-timeout value="600" />
<parameter-history-size value="10" />
</method-execution>
</execution-engine-settings>
<operation-settings>
<transactions>
<uncommitted-changes>
<on-project-close value="ASK" />
<on-disconnect value="ASK" />
<on-autocommit-toggle value="ASK" />
</uncommitted-changes>
<multiple-uncommitted-changes>
<on-commit value="ASK" />
<on-rollback value="ASK" />
</multiple-uncommitted-changes>
</transactions>
<session-browser>
<disconnect-session value="ASK" />
<kill-session value="ASK" />
<reload-on-filter-change value="false" />
</session-browser>
<compiler>
<compile-type value="KEEP" />
<compile-dependencies value="ASK" />
<always-show-controls value="false" />
</compiler>
</operation-settings>
<ddl-file-settings>
<extensions>
<mapping file-type-id="VIEW" extensions="vw" />
<mapping file-type-id="TRIGGER" extensions="trg" />
<mapping file-type-id="PROCEDURE" extensions="prc" />
<mapping file-type-id="FUNCTION" extensions="fnc" />
<mapping file-type-id="PACKAGE" extensions="pkg" />
<mapping file-type-id="PACKAGE_SPEC" extensions="pks" />
<mapping file-type-id="PACKAGE_BODY" extensions="pkb" />
<mapping file-type-id="TYPE" extensions="tpe" />
<mapping file-type-id="TYPE_SPEC" extensions="tps" />
<mapping file-type-id="TYPE_BODY" extensions="tpb" />
</extensions>
<general>
<lookup-ddl-files value="true" />
<create-ddl-files value="false" />
<synchronize-ddl-files value="true" />
<use-qualified-names value="false" />
<make-scripts-rerunnable value="true" />
</general>
</ddl-file-settings>
<general-settings>
<regional-settings>
<date-format value="MEDIUM" />
<number-format value="UNGROUPED" />
<locale value="SYSTEM_DEFAULT" />
<use-custom-formats value="false" />
</regional-settings>
<environment>
<environment-types>
<environment-type id="development" name="Development" description="Development environment" color="-2430209/-12296320" readonly-code="false" readonly-data="false" />
<environment-type id="integration" name="Integration" description="Integration environment" color="-2621494/-12163514" readonly-code="true" readonly-data="false" />
<environment-type id="production" name="Production" description="Productive environment" color="-11574/-10271420" readonly-code="true" readonly-data="true" />
<environment-type id="other" name="Other" description="" color="-1576/-10724543" readonly-code="false" readonly-data="false" />
</environment-types>
<visibility-settings>
<connection-tabs value="true" />
<dialog-headers value="true" />
<object-editor-tabs value="true" />
<script-editor-tabs value="false" />
<execution-result-tabs value="true" />
</visibility-settings>
</environment>
</general-settings>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GitLabDefaultAccount">
<option name="defaultAccountId" value="3f871339-5570-4958-a075-631c6ecffa55" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_X" default="true" project-jdk-name="openjdk-23" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/DAELaura2526.iml" filepath="$PROJECT_DIR$/.idea/DAELaura2526.iml" />
</modules>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>
\ No newline at end of file
package com.ayto.incidencias;
import com.ayto.incidencias.web.AuthInterceptor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@SpringBootApplication
public class App implements WebMvcConfigurer {
private final AuthInterceptor auth;
public App(AuthInterceptor auth){ this.auth = auth; }
public static void main(String[] args){ SpringApplication.run(App.class, args); }
@Override public void addInterceptors(InterceptorRegistry reg){
reg.addInterceptor(auth).addPathPatterns("/api/**");
}
}
\ No newline at end of file
package com.ayto.incidencias.repo.memoria;
import com.ayto.incidencias.dominio.Incidencia;
import com.ayto.incidencias.repo.IncidenciaRepo;
import org.springframework.stereotype.Repository;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
@Repository
public class IncidenciaRepoMem implements IncidenciaRepo {
private final Map<Long,Incidencia> datos = new ConcurrentHashMap<>();
private final AtomicLong seq = new AtomicLong(0);
public Incidencia save(Incidencia i){
if(i.getId()==null) i.setId(seq.incrementAndGet());
datos.put(i.getId(), i);
return i;
}
public Optional<Incidencia> findById(Long id){ return Optional.ofNullable(datos.get(id)); }
public void delete(Long id){ datos.remove(id); }
public List<Incidencia> findByAutor(Long uid){
return datos.values().stream()
.filter(i -> i.getAutor()!=null && i.getAutor().getId().equals(uid))
.toList();
}
public List<Incidencia> findAll(){ return new ArrayList<>(datos.values()); }
}
\ No newline at end of file
package com.ayto.incidencias.repo.memoria;
import com.ayto.incidencias.dominio.TipoIncidencia;
import com.ayto.incidencias.repo.TipoIncidenciaRepo;
import org.springframework.stereotype.Repository;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
@Repository
public class TipoIncidenciaRepoMem implements TipoIncidenciaRepo {
private final Map<Long,TipoIncidencia> datos = new ConcurrentHashMap<>();
private final AtomicLong seq = new AtomicLong(0);
public TipoIncidencia save(TipoIncidencia t){
if(t.getId()==null) t.setId(seq.incrementAndGet());
datos.put(t.getId(), t);
return t;
}
public void delete(Long id){ datos.remove(id); }
public boolean existsByNombre(String n){
return datos.values().stream().anyMatch(t -> t.getNombre().equalsIgnoreCase(n));
}
public Optional<TipoIncidencia> findById(Long id){ return Optional.ofNullable(datos.get(id)); }
public List<TipoIncidencia> findAll(){ return new ArrayList<>(datos.values()); }
}
\ No newline at end of file
package com.ayto.incidencias.repo.memoria;
import com.ayto.incidencias.dominio.Usuario;
import com.ayto.incidencias.repo.UsuarioRepo;
import org.springframework.stereotype.Repository;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@Repository
public class UsuarioRepoMem implements UsuarioRepo {
private final Map<String,Usuario> porLogin = new ConcurrentHashMap<>();
public Usuario save(Usuario u){ porLogin.put(u.getLogin(), u); return u; }
public Optional<Usuario> findByLogin(String login){ return Optional.ofNullable(porLogin.get(login)); }
}
\ No newline at end of file
package com.ayto.incidencias.servicio;
import com.ayto.incidencias.dominio.*;
import com.ayto.incidencias.repo.*;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
@Service
public class ServicioIncidencias {
private final IncidenciaRepo incidencias;
private final TipoIncidenciaRepo tipos;
public ServicioIncidencias(IncidenciaRepo incidencias, TipoIncidenciaRepo tipos){
this.incidencias = incidencias; this.tipos = tipos;
}
public Incidencia crearIncidencia(Usuario autor, Long tipoId, String desc,
Direccion dir, CoordenadaGPS gps){
TipoIncidencia tipo = tipos.findById(tipoId).orElseThrow();
Incidencia i = new Incidencia();
i.setAutor(autor); i.setTipo(tipo); i.setDescripcion(desc);
i.setDireccion(dir); i.setUbicacionGPS(gps);
i.setFecha(LocalDateTime.now()); i.setEstado(EstadoIncidencia.PENDIENTE);
return incidencias.save(i);
}
public List<Incidencia> listarMisIncidencias(Long autorId){ return incidencias.findByAutor(autorId); }
public List<Incidencia> buscar(Long tipoId, EstadoIncidencia estado){
return incidencias.findAll().stream()
.filter(i -> tipoId==null || (i.getTipo()!=null && i.getTipo().getId().equals(tipoId)))
.filter(i -> estado==null || i.getEstado()==estado)
.toList();
}
public void borrarSiPendiente(Long autorId, Long incId){
TipoIncidencia inc = incidencias.findById(incId).orElseThrow().getTipo();
if(!inc.getAutor().getId().equals(autorId)) throw new SecurityException("No puedes borrar de otros");
if(inc.getEstado()!=EstadoIncidencia.PENDIENTE) throw new IllegalStateException("Sólo PENDIENTE");
incidencias.delete(incId);
}
public Incidencia adminCambiarEstado(Long id, EstadoIncidencia nuevo){
TipoIncidencia inc = incidencias.findById(id).orElseThrow().getTipo();
inc.setEstado(nuevo);
return incidencias.save(inc);
}
public void adminBorrar(Long id){ incidencias.delete(id); }
}
\ No newline at end of file
package com.ayto.incidencias.servicio;
import com.ayto.incidencias.dominio.TipoIncidencia;
import com.ayto.incidencias.repo.*;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ServicioTiposIncidencia {
private final TipoIncidenciaRepo tipos;
private final IncidenciaRepo incidencias;
public ServicioTiposIncidencia(TipoIncidenciaRepo tipos, IncidenciaRepo incidencias){
this.tipos = tipos; this.incidencias = incidencias;
}
public TipoIncidencia crear(String nombre, String descripcion){
if(tipos.existsByNombre(nombre)) throw new IllegalArgumentException("Tipo duplicado");
var t = new TipoIncidencia(); t.setNombre(nombre); t.setDescripcion(descripcion);
return tipos.save(t);
}
public void eliminar(Long id){
boolean enUso = incidencias.findAll().stream()
.anyMatch(i -> i.getTipo()!=null && i.getTipo().getId().equals(id));
if(enUso) throw new IllegalStateException("No se puede borrar: tipo en uso");
tipos.delete(id);
}
public List<TipoIncidencia> listar(){ return tipos.findAll(); }
}
\ No newline at end of file
package com.ayto.incidencias.servicio;
import com.ayto.incidencias.dominio.*;
import com.ayto.incidencias.repo.UsuarioRepo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@Service
public class ServicioUsuarios {
private final UsuarioRepo usuarios;
public ServicioUsuarios(UsuarioRepo usuarios,
@Value("${app.admin.login}") String adminLogin,
@Value("${app.admin.password}") String adminPass) {
this.usuarios = usuarios;
// Asegura admin fijo
usuarios.findByLogin(adminLogin).or(() -> {
Usuario a = new Usuario();
a.setLogin(adminLogin); a.setClaveHash(adminPass);
a.setNombre("Admin"); a.setApellidos("Sistema");
a.setRol(RolUsuario.ADMIN);
usuarios.save(a);
return java.util.Optional.of(a);
});
}
public Usuario autenticar(String login, String clave){
return usuarios.findByLogin(login)
.filter(u -> u.getClaveHash()!=null && u.getClaveHash().equals(clave))
.orElseThrow(() -> new RuntimeException("Credenciales inválidas"));
}
public Usuario registrar(Usuario u){ return usuarios.save(u); }
public Usuario actualizar(Usuario u){ return usuarios.save(u); }
}
\ No newline at end of file
package com.ayto.incidencias.web;
import com.ayto.incidencias.dominio.Usuario;
import com.ayto.incidencias.servicio.ServicioUsuarios;
import jakarta.servlet.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
@Component
public class AuthInterceptor implements HandlerInterceptor {
public static final String ATTR_USER = "AUTH_USER";
private final ServicioUsuarios usuarios;
public AuthInterceptor(ServicioUsuarios usuarios){ this.usuarios = usuarios; }
@Override public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler){
// Permite registrar sin credenciales
if(req.getMethod().equals("POST") && req.getRequestURI().equals("/api/usuarios/registro")) return true;
String login = req.getHeader("X-Login");
String pass = req.getHeader("X-Password");
try {
Usuario u = usuarios.autenticar(login, pass);
req.setAttribute(ATTR_USER, u);
return true;
} catch(Exception e){
res.setStatus(401);
return false;
}
}
}
\ No newline at end of file
package com.ayto.incidencias.web;
import com.ayto.incidencias.dominio.*;
import com.ayto.incidencias.servicio.ServicioIncidencias;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController @RequestMapping("/api/incidencias")
public class IncidenciaController {
private final ServicioIncidencias svc;
public IncidenciaController(ServicioIncidencias svc){ this.svc = svc; }
@PostMapping
public Incidencia crear(HttpServletRequest req,
@RequestParam Long tipoId,
@RequestParam String descripcion,
@RequestParam String calle, @RequestParam String numero,
@RequestParam String ciudad, @RequestParam String provincia, @RequestParam String cp,
@RequestParam(required=false) Double lat, @RequestParam(required=false) Double lon){
var u = (Usuario) req.getAttribute(AuthInterceptor.ATTR_USER);
var dir = new Direccion(); dir.setCalle(calle); dir.setNumero(numero);
dir.setCiudad(ciudad); dir.setProvincia(provincia); dir.setCp(cp);
CoordenadaGPS gps = (lat!=null && lon!=null) ? new CoordenadaGPS() : null;
if(gps!=null){ gps.setLatitud(lat); gps.setLongitud(lon); }
return svc.crearIncidencia(u, tipoId, descripcion, dir, gps);
}
@GetMapping("/mias")
public List<Incidencia> mias(HttpServletRequest req){
var u = (Usuario) req.getAttribute(AuthInterceptor.ATTR_USER);
return svc.listarMisIncidencias(u.getId());
}
@GetMapping
public List<Incidencia> buscar(@RequestParam(required=false) Long tipoId,
@RequestParam(required=false) EstadoIncidencia estado){
return svc.buscar(tipoId, estado);
}
@DeleteMapping("/{id}")
public void borrarPropia(HttpServletRequest req, @PathVariable Long id){
Usuario u = (Usuario) req.getAttribute(AuthInterceptor.ATTR_USER);
svc.borrarSiPendiente(u.getId(), id);
}
@PatchMapping("/{id}/estado")
public Incidencia cambiarEstado(HttpServletRequest req, @PathVariable Long id, @RequestParam EstadoIncidencia estado){
Usuario u = (Usuario) req.getAttribute(AuthInterceptor.ATTR_USER);
if(u.getRol()!=RolUsuario.ADMIN) throw new SecurityException("Sólo admin");
return svc.adminCambiarEstado(id, estado);
}
@DeleteMapping("/{id}/admin")
public void borrarAdmin(HttpServletRequest req, @PathVariable Long id){
Usuario u = (Usuario) req.getAttribute(AuthInterceptor.ATTR_USER);
if(u.getRol()!=RolUsuario.ADMIN) throw new SecurityException("Sólo admin");
svc.adminBorrar(id);
}
}
\ No newline at end of file
package com.ayto.incidencias.web;
import com.ayto.incidencias.dominio.*;
import com.ayto.incidencias.servicio.ServicioTiposIncidencia;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController @RequestMapping("/api/tipos")
public class TipoIncidenciaController {
private final ServicioTiposIncidencia tipos;
public TipoIncidenciaController(ServicioTiposIncidencia tipos){ this.tipos = tipos; }
@GetMapping public List<TipoIncidencia> listar(){ return tipos.listar(); }
@PostMapping
public TipoIncidencia crear(HttpServletRequest req, @RequestParam String nombre, @RequestParam String descripcion){
Usuario u = (Usuario) req.getAttribute(AuthInterceptor.ATTR_USER);
if(u.getRol()!=RolUsuario.ADMIN) throw new SecurityException("Sólo admin");
return tipos.crear(nombre, descripcion);
}
@DeleteMapping("/{id}")
public void eliminar(HttpServletRequest req, @PathVariable Long id){
Usuario u = (Usuario) req.getAttribute(AuthInterceptor.ATTR_USER);
if(u.getRol()!=RolUsuario.ADMIN) throw new SecurityException("Sólo admin");
tipos.eliminar(id);
}
}
\ No newline at end of file
package com.ayto.incidencias.web;
import com.ayto.incidencias.dominio.Usuario;
import com.ayto.incidencias.servicio.ServicioUsuarios;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.*;
@RestController @RequestMapping("/api/usuarios")
public class UsuarioController {
private final ServicioUsuarios svc;
public UsuarioController(ServicioUsuarios svc){ this.svc = svc; }
@PostMapping("/registro")
public Usuario registrar(@RequestBody Usuario u){ return svc.registrar(u); }
@PutMapping("/me")
public Usuario actualizar(HttpServletRequest req, @RequestBody Usuario u){
Usuario actual = (Usuario) req.getAttribute(AuthInterceptor.ATTR_USER);
u.setId(actual.getId()); u.setLogin(actual.getLogin()); u.setRol(actual.getRol());
return svc.actualizar(u);
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment