Dieser Artikel ist eine Aufzeichnung, als ich zum ersten Mal versuchte, eine WEB-Anwendung von Grund auf neu zu erstellen. Ich konnte die technischen Details nicht erklären, da es einige Teile gibt, die ich nicht verstehe, aber da ich den Ablauf vom Design zur Implementierung und den Quellcode veröffentlicht habe, hoffe ich, dass er als Referenz für andere Anfänger dient.
Eine WEB-Anwendung, die die Startzeit der Arbeit aufzeichnet, nach 50 Minuten eine Warnung anzeigt und die Endzeit der Arbeit aufzeichnet.
Projektbaum
.
├── src
│ ├── main
│ │ ├── java
│ │ │ └── jp
│ │ │ └── co
│ │ │ └── anyplus
│ │ │ ├── Entity
│ │ │ │ └── TasksEntity.java
│ │ │ ├── Repository
│ │ │ │ └── TasksRepository.java
│ │ │ ├── ServletInitializer.java
│ │ │ ├── TaskTrackerApplication.java
│ │ │ ├── controller
│ │ │ │ └── IndexController.java
│ │ │ ├── form
│ │ │ │ └── TaskForm.java
│ │ │ └── service
│ │ │ ├── TaskCancelService.java
│ │ │ └── TaskRegisterService.java
│ │ ├── resources
│ │ │ ├── application.properties
│ │ │ ├── static
│ │ │ │ ├── css
│ │ │ │ │ ├── bootstrap-grid.css
│ │ │ │ │ ├── bootstrap-grid.css.map
│ │ │ │ │ ├── bootstrap-grid.min.css
│ │ │ │ │ ├── bootstrap-grid.min.css.map
│ │ │ │ │ ├── bootstrap-reboot.css
│ │ │ │ │ ├── bootstrap-reboot.css.map
│ │ │ │ │ ├── bootstrap-reboot.min.css
│ │ │ │ │ ├── bootstrap-reboot.min.css.map
│ │ │ │ │ ├── bootstrap.css
│ │ │ │ │ ├── bootstrap.css.map
│ │ │ │ │ ├── bootstrap.min.css
│ │ │ │ │ ├── bootstrap.min.css.map
│ │ │ │ │ └── jQuery.countdownTimer.css
│ │ │ │ ├── images
│ │ │ │ └── js
│ │ │ │ ├── bootstrap.bundle.js
│ │ │ │ ├── bootstrap.bundle.js.map
│ │ │ │ ├── bootstrap.bundle.min.js
│ │ │ │ ├── bootstrap.bundle.min.js.map
│ │ │ │ ├── bootstrap.js
│ │ │ │ ├── bootstrap.js.map
│ │ │ │ ├── bootstrap.min.js
│ │ │ │ ├── bootstrap.min.js.map
│ │ │ │ ├── import.js
│ │ │ │ ├── jQuery.countdownTimer.js
│ │ │ │ ├── jQuery.countdownTimer.min.js
│ │ │ │ ├── jQuery.countdownTimer.min.js.map
│ │ │ │ ├── jquery-3.4.1.min.js
│ │ │ │ ├── localisation
│ │ │ │ └── view-index.js
│ │ │ └── templates
│ │ │ └── index.html
Ich habe die Funktion so einfach wie möglich gemacht und davon ausgegangen, dass sie vorerst an einem Tag erstellt werden kann.
// Die Ergebnisanzeige wurde diesmal verschoben. Ich möchte es in Zukunft implementieren.
Das Sequenzdiagramm ist wie folgt.
Erstellen Sie nur eine Tabelle, da Sie nur die von Ihnen ausgeführten Aufgaben speichern müssen.
** Aufgabentabelle **
Säule | Erläuterung |
---|---|
task_id | Sequenzwert |
task_name | Aufgabennname |
task_category | Arbeitskategorie. |
task_starttime | Arbeitsbeginnzeit |
task_endtime | Arbeitsendzeit |
userid | BENUTZERIDENTIFIKATION. Weil es keine Benutzerfunktion gibtsystem Vorausgesetzt, dass nur eingegeben werden kann |
DB Wir haben eine Datenbank für diese WEB-Anwendung vorbereitet und die Tabelle wie folgt erstellt.
DDL
CREATE TABLE public.tasks (
task_id bigserial NOT NULL,
task_name varchar(200) NULL,
task_category varchar(200) NULL,
task_starttime timestamp NULL,
task_endtime timestamp NULL,
userid varchar(20) NULL
);
Erstellen Sie ein Projekt. Dieses Mal habe ich Folgendes aus dem Spring Starter Project gemacht.
Das oben festgelegte Maven-Repository lautet wie folgt.
--SpringBootDevTool: Ein praktisches Entwicklungstool, das automatisch kompiliert und neu gestartet wird, wenn eine Änderung der Java-Quelle ausgelöst wird. --SpringDataJpa: Eine praktische Bibliothek für DB-Operationen. Es führt eine automatische Typkonvertierung zwischen Java <-> DB durch. --PostgreSQL-Treiber: Treiber für den Zugriff auf PostgreSQL
Klicken Sie nach dem Erstellen des Projekts mit der rechten Maustaste auf Projekt> Ausführen> Maven-Installation, um die in pom.xml definierte Repository-Datei zu installieren.
In diesem Verfahren wird pom.xml automatisch im Spring Starter-Projekt generiert. Wenn Sie die Version ändern oder die Bibliothek manuell hinzufügen, ist es besser, pom.xml direkt zu bearbeiten und Maven Install oder Maven Clean and Install durchzuführen. Das Maven-Repository finden Sie auf dieser Site (https://mvnrepository.com).
Öffnen Sie als Nächstes die Datei application.properties und legen Sie die Einstellungen für die DB-Verbindung fest.
application.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/[Name der Datenbank]
spring.datasource.username=[Datenbankbenutzer]
spring.datasource.password=[Datenbankkennwort]
spring.datasource.driver-class-name=org.postgresql.Driver
Führen Sie es aus, um festzustellen, ob das Projekt ohne Probleme bereit ist. (Klicken Sie mit der rechten Maustaste auf das Projekt> Ausführen> Spring Boot-Anwendung.)
Stellen Sie sicher, dass die folgende Meldung im Konsolenprotokoll ausgegeben wird, und fahren Sie mit dem nächsten fort.
o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s):- -(Abkürzung)- -
jp.co.anyplus.TaskTrackerApplication : Started [Anwendungsname] in [Numerischer Wert] seconds (JVM running for [Numerischer Wert])
Geben Sie zunächst die erforderlichen Bibliotheken ein. Es gab jQuery als Erweiterung von JavaScript, Bootstrap für das Layout und Countdown-Timer für den Timer, also habe ich dies verwendet.
CountdownTimer (https://github.com/harshen/jQuery-countdownTimer/) Eine jQuery-Bibliothek mit einem vollständigen Satz von Countdown- und Countdown-Funktionen für Datum und Uhrzeit. Es war perfekt für diese App, da es alle Funktionen der Layoutspezifikation und Start / Stopp bietet.
html Diesmal habe ich nur einen Bildschirm gemacht.
Das Layout wird von Bootstrap festgelegt. Der Anzeigebereich des Timers wird durch "" bestimmt, und der Anzeigeort wird für die Teile des Coundown-Timers angegeben. Das Standard-CSS von CoundownTimer ist etwas unbefriedigend, daher bearbeite ich es, aber ich werde es hier weglassen.
index.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" type="text/css" href="/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="/css/jQuery.countdownTimer.css" />
<title>TaskTracker</title>
</head>
<body>
<main class="container">
<h1>Task Tracker</h1>
<p>Notieren Sie die Aufgabe.</p>
<div class="container text-center">
<div id="taskMainContainer" class="jumbotron col-md-8 mx-auto">
<form id="TaskForm" th:object="${taskForm}">
<div class="row">
<div class="col display-4"><span id="timerView">50:00</span></div>
</div>
<div class="row mt-3 justify-content-md-center">
<div class="col-md-4 "><button id="startButton" type="button" class="btn btn-block btn-success" disabled>Start</button></div>
<div class="col-md-4 "><button id="stopButton" type="button" class="btn btn-block btn-danger" disabled>Stop</button></div>
</div>
<div class="row mt-3 justify-content-md-center">
<div class="col-md-4 p-1"><input th:field="*{category}" type="text" class="form-control" placeholder="Kategorie"></div>
<div class="col-md-8 p-1"><input th:field="*{taskName}" type="text" class="form-control" placeholder="Aufgabe"></div>
<div><input th:field="*{taskId}" type="text" class="form-control" hidden></div>
</div>
</form>
</div>
</div>
</main>
<script src="/js/import.js"></script>
<script src="/js/view-index.js"></script>
</body>
</html>
JavaScript Ich habe import.js zum Importieren der Bibliothek und view-index.js zum Beschreiben der Bildschirmverarbeitung erstellt.
import.js
/**
* import
**/
document.write('<script type="text/javascript" src="/js/jquery-3.4.1.min.js"></script>');
document.write('<script type="text/javascript" src="/js/bootstrap.bundle.min.js"></script>');
view-index.js
/*--------------------------------
* index.JS für HTML
--------------------------------*/
document.write('<script type="text/javascript" src="/js/jQuery.countdownTimer.js"></script>');
$(function(){
$("#timerView").countdowntimer({
minutes :50,
seconds :00,
displayFormat : "MS",
size : "xl",
timeUp : taskFinish
});
$("#timerView").countdowntimer("stop", "stop");
stopbtnOn();
});
/*--------------------------------
*Aufgabe starten
--------------------------------*/
$('#startButton').on('click', taskStart);
function taskStart(){
startbtnOn();
$("#timerView").countdowntimer("stop", "start");
var form = $('#TaskForm').serializeArray();
var formdata = {};
jQuery.each(form, function(i, e) {
formdata[e.name] = e.value;
});
$.ajax({
url:'/',
type:'POST',
contentType : 'application/json; charset=utf-8',
data: JSON.stringify(formdata)
}).done( (data) => {
console.log("success");
console.log(data);
$('#taskId').val(data);
}).fail( (data) => {
console.log("fail");
console.log(data);
});
}
/*--------------------------------
*Aufgabe stoppen
--------------------------------*/
$('#stopButton').on('click', taskStop);
function taskStop(){
stopbtnOn();
$("#timerView").countdowntimer("stop", "stop");
var form = $('#TaskForm').serializeArray();
var formdata = {};
jQuery.each(form, function(i, e) {
formdata[e.name] = e.value;
});
$.ajax({
url:'/stop',
type:'POST',
contentType : 'application/json; charset=utf-8',
data: JSON.stringify(formdata)
}).done( (data) => {
console.log("success");
console.log(data);
}).fail( (data) => {
console.log("fail");
console.log(data);
});
}
/*--------------------------------
*Aufgabe erledigt
--------------------------------*/
function taskFinish(){
setTimeout(() => {
taskStop();
alert("Finish!!");
$("#timerView").countdowntimer("stop", "stop");
stopbtnOn();
}, 1000);
}
/*--------------------------------
*Tastensteuerung: Wenn die Starttaste eingeschaltet ist
--------------------------------*/
function startbtnOn(){
$('#startButton').prop("disabled", true);
$('#stopButton').prop("disabled", false);
}
/*--------------------------------
*Tastensteuerung: Wenn die Stopptaste eingeschaltet ist
--------------------------------*/
function stopbtnOn(){
$('#startButton').prop("disabled", false);
$('#stopButton').prop("disabled", true);
}
Erstellen Sie zunächst eine TasksEntity-Klasse zum Speichern von Tabellendaten und eine TasksRepository-Klasse zum Bearbeiten der in der Datenbank erstellten Tabelle.
TasksEntity.java
package jp.co.anyplus.Entity;
import java.sql.Timestamp;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="tasks")
public class TasksEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="task_id")
private Long taskId;
@Column(name="task_name")
private String taskName;
@Column(name="task_category")
private String taskCategory;
@Column(name="task_starttime")
private Timestamp taskStartTime;
@Column(name="task_endtime")
private Timestamp taskEndTime;
@Column(name="userid")
private String userid;
public Long getTaskId() {
return taskId;
}
public void setTaskId(Long taskId) {
this.taskId = taskId;
}
public String getTaskName() {
return taskName;
}
public void setTaskName(String taskName) {
this.taskName = taskName;
}
public String getTaskCategory() {
return taskCategory;
}
public void setTaskCategory(String taskCategory) {
this.taskCategory = taskCategory;
}
public Timestamp getTaskStartTime() {
return taskStartTime;
}
public void setTaskStartTime(Timestamp taskStartTime) {
this.taskStartTime = taskStartTime;
}
public Timestamp getTaskEndTime() {
return taskEndTime;
}
public void setTaskEndTime(Timestamp taskEndTime) {
this.taskEndTime = taskEndTime;
}
public String getUserid() {
return userid;
}
public void setUserid(String userid) {
this.userid = userid;
}
}
TasksRepository.java
package jp.co.anyplus.Repository;
import java.sql.Timestamp;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import jp.co.anyplus.Entity.TasksEntity;
@Repository
public interface TasksRepository extends JpaRepository<TasksEntity, Long> {
@Query(value="select current_timestamp", nativeQuery = true)
public Timestamp getCurrentTime();
@Query(value="select * from tasks where task_id = :taskId"
, nativeQuery = true)
public TasksEntity getByTaskId(@Param("taskId")Long taskId);
}
Da das von TasksRepository.java geerbte JpaRepository die Funktion von Spring Data JPA verwenden kann (Referenzartikel) Es scheint, dass der Inhalt dieser Abfrage automatisch aus dem Methodennamen generiert werden kann, ohne SQL zu schreiben.
Erstellen Sie um die Controller-Ebene herum, um von der Ansicht zum Modell zu gelangen. IndexController.java der Cotroller-Klasse, TaskRegisterService.java und TaskCancelService.java der Service-Klasse, Bildschirm <-> Wir haben insgesamt 4 Klassen von TaskForm.java für die Weitergabe von Aufgabeninformationen zwischen Servern vorbereitet.
IndexController.java
//Paket- und Importanweisungen wurden weggelassen
@Controller
public class IndexController {
@Autowired
TaskRegisterService registerService;
@Autowired
TaskCancelService cancelService;
/**
*Erste Anzeigeverarbeitung
*
* @param mav ModelAndView
* @Rückgabeinformationen zurückgeben
*/
@GetMapping("/")
public ModelAndView init(ModelAndView mav) {
TaskForm taskForm = new TaskForm();
mav.addObject("taskForm", taskForm);
mav.setViewName("index.html");
return mav;
}
/**
*Prozess der Aufgabenregistrierung
*
* @param model Model
* @param taskForm Aufgabeninformationsformular
* @return Neu zugewiesene Task-ID
*/
@PostMapping("/")
@ResponseBody
public String taskRegister(Model model, @RequestBody TaskForm taskForm) {
try {
TasksEntity regTask = registerService.registerTask(taskForm);
return regTask.getTaskId().toString();
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
/**
*Task-End-Verarbeitung
*
* @param model Model
* @param taskForm Aufgabeninformationsformular
* @Verarbeitungsnachricht zurückgeben
*/
@PostMapping("/stop")
@ResponseBody
public String taskEnd(Model model, @RequestBody TaskForm taskForm) {
try {
cancelService.endTask(taskForm);
} catch(RuntimeException e) {
e.printStackTrace();
return "error";
}
return "task canceling success";
}
}
Ich habe die Klassen @ResponseBody, @ PostMapping / @ GetMapping und Model, die für die Kommunikation zwischen dem Bildschirm und dem Server verwendet werden, nicht verstanden. Wenn Sie verstehen, möchte ich die Quelle überprüfen und einen Qiita-Artikel verfassen.
TaskRegisterService.java
//Paket- und Importanweisungen wurden weggelassen
@Service
public class TaskRegisterService {
@Autowired
TasksRepository tasksRep;
@Transactional(rollbackOn = Exception.class)
public TasksEntity registerTask(TaskForm taskForm) throws RuntimeException {
Timestamp currenttime = tasksRep.getCurrentTime();
TasksEntity taskEntity = new TasksEntity();
taskEntity.setTaskName(taskForm.getTaskName());
taskEntity.setTaskCategory(taskForm.getCategory());
taskEntity.setTaskStartTime(currenttime);
taskEntity.setUserid("system");
try {
TasksEntity regTask = tasksRep.saveAndFlush(taskEntity);
return regTask;
} catch (RuntimeException e) {
//Protokoll: Registrierung fehlgeschlagen.
throw e;
}
}
}
TaskCancelService.java
//Paket- und Importanweisungen wurden weggelassen
@Service
public class TaskCancelService {
@Autowired
TasksRepository tasksRep;
@Transactional(rollbackOn = Exception.class)
public void endTask(TaskForm taskForm) throws RuntimeException {
Timestamp currenttime = tasksRep.getCurrentTime();
TasksEntity taskEntity = tasksRep.getByTaskId(Long.parseLong(taskForm.getTaskId()));
taskEntity.setTaskId(Long.parseLong(taskForm.getTaskId()));
taskEntity.setTaskEndTime(currenttime);
try {
tasksRep.saveAndFlush(taskEntity);
} catch (RuntimeException e) {
//Protokoll: Aktualisierung fehlgeschlagen.
throw e;
}
}
}
TaskForm.java
//Paket- und Importanweisungen wurden weggelassen
@SuppressWarnings("serial")
public class TaskForm implements java.io.Serializable {
private String taskId;
private String category;
private String taskName;
public String getTaskId() {
return taskId;
}
public void setTaskId(String taskId) {
this.taskId = taskId;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getTaskName() {
return taskName;
}
public void setTaskName(String taskName) {
this.taskName = taskName;
}
}
Recommended Posts