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);
}
}