feat: add email change

This commit is contained in:
Sergey Krylov 2026-02-17 05:24:53 +03:00
parent be986d1467
commit 9b040ba885
31 changed files with 789 additions and 254 deletions

View File

@ -0,0 +1,26 @@
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html lang="en">
<head>
<title>Charm Profiles</title>
<%@ include file="style.html" %>
</head>
<body>
<%@ include file="header.jsp" %>
<div>
<h3 style="color: red">${requestScope.wordBundle.getWord("email-warning")}</h3>
<form method="post" action="/email">
<input type="hidden" name="_method" value="put"/>
<input type="hidden" name="id" value="${requestScope.profile.id}">
<table>
<tr>
<td><h3>${requestScope.wordBundle.getWord("email")}</h3></td>
<td><input type="email" name="email" value="${requestScope.profile.email}"></td>
</tr>
</table>
<button type="submit">${requestScope.wordBundle.getWord("save")}</button>
</form>
</div>
<%@ include file="footer.jsp" %>
</body>
</html>

View File

@ -0,0 +1,15 @@
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html lang="en">
<head>
<title>Charm Bad Request</title>
<%@ include file="style.html" %>
</head>
<body>
<%@ include file="header.jsp" %>
<div>
<h3>400 - ${requestScope.wordBundle.getWord("page-bad-request")}</h3>
</div>
<%@ include file="footer.jsp" %>
</body>
</html>

View File

@ -0,0 +1,15 @@
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html lang="en">
<head>
<title>Charm Not Found</title>
<%@ include file="style.html" %>
</head>
<body>
<%@ include file="header.jsp" %>
<div>
<h3>404 - ${requestScope.wordBundle.getWord("page-not-found")}</h3>
</div>
<%@ include file="footer.jsp" %>
</body>
</html>

View File

@ -1,5 +1,5 @@
<!DOCTYPE html>
<%@ page contentType="text/html;charset=UTF-8" %>
<div>
<hr>
<h3>All rights reserved 2024</h3>
<small>${requestScope.wordBundle.getWord("all-rights-reserved")} 2024</small>
</div>

View File

@ -1,14 +1,9 @@
<%@ page contentType="text/html;charset=UTF-8" %>
<div>
<h3>Charm <3</h3>
<form method="post" action="/lang">
<button type="submit" name="lang" value="ru">ru</button>
<button type="submit" name="lang" value="en">en</button>
</form>
<hr>
<form action="/lang" method="post">
<button name="lang" value="ru">Русский</button>
<button name="lang" value="en">English</button>
</form>
<h3>My current language: ${cookie["lang"].value}</h3>
<hr>
</div>

View File

@ -1,66 +1,59 @@
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html lang="en">
<head>
<title>Charm Profile</title>
</head>
<body>
<%@ include file="header.jsp" %>
<div>
<form method="post" action="/profile">
<c:if test="${requestScope.profile.id != null}">
<input type="hidden" name="_method" value="put"/>
</c:if>
<input type="hidden" name="id" value="${requestScope.profile.id}">
<table>
<tr>
<td><h3>${requestScope.wordBundle.getWord("email")}</h3></td>
<td><input type="email" name="email" value="${requestScope.profile.email}"></td>
</tr>
<tr>
<td><h3>${requestScope.wordBundle.getWord("name")}</h3></td>
<td><input type="text" name="name" value="${requestScope.profile.name}"></td>
</tr>
<tr>
<td><h3>${requestScope.wordBundle.getWord("surname")}</h3></td>
<td><input type="text" name="surname" value="${requestScope.profile.surname}"></td>
</tr>
<tr>
<td><h3>${requestScope.wordBundle.getWord("birth-date")}</h3></td>
<td><input type="date" name="birthDate" value="${requestScope.profile.birthDate}"></td>
</tr>
<tr>
<td><h3>${requestScope.wordBundle.getWord("age")}</h3></td>
<td><h3>${requestScope.profile.age}</h3></td>
</tr>
<tr>
<td><h3>${requestScope.wordBundle.getWord("about")}</h3></td>
<td><input type="text" name="about" value="${requestScope.profile.about}"></td>
</tr>
<tr>
<td><h3>${requestScope.wordBundle.getWord("gender")}</h3></td>
<td>
<select name="gender">
<option value="${requestScope.profile.gender}" selected hidden>
${requestScope.profile.gender}
</option>
<c:forEach var="gender" items="${applicationScope.genders}">
<option value="${gender}">${requestScope.wordBundle.getWord(gender)}</option>
</c:forEach>
</select>
</td>
</tr>
</table>
<button type="submit">${requestScope.wordBundle.getWord("save")}</button>
</form>
<c:if test="${requestScope.profile.id != null}">
<form method="post" action="/profile">
<input type="hidden" name="_method" value="delete"/>
<input type="hidden" name="id" value="${requestScope.profile.id}">
<button type="submit">${requestScope.wordBundle.getWord("delete")}</button>
</form>
</c:if>
</div>
<%@ include file="footer.jsp" %>
</body>
<head>
<title>Charm Profile</title>
<%@ include file="style.html" %>
</head>
<body>
<%@ include file="header.jsp" %>
<div>
<form method="post" action="/profile">
<input type="hidden" name="_method" value="put"/>
<input type="hidden" name="id" value="${requestScope.profile.id}">
<table>
<tr>
<td><h3>${requestScope.wordBundle.getWord("email")}</h3></td>
<td><a href="/email?id=${requestScope.profile.id}">${requestScope.profile.email}</a></td>
</tr>
<tr>
<td><h3>${requestScope.wordBundle.getWord("name")}</h3></td>
<td><input type="text" name="name" value="${requestScope.profile.name}"></td>
</tr>
<tr>
<td><h3>${requestScope.wordBundle.getWord("surname")}</h3></td>
<td><input type="text" name="surname" value="${requestScope.profile.surname}"></td>
</tr>
<tr>
<td><h3>${requestScope.wordBundle.getWord("birth-date")}</h3></td>
<td><input type="date" name="birthDate" value="${requestScope.profile.birthDate}"></td>
</tr>
<tr>
<td><h3>${requestScope.wordBundle.getWord("about")}</h3></td>
<td><input type="text" name="about" value="${requestScope.profile.about}"></td>
</tr>
<tr>
<td><h3>${requestScope.wordBundle.getWord("gender")}</h3></td>
<td>
<select name="gender">
<option value="${requestScope.profile.gender}" selected hidden>
${requestScope.wordBundle.getWord(requestScope.profile.gender)}
</option>
<c:forEach var="gender" items="${applicationScope.genders}">
<option value="${gender}">${requestScope.wordBundle.getWord(gender)}</option>
</c:forEach>
</select>
</td>
</tr>
</table>
<button type="submit">${requestScope.wordBundle.getWord("save")}</button>
</form>
<form method="post" action="/registration">
<input type="hidden" name="_method" value="delete"/>
<input type="hidden" name="id" value="${requestScope.profile.id}">
<button type="submit">${requestScope.wordBundle.getWord("delete")}</button>
</form>
</div>
<%@ include file="footer.jsp" %>
</body>
</html>

View File

@ -3,12 +3,7 @@
<html lang="en">
<head>
<title>Charm Profiles</title>
<style>
table, td {
border: 1px solid;
border-collapse: collapse;
}
</style>
<%@ include file="style.html" %>
</head>
<body>
<%@ include file="header.jsp" %>
@ -20,14 +15,30 @@
<td><h3>${requestScope.wordBundle.getWord("name")}</h3></td>
<td><h3>${requestScope.wordBundle.getWord("surname")}</h3></td>
<td><h3>${requestScope.wordBundle.getWord("age")}</h3></td>
<td><h3>${requestScope.wordBundle.getWord("status")}</h3></td>
</tr>
<c:forEach var="profile" items="${requestScope.profiles}">
<tr>
<td><h3>${profile.id}</h3></td>
<td><h3>${profile.email}</h3></td>
<td><h3>${profile.name}</h3></td>
<td><h3>${profile.surname}</h3></td>
<td><h3>${profile.age}</h3></td>
<td><h4>${profile.id}</h4></td>
<td><h4>${profile.email}</h4></td>
<td><h4>${profile.name}</h4></td>
<td><h4>${profile.surname}</h4></td>
<td><h4>${profile.age}</h4></td>
<td>
<form action="/profile" method="post">
<input type="hidden" name="_method" value="put"/>
<input type="hidden" name="id" value="${profile.id}">
<select name="status">
<option value="${profile.status}" selected hidden>
${requestScope.wordBundle.getWord(profile.status)}
</option>
<c:forEach var="status" items="${applicationScope.statuses}">
<option value="${status}">${requestScope.wordBundle.getWord(status)}</option>
</c:forEach>
</select>
<button type="submit">${requestScope.wordBundle.getWord("save")}</button>
</form>
</td>
</tr>
</c:forEach>
</table>

View File

@ -0,0 +1,26 @@
<%@ page contentType="text/html;charset=UTF-8" %>
<html lang="en">
<head>
<title>Charm Registration</title>
<%@ include file="style.html" %>
</head>
<body>
<%@ include file="header.jsp" %>
<div>
<form method="post" action="/registration">
<table>
<tr>
<td><h3>${requestScope.wordBundle.getWord("email")}</h3></td>
<td><input type="email" name="email" placeholder="user@charm.ru"></td>
</tr>
<tr>
<td><h3>${requestScope.wordBundle.getWord("password")}</h3></td>
<td><input type="password" name="password"></td>
</tr>
</table>
<button type="submit">${requestScope.wordBundle.getWord("save")}</button>
</form>
</div>
<%@ include file="footer.jsp" %>
</body>
</html>

View File

@ -0,0 +1,57 @@
<!DOCTYPE html>
<html lang="en">
<style>
.saveImage {
width: 75px;
}
input {
width: 500px;
font-size: 15px;
font-family: "Roboto", sans-serif;
}
select {
width: 500px;
font-size: 15px;
font-family: "Roboto", sans-serif;
}
img {
max-width: 500px;
max-height: 500px;
}
textarea {
resize: none;
width: 500px;
font-size: 15px;
font-family: "Roboto", sans-serif;
}
div {
text-align: center;
font-family: "Roboto", sans-serif;
}
table {
margin-left: auto;
margin-right: auto;
border-collapse: collapse;
}
td {
text-align: left;
padding: 10px;
}
tr {
text-align: center;
padding: 10px;
border: 1px solid;
border-top: none;
border-left: none;
border-right: none;
}
</style>
</html>

View File

@ -20,4 +20,13 @@
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
version="6.0">
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/jsp/error404.jsp</location>
</error-page>
<error-page>
<error-code>400</error-code>
<location>/WEB-INF/jsp/error400.jsp</location>
</error-page>
</web-app>

View File

@ -1,13 +1,21 @@
about=About
active=Active
age=Age
all-rights-reserved=All rights reserved
birth-date=Birth date
confirm-password=Confirm password
delete=Delete
email=Email
email-warning=Changing your mail may have negative effects
female=Female
gender=Gender
inactive=Inactive
male=Male
name=Name
other=Other
page-bad-request=Bad request
page-not-found=Page not found
password=Password
save=Save
status=Status
surname=Surname

View File

@ -1,13 +1,21 @@
about=About
active=Active
age=Age
all-rights-reserved=All rights reserved
birth-date=Birth date
confirm-password=Confirm password
delete=Delete
email=Email
email-warning=Changing your mail may have negative effects
female=Female
gender=Gender
inactive=Inactive
male=Male
name=Name
other=Other
page-bad-request=Bad request
page-not-found=Page not found
password=Password
save=Save
status=Status
surname=Surname

View File

@ -1,13 +1,21 @@
about=\u041E \u0441\u0435\u0431\u0435
active=\u0410\u043A\u0442\u0438\u0432\u043D\u044B\u0439
age=\u0412\u043E\u0437\u0440\u0430\u0441\u0442
all-rights-reserved=\u0412\u0441\u0435 \u043F\u0440\u0430\u0432\u0430 \u0437\u0430\u0449\u0438\u0449\u0435\u043D\u044B
birth-date=\u0414\u0430\u0442\u0430 \u0440\u043E\u0436\u0434\u0435\u043D\u0438\u044F
confirm-password=\u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u0435 \u043F\u0430\u0440\u043E\u043B\u044C
delete=\u0423\u0434\u0430\u043B\u0438\u0442\u044C
email=\u041F\u043E\u0447\u0442\u0430
email-warning=\u0421\u043C\u0435\u043D\u0430 \u043F\u043E\u0447\u0442\u044B \u043C\u043E\u0436\u0435\u0442 \u043F\u0440\u0438\u0432\u0435\u0441\u0442\u0438 \u043A \u043D\u0435\u0436\u0435\u043B\u0430\u0442\u0435\u043B\u044C\u043D\u044B\u043C \u043F\u043E\u0441\u043B\u0435\u0434\u0441\u0442\u0432\u0438\u044F\u043C
female=\u0416\u0435\u043D\u0449\u0438\u043D\u0430
gender=\u041F\u043E\u043B
inactive=\u041D\u0435\u0430\u043A\u0442\u0438\u0432\u043D\u044B\u0439
male=\u041C\u0443\u0436\u0447\u0438\u043D\u0430
name=\u0418\u043C\u044F
other=\u0414\u0440\u0443\u0433\u043E\u0435
page-bad-request=\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 \u0437\u0430\u043F\u0440\u043E\u0441
page-not-found=\u0421\u0442\u0440\u0430\u043D\u0438\u0446\u0430 \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u0430
password=\u041F\u0430\u0440\u043E\u043B\u044C
save=\u0421\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C
status=\u0421\u0442\u0430\u0442\u0443\u0441
surname=\u0424\u0430\u043C\u0438\u043B\u0438\u044F

View File

@ -0,0 +1,52 @@
package ru.charm.back.controller;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import ru.charm.back.dto.ProfileGetDto;
import ru.charm.back.dto.ProfileUpdateDto;
import ru.charm.back.mapper.RequestToProfileUpdateDtoMapper;
import ru.charm.back.model.Gender;
import ru.charm.back.model.Profile;
import ru.charm.back.model.exception.DuplicateEmailException;
import ru.charm.back.service.ProfileService;
import java.io.IOException;
import java.time.LocalDate;
import java.util.Optional;
import static jakarta.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
@WebServlet("/email")
public class EmailController extends HttpServlet {
private final ProfileService service = ProfileService.getInstance();
private final RequestToProfileUpdateDtoMapper requestToProfileUpdateDtoMapper = RequestToProfileUpdateDtoMapper.getInstance();
@Override
protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String sId = req.getParameter("id");
String forwardUri = "/notFound";
if (sId != null) {
Optional<ProfileGetDto> optProfileGetDto = service.findById(Long.parseLong(sId));
if (optProfileGetDto.isPresent()) {
req.setAttribute("profile", optProfileGetDto.get());
forwardUri = "/WEB-INF/jsp/email.jsp";
}
}
req.getRequestDispatcher(forwardUri).forward(req, resp);
}
@Override
protected void doPut (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ProfileUpdateDto dto = requestToProfileUpdateDtoMapper.map(req, new ProfileUpdateDto());
try {
service.update(dto);
resp.sendRedirect(String.format("/profile?id=%s", dto.getId()));
} catch (DuplicateEmailException e) {
resp.sendError(SC_BAD_REQUEST);
}
}
}

View File

@ -6,73 +6,47 @@ import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import ru.charm.back.dto.ProfileGetDto;
import ru.charm.back.model.Gender;
import ru.charm.back.model.Profile;
import ru.charm.back.dto.ProfileUpdateDto;
import ru.charm.back.mapper.RequestToProfileUpdateDtoMapper;
import ru.charm.back.service.ProfileService;
import java.io.IOException;
import java.time.LocalDate;
import java.util.Optional;
import static jakarta.servlet.http.HttpServletResponse.SC_NOT_FOUND;
@WebServlet("/profile")
public class ProfileController extends HttpServlet {
private final ProfileService service = ProfileService.getInstance();
private final RequestToProfileUpdateDtoMapper requestToProfileUpdateDtoMapper = RequestToProfileUpdateDtoMapper.getInstance();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String sId = req.getParameter("id");
String forwardUri = "/notFound";
String forwardUri = null;
if (sId != null) {
Optional<ProfileGetDto> optProfileGetDto = service.findById(Long.parseLong(sId));
if (optProfileGetDto.isPresent()) {
req.setAttribute("profile", optProfileGetDto.get());
Optional<ProfileGetDto> optProfileDto = service.findById(Long.parseLong(sId));
if (optProfileDto.isPresent()) {
req.setAttribute("profile", optProfileDto.get());
forwardUri = "/WEB-INF/jsp/profile.jsp";
}
} else {
req.setAttribute("profiles", service.findAll());
forwardUri = "/WEB-INF/jsp/profiles.jsp";
}
req.getRequestDispatcher(forwardUri).forward(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Profile profile = getProfile(req);
service.save(profile);
resp.sendRedirect(String.format("/profile?id=%s", profile.getId()));
}
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Profile profile = getProfile(req);
service.update(profile);
resp.sendRedirect(String.format("/profile?id=%s", profile.getId()));
}
@Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String sId = req.getParameter("id");
if (!sId.isBlank()) {
service.delete(Long.parseLong(sId));
if (forwardUri == null) {
resp.sendError(SC_NOT_FOUND);
} else {
req.getRequestDispatcher(forwardUri).forward(req, resp);
}
resp.sendRedirect("/registration");
}
private Profile getProfile(HttpServletRequest req) {
Profile profile = new Profile();
String sId = req.getParameter("id");
if (!sId.isBlank()) {
profile.setId(Long.parseLong(sId));
}
profile.setEmail(req.getParameter("email"));
profile.setName(req.getParameter("name"));
profile.setSurname(req.getParameter("surname"));
profile.setAbout(req.getParameter("about"));
profile.setGender(Gender.valueOf(req.getParameter("gender")));
profile.setBirthday(LocalDate.parse(req.getParameter("birthDate")));
return profile;
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {
ProfileUpdateDto dto = requestToProfileUpdateDtoMapper.map(req);
service.update(dto);
String referer = req.getHeader("referer");
resp.sendRedirect(referer);
}
}
}

View File

@ -2,41 +2,49 @@ package ru.charm.back.dao;
import ru.charm.back.model.Gender;
import ru.charm.back.model.Profile;
import ru.charm.back.model.Status;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
public class ProfileDao {
private final ConcurrentHashMap<Long, Profile> storage;
private final AtomicLong idStorage;
private static final ProfileDao INSTANCE = new ProfileDao();
private final AtomicLong idStorage;
private final ConcurrentHashMap<Long, Profile> storage;
private ProfileDao() {
this.storage = new ConcurrentHashMap<>();
Profile profile = new Profile();
profile.setId(1L);
profile.setEmail("ivanov@mail.ru");
profile.setPassword("123");
profile.setName("Ivan");
profile.setSurname("Ivanov");
profile.setBirthDate(LocalDate.parse("2001-12-03"));
profile.setAbout("I am QA");
profile.setGender(Gender.MALE);
profile.setBirthday(LocalDate.parse("2000-01-01"));
profile.setStatus(Status.ACTIVE);
this.storage.put(1L, profile);
Profile profile1 = new Profile();
profile1.setId(2L);
profile1.setEmail("sidorova@mail.ru");
profile1.setPassword("456");
profile1.setName("Elena");
profile1.setSurname("Sidorova");
profile1.setBirthDate(LocalDate.parse("1999-09-01"));
profile1.setAbout("I am Java Dev");
profile1.setBirthday(LocalDate.parse("2000-01-01"));
profile1.setGender(Gender.FEMALE);
profile1.setStatus(Status.INACTIVE);
this.storage.put(2L, profile1);
this.idStorage = new AtomicLong(2L);
this.idStorage = new AtomicLong(3L);
}
public static ProfileDao getInstance() {
@ -44,30 +52,32 @@ public class ProfileDao {
}
public Profile save(Profile profile) {
long id = idStorage.incrementAndGet();
profile.setId(id);
storage.put(id, profile);
profile.setId(idStorage.getAndIncrement());
storage.put(profile.getId(), profile);
return profile;
}
public Optional<Profile> findById(Long id) {
if (id == null) return Optional.empty();
return Optional.ofNullable(storage.get(id));
}
public void update(Profile profile) {
Long id = profile.getId();
if (id == null) {
return;
}
storage.put(id, profile);
}
public List<Profile> findAll() {
return new ArrayList<>(storage.values());
}
public Boolean delete(Long id) {
public void update(Profile profile) {
Long id = profile.getId();
if (id == null) return;
storage.put(id, profile);
}
public boolean delete(Long id) {
if (id == null) return false;
return storage.remove(id) != null;
}
public Set<String> getAllEmails() {
return storage.values().stream().map(Profile::getEmail).collect(Collectors.toSet());
}
}

View File

@ -1,34 +1,20 @@
package ru.charm.back.dto;
import ru.charm.back.model.Gender;
import ru.charm.back.model.Status;
import java.time.LocalDate;
public class ProfileGetDto {
public class ProfileGetDto {
private Long id;
private String email;
private String name;
private String surname;
private LocalDate birthDate;
private Integer age;
private String about;
private Gender gender;
private LocalDate birthDate;
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public LocalDate getBirthDate() {
return birthDate;
}
public void setBirthDate(LocalDate birthDate) {
this.birthDate = birthDate;
}
private Status status;
public Long getId() {
return id;
@ -62,6 +48,22 @@ public class ProfileGetDto {
this.surname = surname;
}
public LocalDate getBirthDate() {
return birthDate;
}
public void setBirthDate(LocalDate birthDate) {
this.birthDate = birthDate;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getAbout() {
return about;
}
@ -78,14 +80,11 @@ public class ProfileGetDto {
this.gender = gender;
}
@Override
public String toString() {
return "Profile{" +
"id=" + id +
", email='" + email + '\'' +
", name='" + name + '\'' +
", surname='" + surname + '\'' +
", about='" + about + '\'' +
'}';
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
}

View File

@ -0,0 +1,91 @@
package ru.charm.back.dto;
import ru.charm.back.model.Gender;
import ru.charm.back.model.Status;
import java.time.LocalDate;
public class ProfileUpdateDto {
private Long id;
private String email;
private String password;
private String name;
private String surname;
private LocalDate birthDate;
private String about;
private Gender gender;
private Status status;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public LocalDate getBirthDate() {
return birthDate;
}
public void setBirthDate(LocalDate birthDate) {
this.birthDate = birthDate;
}
public String getAbout() {
return about;
}
public void setAbout(String about) {
this.about = about;
}
public Gender getGender() {
return gender;
}
public void setGender(Gender gender) {
this.gender = gender;
}
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
}

View File

@ -0,0 +1,22 @@
package ru.charm.back.dto;
public class RegistrationDto {
private String email;
private String password;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}

View File

@ -3,4 +3,5 @@ package ru.charm.back.mapper;
public interface Mapper<From, To> {
To map(From from);
To map(From from, To to);
}

View File

@ -1,32 +0,0 @@
package ru.charm.back.mapper;
import ru.charm.back.dto.ProfileGetDto;
import ru.charm.back.model.Profile;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
public class ProfileGetDtoMapper implements Mapper<Profile, ProfileGetDto> {
private static final ProfileGetDtoMapper INSTANCE = new ProfileGetDtoMapper();
public static ProfileGetDtoMapper getInstance() {
return INSTANCE;
}
@Override
public ProfileGetDto map(Profile profile) {
ProfileGetDto dto = new ProfileGetDto();
dto.setId(profile.getId());
dto.setEmail(profile.getEmail());
dto.setName(profile.getName());
dto.setSurname(profile.getSurname());
dto.setBirthDate(profile.getBirthday());
dto.setAge(Math.toIntExact(ChronoUnit.YEARS.between(profile.getBirthday(), LocalDate.now())));
dto.setGender(profile.getGender());
dto.setAbout(profile.getAbout());
return dto;
}
}

View File

@ -0,0 +1,41 @@
package ru.charm.back.mapper;
import ru.charm.back.dto.ProfileGetDto;
import ru.charm.back.model.Profile;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
public class ProfileToProfileGetDtoMapper implements Mapper<Profile, ProfileGetDto> {
private static final ProfileToProfileGetDtoMapper INSTANCE = new ProfileToProfileGetDtoMapper();
private ProfileToProfileGetDtoMapper() {
}
public static ProfileToProfileGetDtoMapper getInstance() {
return INSTANCE;
}
@Override
public ProfileGetDto map(Profile profile) {
return map(profile, new ProfileGetDto());
}
@Override
public ProfileGetDto map(Profile profile, ProfileGetDto dto) {
dto.setId(profile.getId());
dto.setEmail(profile.getEmail());
dto.setName(profile.getName());
dto.setSurname(profile.getSurname());
dto.setBirthDate(profile.getBirthDate());
if (profile.getBirthDate() != null) {
dto.setAge(Math.toIntExact(ChronoUnit.YEARS.between(profile.getBirthDate(), LocalDate.now())));
}
dto.setAbout(profile.getAbout());
dto.setGender(profile.getGender());
dto.setStatus(profile.getStatus());
return dto;
}
}

View File

@ -0,0 +1,49 @@
package ru.charm.back.mapper;
import ru.charm.back.dto.ProfileUpdateDto;
import ru.charm.back.model.Profile;
public class ProfileUpdateDtoToProfileMapper implements Mapper<ProfileUpdateDto, Profile> {
private static final ProfileUpdateDtoToProfileMapper INSTANCE = new ProfileUpdateDtoToProfileMapper();
private ProfileUpdateDtoToProfileMapper() {
}
public static ProfileUpdateDtoToProfileMapper getInstance() {
return INSTANCE;
}
@Override
public Profile map(ProfileUpdateDto dto) {
return map(dto, new Profile());
}
@Override
public Profile map(ProfileUpdateDto dto, Profile profile) {
profile.setId(dto.getId());
if (dto.getEmail() != null) {
profile.setEmail(dto.getEmail());
}
if (dto.getName() != null) {
profile.setName(dto.getName());
}
if (dto.getSurname() != null) {
profile.setSurname(dto.getSurname());
}
if (dto.getBirthDate() != null) {
profile.setBirthDate(dto.getBirthDate());
}
if (dto.getAbout() != null) {
profile.setAbout(dto.getAbout());
}
if (dto.getGender() != null) {
profile.setGender(dto.getGender());
}
if (dto.getStatus() != null) {
profile.setStatus(dto.getStatus());
}
return profile;
}
}

View File

@ -0,0 +1,30 @@
package ru.charm.back.mapper;
import ru.charm.back.dto.RegistrationDto;
import ru.charm.back.model.Profile;
import ru.charm.back.model.Status;
public class RegistrationDtoToProfileMapper implements Mapper<RegistrationDto, Profile> {
private static final RegistrationDtoToProfileMapper INSTANCE = new RegistrationDtoToProfileMapper();
private RegistrationDtoToProfileMapper() {
}
public static RegistrationDtoToProfileMapper getInstance() {
return INSTANCE;
}
@Override
public Profile map(RegistrationDto dto) {
return map(dto, new Profile());
}
@Override
public Profile map(RegistrationDto dto, Profile profile) {
profile.setEmail(dto.getEmail());
profile.setPassword(dto.getPassword());
profile.setStatus(Status.INACTIVE);
return profile;
}
}

View File

@ -0,0 +1,55 @@
package ru.charm.back.mapper;
import jakarta.servlet.http.HttpServletRequest;
import ru.charm.back.dto.ProfileUpdateDto;
import ru.charm.back.model.Gender;
import ru.charm.back.model.Status;
import java.time.LocalDate;
import static ru.charm.back.utils.StringUtils.isBlank;
public class RequestToProfileUpdateDtoMapper implements Mapper<HttpServletRequest, ProfileUpdateDto> {
private static final RequestToProfileUpdateDtoMapper INSTANCE = new RequestToProfileUpdateDtoMapper();
private RequestToProfileUpdateDtoMapper() {
}
public static RequestToProfileUpdateDtoMapper getInstance() {
return INSTANCE;
}
@Override
public ProfileUpdateDto map(HttpServletRequest req) {
return map(req, new ProfileUpdateDto());
}
@Override
public ProfileUpdateDto map(HttpServletRequest req, ProfileUpdateDto dto) {
String id = req.getParameter("id");
if (!isBlank(id)) {
dto.setId(Long.parseLong(id));
}
String email = req.getParameter("email");
if (!isBlank(email)) {
dto.setEmail(email);
}
dto.setName(req.getParameter("name"));
dto.setSurname(req.getParameter("surname"));
String birthDate = req.getParameter("birthDate");
if (!isBlank(birthDate)) {
dto.setBirthDate(LocalDate.parse(birthDate));
}
dto.setAbout(req.getParameter("about"));
String gender = req.getParameter("gender");
if (!isBlank(gender)) {
dto.setGender(Gender.valueOf(gender));
}
String status = req.getParameter("status");
if (!isBlank(status)) {
dto.setStatus(Status.valueOf(status));
}
return dto;
}
}

View File

@ -0,0 +1,28 @@
package ru.charm.back.mapper;
import jakarta.servlet.http.HttpServletRequest;
import ru.charm.back.dto.RegistrationDto;
public class RequestToRegistrationDtoMapper implements Mapper<HttpServletRequest, RegistrationDto> {
private static final RequestToRegistrationDtoMapper INSTANCE = new RequestToRegistrationDtoMapper();
private RequestToRegistrationDtoMapper() {
}
public static RequestToRegistrationDtoMapper getInstance() {
return INSTANCE;
}
@Override
public RegistrationDto map(HttpServletRequest req) {
return map(req, new RegistrationDto());
}
@Override
public RegistrationDto map(HttpServletRequest req, RegistrationDto dto) {
dto.setEmail(req.getParameter("email"));
dto.setPassword(req.getParameter("password"));
return dto;
}
}

View File

@ -5,19 +5,13 @@ import java.time.LocalDate;
public class Profile {
private Long id;
private String email;
private String password;
private String name;
private String surname;
private LocalDate birthDate;
private String about;
private Gender gender;
private LocalDate birthday;
public LocalDate getBirthday() {
return birthday;
}
public void setBirthday(LocalDate birthday) {
this.birthday = birthday;
}
private Status status;
public Long getId() {
return id;
@ -35,6 +29,14 @@ public class Profile {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() {
return name;
}
@ -51,6 +53,14 @@ public class Profile {
this.surname = surname;
}
public LocalDate getBirthDate() {
return birthDate;
}
public void setBirthDate(LocalDate birthDate) {
this.birthDate = birthDate;
}
public String getAbout() {
return about;
}
@ -67,14 +77,11 @@ public class Profile {
this.gender = gender;
}
@Override
public String toString() {
return "Profile{" +
"id=" + id +
", email='" + email + '\'' +
", name='" + name + '\'' +
", surname='" + surname + '\'' +
", about='" + about + '\'' +
'}';
public Status getStatus() {
return status;
}
}
public void setStatus(Status status) {
this.status = status;
}
}

View File

@ -0,0 +1,5 @@
package ru.charm.back.model;
public enum Status {
ACTIVE, INACTIVE
}

View File

@ -0,0 +1,4 @@
package ru.charm.back.model.exception;
public class DuplicateEmailException extends RuntimeException {
}

View File

@ -2,47 +2,68 @@ package ru.charm.back.service;
import ru.charm.back.dao.ProfileDao;
import ru.charm.back.dto.ProfileGetDto;
import ru.charm.back.mapper.ProfileGetDtoMapper;
import ru.charm.back.model.Profile;
import ru.charm.back.dto.ProfileUpdateDto;
import ru.charm.back.dto.RegistrationDto;
import ru.charm.back.mapper.ProfileToProfileGetDtoMapper;
import ru.charm.back.mapper.ProfileUpdateDtoToProfileMapper;
import ru.charm.back.mapper.RegistrationDtoToProfileMapper;
import ru.charm.back.model.exception.DuplicateEmailException;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
public class ProfileService {
private static final ProfileService INSTANCE = new ProfileService();
private final ProfileDao dao = ProfileDao.getInstance();
private final ProfileGetDtoMapper profileGetDtoMapper = ProfileGetDtoMapper.getInstance();
private final ProfileToProfileGetDtoMapper profileToProfileGetDtoMapper = ProfileToProfileGetDtoMapper.getInstance();
public static ProfileService getInstance() {
return INSTANCE;
private final ProfileUpdateDtoToProfileMapper profileUpdateDtoToProfileMapper = ProfileUpdateDtoToProfileMapper.getInstance();
private final RegistrationDtoToProfileMapper registrationDtoToProfileMapper = RegistrationDtoToProfileMapper.getInstance();
private ProfileService() {
}
public Profile save(Profile profile) {
return dao.save(profile);
public static ProfileService getInstance() {
return INSTANCE;
}
public Long save(RegistrationDto dto) {
return dao.save(registrationDtoToProfileMapper.map(dto)).getId();
}
public Optional<ProfileGetDto> findById(Long id) {
if (id == null) {
return Optional.empty();
}
return dao.findById(id).map(profileGetDtoMapper::map);
}
public Boolean delete(Long id) {
if (id == null) {
return false;
}
return dao.delete(id);
}
public void update(Profile profile) {
dao.update(profile);
return dao.findById(id).map(profileToProfileGetDtoMapper::map);
}
public List<ProfileGetDto> findAll() {
return dao.findAll().stream().map(profileGetDtoMapper::map).toList();
return dao.findAll().stream().map(profileToProfileGetDtoMapper::map).toList();
}
}
public void update(ProfileUpdateDto dto) {
dao.findById(dto.getId())
.ifPresent(profile -> {
checkEmail(profile.getEmail(), dto.getEmail());
dao.update(profileUpdateDtoToProfileMapper.map(dto, profile));
}
);
}
private void checkEmail(String oldEmail, String newEmail) {
if (newEmail == null) return;
Set<String> emails = dao.getAllEmails();
if (!Objects.equals(oldEmail, newEmail) &&
emails.contains(newEmail)) {
throw new DuplicateEmailException();
}
}
public boolean delete(Long id) {
return dao.delete(id);
}
}

View File

@ -0,0 +1,7 @@
package ru.charm.back.utils;
public class StringUtils {
public static boolean isBlank(String str) {
return str == null || str.isBlank();
}
}