<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>SMS DataTech</title>
	<atom:link href="https://www.sms-datatech.co.jp/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.sms-datatech.co.jp</link>
	<description></description>
	<lastBuildDate>Tue, 23 Jun 2026 00:29:37 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://www.sms-datatech.co.jp/wp-content/uploads/2022/09/cropped-cropped-favicon-180x180-1-32x32.png</url>
	<title>SMS DataTech</title>
	<link>https://www.sms-datatech.co.jp</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>商業施設の設備台帳管理をExcelで続ける限界とは？FM・PM業務を効率化する方法</title>
		<link>https://www.sms-datatech.co.jp/column/aut_equipment-list/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=aut_equipment-list</link>
		
		<dc:creator><![CDATA[佐々木]]></dc:creator>
		<pubDate>Tue, 23 Jun 2026 00:29:36 +0000</pubDate>
				<category><![CDATA[コラム]]></category>
		<category><![CDATA[自動化ソリューション]]></category>
		<category><![CDATA[自動化]]></category>
		<category><![CDATA[Pleasanter]]></category>
		<guid isPermaLink="false">https://www.sms-datatech.co.jp/?p=36368</guid>

					<description><![CDATA[<p>商業施設や複数店舗を運営していると、テナントの入れ替え、設備更新、法定点検、修繕対応などに合わせて、設備台帳を確認・更新する機会が多くあります。しかし、設備台帳をExcelで管理している場合、物件数やテナント数が増えるにつれて、「最新版のファイルが分からない」「図面や点検結果が別々の場所にある」「必要な情報を探すだけで時間がかかる」といった課題が起こりやすくなります。ファシリティマネジメント（FM）・プロパティマネジメント（PM）担当者にとって、設備台帳は単なる一覧表ではありません。テナント対応、法定点検、修繕計画、資産価値の維持を支える重要な情報基盤です。本記事では、商業施設や複数物件の設備台帳をExcelで管理する際に起きやすい課題を整理したうえで、設備台帳を一元管理する方法や、Webデータベースを活用した管理体制の整え方を解説します。あわせて、プリザンターを設備台帳管理システムとして活用する方法や、約500店舗規模での設備データ一元管理事例も紹介します。 FM・PMにおける設備台帳とは FM・PMにおける設備台帳とは、建物や施設に設置されている設備の情報を、管理・点検・修繕・更新のために整理した台帳です。単に「設備名と設置日を記録する一覧表」ではありません。商業施設やオフィスビル、複合施設などを管理する現場では、テナント対応、法定点検、修繕計画、資産価値の維持に関わる重要な情報基盤になります。設備台帳に含める情報としては、主に次のようなものがあります。 商業施設や一定規模以上の建物では、建築基準法や消防法に基づく定期報告・点検報告が関係するケースもあります。そのため、設備台帳は日常管理だけでなく、行政への報告や監査対応に必要な資料をすぐに確認できる状態にしておくことが重要です。 商業施設の設備台帳をExcelで管理する5つの課題 Excelは手軽に使えるため、設備台帳の管理にもよく利用されます。一方で、複数物件や多数のテナントを管理する商業施設では、Excelだけの管理に限界を感じる場面も少なくありません。 1. 法定点検の期限・結果が物件ごとに分散する 法定点検や定期報告に関する情報は、期限管理が特に重要です。物件ごとにExcelファイルが分かれていたり、点検結果がPDFや紙で保管されていたりすると、次回期限や是正対応の状況を横断的に確認しにくくなります。Excelは一覧管理には便利ですが、期限が近づいた情報の抽出や、点検結果・届出書類とのひも付けには工夫が必要です。商業施設の設備管理でExcelの限界を感じやすいのは、期限管理、証跡管理、関連書類の管理が同時に求められる場面です。もしExcelの更新漏れで点検義務を怠ってしまった場合、コンプライアンス違反として行政指導の対象になるだけでなく、最悪のケースでは施設の営業停止など、企業の信用を揺るがす重大なリスクに直結します。 2. 複数施設の修繕計画を横断して把握できない PM担当者にとって、修繕履歴や更新予定を横断して把握することは、予算計画や中長期の資産管理に直結します。しかし、物件ごとにExcel台帳が分かれていると、どの設備が老朽化しているのか、どの施設で修繕費が集中しているのか、次年度以降にどの程度の更新コストが見込まれるのかを把握しにくくなります。結果として、突発的な修繕対応が増えたり、優先順位の判断が担当者の経験に依存したりすることがあります。このように「後手後手」の修繕が続くと、本来なら計画的に抑えられたはずのコストが数倍に膨れ上がるだけでなく、突然の設備トラブルでテナントの営業を止めてしまい、損害賠償に発展する恐れもあります。 3. テナント入れ替えのたびに設備情報の確認に時間がかかる 商業施設では、テナントの入れ替えに合わせて、電気容量、空調、給排水、厨房設備、通信環境などの確認が必要になります。しかし、設備情報がExcel、図面、メール添付、共有フォルダ、担当者の個人PCなどに分散していると、必要な情報を探すだけで時間がかかります。さらに、見つけたExcelが最新版かどうか判断できない場合、現地確認や関係者への問い合わせが増え、対応スピードが落ちてしまいます。 4. 担当者に属人化し、異動・退職で情報が失われる プロパティマネジメントにおける台帳管理では、担当者の経験や記憶に頼っている情報が少なくありません。「あの設備の履歴は前任者しか知らない」「どのフォルダに図面があるか分からない」「修繕時の経緯がメールにしか残っていない」といった状態になると、異動や退職のたびに引き継ぎ負荷が大きくなります。Excelは自由度が高い反面、入力ルールや保存場所が統一されにくく、属人化を招きやすい点に注意が必要です。 5. 外部委託先・テナントとの情報共有が煩雑になる 設備管理では、ビル管理会社、工事会社、点検会社、テナントなど、社外の関係者と情報を共有する場面も多くあります。Excelをメールで送付する運用では、どのファイルが最新版か分からなくなりやすく、誤送信や不要な情報共有のリスクも高まります。特に、図面や設備情報には施設運営上の重要情報が含まれるため、必要な相手に必要な範囲だけ共有できる仕組みが求められます。 Excel管理の課題解決には、設備台帳管理システムの一元管理が重要 Excel管理で起きる課題の多くは、単にExcelの機能不足だけが原因ではありません。設備情報、点検情報、図面、修繕履歴、テナント対応履歴が別々に管理されていることで、必要な情報をすぐに確認できない状態になっていることが大きな要因です。そのため、設備台帳の管理を見直す際は、まず「どのツールを使うか」ではなく、「どの情報を、誰が、どのタイミングで確認・更新できる状態にするか」を整理することが重要です。複数物件の設備台帳を管理する方法としては、設備情報を物件・フロア・テナント・設備単位で整理し、点検結果や修繕履歴、図面などの関連情報をひも付けて管理する仕組みが有効です。設備台帳を一元管理できる状態にすると、次のようなメリットがあります。 特に商業施設では、テナント管理と設備情報の一元化が重要です。テナントの入れ替えや改装工事のたびに、設備容量、図面、過去の工事履歴、行政への届出書類などを確認する必要があるため、情報が分散しているほど確認作業に時間がかかります。 失敗しない設備台帳管理システムの選び方 設備台帳をExcelから脱却してシステム化しようと考えた場合、選択肢は大きく分けて2つあります。 システム選定で失敗しないための最大のポイントは、「自社独自の運用ルールや、既存のExcelの手軽さをそのまま活かせるか」という点です。専用パッケージは多機能な反面、コストが高額になりがちで、自社の業務フローをシステム側に合わせなければならないケースも少なくありません。そのため、「コストを抑えつつ、今使っているExcelの使いやすさを活かして自社にフィットしたシステムを作りたい」という企業の間では、Webデータベースを活用して自社専用の設備台帳管理システムを構築する方法が選ばれています。設備台帳をWebデータベースへ移行すると、Excelのように一覧で確認しやすい利便性を活かしながら、情報の一元管理、権限設定、更新履歴の確認が格段にしやすくなり、業務の課題をスムーズに解決できます。たとえば、Webデータベースを活用した設備管理システムでは、次のような具体的なメリットが得られます。 FM・PM業務の課題 Excel管理で起きやすいこと Webデータベースで改善できること テナント入れ替え時の設備確認 最新版のExcelや図面を探す必要がある 物件・フロア・テナント・設備をひも付けて確認できる 法定点検の期限管理 物件ごとにファイルが分かれ、期限管理が漏れやすい 点検期限・結果・是正状況を一元管理できる 修繕計画の把握 複数施設の修繕履歴を横断して見にくい 修繕履歴や更新予定を一覧化・集計できる 担当者交代・引き継ぎ 情報の所在や更新ルールが属人化しやすい 履歴や関連資料を同じ場所に蓄積できる 外部委託先・テナントとの共有 メール添付では版管理や共有範囲の管理が難しい 権限に応じて必要な情報だけを共有できる 設備台帳をWebデータベースで管理する場合、重要なのは「ExcelをそのままWeb化すること」ではありません。現場で使いやすいように、物件、フロア、設備、点検、修繕、図面、テナントなどの情報をどのように関連付けるかを設計することです。この設計ができていないと、せっかくシステム化しても「入力しづらい」「必要な情報を探しにくい」といった状態が残ってしまいます。そのため、自社に最適な設備台帳管理システムを構築する際は、業務の流れに合わせて情報の持ち方を整理することが大切です。そこで、数あるWebデータベースの中でも、「既存のExcelの手軽さを最も活かしやすく、現場に合わせた柔軟な設計が低コストで実現できるツール」として、多くの企業に選ばれているのが「プリザンター」です。 プリザンターを活用した設備台帳管理方法の例 設備台帳をWebデータベースで一元管理する方法の一つとして、プリザンターの活用が考えられます。プリザンターは、ノーコード・ローコードで業務アプリを構築できるWebデータベースです。Excelのような一覧で確認しやすい利便性を活かしながら、情報の一元管理、権限設定、履歴管理、ダッシュボード化などを行うことができます。たとえば、商業施設の設備台帳管理では、次のような使い方が考えられます。 プリザンターを活用することで、設備台帳、点検情報、図面、修繕履歴を別々に管理するのではなく、同じ情報基盤の上で確認・更新しやすくなります。ただし、プリザンターは柔軟性が高い分、自社の業務に合わせた設計が重要です。どの情報を台帳化するのか、誰が更新するのか、どの画面を日常的に使うのかを整理したうえで構築することで、現場で使われる設備台帳管理システムに近づきます。 設備台帳管理システムを導入する際の進め方 設備台帳の管理を見直す際、最初からシステム選定に入る必要はありません。現在の情報がどこにあり、どの情報から一元管理すべきかを整理しないまま進めると、システム化しても使われない台帳になってしまうことがあります。ここでは、ExcelからWebデータベースへの移行を検討する前に取り組みたい3つのステップを紹介します。 ステップ1：今ある台帳・書類を棚卸しする まずは、設備情報がどこに散らばっているかを書き出します。Excelファイル、紙の書類、共有フォルダ、メール添付、個人PC、委託先が保管している資料など、形式を問わず洗い出すことが重要です。設備台帳、点検報告書、図面、修繕見積、工事完了報告書など、関連資料も含めて確認します。この段階で意識したいのは、「整理されていない情報をシステムに移しても、整理された状態にはならない」という点です。まずは現状を可視化し、重複している情報、古い情報、更新責任が曖昧な情報を把握することが第一歩になります。 ステップ2：一元管理する情報の優先順位を決める 次に、どの情報から一元管理するかを決めます。すべての情報を一度にシステム化しようとすると、項目設計が複雑になり、現場の入力負荷も大きくなります。優先すべきなのは、更新漏れや確認遅れが業務リスクにつながる情報です。たとえば、法定点検の期限管理、テナント入れ替え時に必ず参照する設備情報、修繕判断に必要な老朽化状況などは、早い段階で一元管理の対象にしやすい情報です。最初から完璧な台帳を目指すのではなく、業務上のリスクが高い情報から整備することで、現場にとって使う意味のある仕組みになります。 ステップ3：ExcelとWebデータベースの使い分けを決める Excel管理を見直すといっても、Excelを完全になくす必要はありません。重要なのは、元データやマスタ情報をWebデータベースに集約し、必要に応じてExcelを分析や帳票出力に活用するという使い分けです。たとえば、設備情報、点検期限、修繕履歴、図面リンクなどはWebデータベースで一元管理し、会議資料や予算検討用の集計表はExcelに出力して加工するといった運用が考えられます。このように役割を分けることで、Excelの使いやすさを残しながら、情報の分散や版管理の課題を減らすことができます。 設備台帳管理システムの構築でSMSデータテックが支援できること 設備台帳管理システムを構築する際は、ツールの導入だけでなく、自社の業務に合わせた情報設計が重要です。特にFM・PM業務では、物件、フロア、設備、点検、修繕、図面、委託先、テナントなど、関係する情報が多岐にわたります。SMSデータテックでは、プリザンターの導入支援を通じて、設備台帳管理に必要な設計・構築・運用改善を支援しています。具体的には、次のような支援が可能です。 プリザンターは柔軟性の高いツールですが、自由度が高いからこそ、最初の設計が使いやすさを大きく左右します。項目を増やしすぎると入力負荷が高くなり、反対に情報が不足していると業務に活用しにくくなります。そのため、設備台帳管理システムを導入する際は、現在の管理方法や業務フローを整理したうえで、自社に合った形で設計することが大切です。SMSデータテックは、インプリム社主催の「Pleasanter Partner Award 2025」において「Top Revenue Award（トップレベニュー賞）」を受賞しており、プリザンターの導入支援・構築・運用サポートを通じて、企業の業務効率化やDX推進を支援してきた実績があります。 プリザンターによる設備台帳管理システムの導入事例 SMSデータテックでは、JR西日本デイリーサービスネット様におけるプリザンター活用を支援した事例があります。同社では、駅構内の店舗や商業施設、ホテルなどを運営するなかで、店舗・設備に関する情報が紙、Excel、Word、個人PC、サーバーなどに分散していることが課題になっていました。テナントの入れ替え時には、店舗にどのような設備があるのか、いつ更新されたのか、どの資料が最新版なのかを把握する必要があります。しかし、情報の所在や更新状況が分かりにくい状態では、必要な資料を探すだけでも時間がかかります。そこで、約500店舗の設備データ、図面、工事費、議事録、行政への届出書類などをプリザンターへ移行し、Web上で一元管理できる環境づくりを進めました。導入の結果、これまで各拠点や個人PCに散らばっていた「最新の設備情報」や「過去の修繕履歴」へ、関係者全員がアクセスできるようになりました。これにより、情報検索にかかっていた膨大な時間が大幅に削減され、テナント入れ替え時の対応スピード向上や、引き継ぎ・情報共有の効率化という確かな成果に繋がっています。この事例は、複数店舗や複数施設の設備台帳を管理する企業にとって参考になるケースです。設備台帳を単なるExcelファイルとして保管するのではなく、関係者が同じ情報を参照できるWebデータベースとして整備することで、確認作業や情報共有の負担を減らしやすくなります。自社でも同じように、店舗・設備・図面・工事関連資料が複数の場所に分散している場合は、どのような手順で一元管理へ移行したのかを確認しておくと、導入後のイメージを持ちやすくなります。➡約500店舗規模での設備データ一元管理事例を見る まとめ 複数物件や商業施設の設備台帳をExcelだけで管理し続けると、テナント入れ替え時の確認負荷、法定点検の期限管理、修繕計画の把握、担当者への属人化、外部関係者との情報共有といった課題が表面化しやすくなります。FM・PM業務における設備台帳は、単なる一覧表ではなく、施設運営の安全性、業務効率、資産価値の維持を支える情報基盤です。だからこそ、設備情報、点検結果、図面、修繕履歴を分散させず、関係者が必要な情報にアクセスできる状態を整えることが重要です。まずは、現在の台帳・書類・図面・点検記録がどこに保管されているのかを棚卸しし、どの情報を優先的に一元管理すべきかを整理することから始めてみてください。そのうえで、Webデータベースやプリザンターを活用することで、Excelの使いやすさを活かしながら、FM・PM業務に適した管理体制を構築しやすくなります。運用の属人化を防ぐためにも、導入を検討している方はお気軽にご相談ください。</p>
<p>The post <a href="https://www.sms-datatech.co.jp/column/aut_equipment-list/">商業施設の設備台帳管理をExcelで続ける限界とは？FM・PM業務を効率化する方法</a> first appeared on <a href="https://www.sms-datatech.co.jp">SMS DataTech</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>商業施設や複数店舗を運営していると、テナントの入れ替え、設備更新、法定点検、修繕対応などに合わせて、設備台帳を確認・更新する機会が多くあります。しかし、設備台帳をExcelで管理している場合、物件数やテナント数が増えるにつれて、「最新版のファイルが分からない」「図面や点検結果が別々の場所にある」「必要な情報を探すだけで時間がかかる」といった課題が起こりやすくなります。ファシリティマネジメント（FM）・プロパティマネジメント（PM）担当者にとって、設備台帳は単なる一覧表ではありません。テナント対応、法定点検、修繕計画、資産価値の維持を支える重要な情報基盤です。本記事では、商業施設や複数物件の設備台帳をExcelで管理する際に起きやすい課題を整理したうえで、設備台帳を一元管理する方法や、Webデータベースを活用した管理体制の整え方を解説します。あわせて、プリザンターを設備台帳管理システムとして活用する方法や、約500店舗規模での設備データ一元管理事例も紹介します。</p>



<h2 class="wp-block-heading">FM・PMにおける設備台帳とは</h2>



<p><span class="sms-highlight" style="background-color: #fdff84; font-weight: 700; padding: 0 0.08em; box-decoration-break: clone; -webkit-box-decoration-break: clone;">FM・PMにおける設備台帳とは、建物や施設に設置されている設備の情報を、管理・点検・修繕・更新のために整理した台帳です。</span>単に「設備名と設置日を記録する一覧表」ではありません。商業施設やオフィスビル、複合施設などを管理する現場では、テナント対応、法定点検、修繕計画、資産価値の維持に関わる重要な情報基盤になります。設備台帳に含める情報としては、主に次のようなものがあります。</p>



<ul class="wp-block-list">
<li>電気・通信・空調・給排水・消防・厨房設備などの設備情報</li>



<li>図面・竣工書類・工事資料</li>



<li>法定点検の結果・次回期限・届出書類</li>



<li>修繕履歴・工事費・設備の更新サイクル</li>



<li>テナント入れ替え時の確認事項や対応履歴</li>
</ul>



<p>商業施設や一定規模以上の建物では、建築基準法や消防法に基づく定期報告・点検報告が関係するケースもあります。そのため、設備台帳は日常管理だけでなく、行政への報告や監査対応に必要な資料をすぐに確認できる状態にしておくことが重要です。</p>



<h2 class="wp-block-heading">商業施設の設備台帳をExcelで管理する5つの課題</h2>



<p>Excelは手軽に使えるため、設備台帳の管理にもよく利用されます。一方で、複数物件や多数のテナントを管理する商業施設では、Excelだけの管理に限界を感じる場面も少なくありません。</p>



<h3 class="wp-block-heading">1. 法定点検の期限・結果が物件ごとに分散する</h3>



<p>法定点検や定期報告に関する情報は、期限管理が特に重要です。物件ごとにExcelファイルが分かれていたり、点検結果がPDFや紙で保管されていたりすると、次回期限や是正対応の状況を横断的に確認しにくくなります。Excelは一覧管理には便利ですが、期限が近づいた情報の抽出や、点検結果・届出書類とのひも付けには工夫が必要です。商業施設の設備管理でExcelの限界を感じやすいのは、期限管理、証跡管理、関連書類の管理が同時に求められる場面です。もしExcelの更新漏れで点検義務を怠ってしまった場合、コンプライアンス違反として行政指導の対象になるだけでなく、最悪のケースでは施設の営業停止など、企業の信用を揺るがす重大なリスクに直結します。</p>



<h3 class="wp-block-heading">2. 複数施設の修繕計画を横断して把握できない</h3>



<p>PM担当者にとって、修繕履歴や更新予定を横断して把握することは、予算計画や中長期の資産管理に直結します。しかし、物件ごとにExcel台帳が分かれていると、どの設備が老朽化しているのか、どの施設で修繕費が集中しているのか、次年度以降にどの程度の更新コストが見込まれるのかを把握しにくくなります。結果として、突発的な修繕対応が増えたり、優先順位の判断が担当者の経験に依存したりすることがあります。このように「後手後手」の修繕が続くと、本来なら計画的に抑えられたはずのコストが数倍に膨れ上がるだけでなく、突然の設備トラブルでテナントの営業を止めてしまい、損害賠償に発展する恐れもあります。</p>



<h3 class="wp-block-heading">3. テナント入れ替えのたびに設備情報の確認に時間がかかる</h3>



<p>商業施設では、テナントの入れ替えに合わせて、電気容量、空調、給排水、厨房設備、通信環境などの確認が必要になります。しかし、設備情報がExcel、図面、メール添付、共有フォルダ、担当者の個人PCなどに分散していると、必要な情報を探すだけで時間がかかります。さらに、見つけたExcelが最新版かどうか判断できない場合、現地確認や関係者への問い合わせが増え、対応スピードが落ちてしまいます。</p>



<h3 class="wp-block-heading">4. 担当者に属人化し、異動・退職で情報が失われる</h3>



<p>プロパティマネジメントにおける台帳管理では、担当者の経験や記憶に頼っている情報が少なくありません。「あの設備の履歴は前任者しか知らない」「どのフォルダに図面があるか分からない」「修繕時の経緯がメールにしか残っていない」といった状態になると、異動や退職のたびに引き継ぎ負荷が大きくなります。Excelは自由度が高い反面、入力ルールや保存場所が統一されにくく、属人化を招きやすい点に注意が必要です。</p>



<h3 class="wp-block-heading">5. 外部委託先・テナントとの情報共有が煩雑になる</h3>



<p class="has-text-align-left">設備管理では、ビル管理会社、工事会社、点検会社、テナントなど、社外の関係者と情報を共有する場面も多くあります。Excelをメールで送付する運用では、どのファイルが最新版か分からなくなりやすく、誤送信や不要な情報共有のリスクも高まります。特に、図面や設備情報には施設運営上の重要情報が含まれるため、必要な相手に必要な範囲だけ共有できる仕組みが求められます。</p>


<div class="hs-cta-embed hs-cta-simple-placeholder hs-cta-embed-183704894002" style="max-width: 100%; max-height: 100%; width: 750px; height: 268.25px; text-align: center;" data-hubspot-wrapper-cta-id="183704894002"><a href="https://cta-na2.hubspot.com/web-interactives/public/v1/track/redirect?encryptedPayload=AVxigLJ%2Fxxpl6TKLMy6K3Wmmjr%2FzPDSfnlKDySRKcS19SmYujZgxAZoyYDEepkXBCWtHV7UagSIpfsd3vHN02en2SYllIYdDbjI3UvAfq4aUJliXF6d2j71CuzcZjnmowkqPIEBskMkLCk9FT9TwjWAOyQr23WPGOtmEei4IDSo22F3q6cDGnhTQr%2BV3YL77Kv67qLH82jMw8Q%3D%3D&amp;webInteractiveContentId=183704894002&amp;portalId=23171742" target="_blank" rel="noopener"> <img decoding="async" style="height: 100%; width: 100%; object-fit: fill;" src="https://hubspot-no-cache-na2-prod.s3.amazonaws.com/cta/default/23171742/interactive-183704894002.png" alt="脱エクセル プリザンター 製品詳細へ" /> </a></div>


<h2 class="wp-block-heading">Excel管理の課題解決には、設備台帳管理システムの一元管理が重要</h2>



<p>Excel管理で起きる課題の多くは、単にExcelの機能不足だけが原因ではありません。設備情報、点検情報、図面、修繕履歴、テナント対応履歴が別々に管理されていることで、必要な情報をすぐに確認できない状態になっていることが大きな要因です。<strong><mark style="background-color:#fdff84" class="has-inline-color">そのため、設備台帳の管理を見直す際は、まず「どのツールを使うか」ではなく、「どの情報を、誰が、どのタイミングで確認・更新できる状態にするか」を整理することが重要です。</mark></strong>複数物件の設備台帳を管理する方法としては、設備情報を物件・フロア・テナント・設備単位で整理し、点検結果や修繕履歴、図面などの関連情報をひも付けて管理する仕組みが有効です。設備台帳を一元管理できる状態にすると、次のようなメリットがあります。</p>



<ul class="wp-block-list">
<li>最新の設備情報を関係者が同じ場所で確認できる</li>



<li>法定点検の期限や結果を横断的に把握できる</li>



<li>修繕履歴や更新予定をもとに中長期の計画を立てやすくなる</li>



<li>図面や届出書類などの関連資料を設備情報とひも付けられる</li>



<li>担当者交代時の引き継ぎ負荷を軽減できる</li>



<li>外部委託先やテナントに必要な情報だけを共有しやすくなる</li>
</ul>



<p>特に商業施設では、テナント管理と設備情報の一元化が重要です。テナントの入れ替えや改装工事のたびに、設備容量、図面、過去の工事履歴、行政への届出書類などを確認する必要があるため、情報が分散しているほど確認作業に時間がかかります。</p>



<h2 class="wp-block-heading">失敗しない設備台帳管理システムの選び方</h2>



<p>設備台帳をExcelから脱却してシステム化しようと考えた場合、選択肢は大きく分けて2つあります。</p>



<ul class="wp-block-list">
<li>高度な機能があらかじめ備わった「設備管理の専用パッケージシステム」</li>



<li>自社の運用に合わせて自由に画面を作れる「汎用的なWebデータベース」</li>
</ul>



<p><strong><mark style="background-color:#fdff84" class="has-inline-color">システム選定で失敗しないための最大のポイントは、「自社独自の運用ルールや、既存のExcelの手軽さをそのまま活かせるか」という点です。</mark></strong>専用パッケージは多機能な反面、コストが高額になりがちで、自社の業務フローをシステム側に合わせなければならないケースも少なくありません。そのため、「コストを抑えつつ、今使っているExcelの使いやすさを活かして自社にフィットしたシステムを作りたい」という企業の間では、Webデータベースを活用して自社専用の設備台帳管理システムを構築する方法が選ばれています。<br>設備台帳をWebデータベースへ移行すると、Excelのように一覧で確認しやすい利便性を活かしながら、情報の一元管理、権限設定、更新履歴の確認が格段にしやすくなり、業務の課題をスムーズに解決できます。たとえば、Webデータベースを活用した設備管理システムでは、次のような具体的なメリットが得られます。</p>



<table style="border-collapse: collapse; border: 1px solid #ccc; width: 100%;">
<thead>
<tr><th style="width: 25%; border: 1px solid #ccc; padding: 8px; text-align: left;">FM・PM業務の課題</th><th style="width: 37%; border: 1px solid #ccc; padding: 8px; text-align: left;">Excel管理で起きやすいこと</th><th style="width: 38%; border: 1px solid #ccc; padding: 8px; text-align: left;">Webデータベースで改善できること</th></tr>
</thead>
<tbody>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">テナント入れ替え時の設備確認</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">最新版のExcelや図面を探す必要がある</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">物件・フロア・テナント・設備をひも付けて確認できる</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">法定点検の期限管理</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">物件ごとにファイルが分かれ、期限管理が漏れやすい</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">点検期限・結果・是正状況を一元管理できる</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">修繕計画の把握</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">複数施設の修繕履歴を横断して見にくい</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">修繕履歴や更新予定を一覧化・集計できる</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">担当者交代・引き継ぎ</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">情報の所在や更新ルールが属人化しやすい</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">履歴や関連資料を同じ場所に蓄積できる</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">外部委託先・テナントとの共有</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">メール添付では版管理や共有範囲の管理が難しい</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">権限に応じて必要な情報だけを共有できる</td></tr>
</tbody>
</table>



<p>設備台帳をWebデータベースで管理する場合、重要なのは「ExcelをそのままWeb化すること」ではありません。現場で使いやすいように、物件、フロア、設備、点検、修繕、図面、テナントなどの情報をどのように関連付けるかを設計することです。この設計ができていないと、せっかくシステム化しても「入力しづらい」「必要な情報を探しにくい」といった状態が残ってしまいます。<strong><mark style="background-color:#fdff84" class="has-inline-color">そのため、自社に最適な設備台帳管理システムを構築する際は、業務の流れに合わせて情報の持ち方を整理することが大切です。</mark></strong>そこで、数あるWebデータベースの中でも、「既存のExcelの手軽さを最も活かしやすく、現場に合わせた柔軟な設計が低コストで実現できるツール」として、多くの企業に選ばれているのが「プリザンター」です。</p>



<h2 class="wp-block-heading">プリザンターを活用した設備台帳管理方法の例</h2>



<p>設備台帳をWebデータベースで一元管理する方法の一つとして、プリザンターの活用が考えられます。プリザンターは、ノーコード・ローコードで業務アプリを構築できるWebデータベースです。<span class="sms-highlight" style="background-color: #fdff84; font-weight: 700; padding: 0 0.08em; box-decoration-break: clone; -webkit-box-decoration-break: clone;">Excelのような一覧で確認しやすい利便性を活かしながら、情報の一元管理、権限設定、履歴管理、ダッシュボード化などを行うことができます。</span>たとえば、商業施設の設備台帳管理では、次のような使い方が考えられます。</p>



<ul class="wp-block-list">
<li>物件・フロア・テナント・設備を階層的に管理する</li>



<li>設備ごとに図面、点検結果、修繕履歴、工事資料をひも付ける</li>



<li>法定点検の次回期限や対応状況を一覧で確認する</li>



<li>複数施設の修繕履歴や更新予定をダッシュボードで可視化する</li>



<li>担当者や外部委託先ごとに閲覧・編集権限を設定する</li>



<li>Excelからデータを移行し、必要に応じてExcel出力も行う</li>
</ul>



<p>プリザンターを活用することで、設備台帳、点検情報、図面、修繕履歴を別々に管理するのではなく、同じ情報基盤の上で確認・更新しやすくなります。ただし、プリザンターは柔軟性が高い分、自社の業務に合わせた設計が重要です。どの情報を台帳化するのか、誰が更新するのか、どの画面を日常的に使うのかを整理したうえで構築することで、現場で使われる設備台帳管理システムに近づきます。</p>



<h2 class="wp-block-heading">設備台帳管理システムを導入する際の進め方</h2>



<p>設備台帳の管理を見直す際、最初からシステム選定に入る必要はありません。<span class="sms-highlight" style="background-color: #fdff84; font-weight: 700; padding: 0 0.08em; box-decoration-break: clone; -webkit-box-decoration-break: clone;">現在の情報がどこにあり、どの情報から一元管理すべきかを整理しないまま進めると、システム化しても使われない台帳になってしまうことがあります。</span>ここでは、ExcelからWebデータベースへの移行を検討する前に取り組みたい3つのステップを紹介します。</p>



<h3 class="wp-block-heading">ステップ1：今ある台帳・書類を棚卸しする</h3>



<p>まずは、設備情報がどこに散らばっているかを書き出します。<strong><mark style="background-color:#fdff84" class="has-inline-color">Excelファイル、紙の書類、共有フォルダ、メール添付、個人PC、委託先が保管している資料など、形式を問わず洗い出すことが重要です。</mark></strong>設備台帳、点検報告書、図面、修繕見積、工事完了報告書など、関連資料も含めて確認します。この段階で意識したいのは、「整理されていない情報をシステムに移しても、整理された状態にはならない」という点です。まずは現状を可視化し、重複している情報、古い情報、更新責任が曖昧な情報を把握することが第一歩になります。</p>



<h3 class="wp-block-heading">ステップ2：一元管理する情報の優先順位を決める</h3>



<p>次に、どの情報から一元管理するかを決めます。すべての情報を一度にシステム化しようとすると、項目設計が複雑になり、現場の入力負荷も大きくなります。<strong><mark style="background-color:#fdff84" class="has-inline-color">優先すべきなのは、更新漏れや確認遅れが業務リスクにつながる情報です。</mark></strong>たとえば、法定点検の期限管理、テナント入れ替え時に必ず参照する設備情報、修繕判断に必要な老朽化状況などは、早い段階で一元管理の対象にしやすい情報です。最初から完璧な台帳を目指すのではなく、業務上のリスクが高い情報から整備することで、現場にとって使う意味のある仕組みになります。</p>



<h3 class="wp-block-heading">ステップ3：ExcelとWebデータベースの使い分けを決める</h3>



<p>Excel管理を見直すといっても、Excelを完全になくす必要はありません。<strong><mark style="background-color:#fdff84" class="has-inline-color">重要なのは、元データやマスタ情報をWebデータベースに集約し、必要に応じてExcelを分析や帳票出力に活用するという使い分けです。</mark></strong>たとえば、設備情報、点検期限、修繕履歴、図面リンクなどはWebデータベースで一元管理し、会議資料や予算検討用の集計表はExcelに出力して加工するといった運用が考えられます。このように役割を分けることで、Excelの使いやすさを残しながら、情報の分散や版管理の課題を減らすことができます。</p>



<h2 class="wp-block-heading">設備台帳管理システムの構築でSMSデータテックが支援できること</h2>



<p><span class="sms-highlight" style="background-color: #fdff84; font-weight: 700; padding: 0 0.08em; box-decoration-break: clone; -webkit-box-decoration-break: clone;">設備台帳管理システムを構築する際は、ツールの導入だけでなく、自社の業務に合わせた情報設計が重要です。</span>特にFM・PM業務では、物件、フロア、設備、点検、修繕、図面、委託先、テナントなど、関係する情報が多岐にわたります。SMSデータテックでは、プリザンターの導入支援を通じて、設備台帳管理に必要な設計・構築・運用改善を支援しています。具体的には、次のような支援が可能です。</p>



<ul class="wp-block-list">
<li>現在のExcel管理の課題や業務内容を可視化する事前ヒアリング</li>



<li>実務に合わせた画面・項目設計</li>



<li>担当者が直感的に使えるダッシュボードの構築</li>



<li>既存システムとの連携や二重入力の解消</li>



<li>セキュリティ要件に応じた環境設計・構築</li>



<li>導入後の運用改善・活用支援</li>
</ul>



<p>プリザンターは柔軟性の高いツールですが、自由度が高いからこそ、最初の設計が使いやすさを大きく左右します。項目を増やしすぎると入力負荷が高くなり、反対に情報が不足していると業務に活用しにくくなります。<strong><mark style="background-color:#fdff84" class="has-inline-color">そのため、設備台帳管理システムを導入する際は、現在の管理方法や業務フローを整理したうえで、自社に合った形で設計することが大切です。</mark></strong>SMSデータテックは、インプリム社主催の「Pleasanter Partner Award 2025」において「Top Revenue Award（トップレベニュー賞）」を受賞しており、プリザンターの導入支援・構築・運用サポートを通じて、企業の業務効率化やDX推進を支援してきた実績があります。</p>



<h3 class="wp-block-heading">プリザンターによる設備台帳管理システムの導入事例</h3>



<p>SMSデータテックでは、JR西日本デイリーサービスネット様におけるプリザンター活用を支援した事例があります。<br>同社では、駅構内の店舗や商業施設、ホテルなどを運営するなかで、店舗・設備に関する情報が紙、Excel、Word、個人PC、サーバーなどに分散していることが課題になっていました。テナントの入れ替え時には、店舗にどのような設備があるのか、いつ更新されたのか、どの資料が最新版なのかを把握する必要があります。しかし、情報の所在や更新状況が分かりにくい状態では、必要な資料を探すだけでも時間がかかります。<br>そこで、約500店舗の設備データ、図面、工事費、議事録、行政への届出書類などをプリザンターへ移行し、Web上で一元管理できる環境づくりを進めました。<br>導入の結果、これまで各拠点や個人PCに散らばっていた「最新の設備情報」や「過去の修繕履歴」へ、関係者全員がアクセスできるようになりました。これにより、情報検索にかかっていた膨大な時間が大幅に削減され、テナント入れ替え時の対応スピード向上や、引き継ぎ・情報共有の効率化という確かな成果に繋がっています。<br>この事例は、複数店舗や複数施設の設備台帳を管理する企業にとって参考になるケースです。<strong><mark style="background-color:#fdff84" class="has-inline-color">設備台帳を単なるExcelファイルとして保管するのではなく、関係者が同じ情報を参照できるWebデータベースとして整備することで、確認作業や情報共有の負担を減らしやすくなります。</mark></strong>自社でも同じように、店舗・設備・図面・工事関連資料が複数の場所に分散している場合は、どのような手順で一元管理へ移行したのかを確認しておくと、導入後のイメージを持ちやすくなります。<br><strong><a href="https://www.sms-datatech.co.jp/column/aut_jr_dsn/" title=""><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-blue-2-color"><span style="text-decoration: underline;">➡約500店舗規模での設備データ一元管理事例を見る</span></mark></a></strong></p>



<h2 class="wp-block-heading">まとめ</h2>



<p>複数物件や商業施設の設備台帳をExcelだけで管理し続けると、テナント入れ替え時の確認負荷、法定点検の期限管理、修繕計画の把握、担当者への属人化、外部関係者との情報共有といった課題が表面化しやすくなります。<span class="sms-highlight" style="background-color: #fdff84; font-weight: 700; padding: 0 0.08em; box-decoration-break: clone; -webkit-box-decoration-break: clone;">FM・PM業務における設備台帳は、単なる一覧表ではなく、施設運営の安全性、業務効率、資産価値の維持を支える情報基盤です。</span>だからこそ、設備情報、点検結果、図面、修繕履歴を分散させず、関係者が必要な情報にアクセスできる状態を整えることが重要です。まずは、現在の台帳・書類・図面・点検記録がどこに保管されているのかを棚卸しし、どの情報を優先的に一元管理すべきかを整理することから始めてみてください。そのうえで、Webデータベースやプリザンターを活用することで、Excelの使いやすさを活かしながら、FM・PM業務に適した管理体制を構築しやすくなります。運用の属人化を防ぐためにも、導入を検討している方はお気軽にご相談ください。</p>



<figure class="wp-block-image"><img decoding="async" src="https://hubspot-no-cache-na2-prod.s3.amazonaws.com/cta/default/23171742/interactive-183704894010.png" alt="ローコード開発ツールプリザンター 製品詳細へ"/></figure><p>The post <a href="https://www.sms-datatech.co.jp/column/aut_equipment-list/">商業施設の設備台帳管理をExcelで続ける限界とは？FM・PM業務を効率化する方法</a> first appeared on <a href="https://www.sms-datatech.co.jp">SMS DataTech</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>情シス業務を効率化・自動化するには？AI時代に求められる“攻めの情シス”の働き方</title>
		<link>https://www.sms-datatech.co.jp/column/ops_information-system_strategic-it/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=ops_information-system_strategic-it</link>
		
		<dc:creator><![CDATA[佐々木]]></dc:creator>
		<pubDate>Tue, 09 Jun 2026 01:00:00 +0000</pubDate>
				<category><![CDATA[コラム]]></category>
		<category><![CDATA[IT運用サービス]]></category>
		<category><![CDATA[システム運用]]></category>
		<category><![CDATA[業務効率化]]></category>
		<category><![CDATA[AI]]></category>
		<guid isPermaLink="false">https://www.sms-datatech.co.jp/?p=36201</guid>

					<description><![CDATA[<p>企業の情報システム部門、いわゆる情シスは、社内のIT環境を支える重要な存在です。パソコンの不具合対応、アカウント管理、ネットワークの確認、社内システムの問い合わせ、端末管理、SaaS管理など、社員が問題なく働くために欠かせない業務を日々担っています。しかし現在、多くの情シス担当者は「問い合わせ対応に追われている」「手作業が多い」「業務が属人化している」「改善したいのに時間がない」といった課題を抱えています。そこで注目されているのが、AIや自動化ツールを活用した情シス業務の効率化です。情シス業務を効率化できれば、日々の運用負荷を減らし、セキュリティ強化、DX推進、業務改善、IT戦略の立案といった、より価値の高い仕事に時間を使えるようになります。 この記事でわかること なぜ情シス業務の効率化が必要なのか 情シスの仕事は、以前よりも複雑になっています。社内システムだけでなく、クラウドサービス、SaaS、リモートワーク環境、セキュリティ対策、生成AI活用など、対応すべき範囲が広がっているためです。一方で、情シスの人員が十分に増えている企業ばかりではありません。少人数の情シスが、問い合わせ対応、端末管理、アカウント管理、障害対応、社内調整まで担っているケースも多くあります。この状態が続くと、情シスは常に目の前の作業に追われます。結果として、業務改善やDX推進に時間を使えず、会社全体のIT活用も進みにくくなります。情シス業務の効率化は、担当者を楽にするだけではありません。会社全体の生産性を高めるための取り組みです。 情シス業務で効率化しやすい領域 情シス業務の中には、人が毎回対応しなくてもよい業務があります。特に、手順が決まっている作業や、同じ内容が繰り返される問い合わせは、効率化しやすい領域です。 業務領域 よくある課題 効率化の方向性 ヘルプデスク対応 同じ質問が何度も来る FAQ、チャットボット、ナレッジ検索を整備する アカウント管理 入退社・異動時の対応が手作業 申請フローや承認フローを標準化する 端末管理 PCの状態や台数が把握しづらい 管理台帳や端末管理ツールで可視化する SaaS管理 利用状況やライセンスが見えにくい 利用状況を整理し、不要な契約を見直す マニュアル作成 担当者しか手順を知らない 作業手順を文書化し、社内で共有する まず取り組みやすいのは、問い合わせ対応の整理です。問い合わせ内容を確認すると、「パスワードを忘れた」「VPNにつながらない」「社内システムにログインできない」「共有フォルダにアクセスできない」といった、同じような質問が何度も発生していることがあります。 こうした問い合わせは、FAQや社内ナレッジとしてまとめることで、社員が自分で解決しやすくなります。さらにAI検索やチャットボットを組み合わせれば、情シス担当者が毎回手作業で回答する負担を減らせます。 生成AIを使った情シス業務効率化の具体例 生成AIは、情シス業務をすべて代わりに行うものではありません。ただし、文章作成、情報整理、手順書作成、問い合わせ対応の補助などでは大きな効果が期待できます。 生成AIは、情シス担当者の判断を置き換えるものではなく、作業時間を短縮する補助ツールとして使うのが現実的です。そのため、生成AIを活用したとしても「攻めの情シス」にはなりません。 守りの情シスから攻めの情シスへ変わるには 従来の情シスは、トラブル対応や社内ITの維持管理が中心でした。もちろん、これらは今でも重要です。しかし、AI時代の情シスには、会社全体の業務効率化を支える役割も求められています。 攻めの情シスとは、新しいツールを入れるだけではありません。現場の業務を理解し、どこにムダがあるのか、どこを自動化すれば効果が出るのかを考え、会社全体の働き方を良くする情シスです。そのためには、まず日々の運用業務を整理し、負担の大きい作業から順番に効率化することが大切です。 情シス業務を効率化する進め方 情シス業務の効率化は、いきなり大規模なシステムを導入するよりも、現状把握から始めることが重要です。 最初に取り組むべきなのは、問い合わせ内容の整理です。問い合わせを分類するだけでも、どの業務に時間がかかっているかが見えてきます。次に、同じ作業が繰り返されている業務を標準化します。たとえば、入社時のアカウント作成、退職時の権限削除、PC貸与、SaaS申請などは、フローを決めることでミスや漏れを減らせます。そのうえで、FAQ、チャットボット、ワークフロー、端末管理ツールなどを活用すれば、情シス担当者の負担を段階的に減らせます。 AIやツールで効率化が進まない場合はITアウトソーシングも選択肢 AIやツールは便利ですが、導入しただけで業務が自動的に整理されるわけではありません。たとえば生成AIも、元となるFAQや手順、対応履歴が整理されていなければ十分に活用できません。また、ツール導入後も、アカウント管理・権限設定・問い合わせ対応・SaaS管理などの運用負荷は残り続けます。つまり、「ツールを導入すること」と「現場負荷が減ること」は必ずしも同じではないのです。 そのような場合は、ITアウトソーシングの活用も有効です。ITアウトソーシングを活用すると、ヘルプデスク、キッティング、端末管理、アカウント管理、SaaS管理、定型的な運用業務などを外部に任せることができます。これにより、社内の情シス担当者は、業務改善、DX推進、セキュリティ強化、AI活用の検討など、より重要な仕事に集中しやすくなります。特に、以下のような状況では、ITアウトソーシングが有効です。 ITアウトソーシングは、情シスの仕事をなくすものではありません。むしろ、情シスが本来やるべき価値の高い仕事に集中するための手段です。情シス業務の属人化をなくし、AI時代に合った効率的な運用体制を作る手段として、ITアウトソーシングを検討する企業も増えています。 SMSデータテックのITアウトソーシング SMSデータテックのITアウトソーシングでは、ただ業務のアウトソーシングをするのではなく、現状把握として業務の整理から、代行サービス、サポート、引継ぎ対応、情シスの業務効率化や生成AI活用まで幅広いサービスを提供しています。貴社の一員となって小さな困りごとから会社全体の生産性向上までサポートいたします。「忙しすぎて具体的な悩みまで落とし込めない」という状況でも問題ありません。一緒に業務の整理から進めていきます。まずは小さなお悩みをご相談ください。 まとめ AI時代の情シスには、すべての作業を自分たちで抱え込むのではなく、仕組みで効率化していくという考え方が必要です。 情シスの価値は、単にトラブルを処理することだけではありません。ITを使って会社全体の働き方を良くすることにあります。そのためには、日々の運用業務を軽くし、改善や企画に使える時間を作ることが欠かせません。生成AIを活用することで、問い合わせ対応やマニュアル作成、報告書作成などの補助に活用できます。自動化ツールやFAQ、ワークフローを組み合わせることで、日々の運用負荷を減らすこともできます。しかし、社内だけで改善が進まない場合は、ITアウトソーシングを活用することも有効です。現状の把握から業務整理を専門家と一緒に行うことで情シス業務だけでなく、DX推進、業務改善、IT戦略の立案といった攻めの業務もスムーズに進めることができます。AI時代の情シスに必要なのは、すべてを人力で抱え込むことではありません。仕組み化、自動化、外部活用によって、会社全体を支える働き方へ変えていくことです。情シス業務の属人化をなくし、AI時代に合った効率的な運用体制を作りたい方は、ITアウトソーシングの活用を検討してみてください。</p>
<p>The post <a href="https://www.sms-datatech.co.jp/column/ops_information-system_strategic-it/">情シス業務を効率化・自動化するには？AI時代に求められる“攻めの情シス”の働き方</a> first appeared on <a href="https://www.sms-datatech.co.jp">SMS DataTech</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>企業の情報システム部門、いわゆる情シスは、社内のIT環境を支える重要な存在です。パソコンの不具合対応、アカウント管理、ネットワークの確認、社内システムの問い合わせ、端末管理、SaaS管理など、社員が問題なく働くために欠かせない業務を日々担っています。しかし現在、多くの情シス担当者は「問い合わせ対応に追われている」「手作業が多い」「業務が属人化している」「改善したいのに時間がない」といった課題を抱えています。そこで注目されているのが、AIや自動化ツールを活用した情シス業務の効率化です。情シス業務を効率化できれば、日々の運用負荷を減らし、セキュリティ強化、DX推進、業務改善、IT戦略の立案といった、より価値の高い仕事に時間を使えるようになります。</p>



<h4 class="wp-block-heading">この記事でわかること</h4>



<ul class="wp-block-list">
<li>どの業務を効率化・自動化できるか</li>



<li>AIやFAQを使った対応負荷の減らし方</li>



<li>標準化・自動化の進め方</li>



<li>守りの情シスから攻めの情シスへ変わる考え方</li>



<li>ITアウトソーシングを活用する判断ポイント</li>
</ul>



<h2 class="wp-block-heading">なぜ情シス業務の効率化が必要なのか</h2>



<p>情シスの仕事は、以前よりも複雑になっています。社内システムだけでなく、クラウドサービス、SaaS、リモートワーク環境、セキュリティ対策、生成AI活用など、対応すべき範囲が広がっているためです。一方で、情シスの人員が十分に増えている企業ばかりではありません。少人数の情シスが、問い合わせ対応、端末管理、アカウント管理、障害対応、社内調整まで担っているケースも多くあります。この状態が続くと、情シスは常に目の前の作業に追われます。結果として、業務改善やDX推進に時間を使えず、会社全体のIT活用も進みにくくなります。情シス業務の効率化は、担当者を楽にするだけではありません。<strong><mark style="background-color:#fdff84" class="has-inline-color">会社全体の生産性を高めるための取り組みです。</mark></strong></p>



<h2 class="wp-block-heading">情シス業務で効率化しやすい領域</h2>



<p>情シス業務の中には、人が毎回対応しなくてもよい業務があります。特に、手順が決まっている作業や、同じ内容が繰り返される問い合わせは、効率化しやすい領域です。</p>



<table style="border-collapse: collapse; border: 1px solid #ccc; width: 100%;">
<thead>
<tr><th style="width: 20%; border: 1px solid #ccc; padding: 8px; text-align: left;">業務領域</th><th style="width: 30%; border: 1px solid #ccc; padding: 8px; text-align: left;">よくある課題</th><th style="width: 50%; border: 1px solid #ccc; padding: 8px; text-align: left;">効率化の方向性</th></tr>
</thead>
<tbody>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">ヘルプデスク対応</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">同じ質問が何度も来る</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">FAQ、チャットボット、ナレッジ検索を整備する</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">アカウント管理</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">入退社・異動時の対応が手作業</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">申請フローや承認フローを標準化する</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">端末管理</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">PCの状態や台数が把握しづらい</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">管理台帳や端末管理ツールで可視化する</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">SaaS管理</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">利用状況やライセンスが見えにくい</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">利用状況を整理し、不要な契約を見直す</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">マニュアル作成</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">担当者しか手順を知らない</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">作業手順を文書化し、社内で共有する</td></tr>
</tbody>
</table>



<p>まず取り組みやすいのは、問い合わせ対応の整理です。問い合わせ内容を確認すると、「パスワードを忘れた」「VPNにつながらない」「社内システムにログインできない」「共有フォルダにアクセスできない」といった、同じような質問が何度も発生していることがあります。</p>



<p>こうした問い合わせは、FAQや社内ナレッジとしてまとめることで、社員が自分で解決しやすくなります。さらにAI検索やチャットボットを組み合わせれば、情シス担当者が毎回手作業で回答する負担を減らせます。</p>



<div class="hs-cta-embed hs-cta-simple-placeholder hs-cta-embed-188589267013"
  style="max-width:100%; max-height:100%; width:750px;height:268.24493408203125px" data-hubspot-wrapper-cta-id="188589267013">
  <a href="https://cta-na2.hubspot.com/web-interactives/public/v1/track/redirect?encryptedPayload=AVxigLJD0Vq46MsgtgoV9qLYzi1SvcW03tM%2BM84d08NdKf%2BBgw95SzRXrtUx6g9PlE2fRLGqRsS3z77bCr0HzS%2BEGe%2B0NBSIdsBv5qS3gQSfm3aAanQ0TwpAz5y1u9bttzY9NgEy4hM8Jhs68zhSUnP11Yk1z60KBkhCPN8%2BEevK8foE4fwRqgaBIBVYtW3aw%2F5vRKad4qp0iHPEe9%2Bq2sSVgK3t354%3D&#038;webInteractiveContentId=188589267013&#038;portalId=23171742" target="_blank" rel="noopener" crossorigin="anonymous">
    <img decoding="async" alt="&#24773;&#12471;&#12473;&#26989;&#21209;&#21177;&#29575;&#21270;&#12398;&#25163;&#24341;&#12365;&#26360; &#36039;&#26009;&#12480;&#12454;&#12531;&#12525;&#12540;&#12489;&#12506;&#12540;&#12472;&#12408;" loading="lazy" src="https://hubspot-no-cache-na2-prod.s3.amazonaws.com/cta/default/23171742/interactive-188589267013.png" style="height: 100%; width: 100%; object-fit: fill"
      onerror="this.style.display='none'" />
  </a>
</div>



<h2 class="wp-block-heading">生成AIを使った情シス業務効率化の具体例</h2>



<p>生成AIは、情シス業務をすべて代わりに行うものではありません。ただし、文章作成、情報整理、手順書作成、問い合わせ対応の補助などでは大きな効果が期待できます。</p>



<ul class="wp-block-list">
<li><strong>FAQ・問い合わせ対応の自動化</strong><br>「パスワードを忘れた」「Teamsにログインできない」といった問い合わせ内容をAIが整理し、チャットボットを活用することでお問い合わせ対応の自動化を行うことができます。</li>



<li><strong>SaaS・クラウドサービス導入時の情報整理</strong><br>ベンダー資料や設定マニュアルを要約させることで、「どの機能が重要か」「事前に何を確認すべきか」を短時間で把握しやすくなります。</li>



<li><strong>定型業務のマニュアル化</strong><br>PCキッティングやアカウント発行などの作業メモをもとに、教育用マニュアルやチェックリストを作成できます。属人化防止や引き継ぎ効率化にもつながります。</li>
</ul>



<p><span class="sms-highlight" style="background-color: #fdff84; font-weight: 700; padding: 0 0.08em; box-decoration-break: clone; -webkit-box-decoration-break: clone;">生成AIは、情シス担当者の判断を置き換えるものではなく、作業時間を短縮する補助ツールとして使うのが現実的です。</span>そのため、生成AIを活用したとしても「攻めの情シス」にはなりません。</p>



<h2 class="wp-block-heading">守りの情シスから攻めの情シスへ変わるには</h2>



<p>従来の情シスは、トラブル対応や社内ITの維持管理が中心でした。もちろん、これらは今でも重要です。<span class="sms-highlight" style="background-color: #fdff84; font-weight: 700; padding: 0 0.08em; box-decoration-break: clone; -webkit-box-decoration-break: clone;">しかし、AI時代の情シスには、会社全体の業務効率化を支える役割も求められています。</span></p>



<ul class="wp-block-list">
<li><strong>従来の情シス　＜守りが中心＞</strong>
<ul class="wp-block-list">
<li>トラブルが起きてから対応する</li>



<li>問い合わせに人力で答える</li>



<li>端末やアカウントを個別に管理する</li>



<li>IT運用を維持する</li>
</ul>
</li>



<li><strong>これからの情シス　＜守りを固めながら攻めにも時間を使う＞</strong>
<ul class="wp-block-list">
<li>問題が起きにくい仕組みを作る</li>



<li>FAQやAIで仕組み化し、問い合わせの発生を抑える</li>



<li>ルール化・標準化をしてミスを減らす</li>



<li>ITで業務改善を進める</li>
</ul>
</li>
</ul>



<p>攻めの情シスとは、新しいツールを入れるだけではありません。現場の業務を理解し、どこにムダがあるのか、どこを自動化すれば効果が出るのかを考え、会社全体の働き方を良くする情シスです。そのためには、まず日々の運用業務を整理し、負担の大きい作業から順番に効率化することが大切です。</p>



<h2 class="wp-block-heading">情シス業務を効率化する進め方</h2>



<p>情シス業務の効率化は、いきなり大規模なシステムを導入するよりも、現状把握から始めることが重要です。</p>



<ol class="wp-block-list">
<li>現状把握：問い合わせ件数、作業時間、属人化している業務を整理する</li>



<li>優先順位付け：件数が多い業務、ミスが起きやすい業務から着手する</li>



<li>標準化：手順書、申請ルール、対応フローを整える</li>



<li>自動化：FAQ、チャットボット、ワークフロー、管理ツールを活用する</li>



<li>見直し：効果を確認し、改善を続ける</li>
</ol>



<p>最初に取り組むべきなのは、問い合わせ内容の整理です。問い合わせを分類するだけでも、どの業務に時間がかかっているかが見えてきます。次に、同じ作業が繰り返されている業務を標準化します。たとえば、入社時のアカウント作成、退職時の権限削除、PC貸与、SaaS申請などは、フローを決めることでミスや漏れを減らせます。そのうえで、FAQ、チャットボット、ワークフロー、端末管理ツールなどを活用すれば、情シス担当者の負担を段階的に減らせます。</p>



<h2 class="wp-block-heading">AIやツールで効率化が進まない場合はITアウトソーシングも選択肢</h2>



<p>AIやツールは便利ですが、導入しただけで業務が自動的に整理されるわけではありません。たとえば生成AIも、元となるFAQや手順、対応履歴が整理されていなければ十分に活用できません。また、ツール導入後も、アカウント管理・権限設定・問い合わせ対応・SaaS管理などの運用負荷は残り続けます。<span class="sms-highlight" style="background-color: #fdff84; font-weight: 700; padding: 0 0.08em; box-decoration-break: clone; -webkit-box-decoration-break: clone;">つまり、「ツールを導入すること」と「現場負荷が減ること」は必ずしも同じではないのです。</span></p>



<p>そのような場合は、ITアウトソーシングの活用も有効です。ITアウトソーシングを活用すると、ヘルプデスク、キッティング、端末管理、アカウント管理、SaaS管理、定型的な運用業務などを外部に任せることができます。これにより、社内の情シス担当者は、業務改善、DX推進、セキュリティ強化、AI活用の検討など、より重要な仕事に集中しやすくなります。特に、以下のような状況では、ITアウトソーシングが有効です。</p>



<ul class="wp-block-list">
<li>情シス担当者が少人数</li>



<li>AIやツールを活用できるほどデータの整理ができていない</li>



<li>SaaSやシステムが増え続けている</li>



<li>DXやAI活用を進める余裕がない</li>



<li>情シスが“なんでも屋”化している</li>
</ul>



<p>ITアウトソーシングは、情シスの仕事をなくすものではありません。<span class="sms-highlight" style="background-color: #fdff84; font-weight: 700; padding: 0 0.08em; box-decoration-break: clone; -webkit-box-decoration-break: clone;">むしろ、情シスが本来やるべき価値の高い仕事に集中するための手段です。</span>情シス業務の属人化をなくし、AI時代に合った効率的な運用体制を作る手段として、ITアウトソーシングを検討する企業も増えています。</p>



<h2 class="wp-block-heading">SMSデータテックのITアウトソーシング</h2>



<p>SMSデータテックのITアウトソーシングでは、ただ業務のアウトソーシングをするのではなく、現状把握として業務の整理から、代行サービス、サポート、引継ぎ対応、情シスの業務効率化や生成AI活用まで幅広いサービスを提供しています。<span class="sms-highlight" style="background-color: #fdff84; font-weight: 700; padding: 0 0.08em; box-decoration-break: clone; -webkit-box-decoration-break: clone;">貴社の一員となって小さな困りごとから会社全体の生産性向上までサポートいたします。</span>「忙しすぎて具体的な悩みまで落とし込めない」という状況でも問題ありません。一緒に業務の整理から進めていきます。まずは小さなお悩みをご相談ください。</p>



<h2 class="wp-block-heading">まとめ</h2>



<p><span class="sms-highlight" style="background-color: #fdff84; font-weight: 700; padding: 0 0.08em; box-decoration-break: clone; -webkit-box-decoration-break: clone;">AI時代の情シスには、すべての作業を自分たちで抱え込むのではなく、仕組みで効率化していくという考え方が必要です。</span> 情シスの価値は、単にトラブルを処理することだけではありません。ITを使って会社全体の働き方を良くすることにあります。そのためには、日々の運用業務を軽くし、改善や企画に使える時間を作ることが欠かせません。生成AIを活用することで、問い合わせ対応やマニュアル作成、報告書作成などの補助に活用できます。自動化ツールやFAQ、ワークフローを組み合わせることで、日々の運用負荷を減らすこともできます。しかし、社内だけで改善が進まない場合は、ITアウトソーシングを活用することも有効です。現状の把握から業務整理を専門家と一緒に行うことで情シス業務だけでなく、DX推進、業務改善、IT戦略の立案といった攻めの業務もスムーズに進めることができます。<span class="sms-highlight" style="background-color: #fdff84; font-weight: 700; padding: 0 0.08em; box-decoration-break: clone; -webkit-box-decoration-break: clone;">AI時代の情シスに必要なのは、すべてを人力で抱え込むことではありません。</span>仕組み化、自動化、外部活用によって、会社全体を支える働き方へ変えていくことです。情シス業務の属人化をなくし、AI時代に合った効率的な運用体制を作りたい方は、ITアウトソーシングの活用を検討してみてください。</p>



<div class="hs-cta-embed hs-cta-simple-placeholder hs-cta-embed-188589267013"
  style="max-width:100%; max-height:100%; width:750px;height:268.24493408203125px" data-hubspot-wrapper-cta-id="188589267013">
  <a href="https://cta-na2.hubspot.com/web-interactives/public/v1/track/redirect?encryptedPayload=AVxigLJD0Vq46MsgtgoV9qLYzi1SvcW03tM%2BM84d08NdKf%2BBgw95SzRXrtUx6g9PlE2fRLGqRsS3z77bCr0HzS%2BEGe%2B0NBSIdsBv5qS3gQSfm3aAanQ0TwpAz5y1u9bttzY9NgEy4hM8Jhs68zhSUnP11Yk1z60KBkhCPN8%2BEevK8foE4fwRqgaBIBVYtW3aw%2F5vRKad4qp0iHPEe9%2Bq2sSVgK3t354%3D&#038;webInteractiveContentId=188589267013&#038;portalId=23171742" target="_blank" rel="noopener" crossorigin="anonymous">
    <img decoding="async" alt="&#24773;&#12471;&#12473;&#26989;&#21209;&#21177;&#29575;&#21270;&#12398;&#25163;&#24341;&#12365;&#26360; &#36039;&#26009;&#12480;&#12454;&#12531;&#12525;&#12540;&#12489;&#12506;&#12540;&#12472;&#12408;" loading="lazy" src="https://hubspot-no-cache-na2-prod.s3.amazonaws.com/cta/default/23171742/interactive-188589267013.png" style="height: 100%; width: 100%; object-fit: fill"
      onerror="this.style.display='none'" />
  </a>
</div><p>The post <a href="https://www.sms-datatech.co.jp/column/ops_information-system_strategic-it/">情シス業務を効率化・自動化するには？AI時代に求められる“攻めの情シス”の働き方</a> first appeared on <a href="https://www.sms-datatech.co.jp">SMS DataTech</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>AI導入支援会社おすすめ10選！生成AI導入・内製化を成功させる選び方を解説</title>
		<link>https://www.sms-datatech.co.jp/column/aut_ai-implementation-support-companies/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=aut_ai-implementation-support-companies</link>
		
		<dc:creator><![CDATA[後藤]]></dc:creator>
		<pubDate>Wed, 03 Jun 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[コラム]]></category>
		<category><![CDATA[自動化ソリューション]]></category>
		<category><![CDATA[自動化]]></category>
		<category><![CDATA[ITコンサルティング]]></category>
		<category><![CDATA[DX推進]]></category>
		<category><![CDATA[AI]]></category>
		<guid isPermaLink="false">https://www.sms-datatech.co.jp/?p=36247</guid>

					<description><![CDATA[<p>生成AIやAIエージェントの活用が広がるなかで、企業におけるAI導入の重要性は高まっています。 一方で、実際にAI導入を進めようとすると、「何から始めればよいかわからない」「ChatGPTなどを契約したものの、現場で使われていない」「社内ルールやセキュリティ面が不安」といった課題に直面する企業も少なくありません。 AIは、ツールを導入するだけで成果が出るものではありません。自社の業務課題を整理し、業務への落とし込みや社員研修、運用ルールの整備などを進める必要があります。しかし、これらをすべて社内だけで担うには、AI推進体制の整備や最新技術のキャッチアップ、現場への定着支援まで継続的に行う必要があり、負担が大きくなりがちです。 こうした課題を解決する手段として、AI導入を研修・コンサルティング・システム構築・定着支援の面から支援する会社やサービスが増えています。そこで本記事では、AI導入支援会社に依頼できることや選び方、おすすめのAI導入支援会社・サービス10選を紹介します。 AI 導入支援会社とは AI導入支援会社とは、企業がAIを業務で活用できるように、導入前の課題整理から研修、システム構築、運用改善、社内定着までを支援する会社です。 AI導入というと、ChatGPTやAIチャットボット、AI議事録ツールなどを契約することをイメージする方もいるかもしれません。しかし、AIツールを契約しただけでは、業務改善につながらないケースもあります。 たとえば、生成AIを契約しても、社員が使い方を理解していなければ利用は広がりません。また、利用ルールが整備されていない状態では、機密情報や個人情報の取り扱いに不安が残ります。さらに、どの業務にAIを活用すべきかが曖昧なままでは、費用対効果も判断しづらくなります。AI導入支援会社は、こうした課題に対して、AI活用の方針策定、業務の洗い出し、社員研修、AIエージェントや業務システムの構築、導入後の改善などを支援します。 つまり、AI導入支援会社は、単にAIツールを紹介するのではなく、企業がAIを実務で使える状態にするための支援を行う会社だといえます。 AI 導入支援会社に依頼できること AI導入支援会社に依頼できる内容は、会社によって異なります。主な支援内容は、AI活用方針の整理、社員向け研修、業務への落とし込み、AIシステムやAIエージェントの構築、導入後の運用改善などです。 特に、生成AIの業務活用では「どのツールを使うか」だけでなく、「どの業務にどう組み込むか」が重要になります。文章作成や議事録作成のような個人作業の効率化から、社内ナレッジ検索、問い合わせ対応、営業資料作成、データ分析、月次レポート作成など、AIを活用できる領域は多岐にわたります。 ただし、すべての業務に同じように効果が出るわけではありません。最初から大規模な開発を行うよりも、効果が見えやすい業務から小さく始め、現場の反応を見ながら広げていく進め方が現実的です。AI導入支援会社に相談することで、自社の業務内容や組織体制に合わせて、どこからAI活用を始めるべきかを整理しやすくなります。 AI 導入支援会社を選ぶ際のポイント AI導入支援会社を選ぶ際に特に重要なのは、「自社の業務に合わせて、安全に・継続的に使われる状態まで支援できるか」です。知名度やサービス名だけで判断するのではなく、導入前の設計から現場定着、セキュリティ・運用改善まで伴走できるかを確認しましょう。 特に、以下の4点は事前に確認しておくとよいでしょう。 AI導入支援会社といっても、得意領域は会社によって異なります。社員研修や人材育成に強い会社もあれば、AIエージェントやRAG構築などの実装に強い会社もあります。また、業務改革や組織変革とあわせてAI活用を支援する会社もあります。 「社員のAIリテラシーを高めたい」のか、「AIを使った業務システムを構築したい」のか、「まずは自社で何ができるか整理したい」のかによって、選ぶべき会社は変わります。 そのため、サービス名や機能だけで比較するのではなく、自社がAI導入で解決したい課題に対して、どこまで支援してもらえるのかを確認しましょう。 AI 導入支援会社おすすめ10選 ここからは、AI導入を検討している企業におすすめのAI導入支援会社・サービスを紹介します。 本記事では、単体のAIツールではなく、コンサルティング、研修、構築、定着支援などを提供している会社・サービスを中心に取り上げています。 1. デジライズ デジライズは、AIリスキリングとコンサルティングを通じて、企業のAI活用を支援する会社です。 AIを一部の担当者だけで使うのではなく、組織全体に浸透させることを重視している点が特徴です。社員研修に加えて、実際の業務でAIをどう活用するかまで支援を受けられるため、AIを社内に定着させたい企業に向いています。 全社的にAI活用を広げたい企業や、研修と実装支援をまとめて依頼したい企業に適した選択肢です。AI導入を一時的な施策ではなく、組織全体の変化につなげたい場合に検討しやすい会社です。 2. キカガク キカガクは、生成AI導入支援やAI人材育成に強みを持つ企業です。 生成AI搭載システムの開発支援に加えて、導入コンサルティングや研修などの導入後支援にも対応しています。AI導入を外部に任せきりにするのではなく、社内にAIを扱える人材を育てながら進めたい企業に向いています。 特に、将来的な内製化を見据えてAI活用を進めたい企業にとって、教育と実装をバランスよく相談しやすい会社です。AI活用の企画からPoC、開発、人材育成までを組み合わせて進めたい場合に適しています。 3. AVILEN AVILENは、生成AI導入を上流から支援し、ビジネス活用を実現するためのサービスを提供しています。 生成AIの導入支援だけでなく、データ利活用できる組織づくりや、AI技術の企画・開発・導入にも対応している点が特徴です。社員教育とAIソリューションの両方に対応しているため、データ・AI活用を組織的に進めたい企業に向いています。 生成AIの活用だけでなく、データ分析や機械学習を含めたAI活用を検討している企業にも適しています。AI人材育成と実装を並行して進めたい場合に検討しやすい会社です。 4. スキルアップAI スキルアップAIは、AI・DX人材育成を中心に、企業のAI活用力向上を支援する会社です。 人材育成計画の策定から評価、研修、実データを活用した課題解決型プログラム、AI開発・導入支援までを一貫して提供している点が特徴です。 AI導入の前段階として、まずは社員の理解度を高めたい企業や、部署ごとにAI活用力を底上げしたい企業に向いています。単なる座学ではなく、実務に近いテーマでAI活用を学ばせたい場合にも検討しやすい会社です。 5. JMAC JMACは、製造業・サービス業を中心に、業務改善や組織改革、DXなどを支援するコンサルティングファームです。 生成AIを活用した組織変革プログラムを提供しており、AIを単なるツール導入ではなく、業務改革や経営課題の解決につなげたい企業に向いています。 特に、現場業務の改善や組織全体の変革とAI活用を結びつけたい企業に適しています。技術導入だけでなく、業務プロセスや組織のあり方まで見直したい場合に候補となる会社です。 6. パーソルワークスイッチコンサルティング パーソルワークスイッチコンサルティングは、生成AI技術と業務プロセスの両方に精通したコンサルタントが、AIエージェントの業務実装を支援するサービスを提供しています。 特徴は、「どの業務をAIに任せ、どの業務を人間が担うか」の切り分けから、実装、検証、運用安定化まで支援している点です。導入済みのAIツールが現場に定着しないという課題に対しても、伴走型の定着化支援を行っています。 AIを単なるチャット利用にとどめず、業務プロセスの中に組み込みたい企業に向いています。AIエージェントの導入や、既存AIツールの活用定着に課題を感じている企業におすすめです。 7. Ridge-i Ridge-iは、生成AIコンサルティング・開発サービスを提供するAI企業です。 生成AI活用に向けたDX戦略の構築から、AIシステムの設計・開発、オペレーション構築、人材育成までをワンストップで支援している点が特徴です。また、大規模言語モデルの研究開発やチューニング開発にも対応しています。 高度なAI活用や独自AI開発を検討している企業に向いており、生成AIを経営や業務に深く組み込みたい場合に候補となります。単なる研修やツール導入ではなく、戦略設計から開発まで踏み込んで相談したい企業に適しています。 8. Laboro.AI Laboro.AIは、オーダーメイド型AIソリューション「カスタムAI」の開発・提供を行う会社です。 AI開発に先立つビジョンや戦略策定、導入ロードマップの策定、運用のためのコンサルティング支援にも対応している点が特徴です。汎用的なAIツールでは対応しづらい、自社固有の業務課題に合わせたAIを開発したい企業に向いています。 一方で、まずはAI研修や小規模な定着支援から始めたい企業よりも、具体的なAI開発テーマがある企業に適した会社です。独自AIモデルやAIエージェントを自社の業務に合わせて作り込みたい場合に検討しやすいサービスです。 9. 株式会社インディゴデータ 株式会社インディゴデータは、中小企業向けAI伴走支援サービス「ヒトタスAI」を提供する会社です。 特徴は、AI導入を「ツールを入れて終わり」にせず、現場で使われる状態まで支援する点にあります。AI初心者向けの研修・定着支援から、継続的にAI活用を相談できるAI伴走顧問、業務に合わせたAI構築・運用まで、企業の状況に合わせた支援を受けられます。 特に、ChatGPTなどを導入したものの現場で使われていない企業や、AIを使いたいが何から始めればよいかわからない企業に向いています。大規模なAI開発よりも、まずは身近な業務からAI活用を始め、効果を見ながら広げていきたい企業にとって相談しやすいサービスです。 また、Difyなどを活用した業務フロー自動化や、既存システムへのAI機能追加にも対応しているため、研修だけでなく実務への落とし込みまで支援を受けたい企業にも適しています。 10. 株式会社SMSデータテック 株式会社SMSデータテックは、働くAI「Claude Cowork」の導入から組織定着までを支援する「Claude Cowork内製化支援」を提供しています。 特徴は、AIを「質問に答えるもの」として使うだけでなく、実際の業務を進める「働くAI」として活用する点にあります。Claude Coworkは、PC上のファイルにアクセスし、依頼内容をもとにデータの読み取り、整理、分析などを行うことができます。 たとえば、営業データの整理、マーケティング施策の分析、月次レビュー資料の作成、会議資料のたたき台作成など、従来は人が手作業で行っていた業務の効率化が期待できます。 特に、生成AIやSaaS、RPAなどを導入したものの、業務全体の自動化や内製化につながっていない企業に向いています。単なるAIツールの導入ではなく、業務設計から運用定着まで支援を受けたい場合に検討しやすいサービスです。 また、SMSデータテックはシステム運用領域で25年の実績があり、業務効率化だけでなく、セキュリティや運用管理の観点を踏まえてAI活用を設計できる点も強みです。AIを安全に活用しながら、社内で運用できる状態まで整えたい企業に適しています。 AI 導入支援会社のタイプ別の選び方 AI導入支援会社は、大きく分けると「伴走支援・コンサル型」「研修・人材育成型」「開発・実装支援型」「AIエージェント内製化型」の4つに分類できます。 タイプ 向いている企業 主な会社・サービス 伴走支援・コンサル型 何から始めればよいかわからない企業、AIを現場に定着させたい企業 株式会社インディゴデータ（ヒトタスAI）、デジライズ、JMAC 研修・人材育成型 社員のAIリテラシーを高めたい企業、内製化を見据えたい企業 キカガク、AVILEN、スキルアップAI 開発・実装支援型 独自AIや業務システム連携を進めたい企業 Ridge-i、Laboro.AI AIエージェント内製化型 AIに実務を任せ、業務自動化まで進めたい企業 パーソルワークスイッチコンサルティング、株式会社SMSデータテック（Claude Cowork内製化支援） 自社の課題がまだ明確でない場合は、まず伴走支援・コンサル型の会社に相談し、業務課題や導入方針を整理するところから始めるとよいでしょう。特に、現場で使われる状態まで支援してほしい場合は、単発の研修やパッケージ導入ではなく、継続的に改善できる支援体制があるかを確認することが大切です。 一方で、すでに「社内ナレッジ検索をAI化したい」「問い合わせ対応を自動化したい」「独自AIモデルを作りたい」といったテーマが明確な場合は、開発・実装支援型の会社が適しています。 また、生成AIを導入したものの、日々の業務削減につながっていない場合は、AIエージェント内製化型のサービスを検討するとよいでしょう。AIを単なるチャットツールとしてではなく、実務を進める存在として活用することで、より具体的な業務効率化につなげやすくなります。 AI 導入支援会社を選ぶ際の注意点 AI導入支援会社を選ぶ際は、目的が曖昧なまま依頼しないことが大切です。 「とりあえずAIを導入したい」という状態では、支援内容も抽象的になりやすく、導入後の成果を判断しづらくなります。問い合わせ対応を効率化したいのか、資料作成を効率化したいのか、社内ナレッジを検索しやすくしたいのか、社員のAIリテラシーを高めたいのかなど、まずは目的を整理しておきましょう。 また、AI研修だけで終わらせないことも重要です。研修はAI活用の第一歩ですが、研修を受けただけで業務が変わるとは限りません。実際の業務に合わせた活用例やプロンプトテンプレート、社内ルール、運用フローを整備することで、AI活用は定着しやすくなります。 セキュリティやガバナンスも後回しにしてはいけません。生成AIを業務で使う場合は、機密情報や個人情報の扱い、入力してよい情報の範囲、利用ログの管理、権限設定などを整理する必要があります。安全にAI活用を進めるためにも、運用ルールまで相談できる会社を選びましょう。 AI 導入を成功させる進め方 AI導入を成功させるには、段階的に進めることが重要です。 まずは、どの業務に時間がかかっているのか、どの作業が属人化しているのか、どの業務でミスや手戻りが発生しやすいのかを確認します。そこから、AIで効率化しやすい業務を選び、一部の部署や特定業務で試すと、効果や課題を把握しやすくなります。 試験導入で効果が見えたら、AI利用ガイドラインや業務別プロンプト集、確認フロー、活用事例集などを整備します。社員が迷わずAIを使える状態を作ることで、AI活用は現場に定着しやすくなります。 導入後は、利用状況や現場の声をもとに改善を続けましょう。AI導入は一度設定して終わりではありません。業務の変化や利用状況に合わせて改善することで、継続的な業務改善につなげることができます。 ただし、これらをすべて自社だけで実施するには、AI推進室のような体制づくりや、最新技術・セキュリティ動向の継続的なキャッチアップが必要になります。AI技術は変化が速いため、準備に時間をかけすぎると、導入時点で活用方法が古くなってしまう可能性もあります。すべてを社内だけで抱え込まず、必要に応じてAI導入支援サービスを活用することも検討しましょう。 まとめ AI導入支援会社は、企業がAIを業務で活用できるように、課題整理、研修、構築、運用改善、社内定着までを支援する存在です。 特に、AIを導入したいが何から始めればよいかわからない企業や、ChatGPTなどを導入したものの現場で活用が進んでいない企業は、AI導入支援会社への相談がおすすめです。 SMSデータテックではClaude Cowork内製化支援サービスを提供しています。システム運用で培ったセキュリティ・運用管理の知見を踏まえ、貴社の業務・体制に合わせたAI活用可能性と課題を無料診断で整理できますので、お気軽にお問い合わせください。</p>
<p>The post <a href="https://www.sms-datatech.co.jp/column/aut_ai-implementation-support-companies/">AI導入支援会社おすすめ10選！生成AI導入・内製化を成功させる選び方を解説</a> first appeared on <a href="https://www.sms-datatech.co.jp">SMS DataTech</a>.</p>]]></description>
										<content:encoded><![CDATA[<p><span class="sms-highlight" style="background-color: #fdff84; font-weight: 700; padding: 0 0.08em; box-decoration-break: clone; -webkit-box-decoration-break: clone;">生成AIやAIエージェントの活用が広がるなかで、企業におけるAI導入の重要性は高まっています。</span></p>



<p>一方で、実際にAI導入を進めようとすると、「何から始めればよいかわからない」「ChatGPTなどを契約したものの、現場で使われていない」「社内ルールやセキュリティ面が不安」といった課題に直面する企業も少なくありません。</p>



<p>AIは、ツールを導入するだけで成果が出るものではありません。自社の業務課題を整理し、業務への落とし込みや社員研修、運用ルールの整備などを進める必要があります。しかし、これらをすべて社内だけで担うには、AI推進体制の整備や最新技術のキャッチアップ、現場への定着支援まで継続的に行う必要があり、負担が大きくなりがちです。</p>



<p>こうした課題を解決する手段として、AI導入を研修・コンサルティング・システム構築・定着支援の面から支援する会社やサービスが増えています。そこで本記事では、AI導入支援会社に依頼できることや選び方、おすすめのAI導入支援会社・サービス10選を紹介します。</p>



<h2 class="wp-block-heading">AI 導入支援会社とは</h2>



<p><span class="sms-highlight" style="background-color: #fdff84; font-weight: 700; padding: 0 0.08em; box-decoration-break: clone; -webkit-box-decoration-break: clone;">AI導入支援会社とは、企業がAIを業務で活用できるように、導入前の課題整理から研修、システム構築、運用改善、社内定着までを支援する会社です。</span></p>



<p>AI導入というと、ChatGPTやAIチャットボット、AI議事録ツールなどを契約することをイメージする方もいるかもしれません。しかし、AIツールを契約しただけでは、業務改善につながらないケースもあります。</p>



<p><span class="sms-highlight" style="background-color: #fdff84; font-weight: 700; padding: 0 0.08em; box-decoration-break: clone; -webkit-box-decoration-break: clone;">たとえば、生成AIを契約しても、社員が使い方を理解していなければ利用は広がりません。</span>また、利用ルールが整備されていない状態では、機密情報や個人情報の取り扱いに不安が残ります。さらに、どの業務にAIを活用すべきかが曖昧なままでは、費用対効果も判断しづらくなります。AI導入支援会社は、こうした課題に対して、AI活用の方針策定、業務の洗い出し、社員研修、AIエージェントや業務システムの構築、導入後の改善などを支援します。</p>



<p>つまり、AI導入支援会社は、単にAIツールを紹介するのではなく、企業がAIを実務で使える状態にするための支援を行う会社だといえます。</p>



<h2 class="wp-block-heading">AI 導入支援会社に依頼できること</h2>



<p><span class="sms-highlight" style="background-color: #fdff84; font-weight: 700; padding: 0 0.08em; box-decoration-break: clone; -webkit-box-decoration-break: clone;">AI導入支援会社に依頼できる内容は、会社によって異なります。</span>主な支援内容は、AI活用方針の整理、社員向け研修、業務への落とし込み、AIシステムやAIエージェントの構築、導入後の運用改善などです。</p>



<p><span class="sms-highlight" style="background-color: #fdff84; font-weight: 700; padding: 0 0.08em; box-decoration-break: clone; -webkit-box-decoration-break: clone;">特に、生成AIの業務活用では「どのツールを使うか」だけでなく、「どの業務にどう組み込むか」が重要になります。</span>文章作成や議事録作成のような個人作業の効率化から、社内ナレッジ検索、問い合わせ対応、営業資料作成、データ分析、月次レポート作成など、AIを活用できる領域は多岐にわたります。</p>



<p>ただし、すべての業務に同じように効果が出るわけではありません。最初から大規模な開発を行うよりも、効果が見えやすい業務から小さく始め、現場の反応を見ながら広げていく進め方が現実的です。AI導入支援会社に相談することで、自社の業務内容や組織体制に合わせて、どこからAI活用を始めるべきかを整理しやすくなります。</p>



<h2 class="wp-block-heading">AI 導入支援会社を選ぶ際のポイント</h2>



<p><span class="sms-highlight" style="background-color: #fdff84; font-weight: 700; padding: 0 0.08em; box-decoration-break: clone; -webkit-box-decoration-break: clone;">AI導入支援会社を選ぶ際に特に重要なのは、「自社の業務に合わせて、安全に・継続的に使われる状態まで支援できるか」です。</span>知名度やサービス名だけで判断するのではなく、導入前の設計から現場定着、セキュリティ・運用改善まで伴走できるかを確認しましょう。</p>



<p>特に、以下の4点は事前に確認しておくとよいでしょう。</p>



<ul class="wp-block-list">
<li>研修だけでなく、実務への落とし込みまで支援できるか</li>



<li>決められたパッケージではなく、自社業務に合わせて柔軟に設計・改善できるか</li>



<li>機密情報や個人情報の扱い、権限管理などセキュリティ面まで相談できるか</li>



<li>単発の導入支援ではなく、導入後の改善や運用定着まで長期的に支援できるか</li>
</ul>



<p><span class="sms-highlight" style="background-color: #fdff84; font-weight: 700; padding: 0 0.08em; box-decoration-break: clone; -webkit-box-decoration-break: clone;">AI導入支援会社といっても、得意領域は会社によって異なります。</span>社員研修や人材育成に強い会社もあれば、AIエージェントやRAG構築などの実装に強い会社もあります。また、業務改革や組織変革とあわせてAI活用を支援する会社もあります。</p>



<p>「社員のAIリテラシーを高めたい」のか、「AIを使った業務システムを構築したい」のか、「まずは自社で何ができるか整理したい」のかによって、選ぶべき会社は変わります。</p>



<p>そのため、サービス名や機能だけで比較するのではなく、自社がAI導入で解決したい課題に対して、どこまで支援してもらえるのかを確認しましょう。</p>



<div class="hs-cta-embed hs-cta-simple-placeholder hs-cta-embed-350307093208"
  style="max-width:100%; max-height:100%; width:750px;height:268.24493408203125px" data-hubspot-wrapper-cta-id="350307093208">
  <a href="https://cta-na2.hubspot.com/web-interactives/public/v1/track/redirect?encryptedPayload=AVxigLIJCw3dSYjbMd3QxYeTtB4GBE156KmGKL4Mm4C4za8xNbFXFdpndFjEbXyOAsFfxlAcKVWyA495rdYUSsX92W5SpjAEjp%2FcLwXpU15gatht4xVpECCag%2FtpREwTo7zlSWOa9YpOgO4P9lhx3NzlZaKWPHJoGik2N3fP84fSFUQOcq%2BlgLl3mecWsAHPMdJec3RELGp0OJ8XGZmZLWDQt6AD2y3z&#038;webInteractiveContentId=350307093208&#038;portalId=23171742" target="_blank" rel="noopener" crossorigin="anonymous">
    <img decoding="async" alt="SDT_CTA_Claude Cowork" loading="lazy" src="https://hubspot-no-cache-na2-prod.s3.amazonaws.com/cta/default/23171742/interactive-350307093208.png" style="height: 100%; width: 100%; object-fit: fill"
      onerror="this.style.display='none'" />
  </a>
</div>




<p><a href="https://cta-na2.hubspot.com/web-interactives/public/v1/track/redirect?encryptedPayload=AVxigLJSjAz1SL4fqqrv18TOvkLN6PfH9BYrH3lbqwTD7sfdD0IwSYL8d5iT0aX05MUlnzi3es4Q2wQSkgFZ%2BP7wFAu22SswPktfsnlYXytze1p1n9I736tZxD%2FIs0nxkA3GeytmfkntFrSs54aNFmMVQAYaomlhwLobNurjyzK3GtVDa76n40L2B8N4Yrff%2BDZtyYD0s2uaOT%2Brq7KzGywhAjEM4iNm&amp;webInteractiveContentId=350307093208&amp;portalId=23171742" target="_blank" rel="noreferrer noopener"></a></p>



<h2 class="wp-block-heading">AI 導入支援会社おすすめ10選</h2>



<p>ここからは、AI導入を検討している企業におすすめのAI導入支援会社・サービスを紹介します。</p>



<p>本記事では、単体のAIツールではなく、コンサルティング、研修、構築、定着支援などを提供している会社・サービスを中心に取り上げています。</p>



<h3 class="wp-block-heading">1. デジライズ</h3>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1024" height="443" src="https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-134958-1024x443.png" alt="" class="wp-image-36253" srcset="https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-134958-1024x443.png 1024w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-134958-300x130.png 300w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-134958-768x332.png 768w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-134958-1536x664.png 1536w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-134958.png 1919w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-blue-2-color"><span style="text-decoration: underline;"><a href="https://digirise.ai/" target="_blank" rel="noopener" title="">デジライズ</a></span></mark>は、AIリスキリングとコンサルティングを通じて、企業のAI活用を支援する会社です。</p>



<p>AIを一部の担当者だけで使うのではなく、組織全体に浸透させることを重視している点が特徴です。社員研修に加えて、実際の業務でAIをどう活用するかまで支援を受けられるため、AIを社内に定着させたい企業に向いています。</p>



<p>全社的にAI活用を広げたい企業や、研修と実装支援をまとめて依頼したい企業に適した選択肢です。AI導入を一時的な施策ではなく、組織全体の変化につなげたい場合に検討しやすい会社です。</p>



<h3 class="wp-block-heading">2. キカガク</h3>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="464" src="https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135022-1024x464.png" alt="" class="wp-image-36254" srcset="https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135022-1024x464.png 1024w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135022-300x136.png 300w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135022-768x348.png 768w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135022-1536x696.png 1536w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135022.png 1919w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-blue-2-color"><span style="text-decoration: underline;"><a href="https://lp-consulting.kikagaku.co.jp/generative-ai" target="_blank" rel="noopener" title="">キカガク</a></span></mark>は、生成AI導入支援やAI人材育成に強みを持つ企業です。</p>



<p>生成AI搭載システムの開発支援に加えて、導入コンサルティングや研修などの導入後支援にも対応しています。AI導入を外部に任せきりにするのではなく、社内にAIを扱える人材を育てながら進めたい企業に向いています。</p>



<p>特に、将来的な内製化を見据えてAI活用を進めたい企業にとって、教育と実装をバランスよく相談しやすい会社です。AI活用の企画からPoC、開発、人材育成までを組み合わせて進めたい場合に適しています。</p>



<h3 class="wp-block-heading">3. AVILEN</h3>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="461" src="https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135046-1024x461.png" alt="" class="wp-image-36255" srcset="https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135046-1024x461.png 1024w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135046-300x135.png 300w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135046-768x345.png 768w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135046-1536x691.png 1536w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135046.png 1919w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-blue-2-color"><span style="text-decoration: underline;"><a href="https://avilen.co.jp/gai/" target="_blank" rel="noopener" title="">AVILEN</a></span></mark>は、生成AI導入を上流から支援し、ビジネス活用を実現するためのサービスを提供しています。</p>



<p>生成AIの導入支援だけでなく、データ利活用できる組織づくりや、AI技術の企画・開発・導入にも対応している点が特徴です。社員教育とAIソリューションの両方に対応しているため、データ・AI活用を組織的に進めたい企業に向いています。</p>



<p>生成AIの活用だけでなく、データ分析や機械学習を含めたAI活用を検討している企業にも適しています。AI人材育成と実装を並行して進めたい場合に検討しやすい会社です。</p>



<h3 class="wp-block-heading">4. スキルアップAI</h3>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="463" src="https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135124-1024x463.png" alt="" class="wp-image-36256" srcset="https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135124-1024x463.png 1024w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135124-300x136.png 300w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135124-768x347.png 768w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135124-1536x695.png 1536w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135124.png 1919w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-blue-2-color"><span style="text-decoration: underline;"><a href="https://www.skillupai.com/private-training" target="_blank" rel="noopener" title="">スキルアップAI</a></span></mark>は、AI・DX人材育成を中心に、企業のAI活用力向上を支援する会社です。</p>



<p>人材育成計画の策定から評価、研修、実データを活用した課題解決型プログラム、AI開発・導入支援までを一貫して提供している点が特徴です。</p>



<p>AI導入の前段階として、まずは社員の理解度を高めたい企業や、部署ごとにAI活用力を底上げしたい企業に向いています。単なる座学ではなく、実務に近いテーマでAI活用を学ばせたい場合にも検討しやすい会社です。</p>



<h3 class="wp-block-heading">5. JMAC</h3>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="462" src="https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135228-1024x462.png" alt="" class="wp-image-36257" srcset="https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135228-1024x462.png 1024w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135228-300x135.png 300w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135228-768x346.png 768w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135228-1536x692.png 1536w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135228.png 1919w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-blue-2-color"><span style="text-decoration: underline;"><a href="https://www.jmac.co.jp/consulting/category/dx/ai_transformation.html" target="_blank" rel="noopener" title="">JMAC</a></span></mark>は、製造業・サービス業を中心に、業務改善や組織改革、DXなどを支援するコンサルティングファームです。</p>



<p>生成AIを活用した組織変革プログラムを提供しており、AIを単なるツール導入ではなく、業務改革や経営課題の解決につなげたい企業に向いています。</p>



<p>特に、現場業務の改善や組織全体の変革とAI活用を結びつけたい企業に適しています。技術導入だけでなく、業務プロセスや組織のあり方まで見直したい場合に候補となる会社です。</p>



<h3 class="wp-block-heading">6. パーソルワークスイッチコンサルティング</h3>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="459" src="https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135251-1024x459.png" alt="" class="wp-image-36258" srcset="https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135251-1024x459.png 1024w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135251-300x135.png 300w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135251-768x345.png 768w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135251-1536x689.png 1536w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135251.png 1919w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-blue-2-color"><span style="text-decoration: underline;"><a href="https://www.persol-wsc.co.jp/service/generative-ai/" target="_blank" rel="noopener" title="">パーソルワークスイッチコンサルティング</a></span></mark>は、生成AI技術と業務プロセスの両方に精通したコンサルタントが、AIエージェントの業務実装を支援するサービスを提供しています。</p>



<p>特徴は、「どの業務をAIに任せ、どの業務を人間が担うか」の切り分けから、実装、検証、運用安定化まで支援している点です。導入済みのAIツールが現場に定着しないという課題に対しても、伴走型の定着化支援を行っています。</p>



<p>AIを単なるチャット利用にとどめず、業務プロセスの中に組み込みたい企業に向いています。AIエージェントの導入や、既存AIツールの活用定着に課題を感じている企業におすすめです。</p>



<h3 class="wp-block-heading">7. Ridge-i</h3>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="461" src="https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135309-1024x461.png" alt="" class="wp-image-36259" srcset="https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135309-1024x461.png 1024w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135309-300x135.png 300w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135309-768x346.png 768w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135309-1536x692.png 1536w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135309.png 1919w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-blue-2-color"><span style="text-decoration: underline;"><a href="https://ridge-i.com/business/generative" target="_blank" rel="noopener" title="">Ridge-i</a></span></mark>は、生成AIコンサルティング・開発サービスを提供するAI企業です。</p>



<p>生成AI活用に向けたDX戦略の構築から、AIシステムの設計・開発、オペレーション構築、人材育成までをワンストップで支援している点が特徴です。また、大規模言語モデルの研究開発やチューニング開発にも対応しています。</p>



<p>高度なAI活用や独自AI開発を検討している企業に向いており、生成AIを経営や業務に深く組み込みたい場合に候補となります。単なる研修やツール導入ではなく、戦略設計から開発まで踏み込んで相談したい企業に適しています。</p>



<h3 class="wp-block-heading">8. Laboro.AI</h3>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="461" src="https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135634-1024x461.png" alt="" class="wp-image-36260" srcset="https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135634-1024x461.png 1024w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135634-300x135.png 300w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135634-768x346.png 768w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135634-1536x692.png 1536w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135634.png 1919w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-blue-2-color"><span style="text-decoration: underline;"><a href="https://laboro.ai/service/" target="_blank" rel="noopener" title="">Laboro.AI</a></span></mark>は、オーダーメイド型AIソリューション「カスタムAI」の開発・提供を行う会社です。</p>



<p>AI開発に先立つビジョンや戦略策定、導入ロードマップの策定、運用のためのコンサルティング支援にも対応している点が特徴です。汎用的なAIツールでは対応しづらい、自社固有の業務課題に合わせたAIを開発したい企業に向いています。</p>



<p>一方で、まずはAI研修や小規模な定着支援から始めたい企業よりも、具体的なAI開発テーマがある企業に適した会社です。独自AIモデルやAIエージェントを自社の業務に合わせて作り込みたい場合に検討しやすいサービスです。</p>



<h3 class="wp-block-heading">9. 株式会社インディゴデータ</h3>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="462" src="https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135654-1024x462.png" alt="" class="wp-image-36261" srcset="https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135654-1024x462.png 1024w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135654-300x135.png 300w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135654-768x347.png 768w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135654-1536x693.png 1536w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135654.png 1919w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-blue-2-color"><span style="text-decoration: underline;"><a href="https://pig-data.jp/service/hitotasu-ai/" target="_blank" rel="noopener" title="">株式会社インディゴデータ</a></span></mark>は、中小企業向けAI伴走支援サービス「ヒトタスAI」を提供する会社です。</p>



<p>特徴は、AI導入を「ツールを入れて終わり」にせず、現場で使われる状態まで支援する点にあります。AI初心者向けの研修・定着支援から、継続的にAI活用を相談できるAI伴走顧問、業務に合わせたAI構築・運用まで、企業の状況に合わせた支援を受けられます。</p>



<p>特に、ChatGPTなどを導入したものの現場で使われていない企業や、AIを使いたいが何から始めればよいかわからない企業に向いています。大規模なAI開発よりも、まずは身近な業務からAI活用を始め、効果を見ながら広げていきたい企業にとって相談しやすいサービスです。</p>



<p>また、Difyなどを活用した業務フロー自動化や、既存システムへのAI機能追加にも対応しているため、研修だけでなく実務への落とし込みまで支援を受けたい企業にも適しています。</p>



<h3 class="wp-block-heading">10. 株式会社SMSデータテック</h3>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="483" src="https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135724-1024x483.png" alt="" class="wp-image-36262" srcset="https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135724-1024x483.png 1024w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135724-300x142.png 300w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135724-768x362.png 768w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135724-1536x725.png 1536w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/06/スクリーンショット-2026-06-01-135724.png 1918w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-blue-2-color"><span style="text-decoration: underline;"><a href="https://www.sms-datatech.co.jp/products/claude-cowork_insourcing/" title="">株式会社SMSデータテック</a></span></mark>は、働くAI「Claude Cowork」の導入から組織定着までを支援する「Claude Cowork内製化支援」を提供しています。</p>



<p>特徴は、AIを「質問に答えるもの」として使うだけでなく、実際の業務を進める「働くAI」として活用する点にあります。Claude Coworkは、PC上のファイルにアクセスし、依頼内容をもとにデータの読み取り、整理、分析などを行うことができます。</p>



<p>たとえば、営業データの整理、マーケティング施策の分析、月次レビュー資料の作成、会議資料のたたき台作成など、従来は人が手作業で行っていた業務の効率化が期待できます。</p>



<p>特に、生成AIやSaaS、RPAなどを導入したものの、業務全体の自動化や内製化につながっていない企業に向いています。単なるAIツールの導入ではなく、業務設計から運用定着まで支援を受けたい場合に検討しやすいサービスです。</p>



<p>また、SMSデータテックはシステム運用領域で25年の実績があり、業務効率化だけでなく、セキュリティや運用管理の観点を踏まえてAI活用を設計できる点も強みです。AIを安全に活用しながら、社内で運用できる状態まで整えたい企業に適しています。</p>



<h2 class="wp-block-heading">AI 導入支援会社のタイプ別の選び方</h2>



<p><span class="sms-highlight" style="background-color: #fdff84; font-weight: 700; padding: 0 0.08em; box-decoration-break: clone; -webkit-box-decoration-break: clone;">AI導入支援会社は、大きく分けると「伴走支援・コンサル型」「研修・人材育成型」「開発・実装支援型」「AIエージェント内製化型」の4つに分類できます。</span></p>



<table style="border-collapse: collapse; border: 1px solid #ccc; width: 100%;">
<thead>
<tr><th style="width: 20%; border: 1px solid #ccc; padding: 8px; text-align: left;">タイプ</th><th style="width: 40%; border: 1px solid #ccc; padding: 8px; text-align: left;">向いている企業</th><th style="width: 40%; border: 1px solid #ccc; padding: 8px; text-align: left;">主な会社・サービス</th></tr>
</thead>
<tbody>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">伴走支援・コンサル型</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">何から始めればよいかわからない企業、AIを現場に定着させたい企業</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">株式会社インディゴデータ（ヒトタスAI）、デジライズ、JMAC</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">研修・人材育成型</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">社員のAIリテラシーを高めたい企業、内製化を見据えたい企業</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">キカガク、AVILEN、スキルアップAI</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">開発・実装支援型</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">独自AIや業務システム連携を進めたい企業</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">Ridge-i、Laboro.AI</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">AIエージェント内製化型</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">AIに実務を任せ、業務自動化まで進めたい企業</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">パーソルワークスイッチコンサルティング、株式会社SMSデータテック（Claude Cowork内製化支援）</td></tr>
</tbody>
</table>



<p>自社の課題がまだ明確でない場合は、まず伴走支援・コンサル型の会社に相談し、業務課題や導入方針を整理するところから始めるとよいでしょう。特に、現場で使われる状態まで支援してほしい場合は、単発の研修やパッケージ導入ではなく、継続的に改善できる支援体制があるかを確認することが大切です。</p>



<p>一方で、すでに「社内ナレッジ検索をAI化したい」「問い合わせ対応を自動化したい」「独自AIモデルを作りたい」といったテーマが明確な場合は、開発・実装支援型の会社が適しています。</p>



<p>また、生成AIを導入したものの、日々の業務削減につながっていない場合は、AIエージェント内製化型のサービスを検討するとよいでしょう。<span class="sms-highlight" style="background-color: #fdff84; font-weight: 700; padding: 0 0.08em; box-decoration-break: clone; -webkit-box-decoration-break: clone;">AIを単なるチャットツールとしてではなく、実務を進める存在として活用することで、より具体的な業務効率化につなげやすくなります。</span></p>



<div class="hs-cta-embed hs-cta-simple-placeholder hs-cta-embed-350307093208"
  style="max-width:100%; max-height:100%; width:750px;height:268.24493408203125px" data-hubspot-wrapper-cta-id="350307093208">
  <a href="https://cta-na2.hubspot.com/web-interactives/public/v1/track/redirect?encryptedPayload=AVxigLIJCw3dSYjbMd3QxYeTtB4GBE156KmGKL4Mm4C4za8xNbFXFdpndFjEbXyOAsFfxlAcKVWyA495rdYUSsX92W5SpjAEjp%2FcLwXpU15gatht4xVpECCag%2FtpREwTo7zlSWOa9YpOgO4P9lhx3NzlZaKWPHJoGik2N3fP84fSFUQOcq%2BlgLl3mecWsAHPMdJec3RELGp0OJ8XGZmZLWDQt6AD2y3z&#038;webInteractiveContentId=350307093208&#038;portalId=23171742" target="_blank" rel="noopener" crossorigin="anonymous">
    <img decoding="async" alt="SDT_CTA_Claude Cowork" loading="lazy" src="https://hubspot-no-cache-na2-prod.s3.amazonaws.com/cta/default/23171742/interactive-350307093208.png" style="height: 100%; width: 100%; object-fit: fill"
      onerror="this.style.display='none'" />
  </a>
</div>




<p><a href="https://cta-na2.hubspot.com/web-interactives/public/v1/track/redirect?encryptedPayload=AVxigLJSjAz1SL4fqqrv18TOvkLN6PfH9BYrH3lbqwTD7sfdD0IwSYL8d5iT0aX05MUlnzi3es4Q2wQSkgFZ%2BP7wFAu22SswPktfsnlYXytze1p1n9I736tZxD%2FIs0nxkA3GeytmfkntFrSs54aNFmMVQAYaomlhwLobNurjyzK3GtVDa76n40L2B8N4Yrff%2BDZtyYD0s2uaOT%2Brq7KzGywhAjEM4iNm&amp;webInteractiveContentId=350307093208&amp;portalId=23171742" target="_blank" rel="noreferrer noopener"></a></p>



<h2 class="wp-block-heading">AI 導入支援会社を選ぶ際の注意点</h2>



<p><span class="sms-highlight" style="background-color: #fdff84; font-weight: 700; padding: 0 0.08em; box-decoration-break: clone; -webkit-box-decoration-break: clone;">AI導入支援会社を選ぶ際は、目的が曖昧なまま依頼しないことが大切です。</span></p>



<p>「とりあえずAIを導入したい」という状態では、支援内容も抽象的になりやすく、導入後の成果を判断しづらくなります。問い合わせ対応を効率化したいのか、資料作成を効率化したいのか、社内ナレッジを検索しやすくしたいのか、社員のAIリテラシーを高めたいのかなど、まずは目的を整理しておきましょう。</p>



<p>また、AI研修だけで終わらせないことも重要です。研修はAI活用の第一歩ですが、研修を受けただけで業務が変わるとは限りません。実際の業務に合わせた活用例やプロンプトテンプレート、社内ルール、運用フローを整備することで、AI活用は定着しやすくなります。</p>



<p>セキュリティやガバナンスも後回しにしてはいけません。<span class="sms-highlight" style="background-color: #fdff84; font-weight: 700; padding: 0 0.08em; box-decoration-break: clone; -webkit-box-decoration-break: clone;">生成AIを業務で使う場合は、機密情報や個人情報の扱い、入力してよい情報の範囲、利用ログの管理、権限設定などを整理する必要があります。</span>安全にAI活用を進めるためにも、運用ルールまで相談できる会社を選びましょう。</p>



<h2 class="wp-block-heading">AI 導入を成功させる進め方</h2>



<p>AI導入を成功させるには、段階的に進めることが重要です。</p>



<ol class="wp-block-list">
<li>現状業務と課題を整理する</li>



<li>AI化しやすい業務を選ぶ</li>



<li>一部の部署や業務で小さく試す</li>



<li>社内ルールや活用テンプレートを整備する</li>



<li>利用状況を見ながら改善を続ける</li>
</ol>



<p>まずは、どの業務に時間がかかっているのか、どの作業が属人化しているのか、どの業務でミスや手戻りが発生しやすいのかを確認します。そこから、AIで効率化しやすい業務を選び、一部の部署や特定業務で試すと、効果や課題を把握しやすくなります。</p>



<p>試験導入で効果が見えたら、AI利用ガイドラインや業務別プロンプト集、確認フロー、活用事例集などを整備します。<mark style="background-color:#fdff84" class="has-inline-color"><strong>社員が迷わずAIを使える状態を作ることで、AI活用は現場に定着しやすくなります。</strong></mark></p>



<p>導入後は、利用状況や現場の声をもとに改善を続けましょう。AI導入は一度設定して終わりではありません。業務の変化や利用状況に合わせて改善することで、継続的な業務改善につなげることができます。</p>



<p><mark style="background-color:#fdff84" class="has-inline-color"><strong>ただし、これらをすべて自社だけで実施するには、AI推進室のような体制づくりや、最新技術・セキュリティ動向の継続的なキャッチアップが必要になります。</strong></mark>AI技術は変化が速いため、準備に時間をかけすぎると、導入時点で活用方法が古くなってしまう可能性もあります。すべてを社内だけで抱え込まず、必要に応じてAI導入支援サービスを活用することも検討しましょう。</p>



<h2 class="wp-block-heading">まとめ</h2>



<p><span class="sms-highlight" style="background-color: #fdff84; font-weight: 700; padding: 0 0.08em; box-decoration-break: clone; -webkit-box-decoration-break: clone;">AI導入支援会社は、企業がAIを業務で活用できるように、課題整理、研修、構築、運用改善、社内定着までを支援する存在です。</span></p>



<p><span class="sms-highlight" style="background-color: #fdff84; font-weight: 700; padding: 0 0.08em; box-decoration-break: clone; -webkit-box-decoration-break: clone;">特に、AIを導入したいが何から始めればよいかわからない企業や、ChatGPTなどを導入したものの現場で活用が進んでいない企業は、AI導入支援会社への相談がおすすめです。</span></p>



<p>SMSデータテックではClaude Cowork内製化支援サービスを提供しています。システム運用で培ったセキュリティ・運用管理の知見を踏まえ、貴社の業務・体制に合わせたAI活用可能性と課題を無料診断で整理できますので、お気軽にお問い合わせください。</p>



<div class="hs-cta-embed hs-cta-simple-placeholder hs-cta-embed-350307093208"
  style="max-width:100%; max-height:100%; width:750px;height:268.24493408203125px" data-hubspot-wrapper-cta-id="350307093208">
  <a href="https://cta-na2.hubspot.com/web-interactives/public/v1/track/redirect?encryptedPayload=AVxigLIJCw3dSYjbMd3QxYeTtB4GBE156KmGKL4Mm4C4za8xNbFXFdpndFjEbXyOAsFfxlAcKVWyA495rdYUSsX92W5SpjAEjp%2FcLwXpU15gatht4xVpECCag%2FtpREwTo7zlSWOa9YpOgO4P9lhx3NzlZaKWPHJoGik2N3fP84fSFUQOcq%2BlgLl3mecWsAHPMdJec3RELGp0OJ8XGZmZLWDQt6AD2y3z&#038;webInteractiveContentId=350307093208&#038;portalId=23171742" target="_blank" rel="noopener" crossorigin="anonymous">
    <img decoding="async" alt="SDT_CTA_Claude Cowork" loading="lazy" src="https://hubspot-no-cache-na2-prod.s3.amazonaws.com/cta/default/23171742/interactive-350307093208.png" style="height: 100%; width: 100%; object-fit: fill"
      onerror="this.style.display='none'" />
  </a>
</div>




<p><a href="https://cta-na2.hubspot.com/web-interactives/public/v1/track/redirect?encryptedPayload=AVxigLJSjAz1SL4fqqrv18TOvkLN6PfH9BYrH3lbqwTD7sfdD0IwSYL8d5iT0aX05MUlnzi3es4Q2wQSkgFZ%2BP7wFAu22SswPktfsnlYXytze1p1n9I736tZxD%2FIs0nxkA3GeytmfkntFrSs54aNFmMVQAYaomlhwLobNurjyzK3GtVDa76n40L2B8N4Yrff%2BDZtyYD0s2uaOT%2Brq7KzGywhAjEM4iNm&amp;webInteractiveContentId=350307093208&amp;portalId=23171742" target="_blank" rel="noreferrer noopener"></a></p>


<div class="block-block-22">
	<div class="Editor-wrap">
					<div class="Editor-wrap__titleMsg">まずはお気軽にご相談ください</div>
			<div class="Editor-wrap__title">お問い合わせフォーム</div>
			</div>
</div>


<script charset="utf-8" type="text/javascript" src="//js.hsforms.net/forms/embed/v2.js"></script>
<script>
  hbspt.forms.create({
    region: "na1",
    portalId: "23171742",
    formId: "b36c7c3b-83a0-43c4-8daa-8e055d6805e9"
  });
</script>



<p></p><p>The post <a href="https://www.sms-datatech.co.jp/column/aut_ai-implementation-support-companies/">AI導入支援会社おすすめ10選！生成AI導入・内製化を成功させる選び方を解説</a> first appeared on <a href="https://www.sms-datatech.co.jp">SMS DataTech</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>【はじめてのJava】ダッシュボードAPIを作成しよう！⑦試験結果分布グラフ編【完結】</title>
		<link>https://www.sms-datatech.co.jp/column/try_java-7/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=try_java-7</link>
		
		<dc:creator><![CDATA[後藤]]></dc:creator>
		<pubDate>Fri, 29 May 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[やってみた]]></category>
		<guid isPermaLink="false">https://www.sms-datatech.co.jp/?p=35825</guid>

					<description><![CDATA[<p>マーケターからエンジニアへ転身した社員による、Javaを用いたダッシュボードAPI作成の実践録。サーバーサイド初心者に向けて、試験結果分布グラフの設計から実装ステップ、開発を通じて得たリアルな気づきまでをわかりやすく解説します。</p>
<p>The post <a href="https://www.sms-datatech.co.jp/column/try_java-7/">【はじめてのJava】ダッシュボードAPIを作成しよう！⑦試験結果分布グラフ編【完結】</a> first appeared on <a href="https://www.sms-datatech.co.jp">SMS DataTech</a>.</p>]]></description>
										<content:encoded><![CDATA[<h2 class="wp-block-heading">はじめに</h2>



<p>こんにちは！マーケターから未経験のエンジニアに転身した社員Tです。<br>開発を始めて1年が経ち、サーバーサイド開発にも触れるようになりました。<br>今回は、Javaを使用したダッシュボードAPIの作成についてご紹介します。<br>これまでフロントエンド中心の開発を行っていた私が、サーバーサイドのAPI開発に挑戦していく様子をお届けします。</p>



<h2 class="wp-block-heading">本記事の対象の方</h2>



<ul class="wp-block-list">
<li>サーバーサイド開発をこれから学びたい方</li>



<li>JavaでのAPI開発に興味がある方</li>



<li>ダッシュボードの作成を通じて、実践的な技術を学びたい方</li>
</ul>



<h2 class="wp-block-heading">Javaとは？</h2>



<p>Javaは、非常に人気のあるオブジェクト指向プログラミング言語で、さまざまなアプリケーションの開発に広く使用されています。</p>



<p>詳しい説明については、過去のブログ記事をご参照ください。<br><a href="https://www.sms-datatech.co.jp/category/try/" title=""><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-blue-2-color"><span style="text-decoration: underline;">⇒関連記事はこちら</span></mark></strong></a></p>



<h2 class="wp-block-heading">環境構築</h2>



<p>環境構築に関しては、内容が広範囲にわたるため、別の記事で詳細に解説する予定です。</p>



<h2 class="wp-block-heading">ダッシュボードAPIの作成</h2>



<p>今回作成したいのは、こんなダッシュボード画面です。<strong><mark style="background-color:#fdff84" class="has-inline-color">ユーザーがコースを選択することで、自分の学習状況を一目で確認できるようにすることを目指しています。</mark></strong></p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="465" src="https://www.sms-datatech.co.jp/wp-content/uploads/2026/02/image1-3-1024x465.png" alt="" class="wp-image-34969" srcset="https://www.sms-datatech.co.jp/wp-content/uploads/2026/02/image1-3-1024x465.png 1024w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/02/image1-3-300x136.png 300w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/02/image1-3-768x349.png 768w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/02/image1-3-1536x698.png 1536w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/02/image1-3.png 1736w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>全体の作成の流れとしては、下記の通りとなります。</p>



<ol class="wp-block-list">
<li>ユーザー情報を取得</li>



<li>コース情報を取得</li>



<li>各グラフ描画に必要なデータを取得</li>
</ol>



<p>今回は、3.各グラフ描画に必要なデータを取得するAPIの実装について詳しく触れていきたいと思います。5つグラフがありますが、右下の同資格受験者との比較グラフを作成します。</p>



<p>1. ユーザー情報の取得については、過去のブログ記事をご参考ください。<br><a href="https://www.sms-datatech.co.jp/column/try_java-1/" title=""><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-blue-2-color"><span style="text-decoration: underline;">⇒関連記事はこちら</span></mark></strong></a></p>



<p>2. コース情報の取得については、過去のブログ記事をご参考ください。<br><a href="https://www.sms-datatech.co.jp/column/try_java-2/" title=""><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-blue-2-color"><span style="text-decoration: underline;">⇒関連記事はこちら</span></mark></strong></a></p>



<h2 class="wp-block-heading">試験情報取得APIの設計</h2>



<h3 class="wp-block-heading">エンドポイント</h3>



<pre style="background-color:#000;color:#fff;padding:16px;border-radius:6px;overflow-x:auto;font-family:Consolas,Monaco,'Courier New',monospace;font-size:14px;line-height:1.5;margin:0 0 16px 0;"><code style="background-color:transparent;color:#fff;">/top/getScoreDistribution</code></pre>



<ul class="wp-block-list">
<li>HTTPメソッド: POST</li>



<li>リクエストボディ: ユーザーID、コースID</li>



<li>レスポンス: 模擬試験のスコアリスト</li>
</ul>



<h3 class="wp-block-heading">リクエスト例</h3>



<pre style="background-color:#000;color:#fff;padding:16px;border-radius:6px;overflow-x:auto;font-family:Consolas,Monaco,'Courier New',monospace;font-size:14px;line-height:1.5;margin:0 0 16px 0;"><code style="background-color:transparent;color:#fff;">{
    "courseId":"7",
    "userId":"175"
}</code></pre>



<p><strong><mark style="background-color:#fdff84" class="has-inline-color">今回はパスパラメータではなく、リクエストボディからユーザーIDとコースIDを受け取る設計にしています。</mark></strong>これにより、柔軟なデータの受け渡しが可能となります。</p>



<h3 class="wp-block-heading">レスポンス例</h3>



<pre style="background-color:#000;color:#fff;padding:16px;border-radius:6px;overflow-x:auto;font-family:Consolas,Monaco,'Courier New',monospace;font-size:14px;line-height:1.5;margin:0 0 16px 0;"><code style="background-color:transparent;color:#fff;">{
    "scoreDistribution": {
        "A": 5,
        "B": 3,
        "C": 2,
        "D": 10
    },
    "errorMessageList": null
}</code></pre>



<h2 class="wp-block-heading">試験情報取得APIの実装</h2>



<h3 class="wp-block-heading">モデルクラスの作成</h3>



<p>模擬試験履歴を表すエンティティクラスを作成します。このクラスはデータベースの trial_exam_history テーブルに対応し、各試験の履歴情報を管理します。</p>



<div style="margin:1.5em 0;border-radius:6px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.15)"><div style="background:#252526;color:#cccccc;padding:6px 12px;font-family:Consolas,'Courier New',monospace;font-size:12px;border-bottom:1px solid #1e1e1e;border-top-left-radius:6px;border-top-right-radius:6px">TrialExamHistoryEntity.java</div><pre style="background:#1e1e1e;color:#d4d4d4;font-family:Consolas,'Courier New',monospace;font-size:13px;line-height:1.55;padding:14px 16px;margin:0;overflow-x:auto;white-space:pre;border-bottom-left-radius:6px;border-bottom-right-radius:6px"><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 1</span><span style="color:#569cd6">package</span> <span style="color:#9cdcfe">jp</span>.<span style="color:#9cdcfe">co</span>.<span style="color:#9cdcfe">smsdatatech</span>.<span style="color:#9cdcfe">questiongenerate</span>.<span style="color:#9cdcfe">entity</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 2</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 3</span><span style="color:#569cd6">import</span> <span style="color:#9cdcfe">java</span>.<span style="color:#9cdcfe">io</span>.<span style="color:#4ec9b0">Serializable</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 4</span><span style="color:#569cd6">import</span> <span style="color:#9cdcfe">lombok</span>.<span style="color:#4ec9b0">Data</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 5</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 6</span><span style="color:#6a9955">/**</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 7</span><span style="color:#6a9955"> * TrialExamHistoryテーブルEntity</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 8</span><span style="color:#6a9955"> */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 9</span><span style="color:#dcdcaa">@Data</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">10</span><span style="color:#569cd6">public</span> <span style="color:#569cd6">class</span> <span style="color:#4ec9b0">TrialExamHistoryEntity</span> <span style="color:#569cd6">implements</span> <span style="color:#4ec9b0">Serializable</span> {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">11</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">12</span><span style="color:#6a9955">    /** シリアルバージョンUID */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">13</span>    <span style="color:#569cd6">private</span> <span style="color:#569cd6">static</span> <span style="color:#569cd6">final</span> <span style="color:#9cdcfe">long</span> <span style="color:#9cdcfe">serialVersionUID</span> = <span style="color:#b5cea8">1L</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">14</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">15</span><span style="color:#6a9955">    /** 試験履歴ID */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">16</span>    <span style="color:#569cd6">private</span> <span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">trialExamHistoryId</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">17</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">18</span><span style="color:#6a9955">    /** 試験ID */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">19</span>    <span style="color:#569cd6">private</span> <span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">trialExamId</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">20</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">21</span><span style="color:#6a9955">    /** 試験回数 */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">22</span>    <span style="color:#569cd6">private</span> <span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">trialExamTimes</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">23</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">24</span><span style="color:#6a9955">    /** 試験スコア */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">25</span>    <span style="color:#569cd6">private</span> <span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">trialScore</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">26</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">27</span><span style="color:#6a9955">    /** ユーザーID */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">28</span>    <span style="color:#569cd6">private</span> <span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">userId</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">29</span>}</pre></div>



<h3 class="wp-block-heading">MyBatisのMapper設定</h3>



<p>このクエリでは、<strong><mark style="background-color:#fdff84" class="has-inline-color">RANK() 関数を用いて、各ユーザーの試験履歴をスコアの降順でランク付けし、最も高いスコアの試験のみを取得しています。</mark></strong></p>



<div style="margin:1.5em 0;border-radius:6px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.15)"><div style="background:#252526;color:#cccccc;padding:6px 12px;font-family:Consolas,'Courier New',monospace;font-size:12px;border-bottom:1px solid #1e1e1e;border-top-left-radius:6px;border-top-right-radius:6px">TrialExamHistoryMapper.xml</div><pre style="background:#1e1e1e;color:#d4d4d4;font-family:Consolas,'Courier New',monospace;font-size:13px;line-height:1.55;padding:14px 16px;margin:0;overflow-x:auto;white-space:pre;border-bottom-left-radius:6px;border-bottom-right-radius:6px"><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 1</span><span style="color:#569cd6">&lt;mapper</span> <span style="color:#9cdcfe">namespace</span>=<span style="color:#ce9178">&quot;jp.co.smsdatatech.questiongenerate.repository.TrialExamHistoryMapper&quot;</span><span style="color:#569cd6">&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 2</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 3</span>    <span style="color:#569cd6">&lt;select</span> <span style="color:#9cdcfe">id</span>=<span style="color:#ce9178">&quot;selectLatestTrialExamHistoryList&quot;</span> <span style="color:#9cdcfe">resultMap</span>=<span style="color:#ce9178">&quot;TrialExamHistoryResultMap&quot;</span> <span style="color:#9cdcfe">resultType</span>=<span style="color:#ce9178">&quot;jp.co.smsdatatech.questiongenerate.entity.TrialExamHistoryEntity&quot;</span><span style="color:#569cd6">&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 4</span>        <span style="color:#569cd6">SELECT</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 5</span>            <span style="color:#d4d4d4">highest_score_exam.user_id</span>,<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 6</span>            <span style="color:#d4d4d4">highest_score_exam.trial_exam_id</span>,<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 7</span>            <span style="color:#d4d4d4">highest_score_exam.trial_exam_score</span>,<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 8</span>            <span style="color:#d4d4d4">highest_score_exam.trial_exam_times</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 9</span>        <span style="color:#569cd6">FROM</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">10</span>            (<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">11</span>                <span style="color:#569cd6">SELECT</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">12</span>                    <span style="color:#d4d4d4">teh.user_id</span>,<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">13</span>                    <span style="color:#d4d4d4">teh.trial_exam_id</span>,<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">14</span>                    <span style="color:#d4d4d4">teh.trial_exam_score</span>,<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">15</span>                    <span style="color:#d4d4d4">teh.trial_exam_times</span>,<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">16</span>                    <span style="color:#569cd6">RANK</span>() <span style="color:#569cd6">OVER</span> (<span style="color:#569cd6">PARTITION</span> <span style="color:#569cd6">BY</span> <span style="color:#d4d4d4">teh.user_id</span> <span style="color:#569cd6">ORDER</span> <span style="color:#569cd6">BY</span> <span style="color:#d4d4d4">teh.trial_exam_score</span> <span style="color:#569cd6">DESC</span>, <span style="color:#d4d4d4">teh.trial_exam_times</span> <span style="color:#569cd6">DESC</span>) <span style="color:#569cd6">AS</span> <span style="color:#d4d4d4">rank_num</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">17</span>                <span style="color:#569cd6">FROM</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">18</span>                    <span style="color:#d4d4d4">trial_exam_history</span> <span style="color:#d4d4d4">teh</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">19</span>                <span style="color:#569cd6">JOIN</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">20</span>                    <span style="color:#d4d4d4">trial_exam</span> <span style="color:#569cd6">AS</span> <span style="color:#d4d4d4">exam</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">21</span>                    <span style="color:#569cd6">ON</span> <span style="color:#d4d4d4">teh.trial_exam_id</span> = <span style="color:#d4d4d4">exam.trial_exam_id</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">22</span>                <span style="color:#569cd6">WHERE</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">23</span>                    <span style="color:#d4d4d4">exam.course_id</span> = <span style="color:#9cdcfe">#{courseId}</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">24</span>            ) <span style="color:#569cd6">AS</span> <span style="color:#d4d4d4">highest_score_exam</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">25</span>        <span style="color:#569cd6">WHERE</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">26</span>            <span style="color:#d4d4d4">highest_score_exam.rank_num</span> = <span style="color:#b5cea8">1</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">27</span>    <span style="color:#569cd6">&lt;/select</span><span style="color:#569cd6">&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">28</span><span style="color:#569cd6">&lt;/mapper</span><span style="color:#569cd6">&gt;</span></pre></div>



<h3 class="wp-block-heading">サービスクラスの作成</h3>



<p>trialExamHistoryMapper.selectLatestTrialExamHistoryList(courseId) を呼び出し、試験履歴を取得します。</p>



<div style="margin:1.5em 0;border-radius:6px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.15)"><div style="background:#252526;color:#cccccc;padding:6px 12px;font-family:Consolas,'Courier New',monospace;font-size:12px;border-bottom:1px solid #1e1e1e;border-top-left-radius:6px;border-top-right-radius:6px">TrialExamHistoryService.java</div><pre style="background:#1e1e1e;color:#d4d4d4;font-family:Consolas,'Courier New',monospace;font-size:13px;line-height:1.55;padding:14px 16px;margin:0;overflow-x:auto;white-space:pre;border-bottom-left-radius:6px;border-bottom-right-radius:6px"><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 1</span><span style="color:#6a9955">/**</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 2</span><span style="color:#6a9955"> * 指定したコースIDに基づいて、複数ユーザーの最新試験履歴を取得する。</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 3</span><span style="color:#6a9955"> *</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 4</span><span style="color:#6a9955"> * @param courseId コースID</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 5</span><span style="color:#6a9955"> * @return 最新の模擬試験履歴リスト</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 6</span><span style="color:#6a9955"> */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 7</span><span style="color:#dcdcaa">@Transactional</span>(<span style="color:#9cdcfe">readOnly</span> = <span style="color:#569cd6">true</span>)<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 8</span><span style="color:#569cd6">public</span> <span style="color:#4ec9b0">List</span>&lt;<span style="color:#4ec9b0">TrialExamHistoryEntity</span>&gt; <span style="color:#dcdcaa">selectLatestTrialExamHistoryList</span>(<span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">courseId</span>) {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 9</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">10</span>    <span style="color:#9cdcfe">log</span>.<span style="color:#dcdcaa">debug</span>(<span style="color:#ce9178">&quot;コースID {} の最新の模擬試験履歴を取得します。&quot;</span>, <span style="color:#9cdcfe">courseId</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">11</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">12</span><span style="color:#6a9955">    // データベースから複数ユーザーの最新試験履歴を取得</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">13</span>    <span style="color:#4ec9b0">List</span>&lt;<span style="color:#4ec9b0">TrialExamHistoryEntity</span>&gt; <span style="color:#9cdcfe">latestTrialExamHistoryList</span> = <span style="color:#9cdcfe">trialExamHistoryMapper</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">14</span>            .<span style="color:#dcdcaa">selectLatestTrialExamHistoryList</span>(<span style="color:#9cdcfe">courseId</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">15</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">16</span>    <span style="color:#9cdcfe">log</span>.<span style="color:#dcdcaa">debug</span>(<span style="color:#ce9178">&quot;取得した複数ユーザーの最新模擬試験履歴リスト: {}&quot;</span>, <span style="color:#9cdcfe">latestTrialExamHistoryList</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">17</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">18</span>    <span style="color:#569cd6">return</span> <span style="color:#9cdcfe">latestTrialExamHistoryList</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">19</span>}</pre></div>



<h3 class="wp-block-heading">コントローラークラスの作成</h3>



<p>selectLatestTrialExamHistoryList(courseId) で最新の試験履歴を取得。取得した履歴データをもとに、スコア範囲ごとの人数をカウントし、レスポンスとして返却します。</p>



<div style="margin:1.5em 0;border-radius:6px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.15)"><div style="background:#252526;color:#cccccc;padding:6px 12px;font-family:Consolas,'Courier New',monospace;font-size:12px;border-bottom:1px solid #1e1e1e;border-top-left-radius:6px;border-top-right-radius:6px">TopRest.java</div><pre style="background:#1e1e1e;color:#d4d4d4;font-family:Consolas,'Courier New',monospace;font-size:13px;line-height:1.55;padding:14px 16px;margin:0;overflow-x:auto;white-space:pre;border-bottom-left-radius:6px;border-bottom-right-radius:6px"><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 1</span><span style="color:#6a9955">/**</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 2</span><span style="color:#6a9955"> * 最新の模擬試験履歴を取得（スコア範囲ごとの人数分布のみ返却）。</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 3</span><span style="color:#6a9955"> *</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 4</span><span style="color:#6a9955"> * &lt;h3&gt;機能概要&lt;/h3&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 5</span><span style="color:#6a9955"> * コースIDに基づいて、スコア範囲ごとの人数分布を取得する。</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 6</span><span style="color:#6a9955"> *</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 7</span><span style="color:#6a9955"> * &lt;h3&gt;処理フロー&lt;/h3&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 8</span><span style="color:#6a9955"> * 1. リクエストデータのバリデーション&lt;br&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 9</span><span style="color:#6a9955"> * 2. サービスクラスを呼び出して、スコア範囲ごとの人数分布を取得&lt;br&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">10</span><span style="color:#6a9955"> * 3. スコア範囲ごとの人数分布が見つからない場合はエラーメッセージを設定&lt;br&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">11</span><span style="color:#6a9955"> * 4. スコア範囲ごとの人数分布を返却</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">12</span><span style="color:#6a9955"> *</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">13</span><span style="color:#6a9955"> * @param form    UserIdForm</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">14</span><span style="color:#6a9955"> * @param result  バリデーション結果やエラーを格納</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">15</span><span style="color:#6a9955"> * @return スコア範囲ごとの人数分布 ScoreDistributionResponseForm</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">16</span><span style="color:#6a9955"> */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">17</span><span style="color:#dcdcaa">@PostMapping</span>(<span style="color:#ce9178">&quot;/top/getScoreDistribution&quot;</span>)<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">18</span><span style="color:#569cd6">public</span> <span style="color:#4ec9b0">ScoreDistributionResponseForm</span> <span style="color:#dcdcaa">getScoreDistribution</span>(<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">19</span>        <span style="color:#dcdcaa">@RequestBody</span> <span style="color:#dcdcaa">@Validated</span>({ <span style="color:#4ec9b0">ValidationGroups</span>.<span style="color:#4ec9b0">Step1</span>.<span style="color:#569cd6">class</span>, <span style="color:#4ec9b0">ValidationGroups</span>.<span style="color:#4ec9b0">Step2</span>.<span style="color:#569cd6">class</span> }) <span style="color:#4ec9b0">UserIdForm</span> <span style="color:#9cdcfe">form</span>,<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">20</span>        <span style="color:#4ec9b0">BindingResult</span> <span style="color:#9cdcfe">result</span>) {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">21</span>    <span style="color:#9cdcfe">log</span>.<span style="color:#dcdcaa">debug</span>(<span style="color:#ce9178">&quot;TopRest (getScoreDistribution) 呼び出し開始&quot;</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">22</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">23</span>    <span style="color:#4ec9b0">ScoreDistributionResponseForm</span> <span style="color:#9cdcfe">responseForm</span> = <span style="color:#569cd6">new</span> <span style="color:#4ec9b0">ScoreDistributionResponseForm</span>();<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">24</span>    <span style="color:#4ec9b0">List</span>&lt;<span style="color:#4ec9b0">String</span>&gt; <span style="color:#9cdcfe">errorMessageList</span> = <span style="color:#569cd6">new</span> <span style="color:#4ec9b0">ArrayList</span>&lt;&gt;();<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">25</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">26</span><span style="color:#6a9955">    // バリデーションエラーがある場合、詳細なエラーメッセージを追加</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">27</span>    <span style="color:#569cd6">if</span> (<span style="color:#9cdcfe">result</span>.<span style="color:#dcdcaa">hasErrors</span>()) {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">28</span>        <span style="color:#569cd6">for</span> (<span style="color:#4ec9b0">FieldError</span> <span style="color:#9cdcfe">fieldError</span> : <span style="color:#9cdcfe">result</span>.<span style="color:#dcdcaa">getFieldErrors</span>()) {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">29</span>            <span style="color:#4ec9b0">String</span> <span style="color:#9cdcfe">errorMessage</span> = <span style="color:#9cdcfe">messageSource</span>.<span style="color:#dcdcaa">getMessage</span>(<span style="color:#9cdcfe">fieldError</span>, <span style="color:#4ec9b0">Locale</span>.<span style="color:#dcdcaa">getDefault</span>());<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">30</span>            <span style="color:#9cdcfe">errorMessageList</span>.<span style="color:#dcdcaa">add</span>(<span style="color:#9cdcfe">errorMessage</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">31</span>        }<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">32</span>        <span style="color:#9cdcfe">responseForm</span>.<span style="color:#dcdcaa">setErrorMessageList</span>(<span style="color:#9cdcfe">errorMessageList</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">33</span>        <span style="color:#569cd6">return</span> <span style="color:#9cdcfe">responseForm</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">34</span>    }<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">35</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">36</span>    <span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">courseId</span> = <span style="color:#9cdcfe">form</span>.<span style="color:#dcdcaa">getCourseId</span>();<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">37</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">38</span><span style="color:#6a9955">    // 最新の模擬試験履歴リストを取得</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">39</span>    <span style="color:#4ec9b0">List</span>&lt;<span style="color:#4ec9b0">TrialExamHistoryEntity</span>&gt; <span style="color:#9cdcfe">latestTrialExamHistoryList</span> = <span style="color:#9cdcfe">trialExamHistoryService</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">40</span>            .<span style="color:#dcdcaa">selectLatestTrialExamHistoryList</span>(<span style="color:#9cdcfe">courseId</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">41</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">42</span><span style="color:#6a9955">    // リストが取得できなかった場合</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">43</span>    <span style="color:#569cd6">if</span> (<span style="color:#4ec9b0">CollectionUtils</span>.<span style="color:#dcdcaa">isEmpty</span>(<span style="color:#9cdcfe">latestTrialExamHistoryList</span>)) {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">44</span>        <span style="color:#9cdcfe">errorMessageList</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">45</span>                .<span style="color:#dcdcaa">add</span>(<span style="color:#9cdcfe">messageSource</span>.<span style="color:#dcdcaa">getMessage</span>(<span style="color:#4ec9b0">ErrorMessages</span>.<span style="color:#4fc1ff">TRIAL_EXAM_HISTORY_NOT_FOUND</span>, <span style="color:#569cd6">null</span>,<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">46</span>                        <span style="color:#4ec9b0">Locale</span>.<span style="color:#dcdcaa">getDefault</span>()));<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">47</span>        <span style="color:#9cdcfe">responseForm</span>.<span style="color:#dcdcaa">setErrorMessageList</span>(<span style="color:#9cdcfe">errorMessageList</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">48</span>        <span style="color:#9cdcfe">log</span>.<span style="color:#dcdcaa">debug</span>(<span style="color:#ce9178">&quot;最新の模擬試験履歴が見つかりませんでした&quot;</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">49</span>        <span style="color:#569cd6">return</span> <span style="color:#9cdcfe">responseForm</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">50</span>    }<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">51</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">52</span><span style="color:#6a9955">    // スコア範囲ごとの人数をカウントする</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">53</span>    <span style="color:#4ec9b0">Map</span>&lt;<span style="color:#4ec9b0">String</span>, <span style="color:#4ec9b0">Integer</span>&gt; <span style="color:#9cdcfe">scoreDistribution</span> = <span style="color:#569cd6">new</span> <span style="color:#4ec9b0">HashMap</span>&lt;&gt;();<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">54</span>    <span style="color:#569cd6">for</span> (<span style="color:#4ec9b0">TrialExamHistoryEntity</span> <span style="color:#9cdcfe">history</span> : <span style="color:#9cdcfe">latestTrialExamHistoryList</span>) {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">55</span>        <span style="color:#9cdcfe">int</span> <span style="color:#9cdcfe">score</span> = <span style="color:#9cdcfe">history</span>.<span style="color:#dcdcaa">getTrialScore</span>();<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">56</span>        <span style="color:#4ec9b0">ScoreEnum</span> <span style="color:#9cdcfe">range</span> = <span style="color:#4ec9b0">ScoreEnum</span>.<span style="color:#dcdcaa">getRange</span>(<span style="color:#9cdcfe">score</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">57</span>        <span style="color:#569cd6">if</span> (<span style="color:#9cdcfe">range</span> != <span style="color:#569cd6">null</span>) {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">58</span><span style="color:#6a9955">            // スコア範囲に該当する人数を取り出し、1を加算する</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">59</span>            <span style="color:#9cdcfe">int</span> <span style="color:#9cdcfe">currentCount</span> = <span style="color:#9cdcfe">scoreDistribution</span>.<span style="color:#dcdcaa">getOrDefault</span>(<span style="color:#9cdcfe">range</span>.<span style="color:#dcdcaa">name</span>(), <span style="color:#b5cea8">0</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">60</span>            <span style="color:#9cdcfe">scoreDistribution</span>.<span style="color:#dcdcaa">put</span>(<span style="color:#9cdcfe">range</span>.<span style="color:#dcdcaa">name</span>(), <span style="color:#9cdcfe">currentCount</span> + <span style="color:#b5cea8">1</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">61</span>        }<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">62</span>    }<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">63</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">64</span><span style="color:#6a9955">    // スコア分布をResponseFormにセット</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">65</span>    <span style="color:#9cdcfe">responseForm</span>.<span style="color:#dcdcaa">setScoreDistribution</span>(<span style="color:#9cdcfe">scoreDistribution</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">66</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">67</span>    <span style="color:#9cdcfe">log</span>.<span style="color:#dcdcaa">debug</span>(<span style="color:#ce9178">&quot;TopRest (getScoreDistribution) 呼び出し終了&quot;</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">68</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">69</span>    <span style="color:#569cd6">return</span> <span style="color:#9cdcfe">responseForm</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">70</span>}</pre></div>



<h3 class="wp-block-heading">Enumクラスの作成</h3>



<p>今回は、試験のスコア範囲を管理するために ScoreEnum を作成しました。スコアがどのランク（A, B, C, D）に属するかを判定するために使います。</p>



<div style="margin:1.5em 0;border-radius:6px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.15)"><div style="background:#252526;color:#cccccc;padding:6px 12px;font-family:Consolas,'Courier New',monospace;font-size:12px;border-bottom:1px solid #1e1e1e;border-top-left-radius:6px;border-top-right-radius:6px">ScoreEnum.java</div><pre style="background:#1e1e1e;color:#d4d4d4;font-family:Consolas,'Courier New',monospace;font-size:13px;line-height:1.55;padding:14px 16px;margin:0;overflow-x:auto;white-space:pre;border-bottom-left-radius:6px;border-bottom-right-radius:6px"><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 1</span><span style="color:#569cd6">package</span> <span style="color:#9cdcfe">jp</span>.<span style="color:#9cdcfe">co</span>.<span style="color:#9cdcfe">smsdatatech</span>.<span style="color:#9cdcfe">questiongenerate</span>.<span style="color:#9cdcfe">enums</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 2</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 3</span><span style="color:#569cd6">import</span> <span style="color:#9cdcfe">lombok</span>.<span style="color:#4ec9b0">Getter</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 4</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 5</span><span style="color:#6a9955">/**</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 6</span><span style="color:#6a9955"> * スコア範囲を表す列挙型クラス。</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 7</span><span style="color:#6a9955"> *</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 8</span><span style="color:#6a9955"> * &lt;p&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 9</span><span style="color:#6a9955"> * 各範囲はスコアの最小値と最大値によって定義されます。</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">10</span><span style="color:#6a9955"> * スコアを基に適切なスコア範囲を判定する機能を提供します。</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">11</span><span style="color:#6a9955"> * &lt;/p&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">12</span><span style="color:#6a9955"> */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">13</span><span style="color:#dcdcaa">@Getter</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">14</span><span style="color:#569cd6">public</span> <span style="color:#569cd6">enum</span> <span style="color:#4ec9b0">ScoreEnum</span> {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">15</span>    <span style="color:#4ec9b0">A</span>(<span style="color:#b5cea8">90</span>, <span style="color:#b5cea8">100</span>),  // <span style="color:#4fc1ff">A範囲</span>：<span style="color:#b5cea8">90</span>〜<span style="color:#b5cea8">100</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">16</span>    <span style="color:#4ec9b0">B</span>(<span style="color:#b5cea8">80</span>, <span style="color:#b5cea8">89</span>),   // <span style="color:#4fc1ff">B範囲</span>：<span style="color:#b5cea8">80</span>〜<span style="color:#b5cea8">89</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">17</span>    <span style="color:#4ec9b0">C</span>(<span style="color:#b5cea8">70</span>, <span style="color:#b5cea8">79</span>),   // <span style="color:#4fc1ff">C範囲</span>：<span style="color:#b5cea8">70</span>〜<span style="color:#b5cea8">79</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">18</span>    <span style="color:#4ec9b0">D</span>(<span style="color:#b5cea8">0</span>, <span style="color:#b5cea8">69</span>);    // <span style="color:#4fc1ff">D範囲</span>：<span style="color:#b5cea8">0</span>〜<span style="color:#b5cea8">69</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">19</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">20</span>    <span style="color:#569cd6">private</span> <span style="color:#569cd6">final</span> <span style="color:#9cdcfe">int</span> <span style="color:#9cdcfe">minScore</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">21</span>    <span style="color:#569cd6">private</span> <span style="color:#569cd6">final</span> <span style="color:#9cdcfe">int</span> <span style="color:#9cdcfe">maxScore</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">22</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">23</span><span style="color:#6a9955">    // コンストラクタ</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">24</span>    <span style="color:#4ec9b0">ScoreEnum</span>(<span style="color:#9cdcfe">int</span> <span style="color:#9cdcfe">minScore</span>, <span style="color:#9cdcfe">int</span> <span style="color:#9cdcfe">maxScore</span>) {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">25</span>        <span style="color:#569cd6">this</span>.<span style="color:#9cdcfe">minScore</span> = <span style="color:#9cdcfe">minScore</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">26</span>        <span style="color:#569cd6">this</span>.<span style="color:#9cdcfe">maxScore</span> = <span style="color:#9cdcfe">maxScore</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">27</span>    }<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">28</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">29</span><span style="color:#6a9955">    // スコアがこの範囲に含まれるかどうかをチェック</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">30</span>    <span style="color:#569cd6">public</span> <span style="color:#9cdcfe">boolean</span> <span style="color:#dcdcaa">isInRange</span>(<span style="color:#9cdcfe">int</span> <span style="color:#9cdcfe">score</span>) {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">31</span>        <span style="color:#569cd6">return</span> <span style="color:#9cdcfe">score</span> &gt;= <span style="color:#9cdcfe">minScore</span> &amp;&amp; <span style="color:#9cdcfe">score</span> &lt;= <span style="color:#9cdcfe">maxScore</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">32</span>    }<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">33</span>}</pre></div>



<h2 class="wp-block-heading">実装内容の振り返り</h2>



<p><strong><mark style="background-color:#fdff84" class="has-inline-color">今回の実装では、SQLのRANK()関数とENUMクラスを活用して、ダッシュボード画面のデータ集計を効率的に行いました。</mark></strong></p>



<h3 class="wp-block-heading">1. SQLの RANK() 関数について</h3>



<p>RANK()関数は、特定の条件でデータを順位付けするために使用しました。<strong><mark style="background-color:#fdff84" class="has-inline-color">RANK()を活用することで、プログラム側でループを回して順位を計算する必要がなくなり、SQLだけで効率的にデータを取得できるようになりました。</mark></strong></p>



<h3 class="wp-block-heading">2. ENUMクラス ScoreEnum について</h3>



<p>ScoreEnumは、スコアをランク（A, B, C, D）に分類するために作成しました。<strong><mark style="background-color:#fdff84" class="has-inline-color">ENUMを使うことで、スコアの評価ロジックを統一し、可読性を向上させることができました。</mark></strong>また、メソッドを定義することで、スコアがどのランクに属するかを簡単に判定できるようになり、コードの保守性も向上しました。</p>



<h2 class="wp-block-heading">まとめ</h2>



<p><strong><mark style="background-color:#fdff84" class="has-inline-color">今回のブログ記事を通して、ダッシュボード画面のすべての実装が完了しました。</mark></strong>まだまだ改善の余地はあるかと思いますが、より良い書き方を模索しながら、さらに保守性や拡張性の高い実装を目指していきたいと思います。<br>長らく見ていただいてありがとうございました。次回は、テストやデータベース操作について、自身の学びをまとめていきたいと思います。</p><p>The post <a href="https://www.sms-datatech.co.jp/column/try_java-7/">【はじめてのJava】ダッシュボードAPIを作成しよう！⑦試験結果分布グラフ編【完結】</a> first appeared on <a href="https://www.sms-datatech.co.jp">SMS DataTech</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>【はじめてのJava】ダッシュボードAPIを作成しよう！⑥試験回数遷移グラフ編</title>
		<link>https://www.sms-datatech.co.jp/column/try_java-6/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=try_java-6</link>
		
		<dc:creator><![CDATA[後藤]]></dc:creator>
		<pubDate>Thu, 28 May 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[やってみた]]></category>
		<guid isPermaLink="false">https://www.sms-datatech.co.jp/?p=35823</guid>

					<description><![CDATA[<p>マーケターからエンジニアへ転身した社員による、Javaを用いたダッシュボードAPI作成の実践録。サーバーサイド初心者に向けて、試験回数遷移グラフの設計から実装ステップ、開発を通じて得たリアルな気づきまでをわかりやすく解説します。</p>
<p>The post <a href="https://www.sms-datatech.co.jp/column/try_java-6/">【はじめてのJava】ダッシュボードAPIを作成しよう！⑥試験回数遷移グラフ編</a> first appeared on <a href="https://www.sms-datatech.co.jp">SMS DataTech</a>.</p>]]></description>
										<content:encoded><![CDATA[<h2 class="wp-block-heading">はじめに</h2>



<p>こんにちは！マーケターから未経験のエンジニアに転身した社員Tです。<br>開発を始めて1年が経ち、サーバーサイド開発にも触れるようになりました。<br>今回は、Javaを使用したダッシュボードAPIの作成についてご紹介します。<br>これまでフロントエンド中心の開発を行っていた私が、サーバーサイドのAPI開発に挑戦していく様子をお届けします。</p>



<h2 class="wp-block-heading">本記事の対象の方</h2>



<ul class="wp-block-list">
<li>サーバーサイド開発をこれから学びたい方</li>



<li>JavaでのAPI開発に興味がある方</li>



<li>ダッシュボードの作成を通じて、実践的な技術を学びたい方</li>
</ul>



<h2 class="wp-block-heading">Javaとは？</h2>



<p>Javaは、非常に人気のあるオブジェクト指向プログラミング言語で、さまざまなアプリケーションの開発に広く使用されています。</p>



<p>詳しい説明については、過去のブログ記事をご参照ください。<br><a href="https://www.sms-datatech.co.jp/category/try/" title=""><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-blue-2-color"><span style="text-decoration: underline;">⇒関連記事はこちら</span></mark></strong></a></p>



<h2 class="wp-block-heading">環境構築</h2>



<p>環境構築に関しては、内容が広範囲にわたるため、別の記事で詳細に解説する予定です。</p>



<h2 class="wp-block-heading">ダッシュボードAPIの作成</h2>



<p>今回作成したいのは、こんなダッシュボード画面です。<strong><mark style="background-color:#fdff84" class="has-inline-color">ユーザーがコースを選択することで、自分の学習状況を一目で確認できるようにすることを目指しています。</mark></strong></p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="465" src="https://www.sms-datatech.co.jp/wp-content/uploads/2026/02/image1-3-1024x465.png" alt="" class="wp-image-34969" srcset="https://www.sms-datatech.co.jp/wp-content/uploads/2026/02/image1-3-1024x465.png 1024w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/02/image1-3-300x136.png 300w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/02/image1-3-768x349.png 768w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/02/image1-3-1536x698.png 1536w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/02/image1-3.png 1736w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>全体の作成の流れとしては、下記の通りとなります。</p>



<ol class="wp-block-list">
<li>ユーザー情報を取得</li>



<li>コース情報を取得</li>



<li>各グラフ描画に必要なデータを取得</li>
</ol>



<p>今回は、3.各グラフ描画に必要なデータを取得するAPIの実装について詳しく触れていきたいと思います。5つグラフがありますが、左下のスコア遷移表の制作に取り掛かりたいと思います。</p>



<p>1. ユーザー情報の取得については、過去のブログ記事をご参考ください。<br><a href="https://www.sms-datatech.co.jp/column/try_java-1/" title=""><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-blue-2-color"><span style="text-decoration: underline;">⇒関連記事はこちら</span></mark></strong></a></p>



<p>2. コース情報の取得については、過去のブログ記事をご参考ください。<br><a href="https://www.sms-datatech.co.jp/column/try_java-2/" title=""><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-blue-2-color"><span style="text-decoration: underline;">⇒関連記事はこちら</span></mark></strong></a></p>



<h2 class="wp-block-heading">試験情報取得APIの設計</h2>



<h3 class="wp-block-heading">エンドポイント</h3>



<pre style="background-color:#000;color:#fff;padding:16px;border-radius:6px;overflow-x:auto;font-family:Consolas,Monaco,'Courier New',monospace;font-size:14px;line-height:1.5;margin:0 0 16px 0;"><code style="background-color:transparent;color:#fff;">/top/getTrialExamHistory</code></pre>



<ul class="wp-block-list">
<li>HTTPメソッド: POST</li>



<li>リクエストボディ: ユーザーID、コースID</li>



<li>レスポンス: 模擬試験のスコアリスト</li>
</ul>



<h3 class="wp-block-heading">リクエスト例</h3>



<pre style="background-color:#000;color:#fff;padding:16px;border-radius:6px;overflow-x:auto;font-family:Consolas,Monaco,'Courier New',monospace;font-size:14px;line-height:1.5;margin:0 0 16px 0;"><code style="background-color:transparent;color:#fff;">{
    "courseId":"7",
    "userId":"175"
}</code></pre>



<p><strong><mark style="background-color:#fdff84" class="has-inline-color">今回はパスパラメータではなく、リクエストボディからユーザーIDとコースIDを受け取る設計にしています。</mark></strong>これにより、柔軟なデータの受け渡しが可能となります。</p>



<h3 class="wp-block-heading">レスポンス例</h3>



<pre style="background-color:#000;color:#fff;padding:16px;border-radius:6px;overflow-x:auto;font-family:Consolas,Monaco,'Courier New',monospace;font-size:14px;line-height:1.5;margin:0 0 16px 0;"><code style="background-color:transparent;color:#fff;">{
    "trialExamHistoryList": [
        {
            "trialExamHistoryId": 63,
            "trialExamId": 1,
            "trialExamTimes": 5,
            "trialScore": 90,
            "userId": null
        },
        {
            "trialExamHistoryId": 69,
            "trialExamId": 1,
            "trialExamTimes": 2,
            "trialScore": 75,
            "userId": null
        },
        {
            "trialExamHistoryId": 72,
            "trialExamId": 1,
            "trialExamTimes": 1,
            "trialScore": 70,
            "userId": null
        }
    ],
    "errorMessageList": null
}</code></pre>



<h2 class="wp-block-heading">試験情報取得APIの実装</h2>



<h3 class="wp-block-heading">モデルクラスの作成</h3>



<p>模擬試験履歴を表すエンティティクラスを作成します。このクラスはデータベースの trial_exam_history テーブルに対応し、各試験の履歴情報を管理します。</p>



<div style="margin:1.5em 0;border-radius:6px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.15)"><div style="background:#252526;color:#cccccc;padding:6px 12px;font-family:Consolas,'Courier New',monospace;font-size:12px;border-bottom:1px solid #1e1e1e;border-top-left-radius:6px;border-top-right-radius:6px">TrialExamHistoryEntity.java</div><pre style="background:#1e1e1e;color:#d4d4d4;font-family:Consolas,'Courier New',monospace;font-size:13px;line-height:1.55;padding:14px 16px;margin:0;overflow-x:auto;white-space:pre;border-bottom-left-radius:6px;border-bottom-right-radius:6px"><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 1</span><span style="color:#569cd6">package</span> <span style="color:#9cdcfe">jp</span>.<span style="color:#9cdcfe">co</span>.<span style="color:#9cdcfe">smsdatatech</span>.<span style="color:#9cdcfe">questiongenerate</span>.<span style="color:#9cdcfe">entity</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 2</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 3</span><span style="color:#569cd6">import</span> <span style="color:#9cdcfe">java</span>.<span style="color:#9cdcfe">io</span>.<span style="color:#4ec9b0">Serializable</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 4</span><span style="color:#569cd6">import</span> <span style="color:#9cdcfe">lombok</span>.<span style="color:#4ec9b0">Data</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 5</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 6</span><span style="color:#6a9955">/**</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 7</span><span style="color:#6a9955"> * TrialExamHistoryテーブルEntity</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 8</span><span style="color:#6a9955"> */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 9</span><span style="color:#dcdcaa">@Data</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">10</span><span style="color:#569cd6">public</span> <span style="color:#569cd6">class</span> <span style="color:#4ec9b0">TrialExamHistoryEntity</span> <span style="color:#569cd6">implements</span> <span style="color:#4ec9b0">Serializable</span> {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">11</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">12</span><span style="color:#6a9955">    /** シリアルバージョンUID */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">13</span>    <span style="color:#569cd6">private</span> <span style="color:#569cd6">static</span> <span style="color:#569cd6">final</span> <span style="color:#9cdcfe">long</span> <span style="color:#9cdcfe">serialVersionUID</span> = <span style="color:#b5cea8">1L</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">14</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">15</span><span style="color:#6a9955">    /** 試験履歴ID */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">16</span>    <span style="color:#569cd6">private</span> <span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">trialExamHistoryId</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">17</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">18</span><span style="color:#6a9955">    /** 試験ID */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">19</span>    <span style="color:#569cd6">private</span> <span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">trialExamId</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">20</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">21</span><span style="color:#6a9955">    /** 試験回数 */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">22</span>    <span style="color:#569cd6">private</span> <span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">trialExamTimes</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">23</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">24</span><span style="color:#6a9955">    /** 試験スコア */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">25</span>    <span style="color:#569cd6">private</span> <span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">trialScore</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">26</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">27</span><span style="color:#6a9955">    /** ユーザーID */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">28</span>    <span style="color:#569cd6">private</span> <span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">userId</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">29</span>}</pre></div>



<h3 class="wp-block-heading">MyBatisのMapper設定</h3>



<p>MyBatisのMapperインターフェースを定義し、模擬試験履歴を取得するメソッドを宣言します。指定されたユーザーIDとコースIDに基づいて直近5回分の模擬試験履歴を取得しています。</p>



<div style="margin:1.5em 0;border-radius:6px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.15)"><div style="background:#252526;color:#cccccc;padding:6px 12px;font-family:Consolas,'Courier New',monospace;font-size:12px;border-bottom:1px solid #1e1e1e;border-top-left-radius:6px;border-top-right-radius:6px">TrialExamHistoryMapper.java</div><pre style="background:#1e1e1e;color:#d4d4d4;font-family:Consolas,'Courier New',monospace;font-size:13px;line-height:1.55;padding:14px 16px;margin:0;overflow-x:auto;white-space:pre;border-bottom-left-radius:6px;border-bottom-right-radius:6px"><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 1</span><span style="color:#569cd6">package</span> <span style="color:#9cdcfe">jp</span>.<span style="color:#9cdcfe">co</span>.<span style="color:#9cdcfe">smsdatatech</span>.<span style="color:#9cdcfe">questiongenerate</span>.<span style="color:#9cdcfe">repository</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 2</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 3</span><span style="color:#569cd6">import</span> <span style="color:#9cdcfe">java</span>.<span style="color:#9cdcfe">util</span>.<span style="color:#4ec9b0">List</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 4</span><span style="color:#569cd6">import</span> <span style="color:#9cdcfe">org</span>.<span style="color:#9cdcfe">apache</span>.<span style="color:#9cdcfe">ibatis</span>.<span style="color:#9cdcfe">annotations</span>.<span style="color:#4ec9b0">Mapper</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 5</span><span style="color:#569cd6">import</span> <span style="color:#9cdcfe">org</span>.<span style="color:#9cdcfe">apache</span>.<span style="color:#9cdcfe">ibatis</span>.<span style="color:#9cdcfe">annotations</span>.<span style="color:#4ec9b0">Param</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 6</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 7</span><span style="color:#569cd6">import</span> <span style="color:#9cdcfe">jp</span>.<span style="color:#9cdcfe">co</span>.<span style="color:#9cdcfe">smsdatatech</span>.<span style="color:#9cdcfe">questiongenerate</span>.<span style="color:#9cdcfe">entity</span>.<span style="color:#4ec9b0">TrialExamHistoryEntity</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 8</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 9</span><span style="color:#6a9955">/**</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">10</span><span style="color:#6a9955"> * TrialExamHistoryテーブルMapper</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">11</span><span style="color:#6a9955"> */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">12</span><span style="color:#dcdcaa">@Mapper</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">13</span><span style="color:#569cd6">public</span> <span style="color:#569cd6">interface</span> <span style="color:#4ec9b0">TrialExamHistoryMapper</span> {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">14</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">15</span><span style="color:#6a9955">    /**</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">16</span><span style="color:#6a9955">     * 指定されたユーザーID、コースIDに基づいて直近5回分の模擬試験履歴を取得する。</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">17</span><span style="color:#6a9955">     *</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">18</span><span style="color:#6a9955">     * @param userId   ユーザーID</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">19</span><span style="color:#6a9955">     * @param courseId コースID</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">20</span><span style="color:#6a9955">     * @return 模擬試験スコアリスト</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">21</span><span style="color:#6a9955">     */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">22</span>    <span style="color:#4ec9b0">List</span>&lt;<span style="color:#4ec9b0">TrialExamHistoryEntity</span>&gt; <span style="color:#dcdcaa">selectTrialExamHistoryList</span>(<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">23</span>            <span style="color:#dcdcaa">@Param</span>(<span style="color:#ce9178">&quot;userId&quot;</span>) <span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">userId</span>,<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">24</span>            <span style="color:#dcdcaa">@Param</span>(<span style="color:#ce9178">&quot;courseId&quot;</span>) <span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">courseId</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">25</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">26</span><span style="color:#6a9955">    /**</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">27</span><span style="color:#6a9955">     * 指定したコースIDに基づいて、複数ユーザーの最新試験履歴を取得します。</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">28</span><span style="color:#6a9955">     *</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">29</span><span style="color:#6a9955">     * @param courseId コースID</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">30</span><span style="color:#6a9955">     * @return 最新の試験履歴リスト（各ユーザー1件）</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">31</span><span style="color:#6a9955">     */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">32</span>    <span style="color:#4ec9b0">List</span>&lt;<span style="color:#4ec9b0">TrialExamHistoryEntity</span>&gt; <span style="color:#dcdcaa">selectLatestTrialExamHistoryList</span>(<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">33</span>            <span style="color:#dcdcaa">@Param</span>(<span style="color:#ce9178">&quot;courseId&quot;</span>) <span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">courseId</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">34</span>}</pre></div>



<h3 class="wp-block-heading">サービスクラスの作成</h3>



<p>次に、サービス層でデータベースから直近5回分の模擬試験履歴を取得するメソッドを実装します。このメソッドは、ユーザーIDとコースIDをもとに、模擬試験履歴をデータベースから取得します。</p>



<div style="margin:1.5em 0;border-radius:6px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.15)"><div style="background:#252526;color:#cccccc;padding:6px 12px;font-family:Consolas,'Courier New',monospace;font-size:12px;border-bottom:1px solid #1e1e1e;border-top-left-radius:6px;border-top-right-radius:6px">TrialExamHistoryService.java</div><pre style="background:#1e1e1e;color:#d4d4d4;font-family:Consolas,'Courier New',monospace;font-size:13px;line-height:1.55;padding:14px 16px;margin:0;overflow-x:auto;white-space:pre;border-bottom-left-radius:6px;border-bottom-right-radius:6px"><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 1</span><span style="color:#6a9955">/**</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 2</span><span style="color:#6a9955"> * 模擬試験履歴リスト作成Service</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 3</span><span style="color:#6a9955"> */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 4</span><span style="color:#dcdcaa">@Slf4j</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 5</span><span style="color:#dcdcaa">@Service</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 6</span><span style="color:#569cd6">public</span> <span style="color:#569cd6">class</span> <span style="color:#4ec9b0">TrialExamHistoryService</span> {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 7</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 8</span>    <span style="color:#569cd6">private</span> <span style="color:#569cd6">final</span> <span style="color:#4ec9b0">TrialExamHistoryMapper</span> <span style="color:#9cdcfe">trialExamHistoryMapper</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 9</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">10</span><span style="color:#6a9955">    // コンストラクタインジェクションを使用</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">11</span>    <span style="color:#dcdcaa">@Autowired</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">12</span>    <span style="color:#569cd6">public</span> <span style="color:#4ec9b0">TrialExamHistoryService</span>(<span style="color:#4ec9b0">TrialExamHistoryMapper</span> <span style="color:#9cdcfe">trialExamHistoryMapper</span>) {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">13</span>        <span style="color:#569cd6">this</span>.<span style="color:#9cdcfe">trialExamHistoryMapper</span> = <span style="color:#9cdcfe">trialExamHistoryMapper</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">14</span>    }<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">15</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">16</span><span style="color:#6a9955">    /**</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">17</span><span style="color:#6a9955">     * 指定されたユーザーID、コースIDに基づいて直近5回分の模擬試験履歴を取得する。</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">18</span><span style="color:#6a9955">     *</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">19</span><span style="color:#6a9955">     * @param userId   ユーザーID</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">20</span><span style="color:#6a9955">     * @param courseId コースID</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">21</span><span style="color:#6a9955">     * @return 模擬試験履歴リスト</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">22</span><span style="color:#6a9955">     */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">23</span>    <span style="color:#dcdcaa">@Transactional</span>(<span style="color:#9cdcfe">readOnly</span> = <span style="color:#569cd6">true</span>)<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">24</span>    <span style="color:#569cd6">public</span> <span style="color:#4ec9b0">List</span>&lt;<span style="color:#4ec9b0">TrialExamHistoryEntity</span>&gt; <span style="color:#dcdcaa">selectTrialExamHistoryList</span>(<span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">userId</span>, <span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">courseId</span>) {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">25</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">26</span>        <span style="color:#9cdcfe">log</span>.<span style="color:#dcdcaa">debug</span>(<span style="color:#ce9178">&quot;ユーザーID {} のコースID {} の模擬試験履歴を取得します。&quot;</span>, <span style="color:#9cdcfe">userId</span>, <span style="color:#9cdcfe">courseId</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">27</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">28</span><span style="color:#6a9955">        // データベースから模擬試験履歴を取得</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">29</span>        <span style="color:#4ec9b0">List</span>&lt;<span style="color:#4ec9b0">TrialExamHistoryEntity</span>&gt; <span style="color:#9cdcfe">trialExamHistoryList</span> = <span style="color:#9cdcfe">trialExamHistoryMapper</span>.<span style="color:#dcdcaa">selectTrialExamHistoryList</span>(<span style="color:#9cdcfe">userId</span>, <span style="color:#9cdcfe">courseId</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">30</span>        <span style="color:#9cdcfe">log</span>.<span style="color:#dcdcaa">debug</span>(<span style="color:#ce9178">&quot;取得した模擬試験履歴リスト: {}&quot;</span>, <span style="color:#9cdcfe">trialExamHistoryList</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">31</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">32</span>        <span style="color:#569cd6">return</span> <span style="color:#9cdcfe">trialExamHistoryList</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">33</span>    }<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">34</span>}</pre></div>



<h3 class="wp-block-heading">コントローラークラスの作成</h3>



<p>@PostMapping アノテーションを使って、/top/getTrialExamHistory というエンドポイントを定義します。このエンドポイントは、ユーザーIDとコースIDを含むフォームを受け取り、バリデーションエラーがあればエラーメッセージを返します。</p>



<div style="margin:1.5em 0;border-radius:6px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.15)"><div style="background:#252526;color:#cccccc;padding:6px 12px;font-family:Consolas,'Courier New',monospace;font-size:12px;border-bottom:1px solid #1e1e1e;border-top-left-radius:6px;border-top-right-radius:6px">TopRest.java</div><pre style="background:#1e1e1e;color:#d4d4d4;font-family:Consolas,'Courier New',monospace;font-size:13px;line-height:1.55;padding:14px 16px;margin:0;overflow-x:auto;white-space:pre;border-bottom-left-radius:6px;border-bottom-right-radius:6px"><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 1</span><span style="color:#6a9955">/**</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 2</span><span style="color:#6a9955"> * 模擬試験履歴を取得.</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 3</span><span style="color:#6a9955"> *</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 4</span><span style="color:#6a9955"> * &lt;h3&gt;機能概要&lt;/h3&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 5</span><span style="color:#6a9955"> * ユーザーIDおよびコースIDに基づいて、模擬試験履歴を取得する。</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 6</span><span style="color:#6a9955"> *</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 7</span><span style="color:#6a9955"> * &lt;h3&gt;処理フロー&lt;/h3&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 8</span><span style="color:#6a9955"> * 1. リクエストデータのバリデーション&lt;br&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 9</span><span style="color:#6a9955"> * 2. サービスクラスを呼び出して、模擬試験履歴を取得&lt;br&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">10</span><span style="color:#6a9955"> * 3. 履歴が見つからない場合はエラーメッセージを設定&lt;br&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">11</span><span style="color:#6a9955"> * 4. 模擬試験履歴またはエラーメッセージリストを返却</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">12</span><span style="color:#6a9955"> *</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">13</span><span style="color:#6a9955"> * @param form    ユーザーIDとコースIDのリクエストフォーム</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">14</span><span style="color:#6a9955"> * @param result  バリデーション結果やエラーを格納</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">15</span><span style="color:#6a9955"> * @return 模擬試験履歴リスト TrialExamHistoryResponseForm</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">16</span><span style="color:#6a9955"> */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">17</span><span style="color:#dcdcaa">@PostMapping</span>(<span style="color:#ce9178">&quot;/top/getTrialExamHistory&quot;</span>)<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">18</span><span style="color:#569cd6">public</span> <span style="color:#4ec9b0">TrialExamHistoryResponseForm</span> <span style="color:#dcdcaa">getTrialExamHistory</span>(<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">19</span>        <span style="color:#dcdcaa">@RequestBody</span> <span style="color:#dcdcaa">@Validated</span>({ <span style="color:#4ec9b0">ValidationGroups</span>.<span style="color:#4ec9b0">Step1</span>.<span style="color:#569cd6">class</span>, <span style="color:#4ec9b0">ValidationGroups</span>.<span style="color:#4ec9b0">Step2</span>.<span style="color:#569cd6">class</span> }) <span style="color:#4ec9b0">UserIdForm</span> <span style="color:#9cdcfe">form</span>,<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">20</span>        <span style="color:#4ec9b0">BindingResult</span> <span style="color:#9cdcfe">result</span>) {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">21</span>    <span style="color:#9cdcfe">log</span>.<span style="color:#dcdcaa">debug</span>(<span style="color:#ce9178">&quot;TrialExamHistoryRest (getTrialExamHistory) 呼び出し開始&quot;</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">22</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">23</span>    <span style="color:#4ec9b0">TrialExamHistoryResponseForm</span> <span style="color:#9cdcfe">responseForm</span> = <span style="color:#569cd6">new</span> <span style="color:#4ec9b0">TrialExamHistoryResponseForm</span>();<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">24</span>    <span style="color:#4ec9b0">List</span>&lt;<span style="color:#4ec9b0">String</span>&gt; <span style="color:#9cdcfe">errorMessageList</span> = <span style="color:#569cd6">new</span> <span style="color:#4ec9b0">ArrayList</span>&lt;&gt;();<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">25</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">26</span><span style="color:#6a9955">    // バリデーションエラーがある場合、詳細なエラーメッセージを追加</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">27</span>    <span style="color:#569cd6">if</span> (<span style="color:#9cdcfe">result</span>.<span style="color:#dcdcaa">hasErrors</span>()) {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">28</span>        <span style="color:#569cd6">for</span> (<span style="color:#4ec9b0">FieldError</span> <span style="color:#9cdcfe">fieldError</span> : <span style="color:#9cdcfe">result</span>.<span style="color:#dcdcaa">getFieldErrors</span>()) {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">29</span>            <span style="color:#4ec9b0">String</span> <span style="color:#9cdcfe">errorMessage</span> = <span style="color:#9cdcfe">messageSource</span>.<span style="color:#dcdcaa">getMessage</span>(<span style="color:#9cdcfe">fieldError</span>, <span style="color:#4ec9b0">Locale</span>.<span style="color:#dcdcaa">getDefault</span>()); // <span style="color:#4ec9b0">Localeを指定</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">30</span>            <span style="color:#9cdcfe">errorMessageList</span>.<span style="color:#dcdcaa">add</span>(<span style="color:#9cdcfe">errorMessage</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">31</span>        }<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">32</span>        <span style="color:#9cdcfe">responseForm</span>.<span style="color:#dcdcaa">setErrorMessageList</span>(<span style="color:#9cdcfe">errorMessageList</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">33</span>        <span style="color:#569cd6">return</span> <span style="color:#9cdcfe">responseForm</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">34</span>    }<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">35</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">36</span>    <span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">userId</span> = <span style="color:#4ec9b0">Integer</span>.<span style="color:#dcdcaa">parseInt</span>(<span style="color:#9cdcfe">form</span>.<span style="color:#dcdcaa">getUserId</span>());<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">37</span>    <span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">courseId</span> = <span style="color:#9cdcfe">form</span>.<span style="color:#dcdcaa">getCourseId</span>();<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">38</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">39</span><span style="color:#6a9955">    // 模擬試験履歴リストを取得</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">40</span>    <span style="color:#4ec9b0">List</span>&lt;<span style="color:#4ec9b0">TrialExamHistoryEntity</span>&gt; <span style="color:#9cdcfe">trialExamHistoryList</span> = <span style="color:#9cdcfe">trialExamHistoryService</span>.<span style="color:#dcdcaa">selectTrialExamHistoryList</span>(<span style="color:#9cdcfe">userId</span>,<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">41</span>            <span style="color:#9cdcfe">courseId</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">42</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">43</span><span style="color:#6a9955">    // リストが取得できなかった場合</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">44</span>    <span style="color:#569cd6">if</span> (<span style="color:#4ec9b0">CollectionUtils</span>.<span style="color:#dcdcaa">isEmpty</span>(<span style="color:#9cdcfe">trialExamHistoryList</span>)) {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">45</span>        <span style="color:#9cdcfe">errorMessageList</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">46</span>                .<span style="color:#dcdcaa">add</span>(<span style="color:#9cdcfe">messageSource</span>.<span style="color:#dcdcaa">getMessage</span>(<span style="color:#4ec9b0">ErrorMessages</span>.<span style="color:#4fc1ff">TRIAL_EXAM_HISTORY_NOT_FOUND</span>, <span style="color:#569cd6">null</span>,<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">47</span>                        <span style="color:#4ec9b0">Locale</span>.<span style="color:#dcdcaa">getDefault</span>()));<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">48</span>        <span style="color:#9cdcfe">responseForm</span>.<span style="color:#dcdcaa">setErrorMessageList</span>(<span style="color:#9cdcfe">errorMessageList</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">49</span>        <span style="color:#9cdcfe">log</span>.<span style="color:#dcdcaa">debug</span>(<span style="color:#ce9178">&quot;模擬試験履歴が見つかりませんでした&quot;</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">50</span>        <span style="color:#569cd6">return</span> <span style="color:#9cdcfe">responseForm</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">51</span>    }<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">52</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">53</span>    <span style="color:#9cdcfe">responseForm</span>.<span style="color:#dcdcaa">setTrialExamHistoryList</span>(<span style="color:#9cdcfe">trialExamHistoryList</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">54</span>    <span style="color:#9cdcfe">log</span>.<span style="color:#dcdcaa">debug</span>(<span style="color:#ce9178">&quot;TrialExamHistoryRest (getTrialExamHistory) 呼び出し終了&quot;</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">55</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">56</span>    <span style="color:#569cd6">return</span> <span style="color:#9cdcfe">responseForm</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">57</span>}</pre></div>



<h2 class="wp-block-heading">実装内容の振り返り</h2>



<p><strong><mark style="background-color:#fdff84" class="has-inline-color">今回はシンプルに直近5回分の試験履歴を取得する実装でしたが、今後はキャッシュの活用や、SQLクエリ内で全件取得後に絞り込むのではなく、最初から直近5回分だけを取得するように修正することで、データベースへの負荷を軽減でき、より実用的に利用できるようになると感じました。</mark></strong></p>



<h2 class="wp-block-heading">まとめ</h2>



<p>シンプルな実装ではありましたが、SQLの書き方やキャッシュの導入余地など改善点も見えてきました。今後はパフォーマンスを意識した設計を取り入れ、より実用的なAPIに仕上げていきたいと思います。</p><p>The post <a href="https://www.sms-datatech.co.jp/column/try_java-6/">【はじめてのJava】ダッシュボードAPIを作成しよう！⑥試験回数遷移グラフ編</a> first appeared on <a href="https://www.sms-datatech.co.jp">SMS DataTech</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>【はじめてのJava】ダッシュボードAPIを作成しよう！⑤分野別進捗データ取得編</title>
		<link>https://www.sms-datatech.co.jp/column/try_java-5/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=try_java-5</link>
		
		<dc:creator><![CDATA[後藤]]></dc:creator>
		<pubDate>Wed, 27 May 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[やってみた]]></category>
		<guid isPermaLink="false">https://www.sms-datatech.co.jp/?p=35821</guid>

					<description><![CDATA[<p>マーケターからエンジニアへ転身した社員による、Javaを用いたダッシュボードAPI作成の実践録。サーバーサイド初心者に向けて、分野別進捗データ取得APIの設計から実装ステップ、開発を通じて得たリアルな気づきまでをわかりやすく解説します。</p>
<p>The post <a href="https://www.sms-datatech.co.jp/column/try_java-5/">【はじめてのJava】ダッシュボードAPIを作成しよう！⑤分野別進捗データ取得編</a> first appeared on <a href="https://www.sms-datatech.co.jp">SMS DataTech</a>.</p>]]></description>
										<content:encoded><![CDATA[<h2 class="wp-block-heading">はじめに</h2>



<p>こんにちは！マーケターから未経験のエンジニアに転身した社員Tです。<br>開発を始めて1年が経ち、サーバーサイド開発にも触れるようになりました。<br>今回は、Javaを使用したダッシュボードAPIの作成についてご紹介します。<br>これまでフロントエンド中心の開発を行っていた私が、サーバーサイドのAPI開発に挑戦していく様子をお届けします。</p>



<h2 class="wp-block-heading">本記事の対象の方</h2>



<ul class="wp-block-list">
<li>サーバーサイド開発をこれから学びたい方</li>



<li>JavaでのAPI開発に興味がある方</li>



<li>ダッシュボードの作成を通じて、実践的な技術を学びたい方</li>
</ul>



<h2 class="wp-block-heading">Javaとは？</h2>



<p>Javaは、非常に人気のあるオブジェクト指向プログラミング言語で、さまざまなアプリケーションの開発に広く使用されています。</p>



<p>詳しい説明については、過去のブログ記事をご参照ください。<br><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-blue-2-color"><span style="text-decoration: underline;"><a href="https://www.sms-datatech.co.jp/category/try/" title="">⇒関連記事はこちら</a></span></mark></strong></p>



<h2 class="wp-block-heading">環境構築</h2>



<p>環境構築に関しては、内容が広範囲にわたるため、別の記事で詳細に解説する予定です。</p>



<h2 class="wp-block-heading">ダッシュボードAPIの作成</h2>



<p>今回作成したいのは、こんなダッシュボード画面です。<strong><mark style="background-color:#fdff84" class="has-inline-color">ユーザーがコースを選択することで、自分の学習状況を一目で確認できるようにすることを目指しています。</mark></strong></p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="465" src="https://www.sms-datatech.co.jp/wp-content/uploads/2026/02/image1-3-1024x465.png" alt="" class="wp-image-34969" srcset="https://www.sms-datatech.co.jp/wp-content/uploads/2026/02/image1-3-1024x465.png 1024w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/02/image1-3-300x136.png 300w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/02/image1-3-768x349.png 768w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/02/image1-3-1536x698.png 1536w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/02/image1-3.png 1736w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>全体の作成の流れとしては、下記の通りとなります。</p>



<ol class="wp-block-list">
<li>ユーザー情報を取得</li>



<li>コース情報を取得</li>



<li>各グラフ描画に必要なデータを取得</li>
</ol>



<p>今回は、3.各グラフ描画に必要なデータを取得するAPIの実装について詳しく触れていきたいと思います。5つグラフがありますが、右上の各分野別の習得状況の表の制作に取り掛かりたいと思います。</p>



<p>1. ユーザー情報の取得については、過去のブログ記事をご参考ください。<br><a href="https://www.sms-datatech.co.jp/column/try_java-1/" title=""><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-blue-2-color"><span style="text-decoration: underline;">⇒関連記事はこちら</span></mark></strong></a></p>



<p>2. コース情報の取得については、過去のブログ記事をご参考ください。<br><a href="https://www.sms-datatech.co.jp/column/try_java-2/" title=""><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-blue-2-color"><span style="text-decoration: underline;">⇒関連記事はこちら</span></mark></strong></a></p>



<h2 class="wp-block-heading">分野別進捗データ取得APIの設計</h2>



<h3 class="wp-block-heading">エンドポイント</h3>



<pre style="background-color:#000;color:#fff;padding:16px;border-radius:6px;overflow-x:auto;font-family:Consolas,Monaco,'Courier New',monospace;font-size:14px;line-height:1.5;margin:0 0 16px 0;"><code style="background-color:transparent;color:#fff;">/top/getViewHistory</code></pre>



<ul class="wp-block-list">
<li>HTTPメソッド: POST</li>



<li>リクエストボディ: ユーザーID、コースID</li>



<li>レスポンス: 分野別の進捗率のリスト</li>
</ul>



<h3 class="wp-block-heading">リクエスト例</h3>



<pre style="background-color:#000;color:#fff;padding:16px;border-radius:6px;overflow-x:auto;font-family:Consolas,Monaco,'Courier New',monospace;font-size:14px;line-height:1.5;margin:0 0 16px 0;"><code style="background-color:transparent;color:#fff;">{
    "courseId":"7",
    "userId":"175"
}</code></pre>



<p><strong><mark style="background-color:#fdff84" class="has-inline-color">今回はパスパラメータではなく、リクエストボディからユーザーIDとコースIDを受け取る設計にしています。</mark></strong>これにより、柔軟なデータの受け渡しが可能となります。</p>



<h3 class="wp-block-heading">レスポンス例</h3>



<pre style="background-color:#000;color:#fff;padding:16px;border-radius:6px;overflow-x:auto;font-family:Consolas,Monaco,'Courier New',monospace;font-size:14px;line-height:1.5;margin:0 0 16px 0;"><code style="background-color:transparent;color:#fff;">{
    "viewHistoryList": [
        {
            "fieldId": 1,
            "fieldName": "Javaの基礎",
            "fieldMaxProgress": 30.00
        },
        {
            "fieldId": 2,
            "fieldName": "Javaオブジェクト指向",
            "fieldMaxProgress": 40.00
        }
    ],
    "errorMessageList": null
}</code></pre>



<h2 class="wp-block-heading">分野別進捗データAPIの実装</h2>



<h3 class="wp-block-heading">モデルクラスの作成</h3>



<p>ViewHistoryテーブルに対応するEntityクラスを作成します。Lombokを利用しており、getter、setter、toString 等を自動生成します。</p>



<div style="margin:1.5em 0;border-radius:6px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.15)"><div style="background:#252526;color:#cccccc;padding:6px 12px;font-family:Consolas,'Courier New',monospace;font-size:12px;border-bottom:1px solid #1e1e1e;border-top-left-radius:6px;border-top-right-radius:6px">ViewHistoryEntity.java</div><pre style="background:#1e1e1e;color:#d4d4d4;font-family:Consolas,'Courier New',monospace;font-size:13px;line-height:1.55;padding:14px 16px;margin:0;overflow-x:auto;white-space:pre;border-bottom-left-radius:6px;border-bottom-right-radius:6px"><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 1</span><span style="color:#569cd6">package</span> <span style="color:#9cdcfe">jp</span>.<span style="color:#9cdcfe">co</span>.<span style="color:#9cdcfe">smsdatatech</span>.<span style="color:#9cdcfe">questiongenerate</span>.<span style="color:#9cdcfe">entity</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 2</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 3</span><span style="color:#569cd6">import</span> <span style="color:#9cdcfe">java</span>.<span style="color:#9cdcfe">io</span>.<span style="color:#4ec9b0">Serializable</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 4</span><span style="color:#569cd6">import</span> <span style="color:#9cdcfe">java</span>.<span style="color:#9cdcfe">math</span>.<span style="color:#4ec9b0">BigDecimal</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 5</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 6</span><span style="color:#569cd6">import</span> <span style="color:#9cdcfe">lombok</span>.<span style="color:#4ec9b0">Data</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 7</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 8</span><span style="color:#6a9955">/**</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 9</span><span style="color:#6a9955"> *</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">10</span><span style="color:#6a9955"> * ViewHistoryテーブルEntity</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">11</span><span style="color:#6a9955"> */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">12</span><span style="color:#dcdcaa">@Data</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">13</span><span style="color:#569cd6">public</span> <span style="color:#569cd6">class</span> <span style="color:#4ec9b0">ViewHistoryEntity</span> <span style="color:#569cd6">implements</span> <span style="color:#4ec9b0">Serializable</span> {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">14</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">15</span><span style="color:#6a9955">    /** シリアルバージョンUID */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">16</span>    <span style="color:#569cd6">private</span> <span style="color:#569cd6">static</span> <span style="color:#569cd6">final</span> <span style="color:#9cdcfe">long</span> <span style="color:#9cdcfe">serialVersionUID</span> = <span style="color:#b5cea8">1L</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">17</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">18</span><span style="color:#6a9955">    /** 分野ID */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">19</span>    <span style="color:#569cd6">private</span> <span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">fieldId</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">20</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">21</span><span style="color:#6a9955">    /** 分野名 */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">22</span>    <span style="color:#569cd6">private</span> <span style="color:#4ec9b0">String</span> <span style="color:#9cdcfe">fieldName</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">23</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">24</span><span style="color:#6a9955">    /** 最大進捗率（集計結果） */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">25</span>    <span style="color:#569cd6">private</span> <span style="color:#4ec9b0">BigDecimal</span> <span style="color:#9cdcfe">fieldMaxProgress</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">26</span>}</pre></div>



<h3 class="wp-block-heading">MyBatisのMapper設定</h3>



<p>MyBatis を使用して view_history テーブルからデータを取得する SQL マッピングを定義します。</p>



<div style="margin:1.5em 0;border-radius:6px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.15)"><div style="background:#252526;color:#cccccc;padding:6px 12px;font-family:Consolas,'Courier New',monospace;font-size:12px;border-bottom:1px solid #1e1e1e;border-top-left-radius:6px;border-top-right-radius:6px">ViewHistoryMapper.xml</div><pre style="background:#1e1e1e;color:#d4d4d4;font-family:Consolas,'Courier New',monospace;font-size:13px;line-height:1.55;padding:14px 16px;margin:0;overflow-x:auto;white-space:pre;border-bottom-left-radius:6px;border-bottom-right-radius:6px"><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 1</span><span style="color:#569cd6">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 2</span><span style="color:#569cd6">&lt;!DOCTYPE mapper PUBLIC &quot;-//mybatis.org//DTD Mapper 3.0//EN&quot; &quot;http://mybatis.org/dtd/mybatis-3-mapper.dtd&quot;&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 3</span><span style="color:#569cd6">&lt;mapper</span> <span style="color:#9cdcfe">namespace</span>=<span style="color:#ce9178">&quot;jp.co.smsdatatech.questiongenerate.repository.ViewHistoryMapper&quot;</span><span style="color:#569cd6">&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 4</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 5</span>    <span style="color:#569cd6">&lt;select</span> <span style="color:#9cdcfe">id</span>=<span style="color:#ce9178">&quot;getViewHistoryWithMaxProgress&quot;</span> <span style="color:#9cdcfe">resultMap</span>=<span style="color:#ce9178">&quot;ViewHistoryResultMap&quot;</span><span style="color:#569cd6">&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 6</span>        <span style="color:#569cd6">SELECT</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 7</span>            <span style="color:#d4d4d4">subquery.course_id</span>,<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 8</span>            <span style="color:#d4d4d4">subquery.field_id</span>,<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 9</span>            <span style="color:#d4d4d4">subquery.fieldMaxProgress</span>,<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">10</span>            <span style="color:#d4d4d4">f.field_name</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">11</span>        <span style="color:#569cd6">FROM</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">12</span>            (<span style="color:#569cd6">SELECT</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">13</span>                <span style="color:#d4d4d4">vh.course_id</span>,<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">14</span>                <span style="color:#d4d4d4">vh.field_id</span>,<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">15</span>                <span style="color:#569cd6">MAX</span>(<span style="color:#d4d4d4">vh.progress_percentage</span>) <span style="color:#569cd6">AS</span> <span style="color:#d4d4d4">fieldMaxProgress</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">16</span>            <span style="color:#569cd6">FROM</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">17</span>                <span style="color:#d4d4d4">view_history</span> <span style="color:#d4d4d4">vh</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">18</span>            <span style="color:#569cd6">WHERE</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">19</span>                <span style="color:#d4d4d4">vh.user_id</span> = <span style="color:#9cdcfe">#{userId}</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">20</span>                <span style="color:#569cd6">AND</span> <span style="color:#d4d4d4">vh.course_id</span> = <span style="color:#9cdcfe">#{courseId}</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">21</span>                <span style="color:#569cd6">AND</span> <span style="color:#d4d4d4">vh.viewed_at</span> <span style="color:#569cd6">BETWEEN</span> <span style="color:#9cdcfe">#{startOfWeek}</span> <span style="color:#569cd6">AND</span> <span style="color:#9cdcfe">#{endOfWeek}</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">22</span>            <span style="color:#569cd6">GROUP</span> <span style="color:#569cd6">BY</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">23</span>                <span style="color:#d4d4d4">vh.course_id</span>, <span style="color:#d4d4d4">vh.field_id</span>) <span style="color:#569cd6">AS</span> <span style="color:#d4d4d4">subquery</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">24</span>        <span style="color:#569cd6">INNER</span> <span style="color:#569cd6">JOIN</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">25</span>            <span style="color:#d4d4d4">fields</span> <span style="color:#d4d4d4">f</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">26</span>        <span style="color:#569cd6">ON</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">27</span>            <span style="color:#d4d4d4">subquery.field_id</span> = <span style="color:#d4d4d4">f.field_id</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">28</span>    <span style="color:#569cd6">&lt;/select</span><span style="color:#569cd6">&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">29</span><span style="color:#569cd6">&lt;/mapper</span><span style="color:#569cd6">&gt;</span></pre></div>



<h3 class="wp-block-heading">サービスクラスの作成</h3>



<p>Service層でDBからデータを取得する処理を実装します。</p>



<div style="margin:1.5em 0;border-radius:6px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.15)"><div style="background:#252526;color:#cccccc;padding:6px 12px;font-family:Consolas,'Courier New',monospace;font-size:12px;border-bottom:1px solid #1e1e1e;border-top-left-radius:6px;border-top-right-radius:6px">ViewHistoryService.java</div><pre style="background:#1e1e1e;color:#d4d4d4;font-family:Consolas,'Courier New',monospace;font-size:13px;line-height:1.55;padding:14px 16px;margin:0;overflow-x:auto;white-space:pre;border-bottom-left-radius:6px;border-bottom-right-radius:6px"><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 1</span><span style="color:#6a9955">/**</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 2</span><span style="color:#6a9955"> * 学習履歴リスト作成Service</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 3</span><span style="color:#6a9955"> */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 4</span><span style="color:#dcdcaa">@Slf4j</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 5</span><span style="color:#dcdcaa">@Service</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 6</span><span style="color:#569cd6">public</span> <span style="color:#569cd6">class</span> <span style="color:#4ec9b0">ViewHistoryService</span> {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 7</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 8</span>    <span style="color:#569cd6">private</span> <span style="color:#569cd6">final</span> <span style="color:#4ec9b0">ViewHistoryMapper</span> <span style="color:#9cdcfe">viewHistoryMapper</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 9</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">10</span><span style="color:#6a9955">    // コンストラクタインジェクションを使用</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">11</span>    <span style="color:#dcdcaa">@Autowired</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">12</span>    <span style="color:#569cd6">public</span> <span style="color:#4ec9b0">ViewHistoryService</span>(<span style="color:#4ec9b0">ViewHistoryMapper</span> <span style="color:#9cdcfe">viewHistoryMapper</span>) {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">13</span>        <span style="color:#569cd6">this</span>.<span style="color:#9cdcfe">viewHistoryMapper</span> = <span style="color:#9cdcfe">viewHistoryMapper</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">14</span>    }<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">15</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">16</span><span style="color:#6a9955">    /**</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">17</span><span style="color:#6a9955">     * 指定されたユーザーID、コースID、期間に基づいて分野別に最大進捗率を取得する。</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">18</span><span style="color:#6a9955">     *</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">19</span><span style="color:#6a9955">     * @param userId   ユーザーID</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">20</span><span style="color:#6a9955">     * @param courseId コースID</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">21</span><span style="color:#6a9955">     * @return 学習履歴（分野別に最大進捗率）のリスト</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">22</span><span style="color:#6a9955">     */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">23</span>    <span style="color:#dcdcaa">@Transactional</span>(<span style="color:#9cdcfe">readOnly</span> = <span style="color:#569cd6">true</span>)<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">24</span>    <span style="color:#569cd6">public</span> <span style="color:#4ec9b0">List</span>&lt;<span style="color:#4ec9b0">ViewHistoryEntity</span>&gt; <span style="color:#dcdcaa">getViewHistoryWithMaxProgress</span>(<span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">userId</span>, <span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">courseId</span>) {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">25</span><span style="color:#6a9955">        // 週の開始日と終了日を計算</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">26</span>        <span style="color:#4ec9b0">LocalDateTime</span> <span style="color:#9cdcfe">startOfWeek</span> = <span style="color:#4ec9b0">DateUtils</span>.<span style="color:#dcdcaa">getStartOfWeek</span>();<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">27</span>        <span style="color:#4ec9b0">LocalDateTime</span> <span style="color:#9cdcfe">endOfWeek</span> = <span style="color:#4ec9b0">DateUtils</span>.<span style="color:#dcdcaa">getEndOfWeek</span>();<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">28</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">29</span>        <span style="color:#9cdcfe">log</span>.<span style="color:#dcdcaa">debug</span>(<span style="color:#ce9178">&quot;ユーザーID {} のコースID {} の学習履歴と最大進捗率を週の範囲 {} - {} で取得します。&quot;</span>, <span style="color:#9cdcfe">userId</span>, <span style="color:#9cdcfe">courseId</span>, <span style="color:#9cdcfe">startOfWeek</span>, <span style="color:#9cdcfe">endOfWeek</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">30</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">31</span><span style="color:#6a9955">        // データベースから分野別の最大進捗率を直接返す</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">32</span>        <span style="color:#569cd6">return</span> <span style="color:#9cdcfe">viewHistoryMapper</span>.<span style="color:#dcdcaa">getViewHistoryWithMaxProgress</span>(<span style="color:#9cdcfe">userId</span>, <span style="color:#9cdcfe">courseId</span>, <span style="color:#9cdcfe">startOfWeek</span>, <span style="color:#9cdcfe">endOfWeek</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">33</span>    }<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">34</span>}</pre></div>



<h3 class="wp-block-heading">コントローラークラスの作成</h3>



<p>@PostMapping を使用してエンドポイント /top/getViewHistory を提供します。リクエストのバリデーションを行い、最大進捗率を返します。</p>



<div style="margin:1.5em 0;border-radius:6px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.15)"><div style="background:#252526;color:#cccccc;padding:6px 12px;font-family:Consolas,'Courier New',monospace;font-size:12px;border-bottom:1px solid #1e1e1e;border-top-left-radius:6px;border-top-right-radius:6px">TopRest.java</div><pre style="background:#1e1e1e;color:#d4d4d4;font-family:Consolas,'Courier New',monospace;font-size:13px;line-height:1.55;padding:14px 16px;margin:0;overflow-x:auto;white-space:pre;border-bottom-left-radius:6px;border-bottom-right-radius:6px"><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 1</span><span style="color:#6a9955">/**</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 2</span><span style="color:#6a9955"> * 分野別の最大進捗率を取得.</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 3</span><span style="color:#6a9955"> * &lt;h3&gt;機能概要&lt;/h3&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 4</span><span style="color:#6a9955"> * ユーザーIDおよびコースIDに基づいて分野別の最大進捗率を取得し、ViewHistoryResponseFormに詰めて返却する。</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 5</span><span style="color:#6a9955"> *</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 6</span><span style="color:#6a9955"> * &lt;h3&gt;処理フロー&lt;/h3&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 7</span><span style="color:#6a9955"> * 1. リクエストデータのバリデーション&lt;br&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 8</span><span style="color:#6a9955"> * 2. サービスクラスを呼び出して、最大進捗率を取得&lt;br&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 9</span><span style="color:#6a9955"> * 3. 進捗率が見つからない場合はエラーメッセージを設定&lt;br&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">10</span><span style="color:#6a9955"> * 4. 最大進捗率またはエラーメッセージリストを返却</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">11</span><span style="color:#6a9955"> *</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">12</span><span style="color:#6a9955"> * @param form    UserIdForm</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">13</span><span style="color:#6a9955"> * @param result  バリデーション結果やエラーを格納</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">14</span><span style="color:#6a9955"> * @return 最大進捗率リスト ViewHistoryResponseForm</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">15</span><span style="color:#6a9955"> */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">16</span><span style="color:#dcdcaa">@PostMapping</span>(<span style="color:#ce9178">&quot;/top/getViewHistory&quot;</span>)<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">17</span><span style="color:#569cd6">public</span> <span style="color:#4ec9b0">ViewHistoryResponseForm</span> <span style="color:#dcdcaa">getViewHistory</span>(<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">18</span>        <span style="color:#dcdcaa">@RequestBody</span> <span style="color:#dcdcaa">@Validated</span>({ <span style="color:#4ec9b0">ValidationGroups</span>.<span style="color:#4ec9b0">Step1</span>.<span style="color:#569cd6">class</span>, <span style="color:#4ec9b0">ValidationGroups</span>.<span style="color:#4ec9b0">Step2</span>.<span style="color:#569cd6">class</span> }) <span style="color:#4ec9b0">UserIdForm</span> <span style="color:#9cdcfe">form</span>,<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">19</span>        <span style="color:#4ec9b0">BindingResult</span> <span style="color:#9cdcfe">result</span>) {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">20</span>    <span style="color:#9cdcfe">log</span>.<span style="color:#dcdcaa">debug</span>(<span style="color:#ce9178">&quot;TopRest (getViewHistory) 呼び出し開始&quot;</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">21</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">22</span>    <span style="color:#4ec9b0">ViewHistoryResponseForm</span> <span style="color:#9cdcfe">responseForm</span> = <span style="color:#569cd6">new</span> <span style="color:#4ec9b0">ViewHistoryResponseForm</span>();<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">23</span>    <span style="color:#4ec9b0">List</span>&lt;<span style="color:#4ec9b0">String</span>&gt; <span style="color:#9cdcfe">errorMessageList</span> = <span style="color:#569cd6">new</span> <span style="color:#4ec9b0">ArrayList</span>&lt;&gt;();<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">24</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">25</span><span style="color:#6a9955">    // バリデーションエラーがある場合、詳細なエラーメッセージを追加</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">26</span>    <span style="color:#569cd6">if</span> (<span style="color:#9cdcfe">result</span>.<span style="color:#dcdcaa">hasErrors</span>()) {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">27</span>        <span style="color:#569cd6">for</span> (<span style="color:#4ec9b0">FieldError</span> <span style="color:#9cdcfe">fieldError</span> : <span style="color:#9cdcfe">result</span>.<span style="color:#dcdcaa">getFieldErrors</span>()) {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">28</span>            <span style="color:#4ec9b0">String</span> <span style="color:#9cdcfe">errorMessage</span> = <span style="color:#9cdcfe">messageSource</span>.<span style="color:#dcdcaa">getMessage</span>(<span style="color:#9cdcfe">fieldError</span>, <span style="color:#4ec9b0">Locale</span>.<span style="color:#dcdcaa">getDefault</span>());<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">29</span>            <span style="color:#9cdcfe">errorMessageList</span>.<span style="color:#dcdcaa">add</span>(<span style="color:#9cdcfe">errorMessage</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">30</span>        }<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">31</span>        <span style="color:#9cdcfe">responseForm</span>.<span style="color:#dcdcaa">setErrorMessageList</span>(<span style="color:#9cdcfe">errorMessageList</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">32</span>        <span style="color:#569cd6">return</span> <span style="color:#9cdcfe">responseForm</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">33</span>    }<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">34</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">35</span>    <span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">userId</span> = <span style="color:#4ec9b0">Integer</span>.<span style="color:#dcdcaa">parseInt</span>(<span style="color:#9cdcfe">form</span>.<span style="color:#dcdcaa">getUserId</span>());<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">36</span>    <span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">courseId</span> = <span style="color:#9cdcfe">form</span>.<span style="color:#dcdcaa">getCourseId</span>();<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">37</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">38</span><span style="color:#6a9955">    // 最大進捗率のリストを取得</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">39</span>    <span style="color:#4ec9b0">List</span>&lt;<span style="color:#4ec9b0">ViewHistoryEntity</span>&gt; <span style="color:#9cdcfe">viewHistoryList</span> = <span style="color:#9cdcfe">viewHistoryService</span>.<span style="color:#dcdcaa">getViewHistoryWithMaxProgress</span>(<span style="color:#9cdcfe">userId</span>, <span style="color:#9cdcfe">courseId</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">40</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">41</span><span style="color:#6a9955">    // リストが取得できなかった場合</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">42</span>    <span style="color:#569cd6">if</span> (<span style="color:#4ec9b0">CollectionUtils</span>.<span style="color:#dcdcaa">isEmpty</span>(<span style="color:#9cdcfe">viewHistoryList</span>)) {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">43</span>        <span style="color:#9cdcfe">errorMessageList</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">44</span>                .<span style="color:#dcdcaa">add</span>(<span style="color:#9cdcfe">messageSource</span>.<span style="color:#dcdcaa">getMessage</span>(<span style="color:#4ec9b0">ErrorMessages</span>.<span style="color:#4fc1ff">VIEW_HISTORY_NOT_FOUND</span>, <span style="color:#569cd6">null</span>, <span style="color:#4ec9b0">Locale</span>.<span style="color:#dcdcaa">getDefault</span>()));<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">45</span>        <span style="color:#9cdcfe">responseForm</span>.<span style="color:#dcdcaa">setErrorMessageList</span>(<span style="color:#9cdcfe">errorMessageList</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">46</span>        <span style="color:#9cdcfe">log</span>.<span style="color:#dcdcaa">debug</span>(<span style="color:#ce9178">&quot;ViewHistoryList (viewHistoryList) 学習履歴が見つかりません: &quot;</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">47</span>        <span style="color:#569cd6">return</span> <span style="color:#9cdcfe">responseForm</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">48</span>    }<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">49</span>    <span style="color:#9cdcfe">responseForm</span>.<span style="color:#dcdcaa">setViewHistoryList</span>(<span style="color:#9cdcfe">viewHistoryList</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">50</span>    <span style="color:#9cdcfe">log</span>.<span style="color:#dcdcaa">debug</span>(<span style="color:#ce9178">&quot;TopRest (getViewHistory) 呼び出し終了&quot;</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">51</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">52</span>    <span style="color:#569cd6">return</span> <span style="color:#9cdcfe">responseForm</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">53</span>}</pre></div>



<h2 class="wp-block-heading">実装内容の振り返り</h2>



<p><strong><mark style="background-color:#fdff84" class="has-inline-color">バリデーショングループを作成した点は新たな学びがありました。</mark></strong>これまで単一のバリデーションルールを適用していましたが、バリデーショングループを導入することで、処理ごとに異なるバリデーションルールを適用できるようになりました。たとえば、新規登録時と更新時で必須項目を変える必要があるケースに対応しやすくなりました。</p>



<p>さらに、<strong><mark style="background-color:#fdff84" class="has-inline-color">SQL の部分では INNER JOIN を活用し、効率的にデータを取得できるようにしました。</mark></strong>view_history テーブルから最新の進捗データを取得し、fields テーブルと結合することで、分野名を含めた結果を一度のクエリで取得できるようになりました。</p>



<h2 class="wp-block-heading">まとめ</h2>



<p><strong><mark style="background-color:#fdff84" class="has-inline-color">今回の実装を通じて、バリデーションの柔軟な適用方法や共通部品の活用によるコードの整理、SQL の最適化といった学びを得ることができました。</mark></strong>今後も、より効率的なコード設計を意識しながら開発を進めていきたいと思います。</p><p>The post <a href="https://www.sms-datatech.co.jp/column/try_java-5/">【はじめてのJava】ダッシュボードAPIを作成しよう！⑤分野別進捗データ取得編</a> first appeared on <a href="https://www.sms-datatech.co.jp">SMS DataTech</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>【はじめてのJava】ダッシュボードAPIを作成しよう！④総学習時間API編</title>
		<link>https://www.sms-datatech.co.jp/column/try_java-4/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=try_java-4</link>
		
		<dc:creator><![CDATA[後藤]]></dc:creator>
		<pubDate>Tue, 26 May 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[やってみた]]></category>
		<guid isPermaLink="false">https://www.sms-datatech.co.jp/?p=35813</guid>

					<description><![CDATA[<p>マーケターからエンジニアへ転身した社員による、Javaを用いたダッシュボードAPI作成の実践録。サーバーサイド初心者に向けて、総学習時間APIの設計から実装ステップ、開発を通じて得たリアルな気づきまでをわかりやすく解説します。</p>
<p>The post <a href="https://www.sms-datatech.co.jp/column/try_java-4/">【はじめてのJava】ダッシュボードAPIを作成しよう！④総学習時間API編</a> first appeared on <a href="https://www.sms-datatech.co.jp">SMS DataTech</a>.</p>]]></description>
										<content:encoded><![CDATA[<h2 class="wp-block-heading">はじめに</h2>



<p>こんにちは！マーケターから未経験のエンジニアに転身した社員Tです。<br>開発を始めて1年が経ち、サーバーサイド開発にも触れるようになりました。<br>今回は、Javaを使用したダッシュボードAPIの作成についてご紹介します。<br>これまでフロントエンド中心の開発を行っていた私が、サーバーサイドのAPI開発に挑戦していく様子をお届けします。</p>



<h2 class="wp-block-heading">本記事の対象の方</h2>



<ul class="wp-block-list">
<li>サーバーサイド開発をこれから学びたい方</li>



<li>JavaでのAPI開発に興味がある方</li>



<li>ダッシュボードの作成を通じて、実践的な技術を学びたい方</li>
</ul>



<h2 class="wp-block-heading">Javaとは？</h2>



<p>Javaは、非常に人気のあるオブジェクト指向プログラミング言語で、さまざまなアプリケーションの開発に広く使用されています。</p>



<p>詳しい説明については、過去のブログ記事をご参照ください。<br><a href="https://www.sms-datatech.co.jp/category/try/" title=""><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-blue-2-color"><span style="text-decoration: underline;"><a href="https://www.sms-datatech.co.jp/category/try/"><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-blue-2-color"><span style="text-decoration: underline;">⇒過去のブログ一覧</span></mark></strong></a></span></mark></strong></a></p>



<h2 class="wp-block-heading">環境構築</h2>



<p>環境構築に関しては、内容が広範囲にわたるため、別の記事で詳細に解説する予定です。</p>



<h2 class="wp-block-heading">ダッシュボードAPIの作成</h2>



<p>今回作成したいのは、こんなダッシュボード画面です。<strong><mark style="background-color:#fdff84" class="has-inline-color">ユーザーがコースを選択することで、自分の学習状況を一目で確認できるようにすることを目指しています。</mark></strong></p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="465" src="https://www.sms-datatech.co.jp/wp-content/uploads/2026/02/image1-3-1024x465.png" alt="" class="wp-image-34969" srcset="https://www.sms-datatech.co.jp/wp-content/uploads/2026/02/image1-3-1024x465.png 1024w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/02/image1-3-300x136.png 300w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/02/image1-3-768x349.png 768w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/02/image1-3-1536x698.png 1536w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/02/image1-3.png 1736w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>全体の作成の流れとしては、下記の通りとなります。</p>



<ol class="wp-block-list">
<li>ユーザー情報を取得</li>



<li>コース情報を取得</li>



<li>各グラフ描画に必要なデータを取得</li>
</ol>



<p>今回は、3.各グラフ描画に必要なデータを取得するAPIの実装について詳しく触れていきたいと思います。5つグラフがありますが、上段真ん中の総学習時間円グラフの制作に取り掛かりたいと思います。</p>



<p>1. ユーザー情報の取得については、過去のブログ記事をご参考ください。<br><a href="https://www.sms-datatech.co.jp/column/try_java-1/" title=""><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-blue-2-color"><span style="text-decoration: underline;">⇒関連記事はこちら</span></mark></strong></a></p>



<p>2. コース情報の取得については、過去のブログ記事をご参考ください。<br><a href="https://www.sms-datatech.co.jp/column/try_java-2/" title=""><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-blue-2-color"><span style="text-decoration: underline;">⇒関連記事はこちら</span></mark></strong></a></p>



<h2 class="wp-block-heading">総学習時間取得APIの設計</h2>



<h3 class="wp-block-heading">エンドポイント</h3>



<pre class="wp-block-code"><code>/top/getLoginHistoryByUserId</code></pre>



<ul class="wp-block-list">
<li>HTTPメソッド: POST</li>



<li>リクエストボディ: ユーザーID、コースID</li>



<li>レスポンス: 総学習時間</li>
</ul>



<h3 class="wp-block-heading">リクエスト例</h3>



<pre style="background-color:#000;color:#fff;padding:16px;border-radius:6px;overflow-x:auto;font-family:Consolas,Monaco,'Courier New',monospace;font-size:14px;line-height:1.5;margin:0 0 16px 0;"><code style="background-color:transparent;color:#fff;">{
    "courseId":"7",
    "userId":"175"
}</code></pre>



<p><strong><mark style="background-color:#fdff84" class="has-inline-color">今回はパスパラメータではなく、リクエストボディからユーザーIDとコースIDを受け取る設計にしています。</mark></strong>これにより、柔軟なデータの受け渡しが可能となります。</p>



<h3 class="wp-block-heading">レスポンス例</h3>



<pre style="background-color:#000;color:#fff;padding:16px;border-radius:6px;overflow-x:auto;font-family:Consolas,Monaco,'Courier New',monospace;font-size:14px;line-height:1.5;margin:0 0 16px 0;"><code style="background-color:transparent;color:#fff;">{
  "totalLearningTime": 0,
  "errorMessageList": null
}</code></pre>



<h2 class="wp-block-heading">総学習時間APIの実装</h2>



<h3 class="wp-block-heading">モデルクラスの作成</h3>



<p>まず、ExerciseRecords テーブルに対応するエンティティクラスを定義します。このクラスは、データベースから取得した学習記録の情報を保持します。</p>



<div style="margin:1.5em 0;border-radius:6px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.15)"><div style="background:#252526;color:#cccccc;padding:6px 12px;font-family:Consolas,'Courier New',monospace;font-size:12px;border-bottom:1px solid #1e1e1e;border-top-left-radius:6px;border-top-right-radius:6px">ExerciseRecordsEntity.java</div><pre style="background:#1e1e1e;color:#d4d4d4;font-family:Consolas,'Courier New',monospace;font-size:13px;line-height:1.55;padding:14px 16px;margin:0;overflow-x:auto;white-space:pre;border-bottom-left-radius:6px;border-bottom-right-radius:6px"><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 1</span><span style="color:#569cd6">package</span> <span style="color:#9cdcfe">jp</span>.<span style="color:#9cdcfe">co</span>.<span style="color:#9cdcfe">smsdatatech</span>.<span style="color:#9cdcfe">questiongenerate</span>.<span style="color:#9cdcfe">entity</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 2</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 3</span><span style="color:#569cd6">import</span> <span style="color:#9cdcfe">java</span>.<span style="color:#9cdcfe">io</span>.<span style="color:#4ec9b0">Serializable</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 4</span><span style="color:#569cd6">import</span> <span style="color:#9cdcfe">java</span>.<span style="color:#9cdcfe">sql</span>.<span style="color:#4ec9b0">Timestamp</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 5</span><span style="color:#569cd6">import</span> <span style="color:#9cdcfe">lombok</span>.<span style="color:#4ec9b0">Data</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 6</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 7</span><span style="color:#6a9955">/**</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 8</span><span style="color:#6a9955"> *</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 9</span><span style="color:#6a9955"> * ExerciseRecordsテーブルEntity</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">10</span><span style="color:#6a9955"> */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">11</span><span style="color:#dcdcaa">@Data</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">12</span><span style="color:#569cd6">public</span> <span style="color:#569cd6">class</span> <span style="color:#4ec9b0">ExerciseRecordsEntity</span> <span style="color:#569cd6">implements</span> <span style="color:#4ec9b0">Serializable</span> {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">13</span><span style="color:#6a9955">    /** シリアルバージョンUID */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">14</span>    <span style="color:#569cd6">private</span> <span style="color:#569cd6">static</span> <span style="color:#569cd6">final</span> <span style="color:#9cdcfe">long</span> <span style="color:#9cdcfe">serialVersionUID</span> = <span style="color:#b5cea8">1L</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">15</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">16</span><span style="color:#6a9955">    /** ユーザーID */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">17</span>    <span style="color:#569cd6">private</span> <span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">userId</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">18</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">19</span><span style="color:#6a9955">    /** コースID */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">20</span>    <span style="color:#569cd6">private</span> <span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">courseId</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">21</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">22</span><span style="color:#6a9955">    /** 演習開始日時 */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">23</span>    <span style="color:#569cd6">private</span> <span style="color:#4ec9b0">Timestamp</span> <span style="color:#9cdcfe">startDate</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">24</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">25</span><span style="color:#6a9955">    /** 作成日 */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">26</span>    <span style="color:#569cd6">private</span> <span style="color:#4ec9b0">Timestamp</span> <span style="color:#9cdcfe">createDate</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">27</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">28</span><span style="color:#6a9955">    /** 更新日 */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">29</span>    <span style="color:#569cd6">private</span> <span style="color:#4ec9b0">Timestamp</span> <span style="color:#9cdcfe">updateDate</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">30</span>}</pre></div>



<h3 class="wp-block-heading">MyBatisのMapper設定</h3>



<p>ExerciseRecordsMapper では、SQLを定義して、学習時間を合計します。EXTRACT(EPOCH FROM (end_time &#8211; start_time)) を使って学習時間を計算し、その結果を合計して返します。</p>



<div style="margin:1.5em 0;border-radius:6px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.15)"><div style="background:#252526;color:#cccccc;padding:6px 12px;font-family:Consolas,'Courier New',monospace;font-size:12px;border-bottom:1px solid #1e1e1e;border-top-left-radius:6px;border-top-right-radius:6px">ExerciseRecordsMapper.xml</div><pre style="background:#1e1e1e;color:#d4d4d4;font-family:Consolas,'Courier New',monospace;font-size:13px;line-height:1.55;padding:14px 16px;margin:0;overflow-x:auto;white-space:pre;border-bottom-left-radius:6px;border-bottom-right-radius:6px"><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 1</span><span style="color:#569cd6">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 2</span><span style="color:#569cd6">&lt;!DOCTYPE mapper PUBLIC &quot;-//mybatis.org//DTD Mapper 3.0//EN&quot; &quot;http://mybatis.org/dtd/mybatis-3-mapper.dtd&quot;&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 3</span><span style="color:#569cd6">&lt;mapper</span> <span style="color:#9cdcfe">namespace</span>=<span style="color:#ce9178">&quot;jp.co.smsdatatech.questiongenerate.repository.ExerciseRecordsMapper&quot;</span><span style="color:#569cd6">&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 4</span>    <span style="color:#569cd6">&lt;select</span> <span style="color:#9cdcfe">id</span>=<span style="color:#ce9178">&quot;getTotalLearning&quot;</span> <span style="color:#9cdcfe">resultType</span>=<span style="color:#ce9178">&quot;java.math.BigDecimal&quot;</span><span style="color:#569cd6">&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 5</span>        <span style="color:#569cd6">SELECT</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 6</span>            <span style="color:#569cd6">SUM</span>(<span style="color:#569cd6">EXTRACT</span>(<span style="color:#569cd6">EPOCH</span> <span style="color:#569cd6">FROM</span> (<span style="color:#d4d4d4">end_time</span> - <span style="color:#d4d4d4">start_time</span>)) / <span style="color:#b5cea8">3600</span>) <span style="color:#569cd6">AS</span> <span style="color:#d4d4d4">totalLearningTime</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 7</span>        <span style="color:#569cd6">FROM</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 8</span>            <span style="color:#d4d4d4">exercise_records</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 9</span>        <span style="color:#569cd6">WHERE</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">10</span>            <span style="color:#d4d4d4">user_id</span> = <span style="color:#9cdcfe">#{userId}</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">11</span>            <span style="color:#569cd6">AND</span> <span style="color:#d4d4d4">course_id</span> = <span style="color:#9cdcfe">#{courseId}</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">12</span>            <span style="color:#569cd6">AND</span> <span style="color:#d4d4d4">start_time</span> <span style="color:#569cd6">BETWEEN</span> <span style="color:#9cdcfe">#{startOfWeek}</span> <span style="color:#569cd6">AND</span> <span style="color:#9cdcfe">#{endOfWeek}</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">13</span>    <span style="color:#569cd6">&lt;/select</span><span style="color:#569cd6">&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">14</span><span style="color:#569cd6">&lt;/mapper</span><span style="color:#569cd6">&gt;</span></pre></div>



<h3 class="wp-block-heading">サービスクラスの作成</h3>



<p>次に、学習時間を取得するためのサービスクラスを作成します。ここでは、getTotalLearningTime メソッドを定義して、週の開始日と終了日を計算し、その範囲内での学習時間を集計します。</p>



<div style="margin:1.5em 0;border-radius:6px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.15)"><div style="background:#252526;color:#cccccc;padding:6px 12px;font-family:Consolas,'Courier New',monospace;font-size:12px;border-bottom:1px solid #1e1e1e;border-top-left-radius:6px;border-top-right-radius:6px">ExerciseRecordsService.java</div><pre style="background:#1e1e1e;color:#d4d4d4;font-family:Consolas,'Courier New',monospace;font-size:13px;line-height:1.55;padding:14px 16px;margin:0;overflow-x:auto;white-space:pre;border-bottom-left-radius:6px;border-bottom-right-radius:6px"><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 1</span><span style="color:#569cd6">import</span> <span style="color:#9cdcfe">lombok</span>.<span style="color:#9cdcfe">extern</span>.<span style="color:#9cdcfe">slf4j</span>.<span style="color:#4ec9b0">Slf4j</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 2</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 3</span><span style="color:#6a9955">/**</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 4</span><span style="color:#6a9955"> * 演習履歴テーブルService</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 5</span><span style="color:#6a9955"> */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 6</span><span style="color:#dcdcaa">@Slf4j</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 7</span><span style="color:#dcdcaa">@Service</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 8</span><span style="color:#569cd6">public</span> <span style="color:#569cd6">class</span> <span style="color:#4ec9b0">ExerciseRecordsService</span> {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 9</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">10</span>    <span style="color:#569cd6">private</span> <span style="color:#569cd6">final</span> <span style="color:#4ec9b0">ExerciseRecordsMapper</span> <span style="color:#9cdcfe">exerciseRecordsMapper</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">11</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">12</span><span style="color:#6a9955">    // コンストラクタインジェクションを使用</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">13</span>    <span style="color:#dcdcaa">@Autowired</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">14</span>    <span style="color:#569cd6">public</span> <span style="color:#4ec9b0">ExerciseRecordsService</span>(<span style="color:#4ec9b0">ExerciseRecordsMapper</span> <span style="color:#9cdcfe">exerciseRecordsMapper</span>) {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">15</span>        <span style="color:#569cd6">this</span>.<span style="color:#9cdcfe">exerciseRecordsMapper</span> = <span style="color:#9cdcfe">exerciseRecordsMapper</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">16</span>    }<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">17</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">18</span><span style="color:#6a9955">    /**</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">19</span><span style="color:#6a9955">     * ユーザーIDとコースIDを基に、週の範囲内で合計学習時間を取得する。</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">20</span><span style="color:#6a9955">     *</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">21</span><span style="color:#6a9955">     * @param userId   ユーザーID</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">22</span><span style="color:#6a9955">     * @param courseId コースID</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">23</span><span style="color:#6a9955">     * @return 総学習時間</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">24</span><span style="color:#6a9955">     */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">25</span>    <span style="color:#dcdcaa">@Transactional</span>(<span style="color:#9cdcfe">readOnly</span> = <span style="color:#569cd6">true</span>)<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">26</span>    <span style="color:#569cd6">public</span> <span style="color:#4ec9b0">BigDecimal</span> <span style="color:#dcdcaa">getTotalLearningTime</span>(<span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">userId</span>, <span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">courseId</span>) {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">27</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">28</span><span style="color:#6a9955">        // 週の開始日と終了日を計算</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">29</span>        <span style="color:#4ec9b0">LocalDateTime</span> <span style="color:#9cdcfe">startOfWeek</span> = <span style="color:#4ec9b0">DateUtils</span>.<span style="color:#dcdcaa">getStartOfWeek</span>();<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">30</span>        <span style="color:#4ec9b0">LocalDateTime</span> <span style="color:#9cdcfe">endOfWeek</span> = <span style="color:#4ec9b0">DateUtils</span>.<span style="color:#dcdcaa">getEndOfWeek</span>();<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">31</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">32</span>        <span style="color:#9cdcfe">log</span>.<span style="color:#dcdcaa">debug</span>(<span style="color:#ce9178">&quot;ユーザーID {} とコースID {} の合計学習時間を週の範囲 {} - {} で取得します。&quot;</span>, <span style="color:#9cdcfe">userId</span>, <span style="color:#9cdcfe">courseId</span>, <span style="color:#9cdcfe">startOfWeek</span>, <span style="color:#9cdcfe">endOfWeek</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">33</span>        <span style="color:#569cd6">return</span> <span style="color:#9cdcfe">exerciseRecordsMapper</span>.<span style="color:#dcdcaa">getTotalLearning</span>(<span style="color:#9cdcfe">userId</span>, <span style="color:#9cdcfe">courseId</span>, <span style="color:#9cdcfe">startOfWeek</span>, <span style="color:#9cdcfe">endOfWeek</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">34</span>    }<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">35</span>}</pre></div>



<h3 class="wp-block-heading">コントローラークラスの作成</h3>



<p>次に、Spring BootのRESTコントローラを作成し、getTotalLearningTime メソッドを呼び出して、クライアントに学習時間を返却します。</p>



<div style="margin:1.5em 0;border-radius:6px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.15)"><div style="background:#252526;color:#cccccc;padding:6px 12px;font-family:Consolas,'Courier New',monospace;font-size:12px;border-bottom:1px solid #1e1e1e;border-top-left-radius:6px;border-top-right-radius:6px">TopRest.java</div><pre style="background:#1e1e1e;color:#d4d4d4;font-family:Consolas,'Courier New',monospace;font-size:13px;line-height:1.55;padding:14px 16px;margin:0;overflow-x:auto;white-space:pre;border-bottom-left-radius:6px;border-bottom-right-radius:6px"><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 1</span><span style="color:#6a9955">/**</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 2</span><span style="color:#6a9955"> * 総学習時間を取得.</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 3</span><span style="color:#6a9955"> * &lt;h3&gt;機能概要&lt;/h3&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 4</span><span style="color:#6a9955"> * ユーザーIDおよびコースIDに基づいて総学習時間を取得し、TotalLearningTimeResponseFormに詰めて返却する。</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 5</span><span style="color:#6a9955"> *</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 6</span><span style="color:#6a9955"> * &lt;h3&gt;処理フロー&lt;/h3&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 7</span><span style="color:#6a9955"> * 1. リクエストデータのバリデーション&lt;br&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 8</span><span style="color:#6a9955"> * 2. サービスクラスを呼び出して、総学習時間を取得&lt;br&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 9</span><span style="color:#6a9955"> * 3. 学習時間が見つからない場合はエラーメッセージを設定&lt;br&gt;</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">10</span><span style="color:#6a9955"> * 4. 総学習時間またはエラーメッセージリストを返却</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">11</span><span style="color:#6a9955"> *</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">12</span><span style="color:#6a9955"> * @param form    UserIdForm</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">13</span><span style="color:#6a9955"> * @param result  バリデーション結果やエラーを格納</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">14</span><span style="color:#6a9955"> * @return 総学習時間リスト TotalLearningTimeResponseForm</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">15</span><span style="color:#6a9955"> */</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">16</span><span style="color:#dcdcaa">@PostMapping</span>(<span style="color:#ce9178">&quot;/top/getTotalLearningTime&quot;</span>)<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">17</span><span style="color:#569cd6">public</span> <span style="color:#4ec9b0">TotalLearningTimeResponseForm</span> <span style="color:#dcdcaa">getTotalLearningTime</span>(<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">18</span>        <span style="color:#dcdcaa">@RequestBody</span> <span style="color:#dcdcaa">@Validated</span>({ <span style="color:#4ec9b0">ValidationGroups</span>.<span style="color:#4ec9b0">Step1</span>.<span style="color:#569cd6">class</span>, <span style="color:#4ec9b0">ValidationGroups</span>.<span style="color:#4ec9b0">Step2</span>.<span style="color:#569cd6">class</span> }) <span style="color:#4ec9b0">UserIdForm</span> <span style="color:#9cdcfe">form</span>,<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">19</span>        <span style="color:#4ec9b0">BindingResult</span> <span style="color:#9cdcfe">result</span>) {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">20</span>    <span style="color:#9cdcfe">log</span>.<span style="color:#dcdcaa">debug</span>(<span style="color:#ce9178">&quot;TopRest (getTotalLearningTime) 呼び出し開始&quot;</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">21</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">22</span>    <span style="color:#4ec9b0">TotalLearningTimeResponseForm</span> <span style="color:#9cdcfe">responseForm</span> = <span style="color:#569cd6">new</span> <span style="color:#4ec9b0">TotalLearningTimeResponseForm</span>();<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">23</span>    <span style="color:#4ec9b0">List</span>&lt;<span style="color:#4ec9b0">String</span>&gt; <span style="color:#9cdcfe">errorMessageList</span> = <span style="color:#569cd6">new</span> <span style="color:#4ec9b0">ArrayList</span>&lt;&gt;();<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">24</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">25</span><span style="color:#6a9955">    // バリデーションエラーがある場合、詳細なエラーメッセージを追加</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">26</span>    <span style="color:#569cd6">if</span> (<span style="color:#9cdcfe">result</span>.<span style="color:#dcdcaa">hasErrors</span>()) {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">27</span>        <span style="color:#569cd6">for</span> (<span style="color:#4ec9b0">FieldError</span> <span style="color:#9cdcfe">fieldError</span> : <span style="color:#9cdcfe">result</span>.<span style="color:#dcdcaa">getFieldErrors</span>()) {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">28</span>            <span style="color:#4ec9b0">String</span> <span style="color:#9cdcfe">errorMessage</span> = <span style="color:#9cdcfe">messageSource</span>.<span style="color:#dcdcaa">getMessage</span>(<span style="color:#9cdcfe">fieldError</span>, <span style="color:#4ec9b0">Locale</span>.<span style="color:#dcdcaa">getDefault</span>());<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">29</span>            <span style="color:#9cdcfe">errorMessageList</span>.<span style="color:#dcdcaa">add</span>(<span style="color:#9cdcfe">errorMessage</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">30</span>        }<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">31</span>        <span style="color:#9cdcfe">responseForm</span>.<span style="color:#dcdcaa">setErrorMessageList</span>(<span style="color:#9cdcfe">errorMessageList</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">32</span>        <span style="color:#569cd6">return</span> <span style="color:#9cdcfe">responseForm</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">33</span>    }<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">34</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">35</span>    <span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">userId</span> = <span style="color:#4ec9b0">Integer</span>.<span style="color:#dcdcaa">parseInt</span>(<span style="color:#9cdcfe">form</span>.<span style="color:#dcdcaa">getUserId</span>());<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">36</span>    <span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">courseId</span> = <span style="color:#9cdcfe">form</span>.<span style="color:#dcdcaa">getCourseId</span>();<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">37</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">38</span><span style="color:#6a9955">    // サービスで合計学習時間を取得（学習時間がない場合はデフォルト値0.0)</span><br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">39</span>    <span style="color:#4ec9b0">BigDecimal</span> <span style="color:#9cdcfe">totalLearningTime</span> = <span style="color:#9cdcfe">exerciseRecordsService</span>.<span style="color:#dcdcaa">getTotalLearningTime</span>(<span style="color:#9cdcfe">userId</span>, <span style="color:#9cdcfe">courseId</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">40</span>    <span style="color:#569cd6">if</span> (<span style="color:#9cdcfe">totalLearningTime</span> == <span style="color:#569cd6">null</span>) {<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">41</span>        <span style="color:#9cdcfe">totalLearningTime</span> = <span style="color:#4ec9b0">BigDecimal</span>.<span style="color:#4fc1ff">ZERO</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">42</span>    }<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">43</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">44</span>    <span style="color:#9cdcfe">responseForm</span>.<span style="color:#dcdcaa">setTotalLearningTime</span>(<span style="color:#9cdcfe">totalLearningTime</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">45</span>    <span style="color:#9cdcfe">log</span>.<span style="color:#dcdcaa">debug</span>(<span style="color:#ce9178">&quot;TopRest (getTotalLearningTime) 呼び出し終了&quot;</span>);<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">46</span>&nbsp;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">47</span>    <span style="color:#569cd6">return</span> <span style="color:#9cdcfe">responseForm</span>;<br>
<span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">48</span>}</pre></div>



<h2 class="wp-block-heading">実装内容の振り返り</h2>



<h3 class="wp-block-heading">1. ユーティリティクラスの活用</h3>



<p><strong><mark style="background-color:#fdff84" class="has-inline-color">以前作成した DateUtils クラスを活用し、週の開始日と終了日を簡単に取得できました。</mark></strong>このおかげで、日付操作をシンプルに保ち、コードの重複を避けることができました。</p>



<h3 class="wp-block-heading">2. BigDecimalの利用</h3>



<p><strong><mark style="background-color:#fdff84" class="has-inline-color">学習時間の合計を計算するために BigDecimal を使用しました。</mark></strong>BigDecimal は精度の高い数値計算が可能で、浮動小数点誤差を防ぐために最適です。学習時間がない場合は BigDecimal.ZERO を使用してデフォルト値を設定しました。</p>



<h2 class="wp-block-heading">まとめ</h2>



<p><strong><mark style="background-color:#fdff84" class="has-inline-color">今回の実装では、ユーティリティクラスの再利用と BigDecimal の活用で、精度高くシンプルなコードが書けました。</mark></strong>これにより、学習時間計算が正確で信頼性のあるものとなり、保守性が向上しました。<br>次回も、各グラフの作成方法について記載しますのでお楽しみに！</p><p>The post <a href="https://www.sms-datatech.co.jp/column/try_java-4/">【はじめてのJava】ダッシュボードAPIを作成しよう！④総学習時間API編</a> first appeared on <a href="https://www.sms-datatech.co.jp">SMS DataTech</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>【はじめてのJava】ダッシュボードAPIを作成しよう！③ログイン状況可視化編</title>
		<link>https://www.sms-datatech.co.jp/column/try_java-3/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=try_java-3</link>
		
		<dc:creator><![CDATA[後藤]]></dc:creator>
		<pubDate>Mon, 25 May 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[やってみた]]></category>
		<guid isPermaLink="false">https://www.sms-datatech.co.jp/?p=35806</guid>

					<description><![CDATA[<p>マーケターからエンジニアへ転身した社員による、Javaを用いたダッシュボードAPI作成の実践録。サーバーサイド初心者に向けて、ログイン情報取得APIの設計から実装ステップ、開発を通じて得たリアルな気づきまでをわかりやすく解説します。</p>
<p>The post <a href="https://www.sms-datatech.co.jp/column/try_java-3/">【はじめてのJava】ダッシュボードAPIを作成しよう！③ログイン状況可視化編</a> first appeared on <a href="https://www.sms-datatech.co.jp">SMS DataTech</a>.</p>]]></description>
										<content:encoded><![CDATA[<h2 class="wp-block-heading">はじめに</h2>



<p>こんにちは！マーケターから未経験のエンジニアに転身した社員Tです。</p>



<p>開発を始めて1年が経ち、サーバーサイド開発にも触れるようになりました。</p>



<p><strong><mark style="background-color:#fdff84" class="has-inline-color">今回は、Javaを使用したダッシュボードAPIの作成についてご紹介します。</mark></strong></p>



<p>これまでフロントエンド中心の開発を行っていた私が、サーバーサイドのAPI開発に挑戦していく様子をお届けします。</p>



<h2 class="wp-block-heading">本記事の対象の方</h2>



<ul class="wp-block-list">
<li>サーバーサイド開発をこれから学びたい方</li>



<li>JavaでのAPI開発に興味がある方</li>



<li>ダッシュボードの作成を通じて、実践的な技術を学びたい方</li>
</ul>



<h2 class="wp-block-heading">Javaとは？</h2>



<p><strong><mark style="background-color:#fdff84" class="has-inline-color">Javaは、非常に人気のあるオブジェクト指向プログラミング言語で、さまざまなアプリケーションの開発に広く使用されています。</mark></strong></p>



<p>詳しい説明については、過去のブログ記事をご参照ください。<br><a href="https://www.sms-datatech.co.jp/category/try/"><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-blue-2-color"><span style="text-decoration: underline;">⇒過去のブログ一覧</span></mark></strong></a></p>



<h2 class="wp-block-heading">環境構築</h2>



<p>環境構築に関しては、内容が広範囲にわたるため、別の記事で詳細に解説する予定です。</p>



<h2 class="wp-block-heading">ダッシュボードAPIの作成</h2>



<p><strong><mark style="background-color:#fdff84" class="has-inline-color">今回作成したいのは、こんなダッシュボード画面です。</mark></strong>ユーザーがコースを選択することで、自分の学習状況を一目で確認できるようにすることを目指しています。</p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="465" src="https://www.sms-datatech.co.jp/wp-content/uploads/2026/02/image1-3-1024x465.png" alt="" class="wp-image-34969" srcset="https://www.sms-datatech.co.jp/wp-content/uploads/2026/02/image1-3-1024x465.png 1024w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/02/image1-3-300x136.png 300w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/02/image1-3-768x349.png 768w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/02/image1-3-1536x698.png 1536w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/02/image1-3.png 1736w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>全体の作成の流れとしては、下記の通りとなります。</p>



<ol class="wp-block-list">
<li>ユーザー情報を取得</li>



<li>コース情報を取得</li>



<li>各グラフ描画に必要なデータを取得</li>
</ol>



<p>今回は、3.各グラフ描画に必要なデータを取得するAPIの実装について詳しく触れていきたいと思います。左上のログイン状況の表の制作に取り掛かりたいと思います。</p>



<p>1. ユーザー情報の取得については、過去のブログ記事をご参考ください。<br><a href="https://www.sms-datatech.co.jp/column/try_java-1/"><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-blue-2-color"><span style="text-decoration: underline;">⇒【はじめてのJava】ダッシュボードAPIを作成しよう！① ユーザー情報取得編</span></mark></strong></a></p>



<p>2. コース情報の取得については、過去のブログ記事をご参考ください。<br><a href="https://www.sms-datatech.co.jp/column/try_java-2/"><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-blue-2-color"><span style="text-decoration: underline;">⇒【はじめてのJava】ダッシュボードAPIを作成しよう！②コース情報取得編</span></mark></strong></a></p>



<h2 class="wp-block-heading">ログイン情報取得APIの設計</h2>



<p>取得したユーザーIDからログイン情報を取得し返却する。</p>



<h3 class="wp-block-heading">エンドポイント</h3>



<p>/top/getLoginHistoryByUserId</p>



<ul class="wp-block-list">
<li>HTTPメソッド: POST</li>



<li>リクエストボディ: ユーザーID</li>



<li>レスポンス: ログイン履歴のマップ</li>
</ul>



<h3 class="wp-block-heading">リクエスト例</h3>



<pre style="background-color:#000;color:#fff;padding:16px;border-radius:6px;overflow-x:auto;font-family:Consolas,Monaco,'Courier New',monospace;font-size:14px;line-height:1.5;margin:0 0 16px 0;"><code style="background-color:transparent;color:#fff;">{
  "userId":"175"
}</code></pre>



<p><strong><mark style="background-color:#fdff84" class="has-inline-color">今回はパスパラメータではなく、リクエストボディからユーザーIDを受け取る設計にしています。</mark></strong>これにより、柔軟なデータの受け渡しが可能となります。</p>



<h3 class="wp-block-heading">レスポンス例</h3>



<pre style="background-color:#000;color:#fff;padding:16px;border-radius:6px;overflow-x:auto;font-family:Consolas,Monaco,'Courier New',monospace;font-size:14px;line-height:1.5;margin:0 0 16px 0;"><code style="background-color:transparent;color:#fff;">{
  "dailyLoginHistoryMap": {
    "monday": false,
    "tuesday": false,
    "wednesday": false,
    "thursday": false,
    "friday": false,
    "saturday": false,
    "sunday": false
  },
  "errorMessageList": null
}</code></pre>



<h2 class="wp-block-heading">ログイン情報取得APIの実装</h2>



<h3 class="wp-block-heading">ログイン情報を管理するモデルクラスの作成</h3>



<p>まず、ログイン履歴を保持するエンティティクラスを作成します。</p>



<div style="margin:1.5em 0;border-radius:6px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.15)"><div style="background:#252526;color:#cccccc;padding:6px 12px;font-family:Consolas,'Courier New',monospace;font-size:12px;border-bottom:1px solid #1e1e1e;border-top-left-radius:6px;border-top-right-radius:6px">LoginHistoryEntity.java</div><pre style="background:#1e1e1e;color:#d4d4d4;font-family:Consolas,'Courier New',monospace;font-size:13px;line-height:1.55;padding:14px 16px;margin:0;overflow-x:auto;white-space:pre;border-bottom-left-radius:6px;border-bottom-right-radius:6px"><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 1</span><span style="color:#569cd6">package</span> <span style="color:#9cdcfe">jp</span>.<span style="color:#9cdcfe">co</span>.<span style="color:#9cdcfe">smsdatatech</span>.<span style="color:#9cdcfe">questiongenerate</span>.<span style="color:#9cdcfe">entity</span>;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 2</span>&nbsp;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 3</span><span style="color:#569cd6">import</span> <span style="color:#9cdcfe">java</span>.<span style="color:#9cdcfe">io</span>.<span style="color:#4ec9b0">Serializable</span>;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 4</span><span style="color:#569cd6">import</span> <span style="color:#9cdcfe">java</span>.<span style="color:#9cdcfe">sql</span>.<span style="color:#4ec9b0">Timestamp</span>;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 5</span>&nbsp;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 6</span><span style="color:#569cd6">import</span> <span style="color:#9cdcfe">lombok</span>.<span style="color:#4ec9b0">Data</span>;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 7</span>&nbsp;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 8</span><span style="color:#6a9955">/**</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 9</span> <span style="color:#6a9955">* ログイン履歴テーブルEntity</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">10</span> <span style="color:#6a9955">*/</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">11</span><span style="color:#dcdcaa">@Data</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">12</span><span style="color:#569cd6">public</span> <span style="color:#569cd6">class</span> <span style="color:#4ec9b0">LoginHistoryEntity</span> <span style="color:#569cd6">implements</span> <span style="color:#4ec9b0">Serializable</span> {<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">13</span>&nbsp;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">14</span>    <span style="color:#6a9955">/** シリアルバージョンUID */</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">15</span>    <span style="color:#569cd6">private</span> <span style="color:#569cd6">static</span> <span style="color:#569cd6">final</span> <span style="color:#569cd6">long</span> <span style="color:#9cdcfe">serialVersionUID</span> = <span style="color:#b5cea8">1L</span>;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">16</span>&nbsp;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">17</span>    <span style="color:#6a9955">/** ユーザーID */</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">18</span>    <span style="color:#569cd6">private</span> <span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">userId</span>;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">19</span>&nbsp;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">20</span>    <span style="color:#6a9955">/** 法人ID */</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">21</span>    <span style="color:#569cd6">private</span> <span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">corporateId</span>;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">22</span>&nbsp;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">23</span>    <span style="color:#6a9955">/** ログイン日時 */</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">24</span>    <span style="color:#569cd6">private</span> <span style="color:#4ec9b0">Timestamp</span> <span style="color:#9cdcfe">loginDate</span>;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">25</span>}</pre></div>



<h3 class="wp-block-heading">MyBatisのMapper設定</h3>



<p>次に、MyBatisのMapperを使用してデータベースからログイン履歴を取得します。getLoginHistoryByUserIdAndDateRange メソッドは、指定されたユーザーIDと日付範囲に基づいてログイン履歴を取得します。</p>



<div style="margin:1.5em 0;border-radius:6px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.15)"><div style="background:#252526;color:#cccccc;padding:6px 12px;font-family:Consolas,'Courier New',monospace;font-size:12px;border-bottom:1px solid #1e1e1e;border-top-left-radius:6px;border-top-right-radius:6px">LoginHistoryMapper.xml</div><pre style="background:#1e1e1e;color:#d4d4d4;font-family:Consolas,'Courier New',monospace;font-size:13px;line-height:1.55;padding:14px 16px;margin:0;overflow-x:auto;white-space:pre;border-bottom-left-radius:6px;border-bottom-right-radius:6px"><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 1</span><span style="color:#d4d4d4">&lt;</span><span style="color:#569cd6">mapper</span> <span style="color:#9cdcfe">namespace</span><span style="color:#d4d4d4">=</span><span style="color:#ce9178">&quot;jp.co.smsdatatech.questiongenerate.repository.LoginHistoryMapper&quot;</span><span style="color:#d4d4d4">&gt;</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 2</span>&nbsp;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 3</span><span style="color:#d4d4d4">    </span><span style="color:#d4d4d4">&lt;</span><span style="color:#569cd6">select</span> <span style="color:#9cdcfe">id</span><span style="color:#d4d4d4">=</span><span style="color:#ce9178">&quot;getLoginHistoryByUserIdAndDateRange&quot;</span> <span style="color:#9cdcfe">parameterType</span><span style="color:#d4d4d4">=</span><span style="color:#ce9178">&quot;map&quot;</span> <span style="color:#9cdcfe">resultType</span><span style="color:#d4d4d4">=</span><span style="color:#ce9178">&quot;java.time.LocalDateTime&quot;</span><span style="color:#d4d4d4">&gt;</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 4</span><span style="color:#d4d4d4">        SELECT DISTINCT</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 5</span><span style="color:#d4d4d4">            login_date</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 6</span><span style="color:#d4d4d4">        FROM</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 7</span><span style="color:#d4d4d4">            login_history</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 8</span><span style="color:#d4d4d4">        WHERE</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 9</span><span style="color:#d4d4d4">            user_id = #{userId}</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">10</span><span style="color:#d4d4d4">            AND login_date BETWEEN #{startOfWeek} AND #{endOfWeek}</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">11</span><span style="color:#d4d4d4">        ORDER BY</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">12</span><span style="color:#d4d4d4">            login_date DESC</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">13</span><span style="color:#d4d4d4">    </span><span style="color:#d4d4d4">&lt;</span><span style="color:#d4d4d4">/</span><span style="color:#569cd6">select</span><span style="color:#d4d4d4">&gt;</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">14</span><span style="color:#d4d4d4">&lt;</span><span style="color:#d4d4d4">/</span><span style="color:#569cd6">mapper</span><span style="color:#d4d4d4">&gt;</span></pre></div>



<h3 class="wp-block-heading">サービスクラスの作成</h3>



<p>サービスクラスで、ログイン履歴を取得するロジックを構築します。LoginHistoryService クラスでは、ユーザーIDを受け取り、ログイン履歴を取得します。</p>



<div style="margin:1.5em 0;border-radius:6px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.15)"><div style="background:#252526;color:#cccccc;padding:6px 12px;font-family:Consolas,'Courier New',monospace;font-size:12px;border-bottom:1px solid #1e1e1e;border-top-left-radius:6px;border-top-right-radius:6px">LoginHistoryService.java</div><pre style="background:#1e1e1e;color:#d4d4d4;font-family:Consolas,'Courier New',monospace;font-size:13px;line-height:1.55;padding:14px 16px;margin:0;overflow-x:auto;white-space:pre;border-bottom-left-radius:6px;border-bottom-right-radius:6px"><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 1</span><span style="color:#569cd6">public</span> <span style="color:#569cd6">class</span> <span style="color:#4ec9b0">LoginHistoryService</span> {<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 2</span>&nbsp;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 3</span>    <span style="color:#6a9955">/**</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 4</span>     <span style="color:#6a9955">* 指定されたユーザーIDに基づいてログイン履歴を取得する。</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 5</span>     <span style="color:#6a9955">*</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 6</span>     <span style="color:#6a9955">* @param userId ユーザーID</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 7</span>     <span style="color:#6a9955">* @return ログイン履歴（曜日ごとの情報）</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 8</span>     <span style="color:#6a9955">*/</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 9</span>    <span style="color:#dcdcaa">@Transactional</span>(<span style="color:#9cdcfe">readOnly</span> = <span style="color:#569cd6">true</span>)<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">10</span>    <span style="color:#569cd6">public</span> <span style="color:#4ec9b0">Map</span>&lt;<span style="color:#569cd6">String</span>, <span style="color:#4ec9b0">Boolean</span>&gt; <span style="color:#dcdcaa">getLoginHistoryByUserIdAndDateRange</span>(<span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">userId</span>) {<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">11</span>        <span style="color:#6a9955">// 週の開始日と終了日を計算</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">12</span>        <span style="color:#4ec9b0">LocalDateTime</span> <span style="color:#9cdcfe">startOfWeek</span> = <span style="color:#4ec9b0">DateUtils</span>.<span style="color:#dcdcaa">getStartOfWeek</span>();<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">13</span>        <span style="color:#4ec9b0">LocalDateTime</span> <span style="color:#9cdcfe">endOfWeek</span> = <span style="color:#4ec9b0">DateUtils</span>.<span style="color:#dcdcaa">getEndOfWeek</span>();<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">14</span>&nbsp;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">15</span>        <span style="color:#9cdcfe">log</span>.<span style="color:#dcdcaa">debug</span>(<span style="color:#ce9178">&quot;ユーザーID {} のログイン履歴を週の範囲 {} - {} で取得します。&quot;</span>, <span style="color:#9cdcfe">userId</span>, <span style="color:#9cdcfe">startOfWeek</span>, <span style="color:#9cdcfe">endOfWeek</span>);<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">16</span>&nbsp;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">17</span>        <span style="color:#6a9955">// データベースからログイン履歴を取得</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">18</span>        <span style="color:#4ec9b0">List</span>&lt;<span style="color:#4ec9b0">LocalDateTime</span>&gt; <span style="color:#9cdcfe">loginHistory</span> = <span style="color:#9cdcfe">loginHistoryMapper</span>.<span style="color:#dcdcaa">getLoginHistoryByUserIdAndDateRange</span>(<span style="color:#9cdcfe">userId</span>, <span style="color:#9cdcfe">startOfWeek</span>, <span style="color:#9cdcfe">endOfWeek</span>);<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">19</span>&nbsp;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">20</span>        <span style="color:#4ec9b0">Map</span>&lt;<span style="color:#569cd6">String</span>, <span style="color:#4ec9b0">Boolean</span>&gt; <span style="color:#9cdcfe">dailyLoginHistoryMap</span> = <span style="color:#4ec9b0">DateUtils</span>.<span style="color:#dcdcaa">initializeWeekMap</span>();<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">21</span>&nbsp;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">22</span>        <span style="color:#4ec9b0">DateTimeFormatter</span> <span style="color:#9cdcfe">dayFormatter</span> = <span style="color:#4ec9b0">DateTimeFormatter</span>.<span style="color:#dcdcaa">ofPattern</span>(<span style="color:#ce9178">&quot;EEEE&quot;</span>, <span style="color:#4ec9b0">Locale</span>.<span style="color:#4fc1ff">ENGLISH</span>);<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">23</span>        <span style="color:#569cd6">for</span> (<span style="color:#4ec9b0">LocalDateTime</span> <span style="color:#9cdcfe">login</span> : <span style="color:#9cdcfe">loginHistory</span>) {<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">24</span>            <span style="color:#569cd6">if</span> (<span style="color:#9cdcfe">login</span> != <span style="color:#569cd6">null</span>) {<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">25</span>                <span style="color:#569cd6">String</span> <span style="color:#9cdcfe">dayOfWeek</span> = <span style="color:#9cdcfe">login</span>.<span style="color:#dcdcaa">format</span>(<span style="color:#9cdcfe">dayFormatter</span>).<span style="color:#dcdcaa">toLowerCase</span>(<span style="color:#4ec9b0">Locale</span>.<span style="color:#4fc1ff">ROOT</span>);<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">26</span>                <span style="color:#9cdcfe">dailyLoginHistoryMap</span>.<span style="color:#dcdcaa">put</span>(<span style="color:#9cdcfe">dayOfWeek</span>, <span style="color:#569cd6">true</span>);<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">27</span>            }<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">28</span>        }<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">29</span>        <span style="color:#569cd6">return</span> <span style="color:#9cdcfe">dailyLoginHistoryMap</span>;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">30</span>    }<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">31</span>}</pre></div>



<h3 class="wp-block-heading">コントローラークラスの作成</h3>



<p>コントローラークラスでは、APIのエンドポイントを定義し、サービス層を呼び出してレスポンスを返します。</p>



<div style="margin:1.5em 0;border-radius:6px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.15)"><div style="background:#252526;color:#cccccc;padding:6px 12px;font-family:Consolas,'Courier New',monospace;font-size:12px;border-bottom:1px solid #1e1e1e;border-top-left-radius:6px;border-top-right-radius:6px">TopRest.java</div><pre style="background:#1e1e1e;color:#d4d4d4;font-family:Consolas,'Courier New',monospace;font-size:13px;line-height:1.55;padding:14px 16px;margin:0;overflow-x:auto;white-space:pre;border-bottom-left-radius:6px;border-bottom-right-radius:6px"><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 1</span><span style="color:#569cd6">public</span> <span style="color:#569cd6">class</span> <span style="color:#4ec9b0">TopRest</span> {<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 2</span>&nbsp;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 3</span>    <span style="color:#6a9955">/**</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 4</span>     <span style="color:#6a9955">* ログイン状況取得.</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 5</span>     <span style="color:#6a9955">*</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 6</span>     <span style="color:#6a9955">* login_historyテーブルからユーザーIDに紐づくログイン状況を取得し、LoginHistoryResponseFormに詰めて返却する。</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 7</span>     <span style="color:#6a9955">*</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 8</span>     <span style="color:#6a9955">* &lt;h3&gt;処理フロー&lt;/h3&gt;</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 9</span>     <span style="color:#6a9955">* 1. ログイン履歴を取得&lt;br&gt;</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">10</span>     <span style="color:#6a9955">* 2. 取得データを返却</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">11</span>     <span style="color:#6a9955">*</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">12</span>     <span style="color:#6a9955">* &lt;h3&gt;レスポンス構成&lt;/h3&gt;</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">13</span>     <span style="color:#6a9955">* - **loginHistoryByDay**: 曜日ごとのログイン状況（キー: 曜日、値: true/false）&lt;br&gt;</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">14</span>     <span style="color:#6a9955">* - **error**: エラー発生時のメッセージ</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">15</span>     <span style="color:#6a9955">*</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">16</span>     <span style="color:#6a9955">* @param form    Top画面UserId用Form</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">17</span>     <span style="color:#6a9955">* @param result  バリデーション結果</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">18</span>     <span style="color:#6a9955">* @return ログイン履歴情報 LoginHistoryResponseForm</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">19</span>     <span style="color:#6a9955">*/</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">20</span>    <span style="color:#dcdcaa">@PostMapping</span>(<span style="color:#ce9178">&quot;/top/getLoginHistoryByUserId&quot;</span>)<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">21</span>    <span style="color:#569cd6">public</span> <span style="color:#4ec9b0">LoginHistoryResponseForm</span> <span style="color:#dcdcaa">getLoginHistoryByUserId</span>(<span style="color:#dcdcaa">@RequestBody</span> <span style="color:#dcdcaa">@Valid</span> <span style="color:#4ec9b0">UserIdForm</span> <span style="color:#9cdcfe">form</span>, <span style="color:#4ec9b0">BindingResult</span> <span style="color:#9cdcfe">result</span>) {<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">22</span>        <span style="color:#4ec9b0">List</span>&lt;<span style="color:#569cd6">String</span>&gt; <span style="color:#9cdcfe">errorMessageList</span> = <span style="color:#569cd6">new</span> <span style="color:#4ec9b0">ArrayList</span>&lt;&gt;();<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">23</span>        <span style="color:#4ec9b0">LoginHistoryResponseForm</span> <span style="color:#9cdcfe">responseForm</span> = <span style="color:#569cd6">new</span> <span style="color:#4ec9b0">LoginHistoryResponseForm</span>();<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">24</span>&nbsp;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">25</span>        <span style="color:#4ec9b0">Set</span>&lt;<span style="color:#569cd6">String</span>&gt; <span style="color:#9cdcfe">processedFields</span> = <span style="color:#569cd6">new</span> <span style="color:#4ec9b0">HashSet</span>&lt;&gt;();<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">26</span>&nbsp;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">27</span>        <span style="color:#6a9955">// バリデーションエラーがある場合、詳細なエラーメッセージを追加</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">28</span>        <span style="color:#569cd6">if</span> (<span style="color:#9cdcfe">result</span>.<span style="color:#dcdcaa">hasErrors</span>()) {<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">29</span>            <span style="color:#569cd6">for</span> (<span style="color:#4ec9b0">FieldError</span> <span style="color:#9cdcfe">fieldError</span> : <span style="color:#9cdcfe">result</span>.<span style="color:#dcdcaa">getFieldErrors</span>()) {<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">30</span>                <span style="color:#6a9955">// フィールド名を基に重複チェック</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">31</span>                <span style="color:#569cd6">if</span> (!<span style="color:#9cdcfe">processedFields</span>.<span style="color:#dcdcaa">contains</span>(<span style="color:#9cdcfe">fieldError</span>.<span style="color:#dcdcaa">getField</span>())) {<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">32</span>                    <span style="color:#569cd6">String</span> <span style="color:#9cdcfe">errorMessage</span> = <span style="color:#9cdcfe">messageSource</span>.<span style="color:#dcdcaa">getMessage</span>(<span style="color:#9cdcfe">fieldError</span>, <span style="color:#4ec9b0">Locale</span>.<span style="color:#dcdcaa">getDefault</span>());<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">33</span>                    <span style="color:#9cdcfe">errorMessageList</span>.<span style="color:#dcdcaa">add</span>(<span style="color:#9cdcfe">errorMessage</span>);<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">34</span>                    <span style="color:#9cdcfe">processedFields</span>.<span style="color:#dcdcaa">add</span>(<span style="color:#9cdcfe">fieldError</span>.<span style="color:#dcdcaa">getField</span>());<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">35</span>                }<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">36</span>            }<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">37</span>            <span style="color:#9cdcfe">responseForm</span>.<span style="color:#dcdcaa">setErrorMessageList</span>(<span style="color:#9cdcfe">errorMessageList</span>);<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">38</span>            <span style="color:#569cd6">return</span> <span style="color:#9cdcfe">responseForm</span>;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">39</span>        }<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">40</span>&nbsp;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">41</span>        <span style="color:#6a9955">// ユーザーIDを整数に変換</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">42</span>        <span style="color:#4ec9b0">Integer</span> <span style="color:#9cdcfe">userId</span> = <span style="color:#4ec9b0">Integer</span>.<span style="color:#dcdcaa">parseInt</span>(<span style="color:#9cdcfe">form</span>.<span style="color:#dcdcaa">getUserId</span>());<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">43</span>&nbsp;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">44</span>        <span style="color:#6a9955">// サービス層で曜日ごとのログイン履歴を取得</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">45</span>        <span style="color:#4ec9b0">Map</span>&lt;<span style="color:#569cd6">String</span>, <span style="color:#4ec9b0">Boolean</span>&gt; <span style="color:#9cdcfe">dailyLoginHistoryMap</span> = <span style="color:#9cdcfe">loginHistoryService</span>.<span style="color:#dcdcaa">getLoginHistoryByUserIdAndDateRange</span>(<span style="color:#9cdcfe">userId</span>);<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">46</span>&nbsp;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">47</span>        <span style="color:#6a9955">// ログイン履歴が空かどうかをチェック</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">48</span>        <span style="color:#569cd6">if</span> (<span style="color:#9cdcfe">dailyLoginHistoryMap</span>.<span style="color:#dcdcaa">isEmpty</span>()) {<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">49</span>            <span style="color:#9cdcfe">errorMessageList</span>.<span style="color:#dcdcaa">add</span>(<span style="color:#9cdcfe">messageSource</span>.<span style="color:#dcdcaa">getMessage</span>(<span style="color:#4ec9b0">ErrorMessages</span>.<span style="color:#4fc1ff">NO_LOGIN_HISTORY</span>, <span style="color:#569cd6">null</span>, <span style="color:#4ec9b0">Locale</span>.<span style="color:#dcdcaa">getDefault</span>()));<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">50</span>            <span style="color:#9cdcfe">responseForm</span>.<span style="color:#dcdcaa">setErrorMessageList</span>(<span style="color:#9cdcfe">errorMessageList</span>);<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">51</span>            <span style="color:#569cd6">return</span> <span style="color:#9cdcfe">responseForm</span>;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">52</span>        }<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">53</span>&nbsp;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">54</span>        <span style="color:#9cdcfe">responseForm</span>.<span style="color:#dcdcaa">setDailyLoginHistoryMap</span>(<span style="color:#9cdcfe">dailyLoginHistoryMap</span>);<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">55</span>        <span style="color:#569cd6">return</span> <span style="color:#9cdcfe">responseForm</span>;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">56</span>    }<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">57</span>}</pre></div>



<h3 class="wp-block-heading">日付および曜日に関するユーティリティクラス</h3>



<p>ログイン履歴の処理に役立つユーティリティメソッド（例：曜日の初期化や日付の範囲計算など）を別途 DateUtils クラスとして用意します。</p>



<div style="margin:1.5em 0;border-radius:6px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.15)"><div style="background:#252526;color:#cccccc;padding:6px 12px;font-family:Consolas,'Courier New',monospace;font-size:12px;border-bottom:1px solid #1e1e1e;border-top-left-radius:6px;border-top-right-radius:6px">DateUtils.java</div><pre style="background:#1e1e1e;color:#d4d4d4;font-family:Consolas,'Courier New',monospace;font-size:13px;line-height:1.55;padding:14px 16px;margin:0;overflow-x:auto;white-space:pre;border-bottom-left-radius:6px;border-bottom-right-radius:6px"><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 1</span><span style="color:#569cd6">public</span> <span style="color:#569cd6">class</span> <span style="color:#4ec9b0">DateUtils</span> {<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 2</span>&nbsp;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 3</span>    <span style="color:#6a9955">/** 曜日をまとめた配列 */</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 4</span>    <span style="color:#569cd6">protected</span> <span style="color:#569cd6">static</span> <span style="color:#569cd6">final</span> <span style="color:#569cd6">String</span>[] <span style="color:#4fc1ff">DAYS_OF_WEEK</span> = {<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 5</span>            <span style="color:#ce9178">&quot;monday&quot;</span>, <span style="color:#ce9178">&quot;tuesday&quot;</span>, <span style="color:#ce9178">&quot;wednesday&quot;</span>, <span style="color:#ce9178">&quot;thursday&quot;</span>, <span style="color:#ce9178">&quot;friday&quot;</span>, <span style="color:#ce9178">&quot;saturday&quot;</span>, <span style="color:#ce9178">&quot;sunday&quot;</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 6</span>    };<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 7</span>&nbsp;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 8</span>    <span style="color:#6a9955">/**</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none"> 9</span>     <span style="color:#6a9955">* プライベートコンストラクタでインスタンス化を禁止する</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">10</span>     <span style="color:#6a9955">*/</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">11</span>    <span style="color:#569cd6">private</span> <span style="color:#4ec9b0">DateUtils</span>() {<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">12</span>        <span style="color:#569cd6">throw</span> <span style="color:#569cd6">new</span> <span style="color:#4ec9b0">UnsupportedOperationException</span>(<span style="color:#ce9178">&quot;このクラスはインスタンス化できません&quot;</span>);<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">13</span>    }<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">14</span>&nbsp;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">15</span>    <span style="color:#6a9955">/**</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">16</span>     <span style="color:#6a9955">* 指定された日付の週の開始日（月曜日の00:00:00）を取得.</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">17</span>     <span style="color:#6a9955">*</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">18</span>     <span style="color:#6a9955">* @param date 基準日</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">19</span>     <span style="color:#6a9955">* @return 週の開始日</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">20</span>     <span style="color:#6a9955">*/</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">21</span>    <span style="color:#569cd6">public</span> <span style="color:#569cd6">static</span> <span style="color:#4ec9b0">LocalDateTime</span> <span style="color:#dcdcaa">getStartOfWeek</span>(<span style="color:#4ec9b0">LocalDate</span> <span style="color:#9cdcfe">date</span>) {<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">22</span>        <span style="color:#569cd6">return</span> <span style="color:#9cdcfe">date</span>.<span style="color:#dcdcaa">with</span>(<span style="color:#4ec9b0">TemporalAdjusters</span>.<span style="color:#dcdcaa">previousOrSame</span>(<span style="color:#4ec9b0">DayOfWeek</span>.<span style="color:#4fc1ff">MONDAY</span>)).<span style="color:#dcdcaa">atStartOfDay</span>();<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">23</span>    }<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">24</span>&nbsp;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">25</span>    <span style="color:#6a9955">/**</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">26</span>     <span style="color:#6a9955">* 現在週の開始日（月曜日の00:00:00）を取得.</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">27</span>     <span style="color:#6a9955">*</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">28</span>     <span style="color:#6a9955">* @return 現在週の開始日</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">29</span>     <span style="color:#6a9955">*/</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">30</span>    <span style="color:#569cd6">public</span> <span style="color:#569cd6">static</span> <span style="color:#4ec9b0">LocalDateTime</span> <span style="color:#dcdcaa">getStartOfWeek</span>() {<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">31</span>        <span style="color:#569cd6">return</span> <span style="color:#dcdcaa">getStartOfWeek</span>(<span style="color:#4ec9b0">LocalDate</span>.<span style="color:#dcdcaa">now</span>());<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">32</span>    }<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">33</span>&nbsp;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">34</span>    <span style="color:#6a9955">/**</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">35</span>     <span style="color:#6a9955">* 指定された日付の週の終了日（日曜日の23:59:59）を取得.</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">36</span>     <span style="color:#6a9955">*</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">37</span>     <span style="color:#6a9955">* @param date 基準日</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">38</span>     <span style="color:#6a9955">* @return 週の終了日</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">39</span>     <span style="color:#6a9955">*/</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">40</span>    <span style="color:#569cd6">public</span> <span style="color:#569cd6">static</span> <span style="color:#4ec9b0">LocalDateTime</span> <span style="color:#dcdcaa">getEndOfWeek</span>(<span style="color:#4ec9b0">LocalDate</span> <span style="color:#9cdcfe">date</span>) {<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">41</span>        <span style="color:#569cd6">return</span> <span style="color:#9cdcfe">date</span>.<span style="color:#dcdcaa">with</span>(<span style="color:#4ec9b0">TemporalAdjusters</span>.<span style="color:#dcdcaa">nextOrSame</span>(<span style="color:#4ec9b0">DayOfWeek</span>.<span style="color:#4fc1ff">SUNDAY</span>)).<span style="color:#dcdcaa">atTime</span>(<span style="color:#4ec9b0">LocalTime</span>.<span style="color:#4fc1ff">MAX</span>);<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">42</span>    }<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">43</span>&nbsp;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">44</span>    <span style="color:#6a9955">/**</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">45</span>     <span style="color:#6a9955">* 現在週の終了日（日曜日の23:59:59）を取得.</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">46</span>     <span style="color:#6a9955">*</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">47</span>     <span style="color:#6a9955">* @return 現在週の終了日</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">48</span>     <span style="color:#6a9955">*/</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">49</span>    <span style="color:#569cd6">public</span> <span style="color:#569cd6">static</span> <span style="color:#4ec9b0">LocalDateTime</span> <span style="color:#dcdcaa">getEndOfWeek</span>() {<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">50</span>        <span style="color:#569cd6">return</span> <span style="color:#dcdcaa">getEndOfWeek</span>(<span style="color:#4ec9b0">LocalDate</span>.<span style="color:#dcdcaa">now</span>());<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">51</span>    }<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">52</span>&nbsp;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">53</span>    <span style="color:#6a9955">/**</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">54</span>     <span style="color:#6a9955">* 曜日ごとの初期マップを作成（初期値はすべてfalse）.</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">55</span>     <span style="color:#6a9955">*</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">56</span>     <span style="color:#6a9955">* @return 曜日をキーにしたマップ（値はすべてfalse）</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">57</span>     <span style="color:#6a9955">*/</span><br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">58</span>    <span style="color:#569cd6">public</span> <span style="color:#569cd6">static</span> <span style="color:#4ec9b0">Map</span>&lt;<span style="color:#569cd6">String</span>, <span style="color:#4ec9b0">Boolean</span>&gt; <span style="color:#dcdcaa">initializeWeekMap</span>() {<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">59</span>        <span style="color:#4ec9b0">Map</span>&lt;<span style="color:#569cd6">String</span>, <span style="color:#4ec9b0">Boolean</span>&gt; <span style="color:#9cdcfe">weekMap</span> = <span style="color:#569cd6">new</span> <span style="color:#4ec9b0">LinkedHashMap</span>&lt;&gt;();<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">60</span>        <span style="color:#569cd6">for</span> (<span style="color:#569cd6">String</span> <span style="color:#9cdcfe">day</span> : <span style="color:#4fc1ff">DAYS_OF_WEEK</span>) {<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">61</span>            <span style="color:#9cdcfe">weekMap</span>.<span style="color:#dcdcaa">put</span>(<span style="color:#9cdcfe">day</span>, <span style="color:#569cd6">false</span>);<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">62</span>        }<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">63</span>        <span style="color:#569cd6">return</span> <span style="color:#9cdcfe">weekMap</span>;<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">64</span>    }<br><span style="display:inline-block;width:2.5em;color:#858585;text-align:right;padding-right:1em;user-select:none">65</span>}</pre></div>



<h2 class="wp-block-heading">実装内容の振り返り</h2>



<p>今回の実装で、ログイン履歴を週ごとに取得する処理において、複数回使用される処理（例えば、曜日の初期化や日付の範囲計算）をユーティリティクラスにまとめることができました。この部分は非常に勉強になりました。</p>



<p><strong><mark style="background-color:#fdff84" class="has-inline-color">ユーティリティクラスを作成することで、以下のようなメリットを実感できました。</mark></strong></p>



<ul class="wp-block-list">
<li>再利用性の向上：一度作成した共通部品を他の部分でも利用できるようになり、コードの重複を減らすことができました。これにより、将来的にメンテナンスがしやすくなります。</li>



<li>可読性の向上：ユーティリティメソッドを使うことで、メインのビジネスロジックがシンプルになり、読みやすくなりました。例えば、曜日の初期化処理や日付範囲の計算などがユーティリティクラスに隠蔽されているため、コントローラークラスがすっきりとしたものになりました。</li>



<li>テストのしやすさ：共通の処理をユーティリティクラスにまとめることで、その部分を個別にテストすることができ、ロジックの正確性を担保しやすくなりました。</li>
</ul>



<p>これらを踏まえて、ユーティリティクラスはシステム全体のコードをより効率的に、そして保守しやすくするための強力なツールだと改めて実感しました。</p>



<h2 class="wp-block-heading">まとめ</h2>



<p><strong><mark style="background-color:#fdff84" class="has-inline-color">効率的なコードを書くための方法を一つ引き出しを増やせたことが良かったです。</mark></strong>特に、複数のクラスで使い回す共通の処理や汎用的で一貫性が求められる処理をユーティリティクラスにまとめることが、コードの効率化や保守性向上に大いに役立つと感じました。</p>



<p>次回も、各グラフの作成方法について記載しますのでお楽しみに！</p>



<div class="author__card">
  <div class="author-data">
    <img decoding="async" src="https://www.sms-datatech.co.jp/wp-content/uploads/2024/11/author-3.png" alt="筆者イメージ">
    <div>
      <p>筆者：K・T（20代）</p>
      <p>所属：ハイブリット・マルチクラウド部</p>
    </div>
  </div>
  <div class="author-intro">
    <p>マーケターとしてWEB広告運用やアパレルブランドの運営に携わってきたが、エンジニアとしての新たな挑戦を決意した転職組。<br>好奇心が旺盛で、新しいことに挑戦する姿勢が彼女の魅力。<br>マーケター×エンジニアの強みを生かし、UI/UXの提案から実装まで幅広く対応し、彼女自身がブランドを育てているのだろうと思わせてくれる、そんな20代の乙女。</p>
  </div>
</div>


<div class="block-block-22">
	<div class="Editor-wrap">
					<div class="Editor-wrap__titleMsg">まずはお気軽にご相談ください</div>
			<div class="Editor-wrap__title">お問い合わせフォーム</div>
			</div>
</div>


<script charset="utf-8" type="text/javascript" src="//js.hsforms.net/forms/embed/v2.js"></script>
<script>
  hbspt.forms.create({
    region: "na1",
    portalId: "23171742",
    formId: "b36c7c3b-83a0-43c4-8daa-8e055d6805e9"
  });
</script><p>The post <a href="https://www.sms-datatech.co.jp/column/try_java-3/">【はじめてのJava】ダッシュボードAPIを作成しよう！③ログイン状況可視化編</a> first appeared on <a href="https://www.sms-datatech.co.jp">SMS DataTech</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>ClaudeCoworkの教科書｜BtoBマーケターがメルマガ改善で押さえるべき4つのステップ</title>
		<link>https://www.sms-datatech.co.jp/column/aut_claude-cowork_mail-marketing/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=aut_claude-cowork_mail-marketing</link>
		
		<dc:creator><![CDATA[佐々木]]></dc:creator>
		<pubDate>Wed, 20 May 2026 01:00:00 +0000</pubDate>
				<category><![CDATA[コラム]]></category>
		<category><![CDATA[自動化ソリューション]]></category>
		<category><![CDATA[自動化]]></category>
		<category><![CDATA[AI]]></category>
		<guid isPermaLink="false">https://www.sms-datatech.co.jp/?p=36174</guid>

					<description><![CDATA[<p>BtoB マーケティングにおいて、メルマガは見込み顧客との接点を継続するための重要な施策です。新しい記事や資料を案内したり、セミナー情報を届けたり、サービスへの理解を深めてもらったりと、メルマガはさまざまな目的で活用できます。一方で、実際の現場では次のような悩みが起こりがちです。 このような課題を解決する手段として注目したいのが、ClaudeCowork です。ClaudeCowork は、ChatGPT のような生成 AI の AI チャットのように回答を返すだけでなく、指定したファイルやフォルダをもとに、複数ステップの作業を進められるエージェント型 AI です。本記事では、ClaudeCowork を活用して、メルマガ作成・配信結果の分析・改善案作成・リスト最適化までを一貫して効率化する方法を解説します。 この記事がおすすめな方 この記事は、以下のような方におすすめです。 なぜメルマガ業務に ClaudeCowork が向いているのか 通常の AI チャットでも、メルマガの件名案や本文案を作ることはできます。しかし、実際のメルマガ業務で必要なのは、文章を 1 本作ることだけではありません。メルマガ運用では、以下のような情報を見ながら判断する必要があります。 通常の AI チャットでは、これらの情報を毎回貼り付けたり、ファイルをアップロードしたり、出力結果を別ファイルに保存したりする手間が残ります。一方、ClaudeCowork では、指定したフォルダ内の資料を読み込み、整理し、分析し、成果物を出力するまで一気通貫で進められます。メルマガ業務は、毎月・毎週のように繰り返し発生します。単発で文章を作るだけでなく、過去の結果を見て、次回の配信に活かす仕組みを作ることが重要です。そのため、メルマガ運用を一気通貫で自律して行える ClaudeCowork がメルマガ業務には向いています。 メルマガ業務でよくある課題 メルマガは、運用しているだけでは成果が伸びにくい施策です。本来は、配信ごとに結果を確認し、読者の反応を見ながら改善していく必要があります。しかし、多くの現場では、日々の業務に追われて改善まで手が回らないケースがあります。 メルマガ運用で起こりやすい課題 課題 よくある状態 影響 企画が属人化している 担当者の経験や感覚でテーマを決めている 反応のよいテーマを再現しにくい 件名作成に時間がかかる 毎回ゼロから考えている 配信準備の負担が大きい 本文作成が後回しになる 他業務を優先し、直前に作成している 内容の質が安定しにくい 配信結果を見られていない 開封率やクリック率を見るだけで終わる 次回改善につながらない リストを見直せていない 全員に同じ内容を送っている 読者ごとの関心に合わせにくい ノウハウが蓄積されない 過去の成功・失敗が残っていない 担当者が変わると品質が落ちる 特に重要なのは、配信後の分析とリスト最適化です。メルマガは、作成して配信するだけでは終わりません。どの件名が開封されやすかったのか、どのテーマがクリックされやすかったのか、どの読者層が反応したのかを整理することで、次回以降の配信精度を高めることができます。 ClaudeCowork で効率化できるメルマガ業務 ClaudeCowork を活用すると、メルマガ業務のさまざまな工程を効率化できます。メルマガ本文の作成だけに使うのではなく、企画・分析・改善・リスト最適化まで含めて自動化することが可能です。 ClaudeCowork で効率化できる業務一覧 業務 ClaudeCowork に任せやすいこと 人間が確認すべきこと 企画作成 過去配信からテーマ候補を整理する 今回の目的や優先度 件名作成 訴求軸別に複数の件名案を作る 誇張表現やブランドとのズレ 本文作成 構成案や下書きを作成する 事実確認、言い回し、最終表現 CTA 作成 読者に次の行動を促す文言を作る 実際の案内内容や条件との整合性 配信後分析 開封率、クリック率、反応傾向を整理する 数字の背景にある要因の判断 リスト最適化 反応別・関心別に読者を整理する 配信対象として妥当かどうか レポート作成 分析結果をレポート化する 経営・営業視点での解釈 次回改善案 次回配信で試すべき案を出す 実行可否と優先順位 ClaudeCowork の価値は、作業時間の短縮だけではありません。これまで時間が足りずに後回しになっていた内容分析やリスト最適化まで、一連の運用フローに組み込めることが大きなポイントです。 実践！ClaudeCowork でメルマガ改善を進める 4 つのステップ ここからは、ClaudeCowork を使ってメルマガ改善を進める方法を、4 つのステップに分けて紹介します。 ステップ 1：メルマガ改善の目的を決める まず、ClaudeCowork に作業を依頼する前に、メルマガ改善の目的を決めます。ここで大切なのは、メルマガの目的を 1 つに固定しないことです。メルマガは、企業の状況や施策によってさまざまな目的で活用できます。例えば、ノウハウや新しい記事の情報提供による自社サイトへの流入やセミナーの申し込みなどがあります。目的が曖昧なまま ClaudeCowork に依頼すると、きれいな文章はできても、成果につながりにくくなります。そのため、まずは「今回のメルマガで何を改善したいのか」を明確にします。 たとえば、次のように整理します。 ClaudeCowork を使う前の設計が明確であるほど、出力される分析や改善案の精度も上がりやすくなります。 ステップ 2：必要なファイルを整理する 次に、ClaudeCowork に読み込ませる資料を整理します。メルマガ改善では、過去の配信原稿や配信結果だけでなく、商品情報、読者リスト、ブランド表現ルールなども重要です。 用意するファイル例 mailmagazine-improvement/ ├── input/ │ ├── 過去メルマガ原稿.md │ ├── 配信結果.csv │ ├── 顧客リスト.csv │ ├── 商品サービス概要.md │ ├── セミナー情報.md │ ├── 資料一覧.md │ ├── ブランド表現ルール.md │ └── 分析観点.md └── output/ このように、input フォルダと output フォルダを分けると、ClaudeCowork に作業を依頼しやすくなります。input フォルダには、分析や作成に必要な元資料を入れます。output フォルダには、ClaudeCowork が作成する分析レポート、件名案、本文案、リスト改善案などを保存します。 分析観点.md に入れる内容例 # 分析観点 - 開封率が高かった件名の傾向 - 開封率が低かった件名の傾向 - クリック率が高かった本文構成 - クリック率が高かった CTA の傾向 - 反応がよかったテーマ - 反応が低かったテーマ - 読者属性ごとの反応差 - 長期間反応がない読者の傾向 - 次回メルマガで改善すべき点 - 配信リストの見直し候補 ここで重要なのは、ClaudeCowork に「何を見てほしいのか」を先に決めることです。AI に自由に考えさせるよりも、人間が分析観点を定義したほうが、実務で使いやすい成果物になりやすくなります。 ステップ 3：ClaudeCowork に作業を依頼する 資料を整理したら、ClaudeCowork に作業を依頼します。このとき、「メルマガを作ってください」とだけ伝えるのではなく、目的、入力資料、成果物、注意点を明確に指定します。 プロンプト例 input/フォルダ内の資料をすべて読み込み、 過去のメルマガ配信結果を分析してください。 目的は、メルマガ業務を効率化し、 次回以降の配信内容を改善することです。 以下の成果物を output/フォルダに作成してください。 1. 過去メルマガの反応分析レポート 2. 開封率が高かった件名の傾向 3. クリック率が高かった本文・CTA の傾向 4. 反応が高かったテーマの整理 5. 反応が低かったテーマの整理 6. 読者リストの改善案 7. 次回メルマガの企画案 8. 件名案 10 本 9. 本文下書き 3 パターン 10. 次回配信後に見るべき指標 情報が確認できない場合は、推測せず「不明」と書いてください。 作業に入る前に、まず実行プランを提示してください。 プロンプトで押さえるべきポイント ClaudeCowork は、複数ステップの作業を進められる点が特徴です。そのため、単発の文章作成ではなく、「分析して、改善案を出し、次回の下書きを作る」という流れで依頼すると、実務に近い成果物を得やすくなります。 ステップ 4：成果物を確認し、次回配信に活かす ClaudeCowork が出力した成果物は、そのまま使うのではなく、人間が確認して調整します。特に、メルマガは読者に直接届くため、事実確認や表現の調整が欠かせません。 成果物例 1：メルマガ反応分析表 配信テーマ 件名の傾向 開封率 クリック率 CTA 次回への示唆 業務効率化 課題訴求型 高い 中 資料案内 件名の方向性は継続 AI 活用 トレンド訴求型 中 高い 記事案内 本文内の具体例を増やす 導入事例 実績訴求型 高い 高い 事例紹介 次回も有効 セミナー案内 日程訴求型 中 低い 申込案内 訴求軸を見直す ノウハウ提供 解説型 高い 中 資料案内 読者の関心が高い可能性 このような表にすると、どの配信がよかったのかをチームで共有しやすくなります。また、AI 検索や生成 AI に引用される際にも、表形式の情報は要点を抽出しやすくなります。 成果物例 2：リスト最適化案 読者グループ 反応傾向 次回配信方針 AI 活用記事をクリックした読者 業務効率化や AI 活用ノウハウに関心が高い AI 活用記事や実践事例を配信 セミナー案内をクリックした読者 学習意欲や情報収集意欲が高い 関連セミナーや資料を案内 資料案内に反応した読者 具体的な導入検討に進む可能性がある 導入手順や比較資料を案内 開封はするがクリックしない読者 興味はあるが行動に至っていない 本文構成や CTA を改善 長期間反応がない読者 関心が低下している可能性がある 再関心化メールや配信頻度の見直しを検討 リスト最適化で大切なのは、全員に同じ内容を送り続けないことです。読者ごとの関心や反応に合わせて配信内容を変えることで、メルマガの成果を改善しやすくなります。 成果物例 3：次回メルマガ企画案 項目 内容 配信テーマ AI を使ったマーケティング業務効率化 想定読者 AI 活用に興味がある BtoB マーケティング担当者 読者の課題 施策は多いが人手が足りず、分析や改善まで手が回らない 訴求軸 メルマガ作成だけでなく、分析・改善まで効率化できる 本文構成 課題提示 → AI 活用方法 → 成果物例 → 注意点 次回確認指標 開封率、クリック率、クリック先、反応した読者属性 このように、ClaudeCowork を使うと、メルマガを「作るだけ」の業務から、「分析して改善する」業務へ変えやすくなります。 実際に使って分かったポイント ClaudeCowork を活用して特に効果を感じやすいのは、メルマガ本文の作成よりも、むしろ配信後の分析や改善案の整理です。これまでのメルマガ運用では、配信後に開封率やクリック率を見るだけで終わってしまうことが多くありました。しかしClaudeCowork を活用することで、以下のような作業まで一貫して進めやすくなります。 メルマガは、ただ配信数を増やせば成果が出るものではありません。読者の反応を見ながら、内容、件名、CTA、配信対象を少しずつ改善していくことで成果につながります。ClaudeCowork を使うことで、その改善サイクルを日々の運用に組み込みやすくなります。 ClaudeCowork に任せきりにしないための注意点 ClaudeCowork は、メルマガ業務の効率化に役立ちます。ただし、すべてを任せきりにするのは危険です。特にメルマガは顧客や見込み顧客に直接届くため、誤った情報や不自然な表現があると、企業の信頼に影響します。サービス情報やセミナー日程などは必ずチェックを行いましょう。ClaudeCowork は、調査、整理、初稿作成、分析補助の初速を上げる存在です。最終的な判断や戦略決定は、人間が行う前提で使うことが重要です。また、Anthropic 公式ページでは、ClaudeCowork は指定したファイルを扱える一方で、機密情報を含むローカルファイルへのアクセスには注意が必要であることも示されています。 ClaudeCowork 活用はメルマガ以外にも広げられる メルマガ改善で ClaudeCowork の活用に慣れてくると、ほかのマーケティング業務にも展開できます。たとえば、以下のような業務です。 ClaudeCowork は、文章作成だけのツールではありません。業務に必要な資料を読み込み、整理し、分析し、成果物にまとめることで、マーケティング担当者の実務を支援できます。 ⇒ClaudeCoworkの教科書｜BtoBマーケターが競合調査で押さえるべき4つのステップ まとめ：ClaudeCowork はメルマガを「作る業務」から「改善する業務」に変える 本記事では、ClaudeCowork を活用してメルマガ業務を効率化する方法を解説しました。メルマガは送って終わりではなく、読者の反応を分析し、内容や配信リストを改善し続けることで成果につながります。ClaudeCowork を活用すれば、メルマガ作成だけでなく、過去配信の分析、改善案の作成、リスト最適化まで一貫して進めやすくなります。そのため、メルマガ業務を「作る業務」から、「継続的に改善する業務」へ進化させやすくなります。ただし、継続的に活用するには、任せる業務範囲や確認ルールを整理し、再現性のある運用フローとして社内に組み込むことが重要です。SMS データテックには、ClaudeCowork を活用した業務全体の自動化と運用定着を支援する「Claude Cowork 内製化支援」サービスがあります。貴社の業務・体制に合わせた活用可能性と課題を無料相談で整理できます。お気軽にお問い合わせください。</p>
<p>The post <a href="https://www.sms-datatech.co.jp/column/aut_claude-cowork_mail-marketing/">ClaudeCoworkの教科書｜BtoBマーケターがメルマガ改善で押さえるべき4つのステップ</a> first appeared on <a href="https://www.sms-datatech.co.jp">SMS DataTech</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>BtoB マーケティングにおいて、メルマガは見込み顧客との接点を継続するための重要な施策です。新しい記事や資料を案内したり、セミナー情報を届けたり、サービスへの理解を深めてもらったりと、メルマガはさまざまな目的で活用できます。一方で、実際の現場では次のような悩みが起こりがちです。</p>



<ul class="wp-block-list">
<li>メルマガの企画を考える時間がない</li>



<li>件名や本文の作成に毎回時間がかかる</li>



<li>配信後の分析まで手が回らない</li>



<li>開封率やクリック率は見ているが、次回改善に活かせていない</li>



<li>読者リストの見直しや最適化が後回しになっている</li>



<li>マーケティング施策を増やしたいが、人手が足りない</li>
</ul>



<p>このような課題を解決する手段として注目したいのが、ClaudeCowork です。ClaudeCowork は、ChatGPT のような生成 AI の AI チャットのように回答を返すだけでなく、指定したファイルやフォルダをもとに、複数ステップの作業を進められるエージェント型 AI です。本記事では、ClaudeCowork を活用して、メルマガ作成・配信結果の分析・改善案作成・リスト最適化までを一貫して効率化する方法を解説します。</p>



<h4 class="wp-block-heading">この記事がおすすめな方</h4>



<p>この記事は、以下のような方におすすめです。</p>



<ul class="wp-block-list">
<li>メルマガを運用しているが、作成だけで手一杯になっている方</li>



<li>メルマガの開封率やクリック率を改善したい方</li>



<li>配信後の分析やリスト最適化まで手が回っていない方</li>



<li>ClaudeCowork をマーケティング業務に活用したい方</li>



<li>AI を単発の文章作成ではなく、業務プロセスに組み込みたい方</li>



<li>人手不足の中で、より多くのマーケティング施策に取り組みたい方</li>
</ul>



<h2 class="wp-block-heading">なぜメルマガ業務に ClaudeCowork が向いているのか</h2>



<p>通常の AI チャットでも、メルマガの件名案や本文案を作ることはできます。しかし、実際のメルマガ業務で必要なのは、文章を 1 本作ることだけではありません。メルマガ運用では、以下のような情報を見ながら判断する必要があります。</p>



<ul class="wp-block-list">
<li>過去に配信したメルマガ原稿</li>



<li>開封率やクリック率などの配信結果</li>



<li>読者リストや顧客属性</li>



<li>商品・サービス情報</li>



<li>セミナーや資料などの案内内容</li>



<li>ブランド表現ルール</li>



<li>営業現場でよく聞かれる質問</li>



<li>過去に反応がよかったテーマ</li>
</ul>



<p>通常の AI チャットでは、これらの情報を毎回貼り付けたり、ファイルをアップロードしたり、出力結果を別ファイルに保存したりする手間が残ります。<strong><mark style="background-color:#fdff84" class="has-inline-color">一方、ClaudeCowork では、指定したフォルダ内の資料を読み込み、整理し、分析し、成果物を出力するまで一気通貫で進められます。</mark></strong>メルマガ業務は、毎月・毎週のように繰り返し発生します。単発で文章を作るだけでなく、過去の結果を見て、次回の配信に活かす仕組みを作ることが重要です。そのため、メルマガ運用を一気通貫で自律して行える ClaudeCowork がメルマガ業務には向いています。</p>


<div class="hs-cta-embed hs-cta-simple-placeholder hs-cta-embed-350307093208" style="max-width: 100%; max-height: 100%; width: 750px; height: 268.24493408203125px;" data-hubspot-wrapper-cta-id="350307093208"><a href="https://cta-na2.hubspot.com/web-interactives/public/v1/track/redirect?encryptedPayload=AVxigLJSjAz1SL4fqqrv18TOvkLN6PfH9BYrH3lbqwTD7sfdD0IwSYL8d5iT0aX05MUlnzi3es4Q2wQSkgFZ%2BP7wFAu22SswPktfsnlYXytze1p1n9I736tZxD%2FIs0nxkA3GeytmfkntFrSs54aNFmMVQAYaomlhwLobNurjyzK3GtVDa76n40L2B8N4Yrff%2BDZtyYD0s2uaOT%2Brq7KzGywhAjEM4iNm&amp;webInteractiveContentId=350307093208&amp;portalId=23171742" target="_blank" rel="noopener"> <img decoding="async" style="height: 100%; width: 100%; object-fit: fill;" src="https://hubspot-no-cache-na2-prod.s3.amazonaws.com/cta/default/23171742/interactive-350307093208.png" alt="SDT_CTA_Claude Cowork" /> </a></div>


<h2 class="wp-block-heading">メルマガ業務でよくある課題</h2>



<p>メルマガは、運用しているだけでは成果が伸びにくい施策です。本来は、配信ごとに結果を確認し、読者の反応を見ながら改善していく必要があります。しかし、多くの現場では、日々の業務に追われて改善まで手が回らないケースがあります。</p>



<p>メルマガ運用で起こりやすい課題</p>



<table style="border-collapse: collapse; border: 1px solid #ccc; width: 100%;">
<thead>
<tr><th style="width: 25%; border: 1px solid #ccc; padding: 8px; text-align: left;">課題</th><th style="width: 40%; border: 1px solid #ccc; padding: 8px; text-align: left;">よくある状態</th><th style="width: 35%; border: 1px solid #ccc; padding: 8px; text-align: left;">影響</th></tr>
</thead>
<tbody>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">企画が属人化している</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">担当者の経験や感覚でテーマを決めている</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">反応のよいテーマを再現しにくい</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">件名作成に時間がかかる</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">毎回ゼロから考えている</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">配信準備の負担が大きい</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">本文作成が後回しになる</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">他業務を優先し、直前に作成している</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">内容の質が安定しにくい</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">配信結果を見られていない</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">開封率やクリック率を見るだけで終わる</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">次回改善につながらない</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">リストを見直せていない</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">全員に同じ内容を送っている</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">読者ごとの関心に合わせにくい</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">ノウハウが蓄積されない</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">過去の成功・失敗が残っていない</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">担当者が変わると品質が落ちる</td></tr>
</tbody>
</table>



<p>特に重要なのは、配信後の分析とリスト最適化です。メルマガは、作成して配信するだけでは終わりません。<strong><mark style="background-color:#fdff84" class="has-inline-color">どの件名が開封されやすかったのか、どのテーマがクリックされやすかったのか、どの読者層が反応したのかを整理することで、次回以降の配信精度を高めることができます。</mark></strong></p>



<h2 class="wp-block-heading">ClaudeCowork で効率化できるメルマガ業務</h2>



<p>ClaudeCowork を活用すると、メルマガ業務のさまざまな工程を効率化できます。<strong><mark style="background-color:#fdff84" class="has-inline-color">メルマガ本文の作成だけに使うのではなく、企画・分析・改善・リスト最適化まで含めて自動化することが可能です。</mark></strong></p>



<p>ClaudeCowork で効率化できる業務一覧</p>



<table style="border-collapse: collapse; border: 1px solid #ccc; width: 100%;">
<thead>
<tr><th style="width: 20%; border: 1px solid #ccc; padding: 8px; text-align: left;">業務</th><th style="width: 40%; border: 1px solid #ccc; padding: 8px; text-align: left;">ClaudeCowork に任せやすいこと</th><th style="width: 40%; border: 1px solid #ccc; padding: 8px; text-align: left;">人間が確認すべきこと</th></tr>
</thead>
<tbody>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">企画作成</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">過去配信からテーマ候補を整理する</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">今回の目的や優先度</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">件名作成</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">訴求軸別に複数の件名案を作る</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">誇張表現やブランドとのズレ</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">本文作成</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">構成案や下書きを作成する</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">事実確認、言い回し、最終表現</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">CTA 作成</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">読者に次の行動を促す文言を作る</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">実際の案内内容や条件との整合性</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">配信後分析</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">開封率、クリック率、反応傾向を整理する</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">数字の背景にある要因の判断</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">リスト最適化</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">反応別・関心別に読者を整理する</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">配信対象として妥当かどうか</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">レポート作成</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">分析結果をレポート化する</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">経営・営業視点での解釈</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">次回改善案</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">次回配信で試すべき案を出す</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">実行可否と優先順位</td></tr>
</tbody>
</table>



<p>ClaudeCowork の価値は、作業時間の短縮だけではありません。これまで時間が足りずに後回しになっていた内容分析やリスト最適化まで、一連の運用フローに組み込めることが大きなポイントです。</p>



<h2 class="wp-block-heading">実践！ClaudeCowork でメルマガ改善を進める 4 つのステップ</h2>



<p>ここからは、ClaudeCowork を使ってメルマガ改善を進める方法を、4 つのステップに分けて紹介します。</p>



<h3 class="wp-block-heading">ステップ 1：メルマガ改善の目的を決める</h3>



<p>まず、ClaudeCowork に作業を依頼する前に、メルマガ改善の目的を決めます。ここで大切なのは、メルマガの目的を 1 つに固定しないことです。メルマガは、企業の状況や施策によってさまざまな目的で活用できます。例えば、ノウハウや新しい記事の情報提供による自社サイトへの流入やセミナーの申し込みなどがあります。目的が曖昧なまま ClaudeCowork に依頼すると、きれいな文章はできても、成果につながりにくくなります。<strong><mark style="background-color:#fdff84" class="has-inline-color">そのため、まずは「今回のメルマガで何を改善したいのか」を明確にします。</mark></strong></p>



<p>たとえば、次のように整理します。</p>



<ul class="wp-block-list">
<li>開封率を上げたい</li>



<li>クリック率を改善したい</li>



<li>特定テーマへの関心を確認したい</li>



<li>セミナー参加につなげたい</li>



<li>資料ダウンロードを増やしたい</li>



<li>長期間反応がない読者を掘り起こしたい</li>



<li>読者リストを関心別に整理したい</li>
</ul>



<p>ClaudeCowork を使う前の設計が明確であるほど、出力される分析や改善案の精度も上がりやすくなります。</p>



<h3 class="wp-block-heading">ステップ 2：必要なファイルを整理する</h3>



<p>次に、ClaudeCowork に読み込ませる資料を整理します。メルマガ改善では、過去の配信原稿や配信結果だけでなく、商品情報、読者リスト、ブランド表現ルールなども重要です。</p>



<h4 class="wp-block-heading">用意するファイル例</h4>



<pre class="wp-block-preformatted">mailmagazine-improvement/
├── input/
│   ├── 過去メルマガ原稿.md
│   ├── 配信結果.csv
│   ├── 顧客リスト.csv
│   ├── 商品サービス概要.md
│   ├── セミナー情報.md
│   ├── 資料一覧.md
│   ├── ブランド表現ルール.md
│   └── 分析観点.md
└── output/</pre>



<p>このように、input フォルダと output フォルダを分けると、ClaudeCowork に作業を依頼しやすくなります。input フォルダには、分析や作成に必要な元資料を入れます。output フォルダには、ClaudeCowork が作成する分析レポート、件名案、本文案、リスト改善案などを保存します。</p>



<h4 class="wp-block-heading">分析観点.md に入れる内容例</h4>



<pre class="wp-block-preformatted"># 分析観点
- 開封率が高かった件名の傾向
- 開封率が低かった件名の傾向
- クリック率が高かった本文構成
- クリック率が高かった CTA の傾向
- 反応がよかったテーマ
- 反応が低かったテーマ
- 読者属性ごとの反応差
- 長期間反応がない読者の傾向
- 次回メルマガで改善すべき点
- 配信リストの見直し候補</pre>



<p><strong><mark style="background-color:#fdff84" class="has-inline-color">ここで重要なのは、ClaudeCowork に「何を見てほしいのか」を先に決めることです。</mark></strong>AI に自由に考えさせるよりも、人間が分析観点を定義したほうが、実務で使いやすい成果物になりやすくなります。</p>



<h3 class="wp-block-heading">ステップ 3：ClaudeCowork に作業を依頼する</h3>



<p>資料を整理したら、ClaudeCowork に作業を依頼します。このとき、「メルマガを作ってください」とだけ伝えるのではなく、目的、入力資料、成果物、注意点を明確に指定します。</p>



<h4 class="wp-block-heading">プロンプト例</h4>



<pre class="wp-block-preformatted">input/フォルダ内の資料をすべて読み込み、
過去のメルマガ配信結果を分析してください。

目的は、メルマガ業務を効率化し、
次回以降の配信内容を改善することです。

以下の成果物を output/フォルダに作成してください。

1. 過去メルマガの反応分析レポート
2. 開封率が高かった件名の傾向
3. クリック率が高かった本文・CTA の傾向
4. 反応が高かったテーマの整理
5. 反応が低かったテーマの整理
6. 読者リストの改善案
7. 次回メルマガの企画案
8. 件名案 10 本
9. 本文下書き 3 パターン
10. 次回配信後に見るべき指標

情報が確認できない場合は、推測せず「不明」と書いてください。
作業に入る前に、まず実行プランを提示してください。</pre>



<h4 class="wp-block-heading">プロンプトで押さえるべきポイント</h4>



<ul class="wp-block-list">
<li>成果物を番号付きで指定する</li>



<li>目的を明確にする</li>



<li>分析と作成を分ける</li>



<li>「不明な情報は不明」と書かせる</li>



<li>作業前に実行プランを出させる</li>



<li>output フォルダに成果物を保存させる</li>
</ul>



<p>ClaudeCowork は、複数ステップの作業を進められる点が特徴です。そのため、単発の文章作成ではなく、「分析して、改善案を出し、次回の下書きを作る」という流れで依頼すると、実務に近い成果物を得やすくなります。</p>



<h3 class="wp-block-heading">ステップ 4：成果物を確認し、次回配信に活かす</h3>



<p>ClaudeCowork が出力した成果物は、そのまま使うのではなく、人間が確認して調整します。特に、メルマガは読者に直接届くため、事実確認や表現の調整が欠かせません。</p>



<h4 class="wp-block-heading">成果物例 1：メルマガ反応分析表</h4>



<table style="border-collapse: collapse; border: 1px solid #ccc; width: 100%;">
<thead>
<tr><th style="width: 16%; border: 1px solid #ccc; padding: 8px; text-align: left;">配信テーマ</th><th style="width: 16%; border: 1px solid #ccc; padding: 8px; text-align: left;">件名の傾向</th><th style="width: 16%; border: 1px solid #ccc; padding: 8px; text-align: left;">開封率</th><th style="width: 16%; border: 1px solid #ccc; padding: 8px; text-align: left;">クリック率</th><th style="width: 16%; border: 1px solid #ccc; padding: 8px; text-align: left;">CTA</th><th style="width: 20%; border: 1px solid #ccc; padding: 8px; text-align: left;">次回への示唆</th></tr>
</thead>
<tbody>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">業務効率化</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">課題訴求型</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">高い</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">中</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">資料案内</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">件名の方向性は継続</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">AI 活用</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">トレンド訴求型</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">中</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">高い</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">記事案内</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">本文内の具体例を増やす</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">導入事例</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">実績訴求型</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">高い</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">高い</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">事例紹介</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">次回も有効</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">セミナー案内</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">日程訴求型</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">中</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">低い</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">申込案内</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">訴求軸を見直す</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">ノウハウ提供</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">解説型</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">高い</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">中</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">資料案内</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">読者の関心が高い可能性</td></tr>
</tbody>
</table>



<p>このような表にすると、どの配信がよかったのかをチームで共有しやすくなります。また、AI 検索や生成 AI に引用される際にも、表形式の情報は要点を抽出しやすくなります。</p>



<h4 class="wp-block-heading">成果物例 2：リスト最適化案</h4>



<table style="border-collapse: collapse; border: 1px solid #ccc; width: 100%;">
<thead>
<tr><th style="width: 25%; border: 1px solid #ccc; padding: 8px; text-align: left;">読者グループ</th><th style="width: 40%; border: 1px solid #ccc; padding: 8px; text-align: left;">反応傾向</th><th style="width: 35%; border: 1px solid #ccc; padding: 8px; text-align: left;">次回配信方針</th></tr>
</thead>
<tbody>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">AI 活用記事をクリックした読者</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">業務効率化や AI 活用ノウハウに関心が高い</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">AI 活用記事や実践事例を配信</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">セミナー案内をクリックした読者</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">学習意欲や情報収集意欲が高い</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">関連セミナーや資料を案内</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">資料案内に反応した読者</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">具体的な導入検討に進む可能性がある</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">導入手順や比較資料を案内</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">開封はするがクリックしない読者</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">興味はあるが行動に至っていない</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">本文構成や CTA を改善</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">長期間反応がない読者</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">関心が低下している可能性がある</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">再関心化メールや配信頻度の見直しを検討</td></tr>
</tbody>
</table>



<p>リスト最適化で大切なのは、全員に同じ内容を送り続けないことです。読者ごとの関心や反応に合わせて配信内容を変えることで、メルマガの成果を改善しやすくなります。</p>



<h4 class="wp-block-heading">成果物例 3：次回メルマガ企画案</h4>



<table style="border-collapse: collapse; border: 1px solid #ccc; width: 100%;">
<thead>
<tr><th style="width: 30%; border: 1px solid #ccc; padding: 8px; text-align: left;">項目</th><th style="width: 70%; border: 1px solid #ccc; padding: 8px; text-align: left;">内容</th></tr>
</thead>
<tbody>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">配信テーマ</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">AI を使ったマーケティング業務効率化</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">想定読者</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">AI 活用に興味がある BtoB マーケティング担当者</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">読者の課題</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">施策は多いが人手が足りず、分析や改善まで手が回らない</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">訴求軸</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">メルマガ作成だけでなく、分析・改善まで効率化できる</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">本文構成</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">課題提示 → AI 活用方法 → 成果物例 → 注意点</td></tr>
<tr><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">次回確認指標</td><td style="border: 1px solid #ccc; padding: 8px; vertical-align: top;">開封率、クリック率、クリック先、反応した読者属性</td></tr>
</tbody>
</table>



<p>このように、ClaudeCowork を使うと、メルマガを「作るだけ」の業務から、「分析して改善する」業務へ変えやすくなります。</p>


<div class="hs-cta-embed hs-cta-simple-placeholder hs-cta-embed-350307093208" style="max-width: 100%; max-height: 100%; width: 750px; height: 268.24493408203125px;" data-hubspot-wrapper-cta-id="350307093208"><a href="https://cta-na2.hubspot.com/web-interactives/public/v1/track/redirect?encryptedPayload=AVxigLJSjAz1SL4fqqrv18TOvkLN6PfH9BYrH3lbqwTD7sfdD0IwSYL8d5iT0aX05MUlnzi3es4Q2wQSkgFZ%2BP7wFAu22SswPktfsnlYXytze1p1n9I736tZxD%2FIs0nxkA3GeytmfkntFrSs54aNFmMVQAYaomlhwLobNurjyzK3GtVDa76n40L2B8N4Yrff%2BDZtyYD0s2uaOT%2Brq7KzGywhAjEM4iNm&amp;webInteractiveContentId=350307093208&amp;portalId=23171742" target="_blank" rel="noopener"> <img decoding="async" style="height: 100%; width: 100%; object-fit: fill;" src="https://hubspot-no-cache-na2-prod.s3.amazonaws.com/cta/default/23171742/interactive-350307093208.png" alt="SDT_CTA_Claude Cowork" /> </a></div>


<h2 class="wp-block-heading">実際に使って分かったポイント</h2>



<p><span class="sms-highlight" style="background-color: #fdff84; font-weight: 700; padding: 0 0.08em; box-decoration-break: clone; -webkit-box-decoration-break: clone;">ClaudeCowork を活用して特に効果を感じやすいのは、メルマガ本文の作成よりも、むしろ配信後の分析や改善案の整理です。</span>これまでのメルマガ運用では、配信後に開封率やクリック率を見るだけで終わってしまうことが多くありました。しかしClaudeCowork を活用することで、以下のような作業まで一貫して進めやすくなります。</p>



<ul class="wp-block-list">
<li>過去メルマガの内容を読み取る</li>



<li>配信結果を整理する</li>



<li>反応がよかったテーマを抽出する</li>



<li>件名や CTA の傾向を比較する</li>



<li>読者リストごとの反応差を見る</li>



<li>次回配信の改善案を作る</li>



<li>改善案をもとに本文下書きを作る</li>
</ul>



<p>メルマガは、ただ配信数を増やせば成果が出るものではありません。読者の反応を見ながら、内容、件名、CTA、配信対象を少しずつ改善していくことで成果につながります。ClaudeCowork を使うことで、その改善サイクルを日々の運用に組み込みやすくなります。</p>



<h2 class="wp-block-heading">ClaudeCowork に任せきりにしないための注意点</h2>



<p>ClaudeCowork は、メルマガ業務の効率化に役立ちます。ただし、すべてを任せきりにするのは危険です。<span class="sms-highlight" style="background-color: #fdff84; font-weight: 700; padding: 0 0.08em; box-decoration-break: clone; -webkit-box-decoration-break: clone;">特にメルマガは顧客や見込み顧客に直接届くため、誤った情報や不自然な表現があると、企業の信頼に影響します。</span>サービス情報やセミナー日程などは必ずチェックを行いましょう。ClaudeCowork は、調査、整理、初稿作成、分析補助の初速を上げる存在です。最終的な判断や戦略決定は、人間が行う前提で使うことが重要です。また、Anthropic 公式ページでは、ClaudeCowork は指定したファイルを扱える一方で、機密情報を含むローカルファイルへのアクセスには注意が必要であることも示されています。</p>



<h2 class="wp-block-heading">ClaudeCowork 活用はメルマガ以外にも広げられる</h2>



<p>メルマガ改善で ClaudeCowork の活用に慣れてくると、ほかのマーケティング業務にも展開できます。たとえば、以下のような業務です。</p>



<ul class="wp-block-list">
<li>競合調査</li>



<li>ホワイトペーパー作成</li>



<li>セミナー企画</li>



<li>営業資料作成</li>



<li>顧客リスト整理</li>



<li>商談メモの要約</li>



<li>月次レポート作成</li>



<li>SNS 投稿案の作成</li>



<li>FAQ や社内ナレッジの整理</li>
</ul>



<p>ClaudeCowork は、文章作成だけのツールではありません。業務に必要な資料を読み込み、整理し、分析し、成果物にまとめることで、マーケティング担当者の実務を支援できます。</p>



<p><a href="https://www.sms-datatech.co.jp/column/aut_claude-cowork-b2b-competitor-research/" title=""><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-blue-2-color"><span style="text-decoration: underline;">⇒ClaudeCoworkの教科書｜BtoBマーケターが競合調査で押さえるべき4つのステップ</span></mark></strong></a></p>



<h2 class="wp-block-heading">まとめ：ClaudeCowork はメルマガを「作る業務」から「改善する業務」に変える</h2>



<p>本記事では、ClaudeCowork を活用してメルマガ業務を効率化する方法を解説しました。<span class="sms-highlight" style="background-color: #fdff84; font-weight: 700; padding: 0 0.08em; box-decoration-break: clone; -webkit-box-decoration-break: clone;">メルマガは送って終わりではなく、読者の反応を分析し、内容や配信リストを改善し続けることで成果につながります。ClaudeCowork を活用すれば、メルマガ作成だけでなく、過去配信の分析、改善案の作成、リスト最適化まで一貫して進めやすくなります。</span>そのため、メルマガ業務を「作る業務」から、「継続的に改善する業務」へ進化させやすくなります。ただし、継続的に活用するには、任せる業務範囲や確認ルールを整理し、再現性のある運用フローとして社内に組み込むことが重要です。SMS データテックには、ClaudeCowork を活用した業務全体の自動化と運用定着を支援する<strong>「Claude Cowork 内製化支援」</strong>サービスがあります。貴社の業務・体制に合わせた活用可能性と課題を無料相談で整理できます。お気軽にお問い合わせください。</p>


<div class="hs-cta-embed hs-cta-simple-placeholder hs-cta-embed-350307093208" style="max-width: 100%; max-height: 100%; width: 750px; height: 268.24493408203125px;" data-hubspot-wrapper-cta-id="350307093208"><a href="https://cta-na2.hubspot.com/web-interactives/public/v1/track/redirect?encryptedPayload=AVxigLJSjAz1SL4fqqrv18TOvkLN6PfH9BYrH3lbqwTD7sfdD0IwSYL8d5iT0aX05MUlnzi3es4Q2wQSkgFZ%2BP7wFAu22SswPktfsnlYXytze1p1n9I736tZxD%2FIs0nxkA3GeytmfkntFrSs54aNFmMVQAYaomlhwLobNurjyzK3GtVDa76n40L2B8N4Yrff%2BDZtyYD0s2uaOT%2Brq7KzGywhAjEM4iNm&amp;webInteractiveContentId=350307093208&amp;portalId=23171742" target="_blank" rel="noopener"> <img decoding="async" style="height: 100%; width: 100%; object-fit: fill;" src="https://hubspot-no-cache-na2-prod.s3.amazonaws.com/cta/default/23171742/interactive-350307093208.png" alt="SDT_CTA_Claude Cowork" /> </a></div><p>The post <a href="https://www.sms-datatech.co.jp/column/aut_claude-cowork_mail-marketing/">ClaudeCoworkの教科書｜BtoBマーケターがメルマガ改善で押さえるべき4つのステップ</a> first appeared on <a href="https://www.sms-datatech.co.jp">SMS DataTech</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>ClaudeCoworkの教科書｜BtoBマーケターが競合調査で押さえるべき4つのステップ</title>
		<link>https://www.sms-datatech.co.jp/column/aut_claude-cowork-b2b-competitor-research/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=aut_claude-cowork-b2b-competitor-research</link>
		
		<dc:creator><![CDATA[後藤]]></dc:creator>
		<pubDate>Thu, 30 Apr 2026 01:00:00 +0000</pubDate>
				<category><![CDATA[コラム]]></category>
		<category><![CDATA[自動化ソリューション]]></category>
		<category><![CDATA[自動化]]></category>
		<category><![CDATA[AI]]></category>
		<guid isPermaLink="false">https://www.sms-datatech.co.jp/?p=35755</guid>

					<description><![CDATA[<p>BtoBマーケティングにおいて、競合調査は重要ですが時間がかかる業務の代表格です。競合の機能、料金、訴求メッセージ、CTA、導入事例の変化を継続的に追うには、毎回同じような手順を繰り返す必要があり、担当者の工数を着実に削っていきます。 最近はChatGPTやClaudeなどのAIチャットを活用する人も増えていますが、「ファイルを毎回アップロードする」「URLを1つずつ渡す」「出力をコピペでスプレッドシートに貼り付ける」といった作業は依然として残りがちです。単発のAI質問では、業務プロセスとしての効率化までは届きません。 そこで今回は、Claude Cowork（以下、Cowork）を使って、自社のあるBtoB SaaS（以下、製品A）の競合分析レポートをどこまで自動化できるかを検証しました。本記事では、その手順と実際に使って分かった勘所をまとめます。 この記事がおススメな方 なぜChat UIではなく「Cowork」で競合調査を行うのか Claude Coworkとは Claude Coworkは、Anthropicが提供するデスクトップ向けのAIエージェントツールです。チャットUIのように1問1答で答えるだけでなく、ローカルフォルダの読み書き、Web調査、ファイル生成といった一連の作業を、ひとまとまりのタスクとして任せられるのが大きな特徴です。エンジニアでなくても、部下に命令するように自然な言葉で依頼することができ、自分のPC上の資料とWeb情報を組み合わせて、業務単位でAIに作業を任せられます。 実践！製品Aと競合A〜Dを比較してみた 今回の検証は、自社のBtoB SaaS（製品A）を題材に、同カテゴリの競合サービス4社（競合A〜D）を比較する形で行いました。 競合調査は「プロンプト」より「事前設計」で決まる Coworkで競合分析を任せる際、もっとも重要なのはプロンプトではなく事前準備です。 AIに自由に調べさせると、比較軸が散漫になり、最終アウトプットが「それっぽいが浅いサマリー」になりがちです。これを避けるには、人間が先に「何を、どの観点で比較するか」だけ決めておく必要があります。 用意したフォルダ構成 検証では、以下のシンプルな構成にしました。 competitive-analysis/ ├── input/ │ ├── 製品A_サービス概要.md │ ├── 製品A_価格表.xlsx │ ├── 競合リスト.md # 競合A〜DのURL・基本情報 │ ├── 分析観点.md │ └── 過去の分析メモ.md └── output/ 「input/」に判断材料となる資料を入れ、「output/」 を成果物の保存先として指定するだけのシンプルな設計です。ポイントは、入力と出力のディレクトリを物理的に分けておくことです。これだけで「成果物がどこにあるか分からない」問題が消えます。 分析観点.md の中身が成否を分ける 特に重要なのが「分析観点.md」です。今回は以下のような項目を事前に書き出しました。 # 比較観点 - 主要機能 - 料金体系・提供条件 - 対象企業規模 - CRM / MA連携 - レポート機能 - 導入支援・カスタマーサクセス体制 - セキュリティ対応 - LPの主な訴求メッセージ - CTA設計（資料請求 / 無料トライアル / デモ予約 など） ここを丁寧に書いておくと、Coworkが返してくる比較表のクオリティが目に見えて変わります。「比較軸の設計は人間の仕事、調査と整理はCoworkの仕事」という分業が、自動化のコツです。 実行前に「一緒にプランを練る」のがCoworkの作法 ここがCowork特有の使い方なのですが、いきなり「分析を実行して」と命じる前に、先にプランを相談するのがおすすめです。 Coworkはタスクを受け取ると、実行する前にまずプラン（手順案）を提示してくれます。これに対して、 といった修正指示を実行前にぶつけられます。実行が始まってから差し戻すよりも、ここで擦り合わせた方が出力品質が安定します。 実務ではプロンプト送信後にこのプランが提示されるので、慌てず一度読み、納得してから「承認して進めて」と返すだけです。「使い方の型」として覚えておく価値があります。 Coworkに渡したプロンプト 事前準備ができたら、Coworkにゴールを伝えます。実際に渡したプロンプトは以下のとおりです（そのまま転用できる形にしています）。 input/ フォルダ内の資料をすべて読み込み、 競合リスト.md に記載された各社についてWeb調査を行い、 分析観点.md に沿って製品Aの競合分析レポートを作成してください。 成果物はすべて output/ フォルダに保存してください。 作成するもの： 1. 機能比較表 2. 料金・提供条件の比較表 3. ポジショニングマップ 4. 各社の強み・弱み 5. 製品Aの改善余地 6. 次に取るべきマーケティング施策案 情報が確認できない項目は、推測せず「不明」と明記してください。 作業に入る前に、まず実行プランを提示してください。 ポイントは2つです。 Coworkが実行した作業 プランの擦り合わせ後、Coworkは以下の流れを自律的に実行してくれました。 人間がやると地味に時間のかかる「読む → 調べる → 並べる → 表にする → 保存する」を、ひとまとめに任せられるのが大きな価値です。途中で「ここは違う」と思ったらチャット側から介入もできるので、「ある程度任せつつ、要所で口を出す」運用が現実的です。 成果物の中身（一部抜粋） 具体的な比較結果は伏せますが、出力されたファイルの感触をつかんでもらうため、構造とサマリの一部だけ公開します。 機能比較表（フォーマットのみ） 製品Aと競合A〜Dを縦横で並べ、対応状況を記号で整理。「output/feature-matrix.md」と「.xlsx」の両方が生成されました。実際の中身はこんな見た目です（評価値はマスキング）。 比較観点 製品A 競合A 競合B 競合C 競合D 主要機能① ◎ ○ ◎ ○ △ 主要機能② ○ ◎ △ ◎ 不明 CRM / MA連携 ◎ ◎ ○ ◎ ○ レポート機能 ○ ◎ ○ △ 不明 公開料金 あり あり 要問合せ あり 不明 凡例：◎強い / ○対応 / △限定的 / ✕非対応 / 不明 料金・提供条件の整理 公開料金があるサービスとないサービスが混在するため、「公開／要問い合わせ／不明」を明示する形で整理されていました。競合の中でも価格戦略の考え方には明確な違いが出ており、製品AのプライシングLPを見直す材料になりました。 ポジショニングマップ 「機能網羅性」×「導入のしやすさ」の2軸で配置されたところ、競合A・Cは機能網羅型として右上、競合B・Dは導入容易性に振った左下、製品Aは中央やや右下という分布になりました。 イメージとしては以下のような配置です。 なお、後述するとおり軸の妥当性は人間レビューが必須です。 示唆サマリー（Cowork出力をベースに筆者整理） 成果物には1〜2ページの示唆サマリーが含まれていました。具体的な内容は伏せますが、構造としては以下のようなトピック粒度で書かれていました。 このレベルの初稿が出てくるので、チームでの議論のたたき台としてはかなり十分です。 実際に使って分かった注意点 ここからが本記事で一番伝えたい部分です。Coworkは万能ではありません。実務で使うなら、以下の点を理解しておくと事故が減ります。 1. 公開情報だけでは埋まらない項目がある 特に料金や詳細機能は、ログイン後にしか見られないSaaSも多く、「不明」が並ぶことは普通にあります。これは仕様であり、無理に埋めさせない方が品質は安定します。 2. ポジショニングマップの軸は人間が確認する 軸の取り方ひとつで結論が変わるのがポジショニングマップです。Coworkが提案した軸はあくまで叩き台として、最終的にはマーケ担当者が業界文脈で見直す前提で扱うべきです。今回も初稿の軸は1度差し戻し、業界の購買決定要因に近い軸へ調整しました。 3. 競合サイトの表現を鵜呑みにしない 公式サイトの訴求は当然ポジティブに書かれています。それをそのまま比較表に流すと、「全社が万能サービス」のような薄い比較になります。自社視点的な再評価を必ず一段階挟むのがおすすめです。 4. 戦略判断は人間の仕事 施策案は便利ですが、そのまま実行できるレベルではありません。製品Aの予算・リソース・優先度を踏まえた絞り込みは、最後まで人間の役割です。 Coworkは「調査と整理の初速を上げる存在」。戦略の意思決定そのものを置き換えるツールではない、と捉えると現場運用がしやすくなります。 次回からは&#8221;1行で済む&#8221;：Skillsで類似業務を量産する ここまでの流れを1回限りで終わらせるか、毎月再現できる仕組みにできるかで、現場の効率は大きく変わります。 Coworkには Skills（スキル） という、再利用可能なワークフロー機能があります。今回の競合調査で得たノウハウ（フォルダ構成・観点の並び順・出力ファイルの命名規則など）をひとまとめにパッケージ化しておけば、次回からは「製品Aの競合調査をして」と言うだけで、同じクオリティの成果物が再現できるようになります。 つまりSkillsとは、「うまくいった調査プロセス」をテンプレ化して、類似業務を量産できるようにする仕組みです。一度作ってしまえば、競合調査だけでなくレポート作成・LP分析・営業資料更新など、フォーマットが似た業務にも横展開できます。 ただし、Skillsの作り方や、組織内での粒度・命名・運用ルールづくりは別の論点になってきます。「自部門で試したいが、まず何から作ればいいか分からない」「部門展開でつまずきそう」という方は、後述のClaudeCowork内製化支援もご検討ください。 まとめ：ClaudeCoworkは「便利なAI」ではなく「業務プロセス化できるAI」 最後に、本記事の要点を整理します。 「AIで競合調査してみた」では終わらせず、自分の業務フローの中に組み込めるかを意識すると、Coworkの価値はぐっと立ち上がります。 マーケ担当者にとって、競合調査は今後ますます「人がやる作業」から「AIに任せて人がレビューする作業」へとシフトしていくはずです。本記事がその第一歩のヒントになれば幸いです。 ClaudeCoworkを自社の業務に組み込みたい方へ 弊社では、本記事のようなCowork活用の設計から、Skills整備・部門展開までを支援する 「Claude Cowork 内製化支援サービス」 を提供しています。「まず自部門で試したい」「すでに使い始めたが組織に広げる段階で詰まっている」など、フェーズに応じてご相談いただけます。無料診断で、貴社の業務・体制に合わせた活用可能性と課題を整理できますので、お気軽にお問い合わせください。</p>
<p>The post <a href="https://www.sms-datatech.co.jp/column/aut_claude-cowork-b2b-competitor-research/">ClaudeCoworkの教科書｜BtoBマーケターが競合調査で押さえるべき4つのステップ</a> first appeared on <a href="https://www.sms-datatech.co.jp">SMS DataTech</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>BtoBマーケティングにおいて、競合調査は重要ですが時間がかかる業務の代表格です。競合の機能、料金、訴求メッセージ、CTA、導入事例の変化を継続的に追うには、毎回同じような手順を繰り返す必要があり、担当者の工数を着実に削っていきます。</p>



<p>最近はChatGPTやClaudeなどのAIチャットを活用する人も増えていますが、「ファイルを毎回アップロードする」「URLを1つずつ渡す」「出力をコピペでスプレッドシートに貼り付ける」といった作業は依然として残りがちです。<strong><mark style="background-color:#fdff84" class="has-inline-color">単発のAI質問では、業務プロセスとしての効率化までは届きません。</mark></strong></p>



<p>そこで今回は、Claude Cowork（以下、Cowork）を使って、自社のあるBtoB SaaS（以下、製品A）の競合分析レポートをどこまで自動化できるかを検証しました。本記事では、その手順と実際に使って分かった勘所をまとめます。</p>



<p><strong>この記事がおススメな方</strong></p>



<ul class="wp-block-list">
<li>BtoB SaaS・サービスのマーケティング担当者</li>



<li>競合調査を定期的に行っている方</li>



<li>AIを単発質問ではなく、業務プロセスとして組み込みたい方</li>
</ul>



<h2 class="wp-block-heading">なぜChat UIではなく「Cowork」で競合調査を行うのか</h2>



<h3 class="wp-block-heading">Claude Coworkとは</h3>



<p><strong><mark style="background-color:#fdff84" class="has-inline-color">Claude Coworkは、Anthropicが提供するデスクトップ向けのAIエージェントツールです。</mark></strong>チャットUIのように1問1答で答えるだけでなく、ローカルフォルダの読み書き、Web調査、ファイル生成といった一連の作業を、ひとまとまりのタスクとして任せられるのが大きな特徴です。エンジニアでなくても、部下に命令するように自然な言葉で依頼することができ、自分のPC上の資料とWeb情報を組み合わせて、業務単位でAIに作業を任せられます。</p>



<h2 class="wp-block-heading">実践！製品Aと競合A〜Dを比較してみた</h2>



<p>今回の検証は、自社のBtoB SaaS（製品A）を題材に、同カテゴリの競合サービス4社（競合A〜D）を比較する形で行いました。</p>



<ul class="wp-block-list">
<li>自社サービス：製品A（BtoB SaaS、マーケ/営業関連領域）</li>



<li>比較対象　　：競合A～D（同カテゴリの主要プレイヤー）</li>



<li>目的　　　　：マーケティング観点での競合比較と打ち手検討</li>



<li>成果物　　　：機能比較表、料金比較表、ポジショニングマップ、示唆サマリー、施策案</li>
</ul>



<h2 class="wp-block-heading">競合調査は「プロンプト」より「事前設計」で決まる</h2>



<p>Coworkで競合分析を任せる際、もっとも重要なのは<strong><mark style="background-color:#fdff84" class="has-inline-color">プロンプトではなく事前準備です。</mark></strong></p>



<p>AIに自由に調べさせると、比較軸が散漫になり、最終アウトプットが「それっぽいが浅いサマリー」になりがちです。これを避けるには、人間が先に「何を、どの観点で比較するか」だけ決めておく必要があります。</p>



<h3 class="wp-block-heading">用意したフォルダ構成</h3>



<p>検証では、以下のシンプルな構成にしました。</p>



<pre class="wp-block-preformatted">competitive-analysis/
├── input/
│   ├── 製品A_サービス概要.md
│   ├── 製品A_価格表.xlsx
│   ├── 競合リスト.md         # 競合A〜DのURL・基本情報
│   ├── 分析観点.md
│   └── 過去の分析メモ.md
└── output/</pre>



<p><code>「input/</code>」に判断材料となる資料を入れ、「<code>output/</code>」 を成果物の保存先として指定するだけのシンプルな設計です。<strong><mark style="background-color:#fdff84" class="has-inline-color">ポイントは、入力と出力のディレクトリを物理的に分けておくことです。</mark></strong>これだけで「成果物がどこにあるか分からない」問題が消えます。</p>



<h3 class="wp-block-heading">分析観点.md の中身が成否を分ける</h3>



<p>特に重要なのが「<code>分析観点.md</code>」です。今回は以下のような項目を事前に書き出しました。</p>



<pre class="wp-block-preformatted"># 比較観点
- 主要機能
- 料金体系・提供条件
- 対象企業規模
- CRM / MA連携
- レポート機能
- 導入支援・カスタマーサクセス体制
- セキュリティ対応
- LPの主な訴求メッセージ
- CTA設計（資料請求 / 無料トライアル / デモ予約 など）</pre>



<p>ここを丁寧に書いておくと、Coworkが返してくる比較表のクオリティが目に見えて変わります。<strong><mark style="background-color:#fdff84" class="has-inline-color">「比較軸の設計は人間の仕事、調査と整理はCoworkの仕事」という分業が、自動化のコツです。</mark></strong></p>



<h2 class="wp-block-heading">実行前に「一緒にプランを練る」のがCoworkの作法</h2>



<p>ここがCowork特有の使い方なのですが、<strong><mark style="background-color:#fdff84" class="has-inline-color">いきなり「分析を実行して」と命じる前に、先にプランを相談するのがおすすめです。</mark></strong></p>



<p>Coworkはタスクを受け取ると、実行する前にまずプラン（手順案）を提示してくれます。これに対して、</p>



<ul class="wp-block-list">
<li>「比較観点XとYは順序を入れ替えてほしい」</li>



<li>「料金は公開価格と要問合せを別カラムにして」</li>



<li>「競合DはLPだけでなくプレスリリースも見て」</li>
</ul>



<p>といった<strong><mark style="background-color:#fdff84" class="has-inline-color">修正指示を実行前にぶつけられます。</mark></strong>実行が始まってから差し戻すよりも、ここで擦り合わせた方が出力品質が安定します。</p>



<p>実務ではプロンプト送信後にこのプランが提示されるので、慌てず一度読み、納得してから「承認して進めて」と返すだけです。「使い方の型」として覚えておく価値があります。</p>



<h2 class="wp-block-heading">Coworkに渡したプロンプト</h2>



<p>事前準備ができたら、Coworkにゴールを伝えます。実際に渡したプロンプトは以下のとおりです（そのまま転用できる形にしています）。</p>



<pre class="wp-block-preformatted">input/ フォルダ内の資料をすべて読み込み、
競合リスト.md に記載された各社についてWeb調査を行い、
分析観点.md に沿って製品Aの競合分析レポートを作成してください。

成果物はすべて output/ フォルダに保存してください。

作成するもの：
1. 機能比較表
2. 料金・提供条件の比較表
3. ポジショニングマップ
4. 各社の強み・弱み
5. 製品Aの改善余地
6. 次に取るべきマーケティング施策案

情報が確認できない項目は、推測せず「不明」と明記してください。
作業に入る前に、まず実行プランを提示してください。</pre>



<p>ポイントは2つです。</p>



<ol class="wp-block-list">
<li><strong>成果物のリストを明示する</strong>：何を作ってほしいかを箇条書きで指定すると、出力構成のブレがなくなります</li>



<li><strong>「不明は不明と書く」ルールを入れる</strong>：これを書かないと、Coworkは公開情報がない箇所を&#8221;それっぽく&#8221;埋めてしまうことがあります</li>
</ol>



<h2 class="wp-block-heading">Coworkが実行した作業</h2>



<p>プランの擦り合わせ後、Coworkは以下の流れを自律的に実行してくれました。</p>



<ol class="wp-block-list">
<li><code>「input/</code>」フォルダの資料を読み込み、製品Aと比較観点を把握</li>



<li><code>「競合リスト.md</code>」を起点に競合A〜DのWeb調査を実施</li>



<li>比較観点ごとに各社情報を収集・整理</li>



<li>機能比較表・料金比較表をMarkdown／表形式で生成</li>



<li>ポジショニングマップ用の整理データを作成</li>



<li>各社の強み・弱み、製品Aの改善余地、施策案をテキストでサマリー</li>



<li>成果物をすべて「<code>output/</code>」フォルダに保存</li>
</ol>



<p>人間がやると地味に時間のかかる「読む → 調べる → 並べる → 表にする → 保存する」を、ひとまとめに任せられるのが大きな価値です。途中で「ここは違う」と思ったらチャット側から介入もできるので、<strong><mark style="background-color:#fdff84" class="has-inline-color">「ある程度任せつつ、要所で口を出す」運用が現実的です。</mark></strong></p>



<h2 class="wp-block-heading">成果物の中身（一部抜粋）</h2>



<p>具体的な比較結果は伏せますが、出力されたファイルの感触をつかんでもらうため、構造とサマリの一部だけ公開します。</p>



<h3 class="wp-block-heading">機能比較表（フォーマットのみ）</h3>



<p>製品Aと競合A〜Dを縦横で並べ、対応状況を記号で整理。「<code>output/feature-matrix.md</code>」と「<code>.xlsx</code>」の両方が生成されました。実際の中身はこんな見た目です（評価値はマスキング）。</p>



<figure class="wp-block-table"><table><thead><tr><th>比較観点</th><th>製品A</th><th>競合A</th><th>競合B</th><th>競合C</th><th>競合D</th></tr></thead><tbody><tr><td>主要機能①</td><td>◎</td><td>○</td><td>◎</td><td>○</td><td>△</td></tr><tr><td>主要機能②</td><td>○</td><td>◎</td><td>△</td><td>◎</td><td>不明</td></tr><tr><td>CRM / MA連携</td><td>◎</td><td>◎</td><td>○</td><td>◎</td><td>○</td></tr><tr><td>レポート機能</td><td>○</td><td>◎</td><td>○</td><td>△</td><td>不明</td></tr><tr><td>公開料金</td><td>あり</td><td>あり</td><td>要問合せ</td><td>あり</td><td>不明</td></tr></tbody></table></figure>



<p>凡例：◎強い / ○対応 / △限定的 / ✕非対応 / 不明</p>



<h3 class="wp-block-heading">料金・提供条件の整理</h3>



<p>公開料金があるサービスとないサービスが混在するため、「公開／要問い合わせ／不明」を明示する形で整理されていました。競合の中でも価格戦略の考え方には明確な違いが出ており、製品AのプライシングLPを見直す材料になりました。</p>



<h3 class="wp-block-heading">ポジショニングマップ</h3>



<p>「機能網羅性」×「導入のしやすさ」の2軸で配置されたところ、競合A・Cは機能網羅型として右上、競合B・Dは導入容易性に振った左下、製品Aは中央やや右下という分布になりました。</p>



<p>イメージとしては以下のような配置です。</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" width="753" height="345" src="https://www.sms-datatech.co.jp/wp-content/uploads/2026/04/image.png" alt="" class="wp-image-35761" srcset="https://www.sms-datatech.co.jp/wp-content/uploads/2026/04/image.png 753w, https://www.sms-datatech.co.jp/wp-content/uploads/2026/04/image-300x137.png 300w" sizes="(max-width: 753px) 100vw, 753px" /></figure>
</div>


<p>なお、後述するとおり<strong><mark style="background-color:#fdff84" class="has-inline-color">軸の妥当性は人間レビューが必須です。</mark></strong></p>



<h3 class="wp-block-heading">示唆サマリー（Cowork出力をベースに筆者整理）</h3>



<p>成果物には1〜2ページの示唆サマリーが含まれていました。具体的な内容は伏せますが、構造としては以下のようなトピック粒度で書かれていました。</p>



<ul class="wp-block-list">
<li>競合A／Cは大企業向けの高機能化、競合B／Dは中小企業向けの低価格・短期導入を志向。市場が「機能厚め × 大企業」と「軽量 × SMB」の二極に寄っている様子が読み取れる</li>



<li>製品Aは中規模企業向けのバランス型ポジションを取り得るが、現行LPでは訴求軸が機能寄りに振れすぎており、「導入の運用しやすさ」が伝わっていない可能性がある</li>



<li>次の打ち手候補：①LPファーストビューの訴求軸見直し、②比較資料に「運用工数の差分」を追記、③ウェビナーでの導入事例コンテンツ強化</li>
</ul>



<p>このレベルの初稿が出てくるので、<strong><mark style="background-color:#fdff84" class="has-inline-color">チームでの議論のたたき台としてはかなり十分です。</mark></strong></p>



<h2 class="wp-block-heading">実際に使って分かった注意点</h2>



<p>ここからが本記事で一番伝えたい部分です。<strong><mark style="background-color:#fdff84" class="has-inline-color">Coworkは万能ではありません。</mark></strong>実務で使うなら、以下の点を理解しておくと事故が減ります。</p>



<h3 class="wp-block-heading">1. 公開情報だけでは埋まらない項目がある</h3>



<p>特に料金や詳細機能は、ログイン後にしか見られないSaaSも多く、「不明」が並ぶことは普通にあります。<strong><mark style="background-color:#fdff84" class="has-inline-color">これは仕様であり、無理に埋めさせない方が品質は安定します。</mark></strong></p>



<h3 class="wp-block-heading">2. ポジショニングマップの軸は人間が確認する</h3>



<p>軸の取り方ひとつで結論が変わるのがポジショニングマップです。Coworkが提案した軸はあくまで叩き台として、<strong><mark style="background-color:#fdff84" class="has-inline-color">最終的にはマーケ担当者が業界文脈で見直す前提で扱うべきです。</mark></strong>今回も初稿の軸は1度差し戻し、業界の購買決定要因に近い軸へ調整しました。</p>



<h3 class="wp-block-heading">3. 競合サイトの表現を鵜呑みにしない</h3>



<p>公式サイトの訴求は当然ポジティブに書かれています。それをそのまま比較表に流すと、「全社が万能サービス」のような薄い比較になります。<strong><mark style="background-color:#fdff84" class="has-inline-color">自社視点的な再評価を必ず一段階挟むのがおすすめです。</mark></strong></p>



<h3 class="wp-block-heading">4. 戦略判断は人間の仕事</h3>



<p>施策案は便利ですが、<strong><mark style="background-color:#fdff84" class="has-inline-color">そのまま実行できるレベルではありません。</mark></strong>製品Aの予算・リソース・優先度を踏まえた絞り込みは、最後まで人間の役割です。</p>



<p>Coworkは「調査と整理の初速を上げる存在」。戦略の意思決定そのものを置き換えるツールではない、と捉えると現場運用がしやすくなります。</p>



<h2 class="wp-block-heading">次回からは&#8221;1行で済む&#8221;：Skillsで類似業務を量産する</h2>



<p>ここまでの流れを1回限りで終わらせるか、<strong><mark style="background-color:#fdff84" class="has-inline-color">毎月再現できる仕組みにできるかで、現場の効率は大きく変わります。</mark></strong></p>



<p>Coworkには Skills（スキル） という、再利用可能なワークフロー機能があります。今回の競合調査で得たノウハウ（フォルダ構成・観点の並び順・出力ファイルの命名規則など）をひとまとめにパッケージ化しておけば、<strong><mark style="background-color:#fdff84" class="has-inline-color">次回からは「製品Aの競合調査をして」と言うだけで、同じクオリティの成果物が再現できるようになります。</mark></strong></p>



<p>つまりSkillsとは、「うまくいった調査プロセス」をテンプレ化して、類似業務を量産できるようにする仕組みです。一度作ってしまえば、競合調査だけでなくレポート作成・LP分析・営業資料更新など、フォーマットが似た業務にも横展開できます。</p>



<p>ただし、Skillsの作り方や、組織内での粒度・命名・運用ルールづくりは別の論点になってきます。「自部門で試したいが、まず何から作ればいいか分からない」「部門展開でつまずきそう」という方は、後述の<strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-blue-2-color"><span style="text-decoration: underline;"><a href="https://www.sms-datatech.co.jp/products/claude-cowork_insourcing/" title="">ClaudeCowork内製化支援</a></span></mark></strong>もご検討ください。</p>



<h2 class="wp-block-heading">まとめ：ClaudeCoworkは「便利なAI」ではなく「業務プロセス化できるAI」</h2>



<p>最後に、本記事の要点を整理します。</p>



<ul class="wp-block-list">
<li>ClaudeCoworkは「PC上の資料の読み書き」「Web調査」「ファイル生成」を一気通貫で任せられるため、<strong><mark style="background-color:#fdff84" class="has-inline-color">競合調査のような業務単位の作業に向いている</mark></strong></li>



<li>成功の鍵は<strong><mark style="background-color:#fdff84" class="has-inline-color">プロンプトではなく事前設計</mark></strong>。特に分析観点ファイルの精度が成否を分ける</li>



<li>実行前に<strong><mark style="background-color:#fdff84" class="has-inline-color">プランを擦り合わせる</mark></strong>ステップを必ず挟む</li>



<li>ClaudeCoworkは初稿作成と整理作業に強いが、<strong><mark style="background-color:#fdff84" class="has-inline-color">比較軸の妥当性確認と戦略判断は人間の仕事</mark></strong></li>



<li><strong><mark style="background-color:#fdff84" class="has-inline-color">Skillという便利な機能を使えば、類似業務の量産も可能</mark></strong></li>



<li>Coworkは初稿作成と整理作業に強いが、比較軸の妥当性確認と戦略判断は人間の仕事</li>
</ul>



<p>「AIで競合調査してみた」では終わらせず、<strong><mark style="background-color:#fdff84" class="has-inline-color">自分の業務フローの中に組み込めるか</mark></strong>を意識すると、Coworkの価値はぐっと立ち上がります。</p>



<p>マーケ担当者にとって、競合調査は今後ますます「人がやる作業」から「AIに任せて人がレビューする作業」へとシフトしていくはずです。本記事がその第一歩のヒントになれば幸いです。</p>



<h3 class="wp-block-heading">ClaudeCoworkを自社の業務に組み込みたい方へ</h3>



<p>弊社では、本記事のようなCowork活用の設計から、Skills整備・部門展開までを支援する 「<a href="https://www.sms-datatech.co.jp/products/claude-cowork_insourcing/"><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-blue-2-color"><span style="text-decoration: underline;">Claude Cowork 内製化支援サービス</span></mark></strong></a>」 を提供しています。「まず自部門で試したい」「すでに使い始めたが組織に広げる段階で詰まっている」など、フェーズに応じてご相談いただけます。<strong>無料診断</strong>で、貴社の業務・体制に合わせた活用可能性と課題を整理できますので、お気軽にお問い合わせください。</p>



<div class="wp-block-buttons is-content-justification-center is-layout-flex wp-container-core/buttons-is-layout-16018d1d wp-block-buttons-is-layout-flex">
<div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="https://www.sms-datatech.co.jp/products/claude-cowork_insourcing/">Claude Cowork 内製化支援サービスとは？</a></div>
</div>


<div class="block-block-22">
	<div class="Editor-wrap">
					<div class="Editor-wrap__titleMsg">まずはお気軽にご相談ください</div>
			<div class="Editor-wrap__title">お問い合わせフォーム</div>
			</div>
</div>


<script charset="utf-8" type="text/javascript" src="//js.hsforms.net/forms/embed/v2.js"></script>
<script>
  hbspt.forms.create({
    region: "na1",
    portalId: "23171742",
    formId: "b36c7c3b-83a0-43c4-8daa-8e055d6805e9"
  });
</script><p>The post <a href="https://www.sms-datatech.co.jp/column/aut_claude-cowork-b2b-competitor-research/">ClaudeCoworkの教科書｜BtoBマーケターが競合調査で押さえるべき4つのステップ</a> first appeared on <a href="https://www.sms-datatech.co.jp">SMS DataTech</a>.</p>]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
