かんがるーさんの日記

最近自分が興味をもったものを調べた時の手順等を書いています。今は Spring Boot をいじっています。

Spring Boot で書籍の貸出状況確認・貸出申請する Web アプリケーションを作る ( その24 )( 貸出希望書籍 CSV ファイルアップロード画面の作成3 )

概要

Spring Boot で書籍の貸出状況確認・貸出申請する Web アプリケーションを作る ( その23 )( 貸出希望書籍 CSV ファイルアップロード画面の作成2 ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • 貸出希望書籍 CSV ファイルアップロード画面の作成
      • 登録機能を作成します。

参照したサイト・書籍

  1. Spring AMQP x RabbitMQ
    http://www.slideshare.net/keisuke69/spring-amqp-rabbitmq

目次

  1. 登録機能の作成
    1. fileupload.html の修正
    2. RabbitMQ に貸出状況取得タスクへの貸出状況取得依頼のメッセージを送信できるようにする
    3. BooklistService クラスの修正
    4. BooklistController クラスの修正
    5. complete.html の修正
    6. 動作確認
  2. 次回は。。。

手順

登録機能の作成

「登録」ボタンがクリックされたら RabbitMQ に貸出状況取得タスクへの貸出状況取得依頼のメッセージを送信した後、完了画面を表示します。

fileupload.html の修正

  1. src/main/resources/templates/booklist の下の fileupload.html を リンク先の内容 に変更します。

RabbitMQ に貸出状況取得タスクへの貸出状況取得依頼のメッセージを送信できるようにする

  1. 以下の仕様で送信できるようにします。

    • Queue の名前は InquiringStatusOfBookQueue にします。
    • Queue に入れるメッセージのフォーマットは Java 以外でも使用できることを考慮して JSON にします。Spring AMQP の MessageConverter は JsonMessageConverter ( 古い org.codehaus.jackson.map.ObjectMapper が使用されています ) ではなく Jackson2JsonMessageConverter ( 新しい com.fasterxml.jackson.databind.ObjectMapper が使用されています ) を使用します。
  2. build.gradle を リンク先の内容 に変更します。

  3. Gradle projects View の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。

  4. src/main/resources の下の application-develop.properties, application-unittest.properties, application-product.properties を リンク先の内容 に変更します。

  5. 今回使用するキューを定義します。src/main/java/ksbysample/webapp/lending/config の下の Constant.javaリンク先の内容 に変更します。

  6. src/main/java/ksbysample/webapp/lending/config の下の ApplicationConfig.javaリンク先の内容 に変更します。

  7. src/main/java/ksbysample/webapp/lending/service の下に queue パッケージを作成します。

  8. src/main/java/ksbysample/webapp/lending/service/queue の下に InquiringStatusOfBookQueueMessage.java を作成します。作成後、リンク先の内容 に変更します。

  9. src/main/java/ksbysample/webapp/lending/service/queue の下に InquiringStatusOfBookQueueService.java を作成します。作成後、リンク先の内容 に変更します。

BooklistService クラスの修正

  1. src/main/java/ksbysample/webapp/lending/web/booklist の下の BooklistService.javaリンク先の内容 に変更します。

BooklistController クラスの修正

  1. src/main/java/ksbysample/webapp/lending/web/booklist の下の RegisterBooklistForm.javaリンク先の内容 に変更します。

  2. src/main/java/ksbysample/webapp/lending/web/booklist の下の BooklistController.javaリンク先の内容 に変更します。

complete.html の修正

  1. src/main/resources/templates/booklist の下の complete.html を リンク先の内容 に変更します。

動作確認

  1. 動作確認します。

  2. 最初に RabbitMQ にキューが作成されていないことを確認します。RabbitMQ のサービスが起動していない場合には起動します。

    f:id:ksby:20151004113037p:plain

  3. Gradle projects View から bootRun タスクを実行して Tomcat を起動します。

  4. http://localhost:15672/ へアクセスし、RabbitMQ の管理機能のログイン画面が表示されたら guest/guest でログインします。

    トップ画面が表示されて、Queue がまだ作成されていないことが確認できます。

    f:id:ksby:20151004115233p:plain

  5. http://localhost:8080/booklist からテスト.csv をアップロードし、確認画面が表示された後「登録」ボタンをクリックします。

    f:id:ksby:20151004114039p:plain

  6. 貸出申請ID が表示された完了画面が表示されます。

    f:id:ksby:20151004115457p:plain

  7. RabbitMQ の管理画面を更新すると Queue が作成されてメッセージが蓄積されていることが確認できます。

    f:id:ksby:20151004115727p:plain

    「Queues: 1」のボタンをクリックすると Queue 一覧が表示されて InquiringStatusOfBookQueue が作成されていることが確認できます。

    f:id:ksby:20151004115942p:plain

    キュー名の「InquiringStatusOfBookQueue」リンクをクリックして Queue の画面に遷移した後、「Get messages」のところにある「Get Message(s)」ボタンを押して蓄積されているメッセージの内容を確認します。完了画面に表示された貸出申請ID のメッセージが蓄積されていることが確認できます。

    f:id:ksby:20151004120353p:plain

    「Requeue」の選択を No に変更してから「Get Message(s)」ボタンを押してメッセージを削除します。

  8. Ctrl+F2 を押して Tomcat を停止します。

  9. 一旦 commit します。

次回は。。。

テストを作成します。

ソースコード

fileupload.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
    <title>貸出希望書籍 CSV ファイルアップロード</title>
    <!-- Tell the browser to be responsive to screen width -->
    <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"/>
    <link th:replace="common/head-cssjs"/>

    <style type="text/css">
        <!--
        .box-body.no-padding {
            padding-bottom: 10px !important;
        }
        -->
    </style>
</head>

<!-- ADD THE CLASS layout-top-nav TO REMOVE THE SIDEBAR. -->
<body class="skin-blue layout-top-nav">
<div class="wrapper">

    <!-- Main Header -->
    <div th:replace="common/mainparts :: main-header"></div>

    <!-- Full Width Column -->
    <div class="content-wrapper">
        <div class="container">
            <!-- Content Header (Page header) -->
            <section class="content-header">
                <h1>貸出希望書籍 CSV ファイルアップロード</h1>
            </section>

            <!-- Main content -->
            <section class="content">
                <div class="row">
                    <div class="col-xs-12">
                        <div class="box">
                            <div class="box-body no-padding">
                                <form id="registerBooklistForm" method="post" action="/booklist/register" th:action="@{/booklist/register}" th:object="${registerBooklistForm}">
                                    <table class="table table-hover">
                                        <colgroup>
                                            <col width="5%"/>
                                            <col width="35%"/>
                                            <col width="60%"/>
                                        </colgroup>
                                        <thead class="bg-purple">
                                        <tr>
                                            <th>No.</th>
                                            <th>ISBN</th>
                                            <th>書名</th>
                                        </tr>
                                        </thead>
                                        <tbody class="jp-gothic">
                                        <tr th:each="row, iterStat : *{registerBooklistRowList}">
                                            <th th:text="${iterStat.count}">1</th>
                                            <th th:text="${row.isbn}">978-1-4302-5908-4</th>
                                            <th th:text="${row.bookName}">Spring Recipes</th>
                                        </tr>
                                        </tbody>
                                    </table>
                                    <input type="hidden" name="lendingAppId" th:value="*{lendingAppId}"/>
                                    <div class="text-center">
                                        <button class="btn bg-blue js-btn-register"><i class="fa fa-save"></i> 登録</button>
                                        <button class="btn bg-orange js-btn-backindex"><i class="fa fa-undo"></i> ファイルをアップロードし直す</button>
                                    </div>
                                </form>
                            </div>
                        </div>
                    </div>
                </div>
            </section>
            <!-- /.content -->
        </div>
        <!-- /.container -->
    </div>

</div>
<!-- ./wrapper -->

<script th:replace="common/bottom-js"></script>
<script type="text/javascript">
    <!--
    $(document).ready(function() {
        $(".js-btn-register").click(function(){
            $("#registerBooklistForm").submit();
           return false;
        });

        $(".js-btn-backindex").click(function(){
            location.href = "/booklist";
            return false;
        });
    });
    -->
</script>
</body>
</html>
  • <input type="hidden" th:value="*{lendingAppId}"/><input type="hidden" name="lendingAppId" th:value="*{lendingAppId}"/> へ変更します。

build.gradle

dependencies {
    def jdbcDriver = "org.postgresql:postgresql:9.4-1203-jdbc41"

    // spring-boot-gradle-plugin によりバージョン番号が自動で設定されるもの
    // Appendix E. Dependency versions ( http://docs.spring.io/spring-boot/docs/current/reference/html/appendix-dependency-versions.html ) 参照
    compile("org.springframework.boot:spring-boot-starter-web")
    compile("org.springframework.boot:spring-boot-starter-thymeleaf")
    compile("org.thymeleaf.extras:thymeleaf-extras-springsecurity3")
    compile("org.springframework.boot:spring-boot-starter-data-jpa")
    compile("org.springframework.boot:spring-boot-starter-velocity")
    compile("org.springframework.boot:spring-boot-starter-mail")
    compile("org.springframework.boot:spring-boot-starter-security")
    compile("org.springframework.boot:spring-boot-starter-redis")
    compile("org.springframework.boot:spring-boot-starter-amqp")
    compile("org.codehaus.janino:janino")
    testCompile("org.springframework.boot:spring-boot-starter-test")
    // (ここから) gradle でテストを実行した場合に spring-security-test-4.0.2.RELEASE.jar しか classpath に指定されず
    // テストが失敗したため、3.2.8.RELEASE を明記している
    testCompile("org.springframework.security:spring-security-core:3.2.8.RELEASE")
    testCompile("org.springframework.security:spring-security-web:3.2.8.RELEASE")
    // (ここまで) ------------------------------------------------------------------------------------------------------
    testCompile("org.springframework.security:spring-security-test:4.0.2.RELEASE")
    testCompile("org.yaml:snakeyaml")

    // spring-boot-gradle-plugin によりバージョン番号が自動で設定されないもの
    compile("${jdbcDriver}")
    compile("org.seasar.doma:doma:2.4.1")
    compile("org.bgee.log4jdbc-log4j2:log4jdbc-log4j2-jdbc4.1:1.16")
    compile("org.apache.commons:commons-lang3:3.4")
    compile("org.projectlombok:lombok:1.16.4")
    compile("com.google.guava:guava:18.0")
    compile("org.springframework.session:spring-session:1.0.1.RELEASE")
    compile("org.simpleframework:simple-xml:2.7.1")
    compile("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.6.2")
    compile("com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.6.2")
    compile("com.univocity:univocity-parsers:1.5.6")
    testCompile("org.dbunit:dbunit:2.5.1")
    testCompile("com.icegreen:greenmail:1.4.1")
    testCompile("org.assertj:assertj-core:3.2.0")
    testCompile("com.jayway.jsonpath:json-path:2.0.0")
    testCompile("org.jmockit:jmockit:1.19")

    // for Doma-Gen
    domaGenRuntime("org.seasar.doma:doma-gen:2.4.1")
    domaGenRuntime("${jdbcDriver}")
}
  • compile("org.springframework.boot:spring-boot-starter-amqp") を追加する。

application-develop.properties, application-unittest.properties, application-product.properties

■application-develop.properties

spring.datasource.url=jdbc:log4jdbc:postgresql://localhost/ksbylending
spring.datasource.username=ksbylending_user
spring.datasource.password=xxxxxxxx
spring.datasource.driverClassName=net.sf.log4jdbc.sql.jdbcapi.DriverSpy

spring.mail.host=localhost
spring.mail.port=25

spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672

spring.redis.sentinel.master=mymaster
spring.redis.sentinel.nodes=localhost:6381,localhost:6382,localhost:6383

spring.messages.cache-seconds=0
spring.thymeleaf.cache=false
spring.velocity.cache=false
  • spring.rabbitmq.host, spring.rabbitmq.port を追加します。

■application-unittest.properties

spring.datasource.url=jdbc:postgresql://localhost/ksbylending
spring.datasource.username=ksbylending_user
spring.datasource.password=xxxxxxxx
spring.datasource.driverClassName=org.postgresql.Driver

spring.mail.host=localhost
spring.mail.port=25

spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672

spring.redis.sentinel.master=mymaster
spring.redis.sentinel.nodes=localhost:6381,localhost:6382,localhost:6383

spring.thymeleaf.cache=true
  • spring.rabbitmq.host, spring.rabbitmq.port を追加します。

■application-product.properties

server.tomcat.basedir=C:/webapps/ksbysample-webapp-lending

spring.datasource.url=jdbc:postgresql://localhost/ksbylending
spring.datasource.username=ksbylending_user
spring.datasource.password=xxxxxxxx
spring.datasource.driverClassName=org.postgresql.Driver

spring.mail.host=localhost
spring.mail.port=25

spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672

spring.redis.sentinel.master=mymaster
spring.redis.sentinel.nodes=localhost:6381,localhost:6382,localhost:6383

spring.thymeleaf.cache=true
  • spring.rabbitmq.host, spring.rabbitmq.port を追加します。

Constant.java

package ksbysample.webapp.lending.config;

public class Constant {

    /* 
     * RabbitMQ Queue一覧
     */
    public static final String QUEUE_NAME_INQUIRING_STATUSOFBOOK = "InquiringStatusOfBookQueue";

    /* 
     * URL一覧
     */
    public static final String URL_ADMIN_LIBRARY = "/admin/library";

    /* 
     * ログイン後ページのURL
     */
    public static final String URL_AFTER_LOGIN_FOR_ROLE_ADMIN = URL_ADMIN_LIBRARY;

}
  • QUEUE_NAME_INQUIRING_STATUSOFBOOK を追加します。

ApplicationConfig.java

package ksbysample.webapp.lending.config;

import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ApplicationConfig {

    @Autowired
    private ConnectionFactory connectionFactory;
    
    @Bean
    public Queue inquiringStatusOfBookQueue() {
        return new Queue(Constant.QUEUE_NAME_INQUIRING_STATUSOFBOOK, false);
    }

    @Bean
    public RabbitTemplate rabbitTemplate() {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(this.connectionFactory);
        rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
        return rabbitTemplate;
    }
    
}
  • private ConnectionFactory connectionFactory; を追加します。
  • queueInquiringStatusOfBook Bean を追加します。
  • rabbitTemplate Bean を追加して、中で Jackson2JsonMessageConverter をセットします。

InquiringStatusOfBookQueueMessage.java

package ksbysample.webapp.lending.service.queue;

import lombok.Data;

@Data
public class InquiringStatusOfBookQueueMessage {

    private Long lendingAppId;

}

InquiringStatusOfBookQueueService.java

package ksbysample.webapp.lending.service.queue;

import ksbysample.webapp.lending.config.Constant;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class InquiringStatusOfBookQueueService {
    
    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void sendMessage(Long lendingAppId) {
        InquiringStatusOfBookQueueMessage message = new InquiringStatusOfBookQueueMessage();
        message.setLendingAppId(lendingAppId);
        rabbitTemplate.convertAndSend(Constant.QUEUE_NAME_INQUIRING_STATUSOFBOOK, message);
    }

}

BooklistService.java

package ksbysample.webapp.lending.web.booklist;

import ksbysample.webapp.lending.dao.LendingAppDao;
import ksbysample.webapp.lending.dao.LendingBookDao;
import ksbysample.webapp.lending.entity.LendingApp;
import ksbysample.webapp.lending.entity.LendingBook;
import ksbysample.webapp.lending.security.LendingUserDetails;
import ksbysample.webapp.lending.service.file.BooklistCSVRecord;
import ksbysample.webapp.lending.service.file.BooklistCsvFileService;
import ksbysample.webapp.lending.service.queue.InquiringStatusOfBookQueueService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.util.List;

import static ksbysample.webapp.lending.values.LendingAppStatusValues.TENPORARY_SAVE;

@Service
public class BooklistService {

    @Autowired
    private BooklistCsvFileService booklistCsvFileService;

    @Autowired
    private LendingAppDao lendingAppDao;

    @Autowired
    private LendingBookDao lendingBookDao;

    @Autowired
    private InquiringStatusOfBookQueueService inquiringStatusOfBookQueueService;
    
    public Long temporarySaveBookListCsvFile(UploadBooklistForm uploadBooklistForm) {
        // アップロードされたCSVファイルのデータを List に変換する
        List<BooklistCSVRecord> booklistCSVRecordList
                = booklistCsvFileService.convertFileToList(uploadBooklistForm.getFileupload());

        // 現在ログインしているユーザ情報を取得する
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        LendingUserDetails lendingUserDetails = (LendingUserDetails) auth.getPrincipal();

        // lending_app テーブルにデータを保存する
        LendingApp lendingApp = new LendingApp();
        lendingApp.setStatus(TENPORARY_SAVE.getValue());
        lendingApp.setLendingUserId(lendingUserDetails.getUserId());
        lendingAppDao.insert(lendingApp);

        // lending_book テーブルにデータを保存する
        LendingBook lendingBook;
        for (BooklistCSVRecord booklistCSVRecord : booklistCSVRecordList) {
            lendingBook = new LendingBook();
            BeanUtils.copyProperties(booklistCSVRecord, lendingBook);
            lendingBook.setLendingAppId(lendingApp.getLendingAppId());
            lendingBookDao.insert(lendingBook);
        }

        return lendingApp.getLendingAppId();
    }

    public List<LendingBook> getLendingBookList(Long lendingAppId) {
        List<LendingBook> lendingBookList = lendingBookDao.selectByLendingAppId(lendingAppId);
        return lendingBookList;
    }

    public void updateLendingAppStatusToPending(RegisterBooklistForm registerBooklistForm) {
        inquiringStatusOfBookQueueService.sendMessage(registerBooklistForm.getLendingAppId());
    }

}
  • updateLendingAppStatusToPending メソッドを追加します。

RegisterBooklistForm.java

package ksbysample.webapp.lending.web.booklist;

import ksbysample.webapp.lending.entity.LendingBook;
import lombok.Data;
import org.springframework.beans.BeanUtils;

import java.util.List;
import java.util.stream.Collector;
import java.util.stream.Collectors;

@Data
public class RegisterBooklistForm {

    private List<RegisterBooklistRow> registerBooklistRowList;

    private Long lendingAppId;

    public RegisterBooklistForm() {
    }
    
    public RegisterBooklistForm(List<LendingBook> lendingBookList, Long lendingAppId) {
        this.registerBooklistRowList = lendingBookList.stream()
                .map(RegisterBooklistRow::new)
                .collect(Collectors.toList());
        this.lendingAppId = lendingAppId;
    }

    @Data
    public class RegisterBooklistRow {
        private String isbn;
        private String bookName;

        public RegisterBooklistRow(LendingBook lendingBook) {
            BeanUtils.copyProperties(lendingBook, this);
        }
    }

}

BooklistController.java

package ksbysample.webapp.lending.web.booklist;

import ksbysample.webapp.lending.entity.LendingBook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import java.util.List;

@Controller
@RequestMapping("/booklist")
public class BooklistController {

    @Autowired
    private UploadBooklistFormValidator uploadBooklistFormValidator;

    @Autowired
    private BooklistService booklistService;
    
    @InitBinder("uploadBooklistForm")
    public void initBinder(WebDataBinder binder) {
        binder.addValidators(uploadBooklistFormValidator);
    }    

    @RequestMapping
    public String index(UploadBooklistForm uploadBooklistForm) {
        return "booklist/booklist";
    }
    
    @RequestMapping("/fileupload")
    public String fileupload(@Validated UploadBooklistForm uploadBooklistForm
            , BindingResult bindingResult
            , Model model) {
        if (bindingResult.hasErrors()) {
            return "booklist/booklist";
        }

        // アップロードされたCSVファイルのデータをDBに保存する
        Long lendingAppId = booklistService.temporarySaveBookListCsvFile(uploadBooklistForm);

        // 確認画面に表示するデータを取得する
        List<LendingBook> lendingBookList = booklistService.getLendingBookList(lendingAppId);
        RegisterBooklistForm registerBooklistForm = new RegisterBooklistForm(lendingBookList, lendingAppId);
        model.addAttribute("registerBooklistForm", registerBooklistForm);
        
        return "booklist/fileupload";
    }

    @RequestMapping("/register")
    public String register(RegisterBooklistForm registerBooklistForm
            , RedirectAttributes redirectAttributes) {
        booklistService.updateLendingAppStatusToPending(registerBooklistForm);
        redirectAttributes.addFlashAttribute("lendingAppId", registerBooklistForm.getLendingAppId());
        return "redirect:/booklist/complete";
    }

    @RequestMapping("/complete")
    public String complete() {
        return "booklist/complete";
    }

}
  • register メソッドの以下の点を変更します。
    • booklistService.updateLendingAppStatusToPending(registerBooklistForm); を追加します。
    • リダイレクト先の完了画面で lending_app.lending_app_id の値を表示させるために、メソッドの引数に RedirectAttributes redirectAttributes を追加し、メソッド内の処理に redirectAttributes.addFlashAttribute("lendingAppId", registerBooklistForm.getLendingAppId()); を追加します。

complete.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
    <title>貸出希望書籍 CSV ファイルアップロード</title>
    <!-- Tell the browser to be responsive to screen width -->
    <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"/>
    <link th:replace="common/head-cssjs"/>

    <style type="text/css">
        <!--
        .lending-oneline-msgbox {
            height: 70px;
            padding-top: 10px;
        }
        -->
    </style>
</head>

<!-- ADD THE CLASS layout-top-nav TO REMOVE THE SIDEBAR. -->
<body class="skin-blue layout-top-nav">
<div class="wrapper">

    <!-- Main Header -->
    <div th:replace="common/mainparts :: main-header"></div>

    <!-- Full Width Column -->
    <div class="content-wrapper">
        <div class="container">
            <!-- Content Header (Page header) -->
            <section class="content-header">
                <h1>貸出希望書籍 CSV ファイルアップロード</h1>
            </section>

            <!-- Main content -->
            <section class="content">
                <div class="row">
                    <div class="col-xs-12">
                        <div class="lending-oneline-msgbox">
                            <p><span class="text-bold">貸出申請ID:<span th:text="${lendingAppId}">1</span></span><br/>
                                で貸出希望書籍を登録しました。選択中の図書館に蔵書の有無と貸出状況を問い合わせます。</p>
                        </div>
                        <button class="btn bg-blue js-btn-moveindex"><i class="fa fa-file-text"></i> 別の貸出希望書籍を登録する</button>
                    </div>
                </div>
            </section>
            <!-- /.content -->
        </div>
        <!-- /.container -->
    </div>

</div>
<!-- ./wrapper -->

<script th:replace="common/bottom-js"></script>
<script type="text/javascript">
    <!--
    $(document).ready(function() {
        $(".js-btn-moveindex").click(function(){
            location.href = "/booklist";
            return false;
        });
    });
    -->
</script>
</body>
</html>
  • .lending-oneline-msgbox 内で height: 50px;height: 70px; へ変更します。
  • <span class="text-bold">貸出申請ID:<span th:text="${lendingAppId}">1</span></span><br/> を追加します。

履歴

2015/10/04
初版発行。