GoToEat campaign started in October Do you use it? Ministry of Agriculture, Forestry and Fisheries GoToEat Campaign
I used to use it often, but it seems that "Mugen Kura Sushi" is popular in the streets. You can eat at the points you get with GoToEat, and you can get points again with that meal, so you can eat Kura Sushi many times at a good price. (Aside from the moral story) Kura Sushi Official GoToEat Campaign Page
In order to receive points at GoToEat, it is necessary to eat at least 500 yen including tax for lunch and 1000 yen or more including tax for dinner at Kura Sushi. Therefore, I made "Infinite Kura Sushi Gacha" that randomly displays the Kura Sushi menu at a price that exceeds the applicable price.
Click here for the web application I made ↓ ** Mugen Kura Sushi Gacha **
This time I used the "Spring Boot" framework. The whole code is published on github ↓ https://github.com/yutwoking/eternalKurazushi
I'll leave this article as a step in creating these apps in the future.
Implementation of logic part --The reading part of the menu from the official Kura Sushi website --Stored / extracted part in database --Gacha logic part
Spring Boot framework part --build.gradle description --Creating a launcher --Creating a controller --html creation (view creation)
Web application publication (AWS)
First, load the menu from the official website. It is necessary to analyze the html of Official Menu Site. It is analyzed using a library called Jsoup.
Jsoup usage reference https://www.codeflow.site/ja/article/java-with-jsoup
The implementation of the analysis part is as follows
LoadMenu.java
private static List<MenuModel> loadMenuFromSite(String url) throws IOException{
List<MenuModel> models = new LinkedList<>();
Document doc = Jsoup.connect(url).get();
Elements menusBySection = doc.select(".section-body");
for (Element section : menusBySection) {
String sectionName = section.select(".menu-section-header h3").text();
if (!StringUtils.isEmpty(sectionName)) {
Elements menus = section.select(".menu-list").get(0).select(".menu-item");
for (Element menu : menus) {
String name = menu.select(".menu-name").text();
Elements summary = menu.select(".menu-summary li");
if (summary.size() >2) {
int price = stringToInt(summary.get(0).select("p").get(0).text());
int kcal = stringToInt(summary.get(0).select("p").get(1).text());
String area = summary.get(1).select("p").get(1).text();
boolean takeout = toBoolean(summary.get(2).select("p").get(1).text());
models.add(new MenuModel(name, price, kcal, area, takeout, sectionName));
} else if (summary.size() == 2) {
int price = stringToInt(summary.get(0).select("p").get(0).text());
int kcal = stringToInt(summary.get(0).select("p").get(1).text());
String area = "";
boolean takeout = toBoolean(summary.get(1).select("p").get(1).text());
models.add(new MenuModel(name, price, kcal, area, takeout, sectionName));
}
}
}
}
return models;
}
As a basic usage of JSoup,
Document doc = Jsoup.connect(url).get();
Read the url html with
Elements elements = doc.select(".section-body");
Use the select method like this to extract the relevant element.
I regret that the implementation code is hard to see because the if and for statements are nested. .. ..
Next is the implementation around the database. I'm using a java DB access framework called Doma2. Click here for Doma2 official
Doma has the following features.
· Use annotation processing to generate and validate code at compile time · Can map column values on the database to behavioral Java objects -A SQL template called 2-way SQL can be used. -Java 8 java.time.LocalDate, java.util.Optional and java.util.stream.Stream can be used. -No dependence on libraries other than JRE
It is a framework that I often use because I personally like the fact that it can be managed with sql files. The usage is official and Japanese, so it is good to refer to it. The implementation code is omitted here. The whole code is published on github ↓ https://github.com/yutwoking/eternalKurazushi
Gacha.java
public static List<MenuModelForSearch> getResult(Areas area, boolean isLunch){
List<MenuModelForSearch> result = new ArrayList<>();
//Store 500 for lunch and 1000 for dinner in threshold
int threshold = getThreshold(isLunch);
//Store all menus in candidates
List<MenuModelForSearch> candidates = MenuCaches.getSingleton().getMenuList(area, isLunch);
//Check if the total amount of the acquired menu exceeds the threshold, and randomly add menus from candidates until it exceeds.
while (isOverThreshold(threshold, result) == false) {
addElement(result, candidates);
}
//Finally check if the lunch menu is included. If the lunch menu is included, the result will be the lunch menu only.
checkIncludeLunchMenu(result);
return result;
}
See Whole Code for each method.
Regarding SpringBoot, I referred to the following site. https://qiita.com/gosutesu/items/961b71a95daf3a2bce96 https://qiita.com/opengl-8080/items/eb3bf3b5301bae398cc2 https://note.com/ymzk_jp/n/n272dc9e5c5d3
Add plugins and libraries to build.gradle so that gradle can use the Spring Boot framework.
The part where the actual build.gradle of this time is commented as // spring-boot added part in ↓ is added.
build.gradle
plugins {
// Apply the java plugin to add support for Java
id 'java'
// Apply the application plugin to add support for building a CLI application
id 'application'
id 'eclipse'
id 'com.diffplug.eclipse.apt' version '3.25.0'
id 'org.springframework.boot' version '2.3.5.RELEASE' //spring-boot postscript part
id 'io.spring.dependency-management' version '1.0.10.RELEASE'//spring-boot postscript part
}
version = '2.26.0-SNAPSHOT'
ext.dependentVersion = '2.24.0'
task copyDomaResources(type: Sync) {
from sourceSets.main.resources.srcDirs
into compileJava.destinationDir
include 'doma.compile.config'
include 'META-INF/**/*.sql'
include 'META-INF/**/*.script'
}
compileJava {
dependsOn copyDomaResources
options.encoding = 'UTF-8'
}
compileTestJava {
options.encoding = 'UTF-8'
options.compilerArgs = ['-proc:none']
}
repositories {
mavenCentral()
mavenLocal()
maven {url 'https://oss.sonatype.org/content/repositories/snapshots/'}
}
dependencies {
// Use JUnit test framework
testImplementation 'junit:junit:4.12'
// https://mvnrepository.com/artifact/org.jsoup/jsoup
compile group: 'org.jsoup', name: 'jsoup', version: '1.13.1'
// https://mvnrepository.com/artifact/org.apache.commons/commons-lang3
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.11'
annotationProcessor "org.seasar.doma:doma:${dependentVersion}"
implementation "org.seasar.doma:doma:${dependentVersion}"
runtimeOnly 'com.h2database:h2:1.3.175'
// https://mvnrepository.com/artifact/org.postgresql/postgresql
compile group: 'org.postgresql', name: 'postgresql', version: '42.2.8'
// https://mvnrepository.com/artifact/com.zaxxer/HikariCP
compile group: 'com.zaxxer', name: 'HikariCP', version: '3.4.1'
// https://mvnrepository.com/artifact/javax.inject/javax.inject
compile group: 'javax.inject', name: 'javax.inject', version: '1'
// https://mvnrepository.com/artifact/io.vavr/vavr
compile group: 'io.vavr', name: 'vavr', version: '0.10.2'
// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-thymeleaf
compile group: 'org.springframework.boot', name: 'spring-boot-starter-thymeleaf', version: '2.3.5.RELEASE'//spring-boot postscript part
// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web
compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '2.3.5.RELEASE'//spring-boot postscript part
// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter
compile group: 'org.springframework.boot', name: 'spring-boot-starter', version: '2.3.5.RELEASE'//spring-boot postscript part
}
//spring-Added bootJar task to make boot project a service
bootJar {
launchScript()
}
application {
// Define the main class for the application
mainClassName = 'eternalKurazushi.ServerLuncher'
}
eclipse {
classpath {
file {
whenMerged { classpath ->
classpath.entries.removeAll { it.path == '.apt_generated' }
}
withXml { provider ->
def node = provider.asNode()
// specify output path for .apt_generated
node.appendNode( 'classpathentry', [ kind: 'src', output: 'bin/main', path: '.apt_generated'])
}
}
}
jdt {
javaRuntimeName = 'JavaSE-11'
}
}
ServerLuncher.java
@SpringBootApplication
public class ServerLuncher {
public static void main(String[] args) throws Exception {
SpringApplication.run(ServerLuncher.class, args);
LoadMenu.init();
MenuCaches.getSingleton().load();
}
}
All you have to do is add the @SpringBootApplication annotation and implement SpringApplication.run.
LoadMenu.init();
MenuCaches.getSingleton().load();
This part reads the menu when the server starts and stores it in the DB, so that the menu also has a memory. Actually, with this configuration, I may not need a DB, but I am also using a DB in case it is expanded in the future (probably not expanded).
FrontController.java
@Controller
public class FrontController {
@RequestMapping("/")
public String index() {
return "index";
}
@RequestMapping(value = "/result", method = RequestMethod.POST)
public String getResult(@RequestParam("radio_1") String eatTime, @RequestParam("radio_2") String areaString, Model model) {
if (StringUtils.isEmpty(eatTime) || StringUtils.isEmpty(eatTime)) {
return "error";
}
boolean isLunch = eatTime.equals("lunch") ? true : false;
Areas area = Areas.Eastern Japan;
if (areaString.equals("West Japan")) {
area = Areas.West Japan;
} else if (areaString.equals("Kyushu-Okinawa")) {
area = Areas.Kyushu;
}
List<MenuModelForSearch> gachaResult = Gacha.getResult(area, isLunch);
model.addAttribute("list", gachaResult);
model.addAttribute("sum", getSumString(gachaResult));
model.addAttribute("time", eatTime);
model.addAttribute("area", areaString);
return "result";
}
Implement the controller using the @Controller annotation. Specify the corresponding path with @RequestMapping annotation. This is similar to jax-rs. Receive a value from html by using the @RequestParam annotation.
model.addAttribute("list", gachaResult);
You can pass a value to html with addAttribute. In this example, the value of gachaResult is passed to html with the variable name list.
return "result";
Returns the template html of / resources / templates / [Return value of Controller] .html. In this example, /resources/templates/result.html is read and returned.
result.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Infinite Kura Sushi Gacha</title>
</head>
<body bgcolor=#99FFFF>
<div style="text-align: center">
<main>
<h1>Infinite Kura Sushi Gacha</h1>
<p th:text="${time} + ' / ' + ${area}" class="description"></p>
<table border="1" align="center">
<tr>
<th>type</th>
<th>Product name</th>
<th>Price (excluding tax)</th>
<th>calorie</th>
<!--
<th>Service area</th>
<th>Takeaway</th>
-->
</tr>
<tr th:each="menu : ${list}">
<td th:text="${menu.type}"></td>
<td th:text="${menu.name}"></td>
<td th:text="${menu.price} + 'Circle'"></td>
<td th:text="${menu.kcal} + 'kcal'"></td>
<!--
<td th:text="${menu.area}"></td>
<td th:text="${menu.takeout} ? 'Yes' : '不Yes'"></td>
-->
</tr>
</table>
<h3>
<p th:text="${sum}" class="sum"></p>
</h3>
<br>
<form method="POST" action="/result">
<input type="hidden" name="radio_1" th:value="${time}"> <input
type="hidden" name="radio_2" th:value="${area}">
<div style="margin: 2rem 0rem">
<input type="submit" value="Turn the gacha again under the same conditions"
style="width: 250px; height: 50px">
</div>
</form>
<form method="GET" action="/">
<div style="margin: 2rem 0rem">
<input type="submit" value="Return">
</div>
</form>
</main>
</div>
</body>
</html>
th: value = "$ {variable name}" Then, the value received from the controller can be used. You can pass a value to the controller by creating an input and specifying a name using the form tag.
This time, I used AWS to publish the web application. It's a simple app, so I used it
it's dark. Recommended for beginners as this book is very polite and can be practiced. [Network & Server Construction from Amazon Web Services Basics Revised Edition](https://www.amazon.co.jp/Amazon-Web-Services-%E5%9F%BA%E7%A4%8E%E3%81% 8B% E3% 82% 89% E3% 81% AE% E3% 83% 8D% E3% 83% 83% E3% 83% 88% E3% 83% AF% E3% 83% BC% E3% 82% AF- % E3% 82% B5% E3% 83% BC% E3% 83% 90% E3% 83% BC% E6% A7% 8B% E7% AF% 89 / dp / 4822237443)
It's been a while since I created something outside of work. Programming at work is fun, but programming in private is also different fun (selecting a framework, creating an environment, etc.), so I would like to do it regularly to improve my skills.
If I have time, I would like to work on the following.
--Obtain and set your own domain --Code refactoring --https conversion protocol for web applications ――In this article, I wrote the procedure quite quickly, so I will divide it into several articles and summarize them one by one.