家計簿アプリケーション開発におけるScrum進捗:UI統合とユーザー管理機能の実装

1. 進捗状況報告

前日の成果

データ視覚化機能として、棒グラフ、折線グラフ、および円グラフを用いた分析画面の基本実装を完了しました。

本日の作業計画

モジュール 実装機能 担当者 予定時間
メインUI基盤 コード統合およびデバッグ Member A 5時間
デザインUI メインインターフェースのビジュアル調整 Member A 2時間
検索モジュール クエリ検索機能の実装 Member B 2時間
データ表示 明細リストのレンダリング Member C 2時間
プロファイル ユーザー情報管理画面 Member D 5時間

直面した課題

統合フェーズにおいて、一部のメンバーによる細かな修正のコミット漏れが発生し、ビルドエラーに繋がる事象が発生しました。現在は修正パスを特定し、同期を完了させています。

2. 実装コードの抜粋

支出カテゴリの管理ロジック

支出カテゴリを追加・削除するためのイベントハンドラです。JavaFXのListViewとDAOパターンを組み合わせています。

/**
 * 新しい支出カテゴリを登録する
 */
public void handleCategoryRegistration(ActionEvent actionEvent) {
    String inputLabel = categoryInputField.getText();

    if (inputLabel == null || inputLabel.trim().isEmpty()) {
        UIHelper.showAlert(Alert.AlertType.WARNING, "入力エラー", "カテゴリ名を入力してください。");
        return;
    }

    // UIリストへの反映
    categoryListView.getItems().add(inputLabel);
    
    // 永続化層への保存
    CategoryEntity newCategory = new CategoryEntity(inputLabel, "EXPENSE");
    boolean isSaved = categoryRepo.save(newCategory);
    
    if (isSaved) {
        categoryInputField.clear();
    }
}

/**
 * 選択された支出カテゴリを削除する
 */
public void processCategoryRemoval(ActionEvent actionEvent) {
    String targetCategory = (String) categoryListView.getSelectionModel().getSelectedItem();
    
    boolean confirm = UIHelper.showConfirmDialog("削除確認", targetCategory + " を削除してもよろしいですか?");
    
    if (confirm) {
        categoryListView.getItems().remove(targetCategory);
        categoryRepo.remove(new CategoryEntity(targetCategory, "EXPENSE"));
    }
}

インプレース編集機能

ListViewのセルを編集可能にし、変更内容をデータベースに同期させます。

public void enableInlineEditing(ActionEvent actionEvent) {
    categoryListView.setCellFactory(TextFieldListCell.forListView());
    categoryListView.setEditable(true);
}

public void onEditFinished(ListView.EditEvent<String> event) {
    String oldVal = event.getSource().getSelectionModel().getSelectedItem();
    String newVal = event.getNewValue();
    
    ObservableList<String> items = categoryListView.getItems();
    int index = items.indexOf(oldVal);
    if (index != -1) {
        items.set(index, newVal);
    }
    
    categoryRepo.updateCategoryName(newVal, oldVal);
}

ユーザープロフィールとセキュリティ管理

ユーザー情報の読み込みと、MD5ハッシュを用いたパスワード更新処理を実装しています。

/**
 * プロフィール情報の初期化
 */
public void setupProfile() {
    UserAccount currentUser = accountRepo.findById(AuthSession.getUserId());
    
    profileImageView.setImage(new Image("file:" + currentUser.getAvatarPath()));
    userNameField.setText(currentUser.getDisplayName());
    userIdDisplay.setText(String.valueOf(currentUser.getId()));
}

/**
 * パスワード変更の実行
 */
public void executePasswordChange(ActionEvent event) {
    String rawPwd = newPasswordField.getText();
    String confirmPwd = confirmField.getText();

    if (rawPwd.isEmpty() || !rawPwd.equals(confirmPwd)) {
        UIHelper.showError("バリデーションエラー", "パスワードが一致しないか、空です。");
        return;
    }

    String hashedPwd = SecurityUtils.toMD5(rawPwd);
    UserAccount updatedAccount = new UserAccount(
        AuthSession.getUserId(), 
        AuthSession.getUserName(), 
        hashedPwd, 
        AuthSession.getAvatarPath()
    );

    if (accountRepo.update(updatedAccount)) {
        UIHelper.showInfo("成功", "パスワードが更新されました。");
        passwordChangePanel.setVisible(false);
    }
}

アバター画像の変更処理

FileChooserを利用してローカルの画像ファイルを選択し、パスをエスケープ処理した上でDBに保存します。

public void handleAvatarUpdate(MouseEvent event) {
    FileChooser picker = new FileChooser();
    picker.getExtensionFilters().add(new FileChooser.ExtensionFilter("画像ファイル", "*.jpg", "*.png"));
    
    File selectedFile = picker.showOpenDialog(null);
    if (selectedFile != null) {
        String absolutePath = selectedFile.getAbsolutePath();
        // パスのエスケープ処理
        String sanitizedPath = absolutePath.replace("\\", "\\\\");
        
        profileImageView.setImage(new Image("file:" + absolutePath));
        
        UserAccount user = AuthSession.getUser();
        user.setAvatarPath(sanitizedPath);
        accountRepo.update(user);
    }
}

タグ: JavaFX Java Scrum MVC database

8月22日 02:43 投稿