diff --git a/src/main/java/br/com/acta/controller/UsuarioController.java b/src/main/java/br/com/acta/controller/UsuarioController.java index 78becac..7e83a1b 100644 --- a/src/main/java/br/com/acta/controller/UsuarioController.java +++ b/src/main/java/br/com/acta/controller/UsuarioController.java @@ -64,6 +64,12 @@ public ResponseEntity patch(@PathVariable @Positive Long id, return ResponseEntity.ok(usuario); } + @DeleteMapping("/{id}/foto") + public ResponseEntity excluirFoto(@PathVariable @Positive Long id) { + UsuarioResponseDTO usuario = service.excluirFoto(id); + return ResponseEntity.ok(usuario); + } + @DeleteMapping("/{id}") public ResponseEntity excluir(@PathVariable @Positive Long id) { service.excluir(id); diff --git a/src/main/java/br/com/acta/dto/core/usuario/UsuarioMapper.java b/src/main/java/br/com/acta/dto/core/usuario/UsuarioMapper.java index 104657d..8fdd6f9 100644 --- a/src/main/java/br/com/acta/dto/core/usuario/UsuarioMapper.java +++ b/src/main/java/br/com/acta/dto/core/usuario/UsuarioMapper.java @@ -20,6 +20,8 @@ public interface UsuarioMapper @Mapping(target = "ciclos", ignore = true) @Mapping(target = "metas", ignore = true) @Mapping(target = "empresa", ignore = true) + @Mapping(target = "fotoUrl", ignore = true) + @Mapping(target = "fotoPublicId", ignore = true) @Mapping(target = "id", ignore = true) @Mapping(target = "criadoEm", ignore = true) @Mapping(target = "atualizadoEm", ignore = true) @@ -32,6 +34,8 @@ public interface UsuarioMapper @Mapping(target = "ciclos", ignore = true) @Mapping(target = "metas", ignore = true) @Mapping(target = "empresa", ignore = true) + @Mapping(target = "fotoUrl", ignore = true) + @Mapping(target = "fotoPublicId", ignore = true) @Mapping(target = "id", ignore = true) @Mapping(target = "criadoEm", ignore = true) @Mapping(target = "atualizadoEm", ignore = true) diff --git a/src/main/java/br/com/acta/dto/core/usuario/UsuarioResponseDTO.java b/src/main/java/br/com/acta/dto/core/usuario/UsuarioResponseDTO.java index d5d42ca..b15ff28 100644 --- a/src/main/java/br/com/acta/dto/core/usuario/UsuarioResponseDTO.java +++ b/src/main/java/br/com/acta/dto/core/usuario/UsuarioResponseDTO.java @@ -11,6 +11,8 @@ public record UsuarioResponseDTO( TipoUsuario tipo, Long idEmpresa, StatusGeral status, + String fotoUrl, + String fotoPublicId, OffsetDateTime criadoEm, OffsetDateTime atualizadoEm ) { diff --git a/src/main/java/br/com/acta/entity/core/Usuario.java b/src/main/java/br/com/acta/entity/core/Usuario.java index d6e5457..a7c4a69 100644 --- a/src/main/java/br/com/acta/entity/core/Usuario.java +++ b/src/main/java/br/com/acta/entity/core/Usuario.java @@ -9,11 +9,13 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import org.hibernate.annotations.DynamicInsert; import java.util.Set; @Entity @Table(name = "usuario_sistema", schema = "public") +@DynamicInsert // ignora colunas nulas para aplicar default nas urls do cloudinary @Getter @Setter @NoArgsConstructor @@ -27,6 +29,12 @@ public class Usuario extends AuditoriaBase { @Column(name = "firebase_uid", unique = true, length = 128) private String firebaseUid; + @Column(name = "foto_url") + private String fotoUrl; + + @Column(name = "foto_public_id") + private String fotoPublicId; + @Column(name = "tipo_usuario", nullable = false) @Enumerated(EnumType.STRING) private TipoUsuario tipo; diff --git a/src/main/java/br/com/acta/service/UsuarioService.java b/src/main/java/br/com/acta/service/UsuarioService.java index c9ce79d..dcbf6ee 100644 --- a/src/main/java/br/com/acta/service/UsuarioService.java +++ b/src/main/java/br/com/acta/service/UsuarioService.java @@ -25,14 +25,16 @@ @Service public class UsuarioService extends BaseService { + private static final String FOTO_URL_PADRAO = "https://res.cloudinary.com/kcypohk3/image/upload/default-avatar"; + private static final String FOTO_PUBLIC_ID_PADRAO = "default-avatar"; private final EmpresaService empresaService; private final TarefaRepository tarefaRepo; private final MetaRepository metaRepo; private final UsuarioRepository repo; protected final UsuarioMapper mapper; private final PatchConfig patchConfig = new PatchConfig( - Set.of("nome", "email", "tipo", "idEmpresa"), - Set.of("nome", "email") + Set.of("nome", "email", "tipo", "idEmpresa", "fotoUrl"), + Set.of("nome", "email", "fotoUrl") ); public UsuarioService(EmpresaService empresaService, UsuarioRepository repo, UsuarioMapper mapper, TarefaRepository tarefaRepo, MetaRepository metaRepo, AuthService authService){ @@ -59,11 +61,34 @@ public UsuarioResponseDTO patch(Long id, Map campos) { if (repo.existsByEmailLoginIgnoreCase(email)) throw new UniqueViolationException("E-mail"); usuario.setEmailLogin(email); } + if (campos.containsKey("fotoUrl")) { + String fotoUrl = (String) campos.get("fotoUrl"); + usuario.setFotoUrl(fotoUrl); + usuario.setFotoPublicId( + fotoUrl.substring(fotoUrl.indexOf("/upload/") + 8) + .replaceFirst("^v\\d+/", "") + .replaceFirst("\\.[^.]+$", "") + ); + } repo.save(usuario); return mapper.toResponse(usuario); } + @PreAuthorize("authService.isProprioUsuario(#id) or hasAnyRole('ADMIN', 'GESTOR')") + @Transactional + public UsuarioResponseDTO excluirFoto(Long id) { + configurarUsuarioAtual(); + + Usuario usuario = getEntity(id); + + usuario.setFotoUrl(FOTO_URL_PADRAO); + usuario.setFotoPublicId(FOTO_PUBLIC_ID_PADRAO); + + Usuario salvo = repo.save(usuario); + return mapper.toResponse(salvo); + } + @PreAuthorize("hasAnyRole('ADMIN', 'GESTOR') and authService.isUsuarioEmpresa(#idEmpresa)") public List buscar(Long idEmpresa, TipoUsuario tipo) { List usuarios; diff --git a/src/main/resources/sql/script.sql b/src/main/resources/sql/script.sql index 3a4263f..0c7c7aa 100644 --- a/src/main/resources/sql/script.sql +++ b/src/main/resources/sql/script.sql @@ -29,6 +29,8 @@ CREATE TABLE IF NOT EXISTS usuario_sistema ( status VARCHAR(40) NOT NULL CHECK (status IN ('ATIVO', 'INATIVO', 'PENDENTE', 'BLOQUEADO', 'ARQUIVADO')), criado_em TIMESTAMPTZ NOT NULL DEFAULT NOW(), atualizado_em TIMESTAMPTZ, + foto_url TEXT DEFAULT 'https://res.cloudinary.com/kcypohk3/image/upload/default-avatar', + foto_public_id TEXT DEFAULT 'default-avatar', CONSTRAINT ck_firebase_uid CHECK (status <> 'ATIVO' or firebase_uid IS NOT NULL) ); @@ -47,7 +49,7 @@ CREATE TABLE IF NOT EXISTS convite_usuario ( CONSTRAINT ck_data_expiracao CHECK (expira_em > criado_em), CONSTRAINT ck_convite_uso CHECK ((status = 'USADO' AND usado_em IS NOT NULL) OR (status <> 'USADO' AND usado_em IS NULL)) ); - + CREATE TABLE IF NOT EXISTS colaborador ( id BIGSERIAL PRIMARY KEY, id_empresa BIGINT NOT NULL REFERENCES empresa(id) ON DELETE CASCADE,