接客スケジュールを複数設定
このページについて
- 接客に複数のスケジュールを設定する方法をチュートリアル形式で説明します。
- Craft Functionsを定期実行して、API経由で接客の公開・非公開を切り替えます。
概要
このチュートリアルでできること・できないこと、全体の構成と作業の概要を説明します。
何を作るか
2023年3月現在、KARTEの接客では開始終了日、曜日単位、そして時間単位の公開スケジュールが設定できます。しかし、曜日・時間が一定ではない場合や開始終了のタイミングが複数ある場合、その都度公開スケジュールを変更する必要があります。
このハンズオンでは接客に複数のスケジュールを設定する機能をCraftを使って実装します。具体的には、接客の公開・停止スケジュールを複数設定できるようにし、なおかつ祝日の場合の公開・停止を設定する機能を実装します。
このチュートリアルで実装した機能でできることとできないことは次のとおりです。
- できること
- 接客のスケジュールを複数設定できる
- 対象の接客について「開始時刻」、「終了時刻」の組を複数設定し、これらの期間内のみ接客を公開できます。
- 接客ごとに「祝日の場合は公開・非公開」が設定できる。
- 次の設定が可能です。
- 「祝日の場合は公開」スケジュール期間中かつ祝日の場合は公開する
- 「祝日の場合は非公開」スケジュール期間中であっても、祝日の場合は非公開にする
- 「祝日を考慮しない」祝日による公開・非公開を設定しない
- 次の設定が可能です。
- 接客のスケジュールを複数設定できる
- できないこと
- 秒単位の公開・非公開設定
- 接客の公開・非公開を行う処理の頻度が最短で1分となるためです。
- KARTE接客のスケジュールとの同時設定
- これらのスケジュール設定は全て空にする必要があります。
- 秒単位の公開・非公開設定
構成図
作成する機能の全体像は次の通りです。
構成要素は次の通りです。
- Craft Functions
- コード実行環境です。Functionとして記述したコードで次の処理を行います。
- コード内に記述したスケジュール(開始時刻・終了時刻の組)に基づき、対象接客を公開・非公開します。
- Craft Scheduler
- Craft Functionsを定期的に実行する機能です。
- KARTE Action
- KARTEの接客機能です。
- 内閣府ウェブサイト
- 祝日情報の一覧をCSVファイルで公開しています。次のURLからCSVファイルがダウンロードできます。
- https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv
- 管理者
- 接客スケジュールを設定する人です。KARTEアカウントが必要です。
ハンズオン
次の手順で接客スケジュール機能を実装していきます。
前提
KARTEプロジェクトで次の機能を有効化する必要があります。
- API v2
- Craft
作業概要
初回の設定作業では、Functionsを作成し、スケジュールを設定します。
- API v2設定
- Functionsの作成
- コードのデプロイ
- Craft Schedulerの設定
- 接客スケジュールの設定
- コード内の
CAMPAIGN_SCHEDULES
を設定します。 - Functionsを更新します。
- コード内の
運用作業としては接客スケジュールの更新があります。更新の手順は上記の接客スケジュールの設定と同じです。
移行、各作業について説明します。
Step1. API v2設定
- 次のリンクを参考に、API v2のAppを作成し、アクセストークンを控えてください。
- https://developers.karte.io/docs/how-to-use-api-v2
- 作成したAppのscopeに
beta.action.campaign.toggleEnabled
のscopeを設定してください。
Step2 Functionの作成
- 前の手順で設定したAppのアクセストークンをシークレットに登録します。
- KARTE管理画面で[すべてのプロダクト]>[Craft]>[シークレット]を選択し、シークレット一覧画面を開きます。
- [作成]を選択し、シークレット情報を記入します。
- シークレット名は英語大文字とアンダースコアで任意の名前(ex:
KARTE_API_TOKEN
)を指定します。
- シークレット名は英語大文字とアンダースコアで任意の名前(ex:
- データには前の手順で控えたアクセストークンを指定します。
- Functionを作成します。
- KARTE管理画面で[すべてのプロダクト]>[Craft]>[ファンクション]を選択し、ファンクション一覧画面を開きます。
- ファンクション一覧画面で「コードから作成」を選択し、必要項目を入力します。
- ファンクション名には任意の名前を指定してください。ここでは
campaign-scheduler
とします。 - codeは後述の「Functionのコード」を貼り付けてください。
- 「変数」タブを開き、次の値を指定します
- 変数名:
KARTE_API_SECRET_NAME
- 値:先ほど作成したシークレット名
- 型:文字列
- 変数名:
LOG_LEVEL
- 値:
WARN
(他にもDEBUG
,INFO
,ERROR
が有効) - 型:文字列
- 値:
- 変数名:
- ファンクション名には任意の名前を指定してください。ここでは
- 入力が完了したら「デプロイ」を選択します。
- Functionの実行スケジュールを設定します。
- campaign-scheduler Functionの詳細画面から[設定]タブを開きます。
- スケジュール欄で[作成]を選択します。
- 設定をcron式で記述し、[保存]を選択します
- 例
* * * *
毎分実行/5 * * * *
5分ごとに実行10 * * * *
毎時10分に実行
- 例
Functionのコード
Step2. でデプロイするFunctionのコードは次のとおりです。
import api from "api";
/********************************************** * 管理者向け設定 **********************************************/
// 接客スケジュール// デプロイ時に適宜設定するconst CAMPAIGN_SCHEDULES = [ // 祝日判定なしの例 { campaign_id: "aaaaaaaaaaaaaaaaaaaaaaaa", schedules: [ { start: "2023-02-01T16:00:00+09:00", end: "2023-02-03T16:30:00+09:00" }, ], },
// 祝日のみ無効の例 { campaign_id: "bbbbbbbbbbbbbbbbbbbbbbbb", on_holiday: "disable", schedules: [ { start: "2023-02-01T16:00:00+09:00", end: "2023-02-01T16:30:00+09:00" }, ], },
// スケジュール期間内かつ祝日のみ有効にする例 { campaign_id: "cccccccccccccccccccccccc", on_holiday: "enable", schedules: [ { start: "2023-02-01T16:00:00+09:00", end: "2023-02-03T16:30:00+09:00" }, { start: "2023-02-02T16:00:00+09:00", end: "2023-02-13T16:00:00+09:00" }, ], },];
/********************************************** * 各種処理 **********************************************/
const KARTE_API_SECRET_NAME = "<% KARTE_API_SECRET_NAME %>"; // KARTE APIトークンを保存するシークレット名const LOG_LEVEL = "<% LOG_LEVEL %>"; // 'DEBUG', 'INFO', 'WARN', 'ERROR' のいずれか
// 各種定数定義
// UTCからの時差const HOUR_DELTA = 9;
// 時差分を加えた時刻を返すfunction getOffsetDate(date, hourDelta) { const delta = 1000 * 60 * 60 * hourDelta; // msec return new Date(date.getTime() + delta);}
function getDateString(date) { return `${date.getUTCFullYear()}/${ date.getUTCMonth() + 1 }/${date.getUTCDate()}`;}
/** * 祝日一覧CSVから祝日情報を文字列として取得する * @returns 祝日('YYYY-MM-DD')の配列 */async function getHolidays(logger) { const HOLIDAYS_LIST_URL = "https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv"; const res = await fetch(HOLIDAYS_LIST_URL, { method: "GET", headers: { Accept: "text/csv", }, });
if (res.status !== 200) { logger.error(res); throw new Error("cannot get holidays data."); }
// 祝日一覧のCSVから日時情報のみの一覧を取得する return (await res.text()) .split("\n") .slice(1) .map((line) => line.split(",")[0]);}
/** * 接客の公開・非公開を判定する * @param {*} logger logger モジュール * @param {Object} param0 現在日時(date: Date)、祝日一覧(holidays: string[])、接客情報(campaign: object) からなるオブジェクト * @returns 接客の公開(true), 非公開(false) を返す */const isEnable = (logger, { date, holidays, campaign }) => { // 現在時刻がスケジュール内かどうか const onSchedule = campaign.schedules .map((schedule) => { const start = getOffsetDate(new Date(schedule.start), HOUR_DELTA); const end = getOffsetDate(new Date(schedule.end), HOUR_DELTA); if (date.getTime() < start.getTime()) return false; if (date.getTime() >= end.getTime()) return false; return true; }) .filter((v) => v).length > 0;
// 現在が祝日かどうか const dateString = getDateString(date); const onHoliday = holidays.includes(dateString);
if (campaign.on_holiday == null) { return onSchedule; }
if (campaign.on_holiday === "enable") { return onSchedule && onHoliday; } else if (campaign.on_holiday === "disable") { return onSchedule && !onHoliday; }
logger.warn( '"on_holiday" parameter is valid only with "enable" and "disable"' ); return onSchedule;};
export default async function (data, { MODULES }) { const { secret, initLogger } = MODULES; const logger = initLogger({ logLevel: LOG_LEVEL });
// 現在時刻と祝日一覧を取得する logger.log("get date & holidays"); const date = getOffsetDate(new Date(), HOUR_DELTA); const holidays = await getHolidays(logger);
// 接客APIクライアント logger.log("get KARTE API Client"); let action; try { const { [KARTE_API_SECRET_NAME]: token } = await secret.get({ keys: [KARTE_API_SECRET_NAME], }); action = api("@dev-karte/v1.0#16x5jb2alnwwkn1v"); // 補足を参照 action.auth(token); } catch (err) { logger.error(err); throw err; }
logger.log("start check schedule"); for (const campaign of CAMPAIGN_SCHEDULES) { logger.log(JSON.stringify(campaign)); const enabled = await isEnable(logger, { date, holidays, campaign }); const res = await action.postV2betaActionCampaignToggleenabled({ id: campaign.campaign_id, enabled, }); logger.log(JSON.stringify(res)); logger.log( `dateString: ${getDateString( date )}, time: ${date.toISOString()}, campaign: ${ campaign.campaign_id }, enabled: ${enabled}` ); }}
packagesには次の値を指定します。
{ "api": "5.0.8"}
コードの補足をします。
- 祝日一覧は
getHolidays()
で取得します。内閣府のCSVファイルに合わせて祝日一覧を取得しているので、別の情報元から祝日一覧を取得する場合はこの部分を書き換えればOKです。 - 祝日判定のため、世界標準時からの時差を
HOUR_DELTA
で定義しています。日本時間は標準時より9時間進んでいるため、(+) 9を設定しています。別のタイムゾーンで祝日を判定する場合はこの値を変更してください。 - 42, 43行目でFunctionの変数を展開しています。詳細は 変数の利用 を参照してください。
api()
のパラメータは次のAPIリファレンスのNode.jsサンプルに記載のSpec URIを指定してください。
Step3. 接客スケジュールの設定・更新
次の手順で接客スケジュールを設定・更新します。
- campaign-schedulerファンクションの詳細画面を開き、[基本設定]タブの[編集]を選択します。
- [code]の編集欄で
CAMPAIGN_SCHEDULES
変数の定義を変更します。- 詳細は後述の「接客スケジュールの設定方法」を参照してください。
- [保存]を選択し、Functionを更新します。
接客スケジュールの設定方法
接客スケジュールはコード内の変数 CAMPAIGN_SCHEDULES
で定義します。
const CAMPAIGN_SCHEDULES = [ { campaign_id: 'xxxxxxxxxxxxxxxxxx', on_holiday: '(enable|disable)' schedules: [ { start: 'start_date', end: 'end_date' }, ... ], }, ...];
CAMPAIGN_SCHEDULES
は次の要素からなるオブジェクトの配列として定義しています。
要素 | 説明 |
---|---|
campaign_id | 接客サービス詳細画面のURLの末尾24桁を指定してください。 ex) URLが https://admin.karte.io/p/0123456789abcdef01234567/service/1234567890abcdef12345678 の場合、 service/ 以降の24桁の文字列 1234567890abcdef12345678 を指定します。 |
on_holiday | 祝日の場合の挙動を指定します。 enable の場合は祝日の場合のみ有効となります。 disableの場合は祝日の場合のみ無効となります。 この要素は省略可能です。省略した場合、祝日の挙動は平日と変わりません。 |
schedules | start end を要素に持つオブジェクトの配列としてスケジュールの開始終了日時を指定します。日時はRFC3309の表記に従って指定してください。 ex) 2023-02-01T16:00:00+09:00 = 2023/02/01 16:00(日本時間) 2023-03-01T12:00:00Z = 2023/03/01 12:00(世界標準時) = 2023/03/01 21:00(日本時間) |
設定例は次の通りです。
-
設定例
// ダブルスラッシュで始まる行はコメントです。// Craft Functionはこの行を無視してプログラムを実行するので、設定の際に適宜ご活用ください。const CAMPAIGN_SCHEDULES = [// 2/1 16:00 - 16:30 (日本時間) の間接客を公開する{campaign_id: '1234567890abcdef11111111',schedules: [{ start: '2023-02-01T16:00:00+09:00', end: '2023-02-01T16:30:00+09:00' }],},// 2/1 16:00 - 2/15 16:00 の間接客を公開する// 祝日(この場合は建国記念日)のみ無効となる{campaign_id: '1234567890abcdef22222222',on_holiday: 'disable',schedules: [{ start: '2023-02-01T16:00:00+09:00', end: '2023-02-15T16:00:00+09:00' }],},// スケジュール期間内かつ祝日のみ有効// この設定では日本時間の 2023-02-11 のみ接客を公開する{campaign_id: '1234567890abcdef33333333',on_holiday: 'enable',schedules: [{ start: '2023-02-01T16:00:00+09:00', end: '2023-02-01T16:30:00+09:00' },{ start: '2023-02-02T16:00:00+09:00', end: '2023-02-15T16:00:00+09:00' },],},];
停止/削除方法
この機能の停止方法、削除方法は次のとおりです。
- 停止方法
- Craft Functionsのスケジュール設定を削除します。
- スケジュールの削除により、Craft Functionsの定期実行が終了します。
- Craft Functionsのスケジュール設定を削除します。
- 完全に削除する方法
- (初回のみ) の手順で作成した各コンポーネントを削除します。
- Function
- シークレット設定
- API v2 App
- (初回のみ) の手順で作成した各コンポーネントを削除します。
まとめ
このハンズオンでは、Craftを活用して「複数の接客スケジュール設定」機能を実装しました。具体的には、現在時刻と接客スケジュールを判定して接客の公開・非公開を行うFunctionを作成し、そのFunctionを定期実行することで、標準機能では設定できないスケジュール設定を実現しました。また、内閣府Webサイトの休日情報に基づく休祝日判定も実装しました。