Spring BootとVue.jsを用いた大学院入試情報管理システムの設計と実装

概要 情報技術の急速な発展に伴い、大学院入試に関する情報収集方法は従来のオフライン手段からオンラインプラットフォームへと移行しつつある。受験生にとって効率的で正確かつリアルタイムな情報取得ニーズが高まる一方で、既存の情報プラットフォームでは情報の分散化、更新遅延、ユーザーエクスペリエンスの劣化といった課題が顕在化している。これらの問題に対応するため、Spring BootとVue.jsに基づく大学院入試情報管理システムの設計・構築は重要な意義を持つ。本システムは入試政策、大学情報、学習教材、過去問などの主要コンテンツを統合し、パーソナライズドレコメンデーション機能と双方向コミュニケーション機能を提供することで、受験生に対するワンストップ情報サービスを実現することを目的としている。キーワード:大学院入試情報、情報統合、パーソナライズドレコメンデーション、Spring Boot、Vue.js。

システム全体はフロントエンド・バックエンド分離型アーキテクチャを採用しており、バックエンドはSpring Bootフレームワークに基づきRESTful APIインターフェースを提供し、高並列処理および分散配置をサポートする。フロントエンドはVue.jsフレームワークとElement UIコンポーネントライブラリを組み合わせることで、ユーザーインターフェースの直感性とレスポンスタイミングを確保している。データベースにはMySQLを使用し、JPAによるデータ永続化を実現している。システム機能としては、ユーザー登録ログイン、情報カテゴリ別閲覧、キーワード検索、コメントインタラクション、教材ダウンロード、管理者向けバックエンド管理などが含まれる。Redisによるホットデータキャッシュによりシステムパフォーマンスを向上させている。キーワード:Spring Boot、Vue.js、MySQL、RESTful API、Redis。

データテーブル構造 ユーザー基本情報テーブル ユーザー基本情報テーブルはシステム登録ユーザーの基礎情報を格納するものであり、ユーザーIDが主キーとなる。登録日時情報は自動生成関数により設定される。このテーブルにはユーザーのアカウント、パスワード、個人情報、権限レベルなどが記録されており、その構造は表3-1の通りである。

フィールド名 データ型 説明
user_identifier BIGINT ユーザー固有識別子(主キー)
account_login VARCHAR(50) ユーザー認証アカウント(ユニーク制約)
encrypted_password VARCHAR(100) 暗号化されたパスワード
display_name VARCHAR(30) 表示用ニックネーム
contact_email VARCHAR(50) 連絡用メールアドレス
mobile_number VARCHAR(20) 携帯電話番号
registration_timestamp TIMESTAMP 登録日時(自動生成)
latest_access_time TIMESTAMP 最終アクセス日時
access_privilege INT アクセス権限(0:一般ユーザー、1:管理者)

大学院入試情報テーブル 大学院入試情報テーブルはシステム内で配信される各種入試関連情報を保存するものであり、情報IDが主キーとなる。投稿日時はシステムにより自動記録される。このテーブルには情報タイトル、詳細内容、カテゴリ区分、閲覧回数などの属性が含まれ、構造は表3-2の通りである。

フィールド名 データ型 説明
info_identifier BIGINT 情報固有識別子(主キー)
info_title VARCHAR(100) 情報タイトル
info_content TEXT 情報詳細本文
info_category VARCHAR(30) 情報カテゴリ(政策、大学、学習指導など)
post_timestamp TIMESTAMP 投稿日時(自動生成)
view_counter INT 閲覧回数カウンタ
poster_id BIGINT 投稿者ID(外部キー参照)

ユーザーコメントテーブル ユーザーコメントテーブルは情報に対するユーザーのコメントインタラクションを記録するものであり、コメントIDが主キーとなる。コメント投稿日時はシステムにより自動生成される。このテーブルにはコメント内容と関連情報・ユーザー情報が格納され、構造は表3-3の通りである。

フィールド名 データ型 説明
comment_identifier BIGINT コメント固有識別子(主キー)
comment_body TEXT コメント本文
comment_timestamp TIMESTAMP コメント投稿日時(自動生成)
commenter_id BIGINT コメント投稿者ID(外部キー参照)
related_info_id BIGINT 関連情報ID(外部キー参照)
parent_comment_ref BIGINT 親コメントID(返信機能用)

技術アーキテクチャスタック バックエンド技術:Spring Boot Spring Bootは現代的なJava企業開発の核となるフレームワークとして、「規約優先設定」の設計理念によりアプリケーション開発パターンを再定義している。主な特徴は以下の通り:

ゼロコンフィギュレーション起動:自動設定機構を統合し、XML設定ファイルの作成工数を大幅削減 組込みサーバー:Tomcat/Jetty/Undertowを内蔵し、スタンドアロンJAR形式での配置をサポート プロダクションレディ:Actuator監視コンポーネントを統合し、ヘルスチェックやメトリクス収集などの企業向け機能を提供 マイクロサービス対応:分散アーキテクチャを自然にサポートし、Spring Cloudエコシステムとのシームレスな統合を実現

開発利点:Starter依存関係体系とスマート自動アセンブリにより、開発者は基盤インフラ構築ではなく業務ロジック実装に集中できる。単一実行可能JARによる配置モードは運用管理プロセスを大幅に簡素化する。

フロントエンド技術:Vue.js Vue.jsはプログレッシブ型フレームワーク設計と卓越した開発体験により、現代フロントエンド開発の最適解として位置付けられている。技術的特徴は以下の通り:

リアクティブデータフロー:依存追跡に基づくリアクティブシステムにより、効率的なビュー更新を実現 コンポーネント指向アーキテクチャ:単一ファイルコンポーネント(SFC)設計により、スタイル・ロジック・テンプレートの完全なカプセル化を実現 柔軟なプログレッシブ設計:単純なビューレイヤーライブラリから完全なSPAソリューションまで拡張可能 豊富なエコシステム:Vue Router、Vuex/Pinia、Vue CLIなどの公式ツールチェーンが整備済み

開発効率:直感的なテンプレート構文と強力なディレクティブシステムにより、複雑なユーザーインタラクションを簡潔に表現可能。優れたTypeScriptサポートと開発者ツールにより、大規模プロジェクトにおける確かな開発保証を提供する。

主要コード実装

package com.graduatesystem;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

@SpringBootApplication
@MapperScan(basePackages = {"com.graduatesystem.dao"})
public class GraduateInfoSystemApplication extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(GraduateInfoSystemApplication.class, args);
    }
    
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder appBuilder) {
        return appBuilder.sources(GraduateInfoSystemApplication.class);
    }
}
package com.graduatesystem.controller;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Date;
import java.util.List;
import javax.servlet.http.HttpServletRequest;

import com.graduatesystem.utils.ValidationUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.graduatesystem.annotation.PublicAccess;

import com.graduatesystem.entity.UserInfoEntity;
import com.graduatesystem.entity.view.UserInfoView;

import com.graduatesystem.service.UserInfoService;
import com.graduatesystem.service.AuthTokenService;
import com.graduatesystem.utils.PaginationUtils;
import com.graduatesystem.utils.ResponseResult;
import com.graduatesystem.utils.MyBatisPlusUtil;
import com.graduatesystem.utils.MapConversionUtil;
import com.graduatesystem.utils.CommonOperations;
import java.io.IOException;

/**
 * ユーザー管理
 * バックエンドAPI
 * @author 
 * @email 
 * @date 2024-08-15 09:30:45
 */
@RestController
@RequestMapping("/userinfo")
public class UserInfoController {
    @Autowired
    private UserInfoService userInfoService;

    @Autowired
    private AuthTokenService authTokenService;
    
    /**
     * ユーザーログイン
     */
    @PublicAccess
    @RequestMapping(value = "/authenticate")
    public ResponseResult authenticate(String username, String password, String verificationCode, HttpServletRequest request) {
        UserInfoEntity user = userInfoService.selectOne(new EntityWrapper<UserInfoEntity>().eq("user_account", username));
        if(user == null || !user.getPassword().equals(password)) {
            return ResponseResult.error("アカウントまたはパスワードが無効です");
        }
        
        String token = authTokenService.createToken(user.getId(), username, "userinfo", "ユーザー");
        return ResponseResult.success().put("token", token);
    }

    /**
     * ユーザー登録
     */
    @PublicAccess
    @RequestMapping("/signup")
    public ResponseResult signup(@RequestBody UserInfoEntity userInfo) {
        UserInfoEntity existingUser = userInfoService.selectOne(new EntityWrapper<UserInfoEntity>().eq("user_account", userInfo.getUserAccount()));
        if(existingUser != null) {
            return ResponseResult.error("このアカウントは既に登録されています");
        }
        Long userId = new Date().getTime();
        userInfo.setId(userId);
        userInfoService.insert(userInfo);
        return ResponseResult.success();
    }

    /**
     * セッション終了
     */
    @RequestMapping("/terminate-session")
    public ResponseResult terminateSession(HttpServletRequest request) {
        request.getSession().invalidate();
        return ResponseResult.success("正常にログアウトしました");
    }
    
    /**
     * 現在のセッションユーザー情報を取得
     */
    @RequestMapping("/current-session")
    public ResponseResult getCurrentUserInfo(HttpServletRequest request) {
        Long userId = (Long)request.getSession().getAttribute("currentUserId");
        UserInfoEntity user = userInfoService.selectById(userId);
        return ResponseResult.success().put("data", user);
    }
    
    /**
     * パスワードリセット
     */
    @PublicAccess
    @RequestMapping(value = "/password-reset")
    public ResponseResult passwordReset(String username, HttpServletRequest request) {
        UserInfoEntity user = userInfoService.selectOne(new EntityWrapper<UserInfoEntity>().eq("user_account", username));
        if(user == null) {
            return ResponseResult.error("アカウントが存在しません");
        }
        user.setPassword("123456");
        userInfoService.updateById(user);
        return ResponseResult.success("パスワードが「123456」にリセットされました");
    }

    /**
     * 管理画面リスト表示
     */
    @RequestMapping("/dashboard-list")
    public ResponseResult dashboardList(@RequestParam Map<String, Object> parameters, UserInfoEntity userInfo,
        HttpServletRequest request) {
        EntityWrapper<UserInfoEntity> wrapper = new EntityWrapper<UserInfoEntity>();

        PaginationUtils pagination = userInfoService.queryPage(parameters, MyBatisPlusUtil.sort(MyBatisPlusUtil.between(MyBatisPlusUtil.likeOrEq(wrapper, userInfo), parameters), parameters));

        return ResponseResult.success().put("data", pagination);
    }
    
    /**
     * フロントエンドリスト表示
     */
    @PublicAccess
    @RequestMapping("/frontend-list")
    public ResponseResult frontendList(@RequestParam Map<String, Object> parameters, UserInfoEntity userInfo, 
        HttpServletRequest request) {
        EntityWrapper<UserInfoEntity> wrapper = new EntityWrapper<UserInfoEntity>();

        PaginationUtils pagination = userInfoService.queryPage(parameters, MyBatisPlusUtil.sort(MyBatisPlusUtil.between(MyBatisPlusUtil.likeOrEq(wrapper, userInfo), parameters), parameters));
        return ResponseResult.success().put("data", pagination);
    }

    /**
     * リスト取得
     */
    @RequestMapping("/fetch-lists")
    public ResponseResult fetchLists(UserInfoEntity userInfo) {
        EntityWrapper<UserInfoEntity> wrapper = new EntityWrapper<UserInfoEntity>();
        wrapper.allEq(MyBatisPlusUtil.allEQMapPre(userInfo, "userinfo")); 
        return ResponseResult.success().put("data", userInfoService.selectListView(wrapper));
    }

     /**
     * 情報検索
     */
    @RequestMapping("/search-info")
    public ResponseResult searchInfo(UserInfoEntity userInfo) {
        EntityWrapper<UserInfoEntity> wrapper = new EntityWrapper<UserInfoEntity>();
        wrapper.allEq(MyBatisPlusUtil.allEQMapPre(userInfo, "userinfo")); 
        UserInfoView userInfoView = userInfoService.selectView(wrapper);
        return ResponseResult.success("ユーザー情報検索成功").put("data", userInfoView);
    }
    
    /**
     * 管理画面詳細表示
     */
    @RequestMapping("/item-detail/{id}")
    public ResponseResult itemDetail(@PathVariable("id") Long id) {
        UserInfoEntity userInfo = userInfoService.selectById(id);
        return ResponseResult.success().put("data", userInfo);
    }

    /**
     * フロントエンド詳細表示
     */
    @PublicAccess
    @RequestMapping("/public-detail/{id}")
    public ResponseResult publicDetail(@PathVariable("id") Long id) {
        UserInfoEntity userInfo = userInfoService.selectById(id);
        return ResponseResult.success().put("data", userInfo);
    }
    
    /**
     * 管理画面保存
     */
    @RequestMapping("/admin-save")
    public ResponseResult adminSave(@RequestBody UserInfoEntity userInfo, HttpServletRequest request) {
        if(userInfoService.selectCount(new EntityWrapper<UserInfoEntity>().eq("user_account", userInfo.getUserAccount())) > 0) {
            return ResponseResult.error("このユーザーIDは既に存在します");
        }
        userInfo.setId(new Date().getTime() + new Double(Math.floor(Math.random() * 1000)).longValue());
        UserInfoEntity existingUser = userInfoService.selectOne(new EntityWrapper<UserInfoEntity>().eq("user_account", userInfo.getUserAccount()));
        if(existingUser != null) {
            return ResponseResult.error("ユーザーが既に存在します");
        }
        userInfo.setId(new Date().getTime());
        userInfoService.insert(userInfo);
        return ResponseResult.success();
    }
    
    /**
     * フロントエンド保存
     */
    @RequestMapping("/create-record")
    public ResponseResult createRecord(@RequestBody UserInfoEntity userInfo, HttpServletRequest request) {
        if(userInfoService.selectCount(new EntityWrapper<UserInfoEntity>().eq("user_account", userInfo.getUserAccount())) > 0) {
            return ResponseResult.error("このユーザーIDは既に存在します");
        }
        userInfo.setId(new Date().getTime() + new Double(Math.floor(Math.random() * 1000)).longValue());
        UserInfoEntity existingUser = userInfoService.selectOne(new EntityWrapper<UserInfoEntity>().eq("user_account", userInfo.getUserAccount()));
        if(existingUser != null) {
            return ResponseResult.error("ユーザーが既に存在します");
        }
        userInfo.setId(new Date().getTime());
        userInfoService.insert(userInfo);
        return ResponseResult.success();
    }

    /**
     * 情報更新
     */
    @RequestMapping("/modify-info")
    @Transactional
    public ResponseResult modifyInfo(@RequestBody UserInfoEntity userInfo, HttpServletRequest request) {
        if(userInfoService.selectCount(new EntityWrapper<UserInfoEntity>().ne("id", userInfo.getId()).eq("user_account", userInfo.getUserAccount())) > 0) {
            return ResponseResult.error("このユーザーIDは既に存在します");
        }
        userInfoService.updateById(userInfo);
        return ResponseResult.success();
    }

    /**
     * データ削除
     */
    @RequestMapping("/remove-data")
    public ResponseResult removeData(@RequestBody Long[] ids) {
        userInfoService.deleteBatchIds(Arrays.asList(ids));
        return ResponseResult.success();
    }
}

タグ: SpringBoot vue.js MySQL RESTful-API redis

6月9日 17:18 投稿