Spring BootとVueを活用した体育館管理システムの設計と実装

はじめに

現代の急速な経済発展と絶え間ない情報技術の進化により、従来のデータ管理方式はソフトウェアによる保存、整理、集中処理へと進化しています。このような環境下で誕生したのが本体育館管理システムであり、管理者が膨大なデータを短時間で処理することを支援します。このソフトウェアツールは、業務処理効率を向上させ、労力を節約する効果があります。本システムは、成熟したSSMフレームワーク、大規模な商用ウェブサイト開発に適したJava言語、そして最も人気のあるRDBMSアプリケーションの一つであるMySQLデータベースを活用して開発されています。ユーザーがオンラインで問題を選択し回答し、成績を確認できる機能を実装しています。管理者は配送先管理、カート管理、施設管理、施設注文管理、辞書管理、イベント管理、イベントお気に入り管理、イベント評価管理、イベント注文管理、商品管理、商品お気に入り管理、商品評価管理、商品注文管理、ユーザー管理、管理者管理などの機能を管理できます。

本体育館管理システムは、操作者のニーズに基づき設計されたシンプルで美しいインターフェースを持ち、機能モジュールのレイアウトは同種のサイトと一貫性があります。基本機能を実現するだけでなく、データ情報のセキュリティ問題に対する実用的なソリューションも提供しています。このプログラムは、管理者が業務を効率的に処理するのを支援すると同時に、データ情報の統合化、標準化、自動化を実現しています。

開発環境

開発言語:Java
フレームワーク:Spring Boot
JDKバージョン:JDK1.8
サーバー:Tomcat7
データベース:MySQL5.7(必ず5.7バージョン)
データベースツール:Navicat11
開発ソフト:Eclipse/MyEclipse/IntelliJ IDEA
Mavenパッケージ:Maven3.3.9
ブラウザ:Google Chrome

管理画面URL:localhost:8080/プロジェクト名/admin/dist/index.html
ユーザー画面URL:localhost:8080/プロジェクト名/front/dist/index.html(ユーザー画面がない場合は不要)

管理者アカウント:admin
管理者パスワード:admin

技術紹介

Javaについて

Javaはオブジェクト指向の静的型付けプログラミング言語です。Java言語はマルチスレッドとオブジェクト指向を特徴としています。その特徴は、方案の属性に基づきいくつかの異なるモジュールに分割し、これらのモジュールは閉鎖的で多様性があり、申請過程において独立性が高いものです。Java言語はコンピュータソフトウェア開発過程において対話操作の目的を達成することができ、様々な形式の交換を通じて必要なデータを効果的に処理し、コンピュータソフトウェア開発の制御可能性と可視性を確保します。Java言語を開発する際、ネットワークインターフェースが保持されており、Javaが保持するデフォルトのネットワークインターフェースは、Webアプリケーションプログラミングが依存するカテゴリライブラリと一致します。Javaで開発されたアプリケーションをより安定して強固にするため、Javaはプログラム内のゴミを自動的に収集し、プログラム内に存在する例外を処理します。Java言語は日常の開発過程で広く使用される一般的な基本言語です。その中でJava言語コースライブラリ、文法規則、キーワードはコンピュータソフトウェアの開発やプログラミングで頻繁に使用されます。

オブジェクト指向プログラミングはJava言語の最も顕著な特徴です。元のインターフェースと補助インターフェース、継承を備えており、同じタイプの単一継現だけでなく、インターフェース間の複数継承も実現し、クラス、インターフェースとインターフェース間、クラスとインターフェース間の効果的な通信を実現します。Javaのオブジェクト指向特性は主に三つの側面を含んでいます:継承、多態性とカプセル化。カプセル化はJavaのコアであり、すべてのデータ操作をカプセル化できます。多態性とは、オブジェクト指向の振る舞いから派生した関連する振る舞いを指します。継承は特殊なプログラミングパターンとして親クラスと子クラスの二つのタイプがあり、これら二つのタイプの属性は同じ機能と特性を持ちます。親クラスの属性特性については、子クラスは継承と最適化を実現できます。

Spring Bootフレームワーク紹介

Spring Bootは近年最も人気のあるバックエンド開発フレームワークであり、その登場は過去のSpringフレーム開発における面倒な設定を一変させ、Springアプリケーションの構築と開発を大幅に簡素化しました。Spring BootフレームワークはSpringフレームワークの優秀な特性をすべて保持しているだけでなく、特定の設定方式を使用し、底層で開発者がプロジェクト作成時に多くの設定を事前に行い、開発時に開発者が面倒な設定をあまり行わなくて済むようにします。さらにSpring Bootでは多くのフレームワークが統合されており、開発者が開発で必要なjarパッケージを探す必要がなくなり、依存パッケージのバージョン競合問題も解決し、依存パッケージの参照の安定性を向上させ、Springアプリケーションの構築と開発プロセスを簡素化しています。

MySQLデータベース

MySQLはリレーショナルデータベース管理システムであり、Oracleの製品です。MySQLの言語は非構造化であり、ユーザーはデータ上で作業できます。このデータベース管理システムは登場以来、社会から広く注目を集めています。各方面において、同等のデータベースと比較して、MySQLの長所は極めて際立っており、その実行速度は速く、適用範囲は広く、データベースのセキュリティという点では独自の特徴があります。言語構造において、MySQLの言語はシンプルであり、他のデータベースで長いコードが必要な操作でも、MySQLはわずか数行で実現できます。総じて言えば、MySQLというリレーショナルデータベース管理システムは、開発者がプロジェクトのデータ開発、保存を行う際の最良の選択肢となっています。MySQLの機能も多様であり、データ操作やデータベースの構築維持などがあります。また、このデータベースのデータ共有性は高く、冗長度は低く、容易に拡張できます。MySQLのセキュリティ面でも独自の特徴があり、ユーザー識別と認証技術を適用し、ビューとデータを暗号化し、情報の信頼性を確保しています。データベースシステムの機能と強さの性質を考慮し、本データベースシステムの設計では主にMySQLを使用してデータ処理を実現しています。Webベースの有料自習室管理システムはMySQLデータベースを使用し、Webアプリケーションという面において、MySQLは最良の選択です。システム全体の開発、構築、実行、維持にとって極めて重要な役割を果たしています。

コード例

ファイルアップロードコントローラ

package com.controller;

import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.UUID;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import com.annotation.IgnoreAuth;
import com.entity.SystemConfig;
import com.service.ConfigService;
import com.utils.ResponseResult;

/**
 * ファイルアップロードコントローラ
 */
@RestController
@RequestMapping("/api/files")
public class FileUploadController {
    
    @Autowired
    private ConfigService configService;
    
    /**
     * ファイルアップロード
     */
    @PostMapping("/upload")
    @IgnoreAuth
    public ResponseResult<String> uploadFile(@RequestParam("file") MultipartFile file, 
                                           @RequestParam(required = false) String configType) throws IOException {
        if (file.isEmpty()) {
            return ResponseResult.error("アップロードファイルが空です");
        }
        
        String originalFilename = file.getOriginalFilename();
        String fileExtension = originalFilename.substring(originalFilename.lastIndexOf(".") + 1);
        
        // アップロードディレクトリの設定
        Resource resource = new org.springframework.core.io.ClassPathResource("static");
        File uploadDir = new File(resource.getURI().getPath(), "/uploads/");
        if (!uploadDir.exists()) {
            uploadDir.mkdirs();
        }
        
        // 一意のファイル名生成
        String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        String uniqueFilename = timestamp + "_" + UUID.randomUUID().toString() + "." + fileExtension;
        File destinationFile = new File(uploadDir, uniqueFilename);
        
        // ファイル保存
        file.transferTo(destinationFile);
        
        // 設定タイプが指定されている場合、システム設定を更新
        if (configType != null && "avatar".equals(configType)) {
            SystemConfig config = configService.findByConfigName("userAvatar");
            if (config == null) {
                config = new SystemConfig();
                config.setConfigName("userAvatar");
                config.setConfigValue(uniqueFilename);
            } else {
                config.setConfigValue(uniqueFilename);
            }
            configService.saveOrUpdate(config);
        }
        
        return ResponseResult.success(uniqueFilename);
    }
    
    /**
     * ファイルダウンロード
     */
    @GetMapping("/download/{filename}")
    @IgnoreAuth
    public ResponseEntity<Resource> downloadFile(@PathVariable String filename) {
        try {
            Resource resource = new org.springframework.core.io.ClassPathResource("static/uploads/" + filename);
            if (!resource.exists()) {
                return ResponseEntity.notFound().build();
            }
            
            return ResponseEntity.ok()
                    .contentType(MediaType.APPLICATION_OCTET_STREAM)
                    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"")
                    .body(resource);
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
}

掲示板コントローラ

package com.controller;

import java.time.LocalDateTime;
import java.util.*;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.annotation.IgnoreAuth;
import com.entity.DiscussionBoard;
import com.entity.view.DiscussionBoardView;
import com.service.DiscussionService;
import com.utils.PageResult;
import com.utils.ResponseResult;
import com.utils.SecurityUtil;

/**
 * 掲示板コントローラ
 */
@RestController
@RequestMapping("/api/discussions")
public class DiscussionController {
    
    @Autowired
    private DiscussionService discussionService;
    
    /**
     * 管理者向け掲示板リスト
     */
    @GetMapping("/admin/list")
    public ResponseResult getAdminList(
            @RequestParam Map params, 
            DiscussionBoard discussion,
            HttpServletRequest request) {
        
        // 管理者以外の場合はユーザーIDでフィルタ
        if (!"admin".equals(request.getSession().getAttribute("role"))) {
            discussion.setUserId(SecurityUtil.getCurrentUserId());
        }
        
        EntityWrapper<DiscussionBoard> wrapper = new EntityWrapper<>();
        PageResult<DiscussionBoard> pageResult = discussionService.queryPage(
            params, 
            discussionService.buildSortWrapper(wrapper, discussion, params)
        );
        
        return ResponseResult.success(pageResult);
    }
    
    /**
     * 一般ユーザー向け掲示板リスト
     */
    @GetMapping("/user/list")
    public ResponseResult getUserList(
            @RequestParam Map params, 
            DiscussionBoard discussion,
            HttpServletRequest request) {
        
        // 管理者以外の場合はユーザーIDでフィルタ
        if (!"admin".equals(request.getSession().getAttribute("role"))) {
            discussion.setUserId(SecurityUtil.getCurrentUserId());
        }
        
        EntityWrapper<DiscussionBoard> wrapper = new EntityWrapper<>();
        PageResult<DiscussionBoard> pageResult = discussionService.queryPage(
            params, 
            discussionService.buildSortWrapper(wrapper, discussion, params)
        );
        
        return ResponseResult.success(pageResult);
    }
    
    /**
     * 公開掲示板リスト(認証不要)
     */
    @GetMapping("/public/list")
    public ResponseResult getPublicList(
            @RequestParam Map params, 
            DiscussionBoard discussion) {
        
        EntityWrapper<DiscussionBoard> wrapper = new EntityWrapper<>();
        PageResult<DiscussionBoard> pageResult = discussionService.queryPage(
            params, 
            discussionService.buildSortWrapper(wrapper, discussion, params)
        );
        
        return ResponseResult.success(pageResult);
    }
    
    /**
     * 掲示板詳細取得
     */
    @GetMapping("/{id}")
    public ResponseResult<DiscussionBoardView> getDiscussionDetail(@PathVariable Long id) {
        DiscussionBoardView discussionView = discussionService.selectViewById(id);
        if (discussionView == null) {
            return ResponseResult.error("掲示板が見つかりません");
        }
        return ResponseResult.success(discussionView);
    }
    
    /**
     * 掲示板作成
     */
    @PostMapping
    public ResponseResult<Void> createDiscussion(@RequestBody DiscussionBoard discussion, 
                                               HttpServletRequest request) {
        discussion.setId(System.currentTimeMillis() + new Random().nextInt(1000));
        discussion.setUserId(SecurityUtil.getCurrentUserId());
        discussion.setCreateTime(LocalDateTime.now());
        
        discussionService.insert(discussion);
        return ResponseResult.success();
    }
    
    /**
     * 掲示板更新
     */
    @PutMapping("/{id}")
    @Transactional
    public ResponseResult<Void> updateDiscussion(@PathVariable Long id, 
                                               @RequestBody DiscussionBoard discussion) {
        DiscussionBoard existingDiscussion = discussionService.selectById(id);
        if (existingDiscussion == null) {
            return ResponseResult.error("掲示板が見つかりません");
        }
        
        discussion.setId(id);
        discussion.setUpdateTime(LocalDateTime.now());
        discussionService.updateById(discussion);
        
        return ResponseResult.success();
    }
    
    /**
     * 掲示板削除
     */
    @DeleteMapping
    public ResponseResult<Void> deleteDiscussions(@RequestBody List<Long> ids) {
        discussionService.deleteBatchIds(ids);
        return ResponseResult.success();
    }
    
    /**
     * 子掲示板取得(階層構造)
     */
    @GetMapping("/{id}/children")
    public ResponseResult<DiscussionBoard> getChildDiscussions(@PathVariable Long id) {
        DiscussionBoard parentDiscussion = discussionService.selectById(id);
        if (parentDiscussion == null) {
            return ResponseResult.error("親掲示板が見つかりません");
        }
        
        List<DiscussionBoard> children = discussionService.selectList(
            new EntityWrapper<DiscussionBoard>().eq("parentId", id)
        );
        
        parentDiscussion.setChildren(children);
        buildDiscussionTree(parentDiscussion);
        
        return ResponseResult.success(parentDiscussion);
    }
    
    /**
     * 掲示板ツリー構築(再帰)
     */
    private void buildDiscussionTree(DiscussionBoard discussion) {
        List<DiscussionBoard> children = discussion.getChildren();
        if (children == null || children.isEmpty()) {
            return;
        }
        
        for (DiscussionBoard child : children) {
            List<DiscussionBoard> grandChildren = discussionService.selectList(
                new EntityWrapper<DiscussionBoard>().eq("parentId", child.getId())
            );
            child.setChildren(grandChildren);
            buildDiscussionTree(child);
        }
    }
    
    /**
     * リマインダーAPI
     */
    @GetMapping("/remind/{fieldName}/{type}")
    public ResponseResult<Integer> getRemindCount(
            @PathVariable String fieldName, 
            @PathVariable String type,
            @RequestParam Map params) {
        
        params.put("field", fieldName);
        params.put("type", type);
        
        Wrapper<DiscussionBoard> wrapper = discussionService.buildRemindWrapper(params);
        int count = discussionService.selectCount(wrapper);
        
        return ResponseResult.success(count);
    }
}

システム機能テスト

本システムはまずローカルサーバーにインストールされテストが行われました。その後、システムの構造と処理方法を十分に理解し、システムの特性を深く把握した上で、ホワイトボックステストとブラックボックステストが実施されました。

ソフトウェアシステム開発の全過程において、開発者は複雑な実際の問題に直面しなければならず、そのためソフトウェアシステムのライフサイクルの各過程で必然的にエラーが発生します。ソフトウェアテストの目的は、エラーを発見しプログラムを実行するステップを実行することであり、その最も重要な目的は、まだ発生していないエラーを見つけることです。

合理的なテスト計画を策定するため、以下の原則に基づいて測定が開始されました:いかなるテストも顧客のニーズに遡ること;顧客使用モデルが確定したらテスト計画を策定し、コーディング前にソフトウェアテスト全体の計画と策定を行うこと;パレトの原理を適用し、エラーの89%以上を占める約20%のテンプレートに重点的にテストを実施し、小規模から段階的に大規模なテストを実施すること。範囲は主に単一プログラムテンプレートのテストから完全に統合されたテンプレートまで;同時にテスト方法を精巧に設計し、可能な限りすべてのプログラムロジックを包括的にカバーし、要求を満たす能力を確保しました。

結論

本システムは、私が調査した他の体育館管理システムと比較して以下の長所があります:機能が全面的で、将来的なプログラム更新が容易、データベース管理が簡単、インターフェースが親しみやすく、操作が便利、効率が高く、セキュリティが良好です。

本システムの技術面での長所は以下の通りです:第一に、Javaを使用して動的ページを実現し、ソフトウェアシステムに優れた保守性と再利用性を備えました。次に、本システムの作成プロセスで使用されているSpring Bootフレームワークは、表示機能とロジックをより効果的に分離し、モジュールの管理を容易にし、特に大規模プロジェクトの作成に適しています。第三に、バックエンドで使用されているMySQLデータベースシステムは、主にXML標準のサポートに集中し、同時に拡張性が高く、応用が容易でセキュリ性が高い長所を持っています。

総じて言えば、ゼロからの卒業設計は全く新しい始まりであり、円満な終了を期待しています。また、この設計が後期の自主学習を通じてより完璧なものとなり、私の自主的な創作経験となることを願っています。

タグ: Spring Boot Vue Java MySQL ウェブアプリケーション

7月2日 19:07 投稿