Spring Boot と Vue.js を基盤とした微格教学動画アノテーションシステムの設計と実装

システム概要

微格教学動画アノテーションシステムは、教育訓練における動画データの解析、評価、管理を効率化するために設計されたプラットフォームです。本システムは、バックエンドに Spring Boot、フロントエンドに Vue.js および uniapp を採用し、クロスプラットフォームでの動作と高い拡張性を実現しています。

技術スタック

バックエンド:Spring Boot

Spring Boot は、Tomcat や Jetty などのサーバーを内蔵しており、別途サーバー環境を構築する必要がありません。主な特徴である自動設定機能により、依存関係に基づいてアプリケーション設定を自動で行うため、開発者は設定作業に割く時間を大幅に削減できます。また、Spring Data や Spring Security などの豊富な Starter を提供しており、高品質なアプリケーションを迅速に構築・拡張することが可能です。

フロントエンド:Vue.js

Vue.js は仮想 DOM を核心技術として採用しており、メモリ上の数据结构を用いて効率的な DOM 操作を実現します。反応型データバインディングとコンポーネント化により、データ変更時に UI が自動的に更新されるため、開発者はロジック実装に集中できます。これにより、保守性が高く柔軟な開発モードを提供します。

持久層:MyBatis-Plus

MyBatis-Plus は MyBatis の機能を拡張した OSS フレームワークであり、MySQL や PostgreSQL など複数のデータベースをサポートします。豊富な API とアノテーションにより ORM 操作を簡素化し、SQL 記述量を削減します。コード生成器により Entity や Mapper を自動生成できるほか、ページネーションや楽観鎖などの機能も標準で提供されています。

システムテスト

システムテストは、品質保証および要件定義との整合性を確認する重要な工程です。ユーザー視点に立ち、様々なシナリオを通じて欠陥を発見・修正することで、運用時の安定性を確保します。

ログイン機能テスト

認証機能において、入力値の検証および権限チェックが正しく動作するかを確認します。境界値テストおよび必須項目の検証を含みます。

入力データ 期待結果 実際結果 分析
ID: admin, Pass: 123456, Captcha: 正 ログイン成功 システム進入可 一致
ID: admin, Pass: wrong, Captcha: 正 パスワードエラー 再入力_prompt 一致
ID: admin, Pass: 123456, Captcha: 誤 验证码エラー 验证码無効 一致
ID: 空, Pass: 123456, Captcha: 正 必須項目エラー ID 入力要求 一致

ユーザー管理機能テスト

ユーザー情報の CRUD 操作における整合性テストを行います。重複登録防止や削除確認ダイアログの動作検証を含みます。

操作内容 期待結果 実際結果 分析
新規ユーザー登録 リストに表示 表示確認可 一致
情報編集 変更内容反映 更新完了 一致
ユーザー削除 確認後削除 データ消失 一致
重複 ID 登録 エラー表示 重複警告 一致

核心実装コード

認証およびトークン管理に関する主要な実装コードを示します。セキュリティ確保のため、インターセプターによる権限検証を実装しています。

認証コントローラー

<!-- 認証エンドポイント -->
@PublicAccess
@PostMapping("/authenticate")
public ApiResponse signIn(@RequestParam String accountId, 
                          @RequestParam String credential, 
                          HttpServletRequest req) {
    UserAccount account = accountService.findByAccountId(accountId);
    if (account == null || !PasswordUtil.matches(credential, account.getPassHash())) {
        return ApiResponse.fail("認証失敗:アカウントまたはパスワードが無効です");
    }
    String sessionKey = authManager.createSession(account.getId(), accountId, account.getRole());
    return ApiResponse.success().data("authKey", sessionKey);
}

トークン生成サービス

<!-- セッションキー生成ロジック -->
public String createSession(Long uid, String name, String roleType) {
    AuthSession existing = repo.findByUserIdAndRole(uid, roleType);
    String key = UUID.randomUUID().toString().replace("-", "");
    Date expiry = new Date(System.currentTimeMillis() + 3600000);
    if (existing != null) {
        existing.setKey(key);
        existing.setExpiryDate(expiry);
        repo.update(existing);
    } else {
        repo.save(new AuthSession(uid, name, roleType, key, expiry));
    }
    return key;
}

セキュリティインターセプター

<!-- 権限検証インターセプター -->
@Component
public class SecurityContextInterceptor implements HandlerInterceptor {

    private static final String AUTH_HEADER_KEY = "X-Auth-Key";

    @Autowired
    private AuthManager authManager;
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
        
        if (RequestMethod.OPTIONS.name().equals(request.getMethod())) {
            response.setStatus(HttpStatus.OK.value());
            return false;
        }
        
        PublicAccess annotation = null;
        if (handler instanceof HandlerMethod) {
            annotation = ((HandlerMethod) handler).getMethodAnnotation(PublicAccess.class);
        }
        
        if (annotation != null) {
            return true;
        }
        
        String key = request.getHeader(AUTH_HEADER_KEY);
        AuthSession session = null;
        if (StringUtils.isNotBlank(key)) {
            session = authManager.validateSession(key);
        }
        
        if (session != null) {
            request.getSession().setAttribute("uid", session.getUserId());
            request.getSession().setAttribute("role", session.getRole());
            return true;
        }
        
        response.setContentType("application/json; charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.print(JSONObject.toJSONString(ApiResponse.fail(401, "ログインが必要です")));
        writer.close();
        return false;
    }
}

データベース設計

トークンおよびセッション管理用のテーブル構造です。ユーザー ID、役割、有効期限などを保持します。

-- ----------------------------
-- Table structure for system_token
-- ----------------------------
DROP TABLE IF EXISTS `system_token`;
CREATE TABLE `system_token` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'primary_key',
  `userid` bigint(20) NOT NULL COMMENT 'user_id',
  `username` varchar(100) NOT NULL COMMENT 'account_name',
  `tablename` varchar(100) DEFAULT NULL COMMENT 'target_table',
  `role` varchar(100) DEFAULT NULL COMMENT 'user_role',
  `token` varchar(200) NOT NULL COMMENT 'session_key',
  `addtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'created_at',
  `expiratedtime` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT 'expires_at',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='auth_session_table';

-- ----------------------------
-- Records of system_token
-- ----------------------------
INSERT INTO `system_token` VALUES ('9', '23', 'cd01', 'xuesheng', '学生', 'al6svx5qkei1wljry5o1npswhdpqcpcg', '2023-02-23 21:46:45', '2023-03-15 14:01:36');
INSERT INTO `system_token` VALUES ('12', '1', 'admin', 'users', '管理者', 'h1pqzsb9bldh93m92j9m2sljy9bt1wdh', '2023-02-27 19:37:01', '2023-03-17 18:23:02');

タグ: spring-boot vue.js MyBatis-Plus video-annotation java-web

9月11日 06:51 投稿