Cet article est un record lorsque j'ai essayé de créer une application WEB à partir de zéro pour la première fois. Je n'ai pas été en mesure d'expliquer les détails techniques car il y a certaines parties que je ne comprends pas, mais depuis que j'ai posté le flux de la conception à la mise en œuvre et le code source, j'espère qu'il servira de référence pour d'autres débutants.
Une application WEB qui enregistre l'heure de début du travail, affiche une alerte après 50 minutes et enregistre l'heure de fin du travail.
Arborescence du projet
.
├── 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
J'ai rendu la fonction aussi simple que possible et je l'ai faite en partant du principe qu'elle peut être réalisée en une journée pour le moment.
// L'affichage des résultats a été reporté cette fois. Je veux le mettre en œuvre à l'avenir.
Le diagramme de séquence est le suivant.
Créez une seule table car vous n'avez besoin de stocker que les tâches que vous avez effectuées.
** Tableau des tâches **
colonne | La description |
---|---|
task_id | Valeur de séquence |
task_name | Nom de la tâche |
task_category | Catégorie de travail. |
task_starttime | Heure de début du travail |
task_endtime | Heure de fin de travail |
userid | IDENTIFIANT D'UTILISATEUR. Parce qu'il n'y a pas de fonction utilisateursystem En supposant que seul peut être saisi |
DB Nous avons préparé une base de données pour cette application WEB et créé le tableau comme suit.
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
);
Créez un projet. Cette fois, je l'ai fait à partir du Spring Starter Project comme suit.
Le référentiel Maven défini ci-dessus est le suivant.
--SpringBootDevTool: Un outil de développement pratique qui se compile et redémarre automatiquement lorsqu'un changement de source Java est déclenché. --SpringDataJpa: une bibliothèque pratique pour les opérations DB. Il effectue une conversion automatique de type entre Java <-> DB. --PostgreSQL Driver: pilote pour accéder à PostgreSQL --Thymeleaf: moteur de modèle. Remplacez automatiquement la variable décrite par la valeur d'attribut par la valeur --SpringWebStarter: un ensemble de définitions qui inclut tous les paramètres et dépendances de bibliothèque requis pour une application WEB.
Après avoir créé le projet, cliquez avec le bouton droit sur le projet> Exécuter> Maven Install pour installer le fichier de référentiel défini dans pom.xml.
Dans cette procédure, pom.xml est généré automatiquement dans le projet Spring starter, Lors du changement de version ou de l'ajout manuel de la bibliothèque, il semble préférable d'éditer directement pom.xml et d'exécuter Maven Install ou Maven Clean and Install. Vous pouvez trouver le référentiel Maven sur ce site.
Ensuite, ouvrez le fichier application.properties et définissez les paramètres de connexion à la base de données.
application.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/[Nom de la base de données]
spring.datasource.username=[Utilisateur de la base de données]
spring.datasource.password=[Mot de passe de la base de données]
spring.datasource.driver-class-name=org.postgresql.Driver
Exécutez-le pour voir si le projet est prêt sans aucun problème. (Faites un clic droit sur le projet> Exécuter> Application Spring Boot)
Confirmez que le message suivant est sorti dans le journal de la console et passez au suivant.
o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s):-(Abréviation)-
jp.co.anyplus.TaskTrackerApplication : Started [Nom de l'application] in [Valeur numérique] seconds (JVM running for [Valeur numérique])
Tout d'abord, mettez les bibliothèques requises. Il y avait jQuery en tant qu'extension de JavaScript, Bootstrap pour la mise en page et Countdown Timer pour la minuterie, alors j'ai utilisé ceci.
CountdownTimer (https://github.com/harshen/jQuery-countdownTimer/) Une bibliothèque jQuery qui a un ensemble complet de fonctions de décompte et de décompte de la date et de l'heure. C'était parfait pour cette application car elle a toutes les fonctions de spécification de mise en page et de démarrage / arrêt.
html Je n'ai fait qu'un seul écran cette fois.
La disposition est décidée par Bootstrap.
La zone d'affichage de la minuterie est déterminée par <span id =" timerView ">
, et l'emplacement d'affichage est spécifié pour les parties de la minuterie Coundown.
Le CSS par défaut de CoundownTimer est un peu insatisfaisant, donc je le modifie, mais je vais l'omettre ici.
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>Enregistrez la tâche.</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="Catégorie"></div>
<div class="col-md-8 p-1"><input th:field="*{taskName}" type="text" class="form-control" placeholder="tâche"></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 J'ai créé import.js pour importer la bibliothèque et view-index.js pour décrire le traitement d'écran.
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 pour 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();
});
/*--------------------------------
*Lancer la tâche
--------------------------------*/
$('#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);
});
}
/*--------------------------------
*Arrêter la tâche
--------------------------------*/
$('#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);
});
}
/*--------------------------------
*Tâche terminée
--------------------------------*/
function taskFinish(){
setTimeout(() => {
taskStop();
alert("Finish!!");
$("#timerView").countdowntimer("stop", "stop");
stopbtnOn();
}, 1000);
}
/*--------------------------------
*Contrôle du bouton: lorsque le bouton de démarrage est activé
--------------------------------*/
function startbtnOn(){
$('#startButton').prop("disabled", true);
$('#stopButton').prop("disabled", false);
}
/*--------------------------------
*Contrôle du bouton: lorsque le bouton d'arrêt est activé
--------------------------------*/
function stopbtnOn(){
$('#startButton').prop("disabled", false);
$('#stopButton').prop("disabled", true);
}
Commencez par créer une classe TasksEntity pour stocker les données de table et une classe TasksRepository pour manipuler la table créée dans la base de données.
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);
}
JpaRepository hérité de TasksRepository.java peut utiliser la fonction de Spring Data JPA (Article de référence). Il semble que le contenu de cette requête puisse être généré automatiquement à partir du nom de la méthode sans écrire de SQL.
Créez autour de la couche Controller pour passer de la vue au modèle. IndexController.java de la classe Cotroller, TaskRegisterService.java et TaskCancelService.java de la classe Service, Écran <-> Nous avons préparé un total de 4 classes de TaskForm.java pour transmettre les informations de tâche entre les serveurs.
IndexController.java
//Instructions de package et d'importation omises
@Controller
public class IndexController {
@Autowired
TaskRegisterService registerService;
@Autowired
TaskCancelService cancelService;
/**
*Traitement d'affichage initial
*
* @param mav ModelAndView
* @retour Informations d'affichage initial
*/
@GetMapping("/")
public ModelAndView init(ModelAndView mav) {
TaskForm taskForm = new TaskForm();
mav.addObject("taskForm", taskForm);
mav.setViewName("index.html");
return mav;
}
/**
*Processus d'enregistrement des tâches
*
* @param model Model
* @param taskForm Formulaire d'informations sur les tâches
* @return ID de tâche nouvellement attribué
*/
@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 "";
}
}
/**
*Traitement de fin de tâche
*
* @param model Model
* @param taskForm Formulaire d'informations sur les tâches
* @retour du message de traitement
*/
@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";
}
}
Je n'ai pas compris les classes @ResponseBody, @ PostMapping / @ GetMapping et Model utilisées pour communiquer entre l'écran et le serveur. Si vous comprenez, j'aimerais revoir la source et faire un article sur Qiita.
TaskRegisterService.java
//Instructions de package et d'importation omises
@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) {
//log: l'enregistrement a échoué.
throw e;
}
}
}
TaskCancelService.java
//Instructions de package et d'importation omises
@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) {
//log: échec de la mise à jour.
throw e;
}
}
}
TaskForm.java
//Instructions de package et d'importation omises
@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