在Heroku上使用Spring Boot + JPA + Flyway来创建LINE Bot

总结

使用Spring Boot、JPA、 Flyway和Heroku创建一个LINE Bot,从数据库中获取回复消息。
由于这只是一个简单的示例代码,所以请宽容地对待细节方面的问题。
此外,关于以下内容,我不会进行解释,请参考链接。

LINE Channelの作成

https://developers.line.biz/ja/docs/messaging-api/getting-started/

herokuでアプリを作成

https://signup.heroku.com/

JPA (Java Persistence API) => Java標準のORM

https://www.vogella.com/tutorials/JavaPersistenceAPI/article.html

Flyway => Migrationツール

Homepage

相关

    • line-bot-sdk-javaを使ってSpring bootでLINE Botを作る

 

    LINE BOT APIのreplyでたまにInvalid reply tokenが発生する

前提 – Qian ti

使用Line提供的样例代码。
https://github.com/line/line-bot-sdk-java.git

环境

    • Java 11.02

 

    Spring Boot 2.4.9 (line-bot-sdk-javaで使われているversion)

重要的文件

当您在LINE上向机器人发送消息时,系统会根据您发送的文字内容从数据库表中提取相应的回复消息并作出回应。回复条件如下:

送信Message返信MessageハローハローワールドhelloHello world!!それ以外返信しない

建筑设计

    • heroku

 

    PostgreSQL 13.3 (on heroku)

目录结构

在示例代码中,仅列出了本次要编辑和新增的文件。

.
├ ─ ─  Procfile
└ ─ ─  sample-spring-boot-kitchensink
     ├ ─ ─  build.gradle
     └ ─ ─  src
         └ ─ ─  main
             ├ ─ ─  java
             │    └ ─ ─  com
             │        └ ─ ─  example
             │            └ ─ ─  bot
             │                └ ─ ─  spring
             │                    ├ ─ ─  KitchenSinkController.java
             │                    ├ ─ ─  models
             │                    │    └ ─ ─  TextMessageModel.java
             │                    ├ ─ ─  repositories
             │                    │    └ ─ ─  TextMessageRepository.java
             │                    └ ─ ─  services
             │                         └ ─ ─  TextMessageService.java
             └ ─ ─  resources
                 ├ ─ ─  application.yml
                 └ ─ ─  db
                     └ ─ ─  migration
                         └ ─ ─  V1_0__create_table_text_messages.sql

可以删除以下三个目录,因为我们将使用样本代码中的sample-spring-boot-kitchensink。

    • sample-manage-audience

 

    • sample-spring-boot-echo

 

    sample-spring-boot-echo-kotlin

数据库表格

创建一个名为text_messages的表用于存放回复消息。由于会使用Flyway进行迁移,因此不需要手动创建。

line-bot-sample::DATABASE=> \d text_messages;
                                    Table "public.text_messages"
 Column  |          Type          | Collation | Nullable |                  Default
---------+------------------------+-----------+----------+-------------------------------------------
 id      | integer                |           | not null | nextval('text_messages_id_seq'::regclass)
 message | character varying(255) |           | not null |
Indexes:
    "text_messages_pkey" PRIMARY KEY, btree (id)

GitHub

完成品在这里
https://github.com/Esfahan/line-bot-java-sample

制作步骤

获取示例代码

使用Line提供的示例代码。

$ git clone https://github.com/line/line-bot-sdk-java.git

使JPA和flyway可用。

将以下内容添加到 sample-spring-boot-kitchensink/build.gradle 文件中。

dependencies {
     implementation project(':line-bot-spring-boot')
     implementation "org.springframework.boot:spring-boot-starter-web"
     implementation 'com.google.guava:guava'
+    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
+    implementation 'org.flywaydb:flyway-core'
+    runtimeOnly 'org.postgresql:postgresql'
 }

创建 application.yml 文件

创建一个新的sample-spring-boot-kitchensink/src/main/resources/application.yml文件。
有关环境变量值的说明将在后面提到。

line.bot:
  channel-token: ${CHANNEL_TOKEN}
  channel-secret: ${CHANNEL_SECRET}
  handler.path: /callback

spring:
  jpa:
    database: POSTGRESQL
    open-in-view: True
  datasource:
    url: ${DATABASE_JDBC_URL}
    username: ${DATABASE_USER}
    password: ${DATABASE_PASSWORD}
    driver-class-name: org.postgresql.Driver
    connectionProperties: useUnicode=true;characterEncoding=utf-8;
  flyway:
    locations: classpath:/db/migration
    baseline-on-migrate: true

其他属性请参考以下链接:
https://spring.pleiades.io/spring-boot/docs/current/reference/html/application-properties.html

创建Migration文件 Migration

以下是关于迁移文件命名规则的参考链接:https://garafu.blogspot.com/2020/06/how-to-use-flyway.html。

这次将以以下名称创建:
V1_0__create_table_text_messages.sql

当按照 application.yml 中 flyway.locations 的定义,在 resources/db/migration/ 目录下放置 sql 文件,就会执行迁移操作。因此,需要创建一个目录并创建 sql 文件。

$ mkdir -p sample-spring-boot-kitchensink/src/main/resources/db/migration/
$ vi sample-spring-boot-kitchensink/src/main/resources/db/migration/V1_0__create_table_text_messages.sql
CREATE TABLE text_messages(
  id serial PRIMARY KEY,
  message varchar(255) NOT NULL
);

创建目录

创建models/,repositories/,services/目录以放置各个源代码。

$ mkdir sample-spring-boot-kitchensink/src/main/java/com/example/bot/spring/{models,repositories,services}

创建Model

在 `sample-spring-boot-kitchensink/src/main/java/com/example/bot/spring/models/TextMessageModel.java` 创建一个新文件。

package com.example.bot.spring.models;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="text_messages")
public class TextMessageModel {
    @Id
    private Integer id;

    public String message;
}

创建存储库

创建以下文件:sample-spring-boot-kitchensink/src/main/java/com/example/bot/spring/repositories/TextMessageRepository.java。

package com.example.bot.spring.repositories;

import com.example.bot.spring.models.TextMessageModel;
import org.springframework.data.jpa.repository.JpaRepository;

public interface TextMessageRepository extends JpaRepository<TextMessageModel, Integer> {
}

创建服务

在中国,本土化的内容重新表达如下:
新建以下文件:
sample-spring-boot-kitchensink/src/main/java/com/example/bot/spring/services/TextMessageService.java

package com.example.bot.spring.services;

import com.example.bot.spring.repositories.TextMessageRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class TextMessageService {
    private String replyTextMessage;
    private boolean replyFlg;

    @Autowired
    TextMessageRepository textMessageRepository;

    public void fetchReplyTextMessage (String message) {
        log.info("fetchReplyTextMessage is called: {}", message);

        Integer id = replyId(message);
        if (id > 0) {
            this.replyTextMessage = textMessageRepository.findById(id).get().message;
            this.replyFlg = true;
        } else {
            this.replyFlg = false;
        }
    }

    public String getReplyMessage() {
        return this.replyTextMessage;
    }

    public boolean getReplyFlg() {
        return this.replyFlg;
    }

    private Integer replyId(String message) {
        switch (message) {
            case "ハロー": {
                return 1;
            }
            case "hello": {
                return 2;
            }
            default: {
                return 0;
            }
        }
    }
}

控制器

编辑下面的文件:
sample-spring-boot-kitchensink/src/main/java/com/example/bot/spring/KitchenSinkController.java

为了简化理解处理内容,我们将handleTextMessageEvent方法重写为使用一个方法来处理全部内容。

+import com.example.bot.spring.services.TextMessageService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

public class KitchenSinkController {
     @Autowired
     private LineBlobClient lineBlobClient;

+    @Autowired
+    private TextMessageService textMessageService;
+
     @EventMapping
     public void handleTextMessageEvent(MessageEvent<TextMessageContent> event) throws Exception {
         TextMessageContent message = event.getMessage();
-        handleTextContent(event.getReplyToken(), event, message);
+
+        log.info("TextMessageContent is called");
+
+        textMessageService.fetchReplyTextMessage(message.getText());
+
+        if (textMessageService.getReplyFlg()) {
+            log.info("replyFlg is true");
+
+            String replyText = textMessageService.getReplyMessage();
+            List<Message> replyMessage = singletonList(new TextMessage(replyText));
+            String replyToken = event.getReplyToken();
+
+            log.info("Returns echo message {}: {}", replyToken, replyText);
+
+            boolean notificationDisabled = false;
+            try {
+                BotApiResponse apiResponse = lineMessagingClient
+                        .replyMessage(new ReplyMessage(replyToken, replyMessage, notificationDisabled))
+                        .get();
+                log.info("Sent messages: {}", apiResponse);
+            } catch (InterruptedException | ExecutionException e) {
+                throw new RuntimeException(e);
+            }
+        }
     }

修改Procfile。

为了使用sample-spring-boot-kitchensink,需修改Procfile如下所示。

-web: java $JAVA_OPTS -jar sample-spring-boot-echo/build/libs/sample-spring-boot-echo-*.jar --server.port=$PORT
+web: java $JAVA_OPTS -jar sample-spring-boot-kitchensink/build/libs/sample-spring-boot-kitchensink-*.jar --server.port=$PORT

.gitignore 文件

如果忽略了.jar文件,就无法部署./gradle/wrapper/gradle-wrapper.jar,并且会出现以下错误,因此需要进行编辑。
错误:无法找到或加载主类org.gradle.wrapper.GradleWrapperMain。

# Package Files #
-*.jar
+#*.jar
 *.war
 *.ear

在Heroku上设置环境变量。

在Heroku上设置以下环境变量。

KeyValueCHANNEL_SECRETLINEのChannel access tokenCHANNEL_TOKENLINEのChannel secretDATABSE_JDBC_URIjdbc:postgresql://herokuのpostgresのhostname:5432/dbnameDATABSE_USERherokuのpostgresのuser nameDATABSE_PASSWORDherokuのpostgresのpassword

部署

将应用程序部署到Heroku。

$ git remote add heroku https://git.heroku.com/${HEROKU_APP_NAME}.git
$ git add --all
$ git commit -m 'Added a sample codeset for Qiita'
$ git push heroku master

确认移民的结果

当将程序部署到Heroku时,将会在PostgreSQL中创建以下表。
对于在本地使用Intellij等开发工具的情况,只需要在IDE中运行程序,就会在连接的数据库中创建表。

$ heroku pg:psql postgresql-abcdefg-12345 --app line-bot-sample
line-bot-sample::DATABASE=> \d
                     List of relations
 Schema |         Name          |   Type   |     Owner
--------+-----------------------+----------+----------------
 public | flyway_schema_history | table    | sample-user
 public | text_messages         | table    | sample-user
 public | text_messages_id_seq  | sequence | sample-user
(3 rows)

插入

将以下内容插入到数据库中。这将作为回复消息。

line-bot-sample::DATABASE=> insert into text_messages (message) values ('ハローワールド');
INSERT 0 1
line-bot-sample::DATABASE=> insert into text_messages (message) values ('Hello world!!');
INSERT 0 1
line-bot-sample::DATABASE=> select * from text_messages;
 id |    message
----+----------------
  1 | ハローワールド
  2 | Hello world!!
(2 rows)

在LINE上設置Webhook URL。

在LINE开发者的Message API的Webhook设置中,注册heroku的URL。

Webhook URL: https://{HEROKU_APP_NAME}.herokuapp.com/callback
Webhook 使用: 设为启用

IMG_5259.PNG

如果要编写测试代码

在下面创建一个名为test的目录,并在该目录下创建测试代码。

$ mkdir -p sample-spring-boot-kitchensink/src/test/java/com/example/bot/spring/

请在汉语中提供以下内容的同义表述:

借鉴

以下是中文的版本:
https://tosi-tech.net/2019/04/flyway-with-spring-boot/ 可以参考此链接了解如何在Spring Boot中使用Flyway进行数据库迁移。

https://tomokazu-kozuma.com/how-to-migrate-mysql-using-flyway/ 你可以参考此链接了解如何使用Flyway进行MySQL数据库迁移的方法。

相关

    LINE BOT APIのreplyでたまにInvalid reply tokenが発生する
广告
将在 10 秒后关闭
bannerAds