LINE SDK for Java 6.0.0を使用した実装

LINE SDK for Java 6.0.0を使用した実装を作成しましょう。



この実装には以下の特徴があります：

1. **LINE SDK の活用**
   - LINE Bot SDK for Java 6.0.0を使用
   - `LineMessagingClient`を使用して LINE Messaging API と通信
   - ボタンテンプレートメッセージを使用してリンクを送信

2. **Spring Framework との統合**
   - `@Service` アノテーションでサービスクラスを定義
   - 設定値は `application.yml` から注入
   - Spring の DI を活用した設計

3. **エラーハンドリングとログ記録**
   - SLF4J を使用したログ出力
   - 適切な例外処理
   - 非同期処理のハンドリング

4. **設定管理**
   - チャンネルトークンやベースURLを外部設定ファイルで管理
   - 環境に応じた設定変更が容易

使用方法：

1. **依存関係の追加**
```xml
<dependency>
    <groupId>com.linecorp.bot</groupId>
    <artifactId>line-bot-sdk-java</artifactId>
    <version>6.0.0</version>
</dependency>
```

2. **設定ファイルの作成**
```yaml
line:
  bot:
    channel-token: YOUR_CHANNEL_TOKEN
  account:
    link:
      base-url: https://example.com/link
```

3. **API呼び出し例**
```
POST /api/line/account-link?userId=xxx&linkToken=yyy
```

この実装を使用する際の注意点：

1. チャンネルトークンは必ず安全に管理してください
2. ベースURLは適切なSSL証明書を持つHTTPSエンドポイントを使用してください
3. リンクトークンの有効期限を考慮した実装を検討してください
4. 必要に応じてメッセージの文言をカスタマイズしてください

実装をカスタマイズする場合は、メッセージテンプレートの内容や、エラーハンドリングの方法、ログ出力の詳細度などを調整できます。


```java:line-account-link-java.java
// pom.xmlの依存関係
/*
<dependency>
    <groupId>com.linecorp.bot</groupId>
    <artifactId>line-bot-sdk-java</artifactId>
    <version>6.0.0</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.32</version>
</dependency>
*/

package com.example.linebot.service;

import com.linecorp.bot.client.LineMessagingClient;
import com.linecorp.bot.model.PushMessage;
import com.linecorp.bot.model.action.URIAction;
import com.linecorp.bot.model.message.TemplateMessage;
import com.linecorp.bot.model.message.template.ButtonsTemplate;
import com.linecorp.bot.model.response.BotApiResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

@Service
public class LineAccountLinkService {

    private static final Logger logger = LoggerFactory.getLogger(LineAccountLinkService.class);

    private final LineMessagingClient lineMessagingClient;
    private final String baseUrl;

    public LineAccountLinkService(
            @Value("${line.bot.channel-token}") String channelToken,
            @Value("${line.account.link.base-url}") String baseUrl
    ) {
        this.lineMessagingClient = LineMessagingClient
                .builder(channelToken)
                .build();
        this.baseUrl = baseUrl;
    }

    /**
     * アカウント連携URLをユーザーに送信します
     *
     * @param userId LINEユーザーID
     * @param linkToken アカウント連携トークン
     * @return 送信結果
     * @throws ExecutionException 実行時エラー
     * @throws InterruptedException 割り込みエラー
     * @throws URISyntaxException URI構文エラー
     */
    public BotApiResponse sendAccountLinkUrl(String userId, String linkToken) 
            throws ExecutionException, InterruptedException, URISyntaxException {
        
        String linkUrl = generateAccountLinkUrl(linkToken);
        
        ButtonsTemplate buttonsTemplate = new ButtonsTemplate(
                null, // サムネイルURL（null可）
                "アカウント連携",
                "以下のボタンをタップしてアカウントを連携してください",
                Collections.singletonList(
                        new URIAction(
                                "アカウントを連携する",
                                new URI(linkUrl),
                                null // エリアパラメータ（null可）
                        )
                )
        );

        TemplateMessage templateMessage = new TemplateMessage(
                "アカウント連携", // 代替テキスト
                buttonsTemplate
        );

        PushMessage pushMessage = new PushMessage(
                userId,
                templateMessage
        );

        CompletableFuture<BotApiResponse> future = lineMessagingClient.pushMessage(pushMessage);

        try {
            BotApiResponse response = future.get();
            logger.info("Account link URL sent successfully. userId: {}, linkToken: {}", userId, linkToken);
            return response;
        } catch (InterruptedException | ExecutionException e) {
            logger.error("Failed to send account link URL. userId: {}, error: {}", userId, e.getMessage(), e);
            throw e;
        }
    }

    /**
     * アカウント連携URLを生成します
     *
     * @param linkToken アカウント連携トークン
     * @return 生成されたURL
     * @throws URISyntaxException URI構文エラー
     */
    private String generateAccountLinkUrl(String linkToken) throws URISyntaxException {
        return new URI(baseUrl + "?linkToken=" + linkToken).toString();
    }
}

// Controllerの実装例
@RestController
@RequestMapping("/api/line")
public class LineAccountLinkController {

    private final LineAccountLinkService lineAccountLinkService;

    @Autowired
    public LineAccountLinkController(LineAccountLinkService lineAccountLinkService) {
        this.lineAccountLinkService = lineAccountLinkService;
    }

    @PostMapping("/account-link")
    public ResponseEntity<String> sendAccountLinkUrl(
            @RequestParam("userId") String userId,
            @RequestParam("linkToken") String linkToken
    ) {
        try {
            BotApiResponse response = lineAccountLinkService.sendAccountLinkUrl(userId, linkToken);
            return ResponseEntity.ok("Account link URL sent successfully");
        } catch (Exception e) {
            return ResponseEntity
                    .status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body("Failed to send account link URL: " + e.getMessage());
        }
    }
}

// application.yml の設定例
/*
line:
  bot:
    channel-token: YOUR_CHANNEL_TOKEN
  account:
    link:
      base-url: https://example.com/link
*/
```