17 Commits

Author SHA1 Message Date
m-shimizu
7362f58c4f Merge branch 'h-hein2' of https://gitea.it-college.co.jp/2026_0910_AI_5G_Java_MUN/last-group-delevelopment-team2 into m_shimizu 2026-10-21 10:02:58 +09:00
8a0b53522b Employee Controller 2026-10-21 10:02:14 +09:00
b298aa01c3 書き出し中一覧に表示 2026-10-21 09:24:53 +09:00
e5f3a3774a お願いします 2026-10-20 16:38:00 +09:00
3d5653418a 05番完了 2026-10-20 16:04:18 +09:00
b6d24a3d05 Merge branch 'h-hein2' of https://gitea.it-college.co.jp/2026_0910_AI_5G_Java_MUN/last-group-delevelopment-team2 into t-eguchi 2026-10-20 15:22:43 +09:00
6faaedaa95 コンフリクト解消 2026-10-20 15:22:30 +09:00
b6ada277a2 途中 2026-10-20 14:59:26 +09:00
b58a996e84 Merge branch 'h-hein2' of https://gitea.it-college.co.jp/2026_0910_AI_5G_Java_MUN/last-group-delevelopment-team2 into t-eguchi 2026-10-20 14:24:38 +09:00
0b89e45386 途中 2026-10-20 14:24:20 +09:00
694cb0f621 Merge branch 'h-hein2' of https://gitea.it-college.co.jp/2026_0910_AI_5G_Java_MUN/last-group-delevelopment-team2 into t-eguchi 2026-10-20 13:45:53 +09:00
ff1c43d80b 途中 2026-10-20 13:45:30 +09:00
528eadec5a コンフリクト解消しました 2026-10-20 11:19:42 +09:00
13f2fdedea 途中経過 2026-10-20 11:16:14 +09:00
4e6ca0eea9 Merge branch 'main' of https://gitea.it-college.co.jp/2026_0910_AI_5G_Java_MUN/last-group-delevelopment-team2 into t-eguchi 2026-10-19 16:19:22 +09:00
d67b790c44 1日目 2026-10-19 16:18:05 +09:00
eddc638c93 Merge branch 'm_shimizu' of https://gitea.it-college.co.jp/2026_0910_AI_5G_Java_MUN/last-group-delevelopment-team2 into t-eguchi 2026-10-19 14:26:22 +09:00
7 changed files with 361 additions and 8 deletions

View File

@@ -1,5 +1,4 @@
package com.example.demo.controller; package com.example.demo.controller;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@@ -37,7 +36,7 @@ public class AdminLoanController {
public String approvalConfirm(@PathVariable Integer id, Model model, RedirectAttributes ra) { public String approvalConfirm(@PathVariable Integer id, Model model, RedirectAttributes ra) {
try { try {
model.addAttribute("r", loanService.getView(id, Const.STATUS_WAITING)); model.addAttribute("r", loanService.getView(id, Const.STATUS_WAITING));
return "approvalConfirm"; return "approvalConfilm";
} catch (BusinessException e) { } catch (BusinessException e) {
ra.addFlashAttribute("errorMessage", e.getMessage()); ra.addFlashAttribute("errorMessage", e.getMessage());
return "redirect:/admin/applications"; return "redirect:/admin/applications";
@@ -50,7 +49,7 @@ public class AdminLoanController {
try { try {
loanService.approve(id, admin); loanService.approve(id, admin);
ra.addFlashAttribute("message", "書籍貸出申請を承認しました。"); ra.addFlashAttribute("message", "書籍貸出申請を承認しました。");
return "redirect:/admin/approval/complete"; return "approvalComplete";
} catch (BusinessException e) { } catch (BusinessException e) {
ra.addFlashAttribute("errorMessage", e.getMessage()); ra.addFlashAttribute("errorMessage", e.getMessage());
return "redirect:/admin/applications"; return "redirect:/admin/applications";
@@ -63,7 +62,7 @@ public class AdminLoanController {
try { try {
loanService.reject(id, admin); loanService.reject(id, admin);
ra.addFlashAttribute("message", "書籍貸出申請を却下しました。"); ra.addFlashAttribute("message", "書籍貸出申請を却下しました。");
return "redirect:/admin/approval/complete"; return "approvalRejected";
} catch (BusinessException e) { } catch (BusinessException e) {
ra.addFlashAttribute("errorMessage", e.getMessage()); ra.addFlashAttribute("errorMessage", e.getMessage());
return "redirect:/admin/applications"; return "redirect:/admin/applications";
@@ -116,3 +115,4 @@ public class AdminLoanController {
return "complete"; return "complete";
} }
} }

View File

@@ -1,5 +1,178 @@
package com.example.demo.controller; package com.example.demo.controller;
public class EmployeeController { import java.util.List;
import java.util.Optional;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttribute;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.example.demo.common.BusinessException;
import com.example.demo.common.Const;
import com.example.demo.common.LoginUser;
import com.example.demo.entity.Employee;
import com.example.demo.form.EmployeeForm;
import com.example.demo.service.EmployeeService;
/**
* EMP-01 社員登録(管理者のみ:/admin/** で拒否)
* EMP-02 社員編集(管理者:メールアドレスで検索して全項目 / 一般:自分のパスワードのみ)
*/
@Controller
public class EmployeeController {
private static final String NEW = "/admin/employees/new";
private static final String EDIT = "/employees/edit";
private final EmployeeService employeeService;
public EmployeeController(EmployeeService employeeService) {
this.employeeService = employeeService;
}
// ===== EMP-01 社員登録 =====
@GetMapping(NEW)
public String registInput(Model model) {
return registView(new EmployeeForm(), List.of(), model);
}
@PostMapping(NEW + "/input")
public String registBack(@ModelAttribute("form") EmployeeForm form, Model model) {
return registView(form, List.of(), model);
}
@PostMapping(NEW + "/confirm")
public String registConfirm(@ModelAttribute("form") EmployeeForm form, Model model) {
List<String> errors = employeeService.validateRegist(form);
if (!errors.isEmpty())
return registView(form, errors, model);
return "employeeRegistConfirm";
}
@PostMapping(NEW + "/execute")
public String registExecute(@ModelAttribute("form") EmployeeForm form,
@SessionAttribute(Const.SESSION_LOGIN_USER) LoginUser admin,
Model model, RedirectAttributes ra) {
try {
employeeService.register(form, admin);
} catch (BusinessException e) {
return registView(form, List.of(e.getMessage()), model);
}
ra.addFlashAttribute("message", "社員登録が完了しました。");
return "redirect:" + NEW + "/complete";
}
@GetMapping(NEW + "/complete")
public String registComplete(Model model) {
return complete(model);
}
private String registView(EmployeeForm form, List<String> errors, Model model) {
model.addAttribute("form", form);
model.addAttribute("errors", errors);
return "employeeRegistInput";
}
// ===== EMP-02 社員編集 =====
/** メニュー「社員編集」:管理者は検索画面へ、一般ユーザーは検索せず自分の編集画面へ */
@GetMapping("/employees/search")
public String search(@SessionAttribute(Const.SESSION_LOGIN_USER) LoginUser user) {
return user.isAdmin() ? "employeeSearch" : "redirect:" + EDIT;
}
@PostMapping("/employees/search")
public String doSearch(@RequestParam(defaultValue = "") String mailAddress,
@SessionAttribute(Const.SESSION_LOGIN_USER) LoginUser user, Model model) {
if (!user.isAdmin())
return "redirect:/menu";
Optional<Employee> e = employeeService.findByMail(mailAddress);
if (e.isEmpty()) {
model.addAttribute("mailAddress", mailAddress);
model.addAttribute("errorMessage", "該当する社員が見つかりません。");
return "employeeSearch";
}
return "redirect:" + EDIT + "?idEmployee=" + e.get().getIdEmployee();
}
@GetMapping(EDIT)
public String editInput(@RequestParam(required = false) Integer idEmployee,
@SessionAttribute(Const.SESSION_LOGIN_USER) LoginUser user,
Model model, RedirectAttributes ra) {
// 一般ユーザーは常に自分自身(リクエストの社員IDは使わない)
Integer target = user.isAdmin() ? idEmployee : user.getIdEmployee();
if (target == null)
return "redirect:/employees/search";
try {
return editView(employeeService.toForm(employeeService.find(target)), List.of(), user, model);
} catch (BusinessException e) {
ra.addFlashAttribute("errorMessage", e.getMessage());
return "redirect:/menu";
}
}
@PostMapping(EDIT + "/input")
public String editBack(@ModelAttribute("form") EmployeeForm form,
@SessionAttribute(Const.SESSION_LOGIN_USER) LoginUser user, Model model) {
fixTarget(form, user);
return editView(form, List.of(), user, model);
}
@PostMapping(EDIT + "/confirm")
public String editConfirm(@ModelAttribute("form") EmployeeForm form,
@SessionAttribute(Const.SESSION_LOGIN_USER) LoginUser user, Model model) {
fixTarget(form, user);
List<String> errors = user.isAdmin()
? employeeService.validateUpdateByAdmin(form)
: employeeService.validateUpdateBySelf(form);
if (!errors.isEmpty())
return editView(form, errors, user, model);
model.addAttribute("admin", user.isAdmin());
return "employeeUpdateConfirm";
}
@PostMapping(EDIT + "/execute")
public String editExecute(@ModelAttribute("form") EmployeeForm form,
@SessionAttribute(Const.SESSION_LOGIN_USER) LoginUser user,
Model model, RedirectAttributes ra) {
fixTarget(form, user);
try {
if (user.isAdmin())
employeeService.updateByAdmin(form, user);
else
employeeService.updateBySelf(form, user);
} catch (BusinessException e) {
return editView(form, List.of(e.getMessage()), user, model);
}
ra.addFlashAttribute("message", "社員編集が完了しました。");
return "redirect:" + EDIT + "/complete";
}
@GetMapping(EDIT + "/complete")
public String editComplete(Model model) {
return complete(model);
}
/** 一般ユーザーの対象は常にセッションの社員ID(URL/フォーム値の書き換えを無視) */
private static void fixTarget(EmployeeForm form, LoginUser user) {
if (!user.isAdmin())
form.setIdEmployee(user.getIdEmployee());
}
private String editView(EmployeeForm form, List<String> errors, LoginUser user, Model model) {
model.addAttribute("form", form);
model.addAttribute("errors", errors);
model.addAttribute("admin", user.isAdmin());
return "employeeUpdateInput";
}
private String complete(Model model) {
model.addAttribute("backUrl", "/menu");
model.addAttribute("backLabel", "メニューに戻る");
return "complete";
}
} }

View File

@@ -1,8 +1,5 @@
package com.example.demo.service; package com.example.demo.service;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -18,6 +15,7 @@ import com.example.demo.form.BookForm;
import com.example.demo.repository.BookCollectionRepository; import com.example.demo.repository.BookCollectionRepository;
/** BOOK-02:書籍登録、BOOK-03:書籍編集 */ /** BOOK-02:書籍登録、BOOK-03:書籍編集 */
@Service @Service
public class BookAdminService { public class BookAdminService {
private final BookCollectionRepository bookRepository; private final BookCollectionRepository bookRepository;
@@ -83,6 +81,7 @@ public class BookAdminService {
if (!errors.isEmpty()) throw new BusinessException(String.join(" ", errors)); if (!errors.isEmpty()) throw new BusinessException(String.join(" ", errors));
} }
private static void apply(BookCollection b, BookForm f) { private static void apply(BookCollection b, BookForm f) {
b.setIsbn(f.getIsbn()); b.setIsbn(f.getIsbn());
b.setNmBook(f.getNmBook()); b.setNmBook(f.getNmBook());
@@ -90,4 +89,5 @@ public class BookAdminService {
b.setPublisher(f.getPublisher().isEmpty() ? null : f.getPublisher()); b.setPublisher(f.getPublisher().isEmpty() ? null : f.getPublisher());
b.setNote(f.getNote().isEmpty() ? null : f.getNote()); b.setNote(f.getNote().isEmpty() ? null : f.getNote());
} }
} }

View File

@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>書籍貸出申請</title>
</head>
<body>
<h1>
   
<div class="rightSection">
<span class="loginInfo">ログイン者名:<span th:text="${session.loginUser.nmEmployee}"></span></span>
<button class="logoutButton" onclick="logoutSubmit()">ログアウト</button>
</div>
</h1>
<h2>書籍貸出申請を承認しました。</h2>
<form action="returnComplete" method="post" >
<div class="msgBox error" th:if="${messageError} != null">
<span class="error" th:text="${messageError.message}"></span>
</div>
</form>
<a th:href="@{/admin/applications}">承認待ち一覧に戻る</a>
<a href="showMenu">メニューに戻る</a>
</body>
</html>

View File

@@ -0,0 +1,84 @@
<!DOCTYPE html>
<html xmlns:th="http://thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>書籍貸出承認確認</title>
</head>
<body>
<h1>
書籍貸出承認確認   
<div class="rightSection">
<span class="loginInfo">ログイン者名:<span th:text="${session.loginUser.nmEmployee}"></span></span>
<button class="logoutButton" onclick="logoutSubmit()">ログアウト</button>
</div>
</h1>
<table>
<tr>
<th class="header">申請ID</th>
<td><input type="text" readonly="readonly" th:value="${r.idRequest}" /></td>
</tr>
<tr>
<th class="header">申請者名</th>
<td><input type="text" readonly="readonly" th:value="${r.nmApplicant}" /></td>
</tr>
<tr>
<th class="header">申請日</th>
<td><input type="text" readonly="readonly" th:value="${r.dateRequest}" /></td>
</tr>
<tr>
<th class="header">ISBN</th>
<td><input type="text" readonly="readonly" th:value="${r.isbn}" /></td>
</tr>
<tr>
<th class="header">書籍名</th>
<td><input type="text" readonly="readonly" th:value="${r.nmBook}" style="width: 300px;" /></td>
</tr>
<tr>
<th class="header">出版社</th>
<td><input type="text" readonly="readonly" th:value="${r.publisher}" /></td>
</tr>
<tr>
<th class="header">特記事項</th>
<td><input type="text" readonly="readonly" th:value="${r.note}" /></td>
</tr>
<tr>
<th class="header">貸出日</th>
<td><input type="text" readonly="readonly" th:value="${r.dateBorrow}" /></td>
</tr>
<tr>
<th class="header">返却予定日</th>
<td><input type="text" readonly="readonly" th:value="${r.dateScheduledReturn}" /></td>
</tr>
</table>
<div class="buttonSection" style="margin-top: 20px;">
<form th:action="@{/admin/applications/{id}/approve(id=${r.idRequest})}" method="post" style="display: inline;">
<button type="submit">承認</button>
</form>
<form th:action="@{/admin/applications/{id}/reject(id=${r.idRequest})}" method="post"
style="display: inline; margin-left: 10px;">
<button type="submit">却下</button>
</form>
</div>
<br>
<a th:href="@{/admin/applications}">承認待ち一覧へ戻る</a>
<script>
function logoutSubmit() {
if (confirm("ログアウトしますか?")) {
window.location.href = "/logout";
}
}
</script>
</body>
</html>

View File

@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>書籍貸出申請</title>
</head>
<body>
<h1>
   
<div class="rightSection">
<span class="loginInfo">ログイン者名:<span th:text="${session.loginUser.nmEmployee}"></span></span>
<button class="logoutButton" onclick="logoutSubmit()">ログアウト</button>
</div>
</h1>
<h2>書籍貸出申請を却下しました。</h2>
<form action="returnRejected" method="post">
<div class="msgBox error" th:if="${messageError} != null">
<span class="error" th:text="${messageError.message}"></span>
</div>
</form>
<a th:href="@{/admin/applications}">承認待ち一覧に戻る</a>
<a href="showMenu">メニューに戻る</a>
</body>
</html>

View File

@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>貸出中書籍一覧</title>
<link rel="stylesheet" th:href="@{/css/style.css}">
</head>
<body>
<div th:replace="~{fragments/header :: header}"></div>
<main>
<h1>貸出中書籍一覧</h1>
<div class="error" th:if="${errorMessage}" th:text="${errorMessage}"></div>
<div class="error" th:each="e : ${errors}" th:text="${e}"></div>
<div class="card">
<p th:if="${#lists.isEmpty(list)}">貸出中の書籍はありません。</p>
<table th:unless="${#lists.isEmpty(list)}">
<thead><tr><th>申請ID</th><th>申請者名</th><th>書籍名</th><th>申請日</th><th>貸出日</th><th>返却予定日</th><th>操作</th></tr></thead>
<tbody>
<tr th:each="r : ${list}">
<td th:text="${r.idRequest}"></td>
<td th:text="${r.nmApplicant}"></td>
<td th:text="${r.nmBook}"></td>
<td th:text="${r.dateRequest}"></td>
<td th:text="${r.dateBorrow}"></td>
<td th:text="${r.dateScheduledReturn}"></td>
<td><a class="btn" th:href="@{/admin/lendings/{id}(id=${r.idRequest})}">返却</a></td>
</tr>
</tbody>
</table>
</div>
<p><a class="btn sub" th:href="@{/menu}">メニューに戻る</a></p>
</main>
</body>
</html>