フロント側をFlutter(スマホ)Thymeleaf(PC)、バックエンド側SpringBootの自動作成勉強中
14:50
①今日は、大分類が変更されたらHTTPで中分類を取ってくる、中分類が変更されたらHTTPで小分類を取ってくるっていうあたりの続きをやってる。SpringBootバックエンド側のエレメントを取ってきてるし、エラーの時はエラーも表示できてる。出来上がりってところ。
①-1. SpringBootバックエンド側で入力エラー(例えば、大分類の未入力エラー)のとき、スマホへの返却に『late File? gazoFlFile; // 画像File』など、ファイル項目があると、バックエンドからFileをJSONで戻そうとして、例外が発生した。ので、Fileは戻さないようにした。(通常のWEB画面でも入力エラーの項目があると、ファイル入力はなかったことになるので同じ感じになる)
でも、ちょっとひねって、”『画像』を再度選び直してください。”ってエラーメッセージ欄に表示するようにした。
①-2. 出来たものは、こんな感じ。
画像情報登録を表示した
大分類をクリックし、内容を表示した
「B・・・」を選択した。HTTPで中分類を取ってドロップダウンリストに設定してる
中分類をクリックし、HTTPで取得した内容を表示した
「03・・・」を選択した。HTTPで小分類を取ってドロップダウンリストに設定してる
小分類をクリックし、HTTPで取得した内容を表示した
「031・・・」を選択した
登録ボタンを押下した、『画像』を必須入力としたので、エラーが出る
『画像』『ファイル』を選択し、『小分類』を未入力にし、登録ボタンを押下する。
『小分類』が未入力のため、エラーになる。『画像』も再入力になってしまうので”『画像』を再度選び直してください。”、『ファイル』も再入力になってしまうので”『ファイル』を再度選び直してください。”をエラーメッセージ欄に表示するようにした。
今度は、『画像』を選択し、『ファイル』は選択しないで、『小分類』を未入力にし、登録ボタンを押下する。
『小分類』が未入力のため、エラーになる。『画像』も再入力になってしまうので”『画像』を再度選び直してください。”、『ファイル』は入力されていなかったのでエラーメッセージはない。
必須入力項目を埋めて登録ボタンを押下する
SpringBootバックエンド側から、登録OKメッセージが返ってきた
16:00
①スマホ側Flutterはこんな感じ
■gazo_register_amend.dart
class _GazoRegisterAmendState extends State<GazoRegisterAmend> { ・・・ Widget build(BuildContext context) { ・・・ List<Widget> _makeWidgets() { ・・・ //--ドロップダウンリスト選択 start---------------------------- contentWidgets.add(Container( margin: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 5), padding: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 0), decoration: CommUtils.commBoxDecoration(), //Wrapによって折り返しするが、SizeBox使って左寄せする child: SizedBox( width: double.infinity, child:Wrap( direction: Axis.horizontal, children: <Widget>[ const Text("大分類", overflow: TextOverflow.clip, ), DropdownButton( items: CommUtils.getItemsFromELEMENTS(_gazoForm.publicDbELEMENTS(_gazoForm.dbELEMENTS, 'categoryCd', '---未選択---'), _gazoForm.categoryCd), value: _gazoForm.categoryCd, icon: const Icon(Icons.arrow_downward), elevation: 16, style: const TextStyle(color: Colors.deepPurple), underline: Container( height: 2, color: Colors.deepPurpleAccent, ), onChanged: (value) async { if (_gazoForm.categoryCd != value as String) { // httpアクセス Map<String, dynamic>? _dbELEMENTS = await CommUtils .createLrgMidChange( context, value, '', 'subcategoryCd', '2', '/elements/elem', 'ary_lrgmidsml_category', _gazoForm.dbELEMENTS, widget.headers, widget.cookies); setState(() { _gazoForm.categoryCd = value; _gazoForm.subcategoryCd = ''; _gazoForm.extracategoryCd = ''; _gazoForm.dbELEMENTS = _dbELEMENTS; }); } }, ), Text( // HTTPで返却されたエラーメッセージを表示する。(エラーなしはText高=0にしている) _gazoForm.gazoErrForm.categoryCdErr.join("\n"), textAlign: TextAlign.left, overflow: TextOverflow.clip, style: TextStyle(height:(_gazoForm.gazoErrForm.categoryCdErr==[])?0:1.2, fontSize: 12, fontWeight: FontWeight.normal, color: Colors.red), ), ]), ), ), ); contentWidgets.add(Container( margin: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 5), padding: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 0), decoration: CommUtils.commBoxDecoration(), //Wrapによって折り返しするが、SizeBox使って左寄せする child: SizedBox( width: double.infinity, child:Wrap( direction: Axis.horizontal, children: <Widget>[ const Text("中分類", overflow: TextOverflow.clip, ), DropdownButton( items: CommUtils.getItemsFromELEMENTS(_gazoForm.publicDbELEMENTS(_gazoForm.dbELEMENTS, 'subcategoryCd', '---未選択---'), _gazoForm.subcategoryCd), value: _gazoForm.subcategoryCd, icon: const Icon(Icons.arrow_downward), elevation: 16, style: const TextStyle(color: Colors.deepPurple), underline: Container( height: 2, color: Colors.deepPurpleAccent, ), onChanged: (value) async { if (_gazoForm.subcategoryCd != value as String) { // httpアクセス Map<String, dynamic>? _dbELEMENTS = await CommUtils .createLrgMidChange( context, _gazoForm.categoryCd, value, 'extracategoryCd', '2', '/elements/elem', 'ary_lrgmidsml_category', _gazoForm.dbELEMENTS, widget.headers, widget.cookies); setState(() { _gazoForm.subcategoryCd = value; _gazoForm.extracategoryCd = ''; _gazoForm.dbELEMENTS = _dbELEMENTS; }); } }, ), Text( // HTTPで返却されたエラーメッセージを表示する。(エラーなしはText高=0にしている) _gazoForm.gazoErrForm.subcategoryCdErr.join("\n"), textAlign: TextAlign.left, overflow: TextOverflow.clip, style: TextStyle(height:(_gazoForm.gazoErrForm.subcategoryCdErr==[])?0:1.2, fontSize: 12, fontWeight: FontWeight.normal, color: Colors.red), ), ]), ), ), ); contentWidgets.add(Container( margin: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 5), padding: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 0), decoration: CommUtils.commBoxDecoration(), //Wrapによって折り返しするが、SizeBox使って左寄せする child: SizedBox( width: double.infinity, child:Wrap( direction: Axis.horizontal, children: <Widget>[ const Text("小分類", overflow: TextOverflow.clip, ), DropdownButton( items: CommUtils.getItemsFromELEMENTS(_gazoForm.publicDbELEMENTS(_gazoForm.dbELEMENTS, 'extracategoryCd', '---未選択---'), _gazoForm.extracategoryCd), value: _gazoForm.extracategoryCd, icon: const Icon(Icons.arrow_downward), elevation: 16, style: const TextStyle(color: Colors.deepPurple), underline: Container( height: 2, color: Colors.deepPurpleAccent, ), onChanged: (value) { setState(() { _gazoForm.extracategoryCd= value as String; }); }, ), Text( // HTTPで返却されたエラーメッセージを表示する。(エラーなしはText高=0にしている) _gazoForm.gazoErrForm.extracategoryCdErr.join("\n"), textAlign: TextAlign.left, overflow: TextOverflow.clip, style: TextStyle(height:(_gazoForm.gazoErrForm.extracategoryCdErr==[])?0:1.2, fontSize: 12, fontWeight: FontWeight.normal, color: Colors.red), ), ]), ), ), ); //--ドロップダウンリスト選択 start---------------------------- ・・・ /// 情報登録、情報変更、情報一覧へのhttpアクセス void httpForInfo(String _modeBase) async { try { String _mode; http.Response response; if (_modeBase == 'list_back') { ・・・ } else { _mode = (_gazoForm.messageForm.mode == 'ins' || _gazoForm.messageForm.mode == 'ins_do')?'ins_do':'upd_do'; widget.headers["content-type"]= "multipart/form-data"; widget.headers["X-XSRF-TOKEN"]= "${widget.cookies['XSRF-TOKEN']}"; final url = Uri.parse("${Consts.myHttpUrl}/members/admin/gazo/gazo/" + _mode); var request = http.MultipartRequest("POST", url); request.fields['formComm.page'] = _gazoForm.messageForm.page.toString(); if (_gazoForm.gazoFlFile != null) { final mimeTypeData = lookupMimeType( _gazoForm.gazoFlFile!.path, headerBytes: [0xFF, 0xD8])!.split('/'); final file = await http.MultipartFile.fromPath( 'gazoForm.gazoFlFile', _gazoForm.gazoFlFile!.path, contentType: MediaType(mimeTypeData[0], mimeTypeData[1])); request.files.add(file); } if (_gazoForm.fileFile != null) { final mimeTypeData = lookupMimeType( _gazoForm.fileFile!.path, headerBytes: [0xFF, 0xD8])!.split('/'); final file = await http.MultipartFile.fromPath( 'gazoForm.fileFile', _gazoForm.fileFile!.path, contentType: MediaType(mimeTypeData[0], mimeTypeData[1])); request.files.add(file); } request.fields['gazoForm.categoryCd'] = _gazoForm.categoryCd; request.fields['gazoForm.subcategoryCd'] = _gazoForm.subcategoryCd; request.fields['gazoForm.extracategoryCd'] = _gazoForm.extracategoryCd; request.headers.addAll(widget.headers); final streamedResponse = await request.send(); response = await http.Response.fromStream(streamedResponse); } if (response.statusCode != 200) { setState(() { int statusCode = response.statusCode; if (response.statusCode == 401 || response.statusCode == 403) { _errorSuccessMsg = "ログインしてください"; } else { _errorSuccessMsg = "エラーが発生しました $statusCode"; } }); // 画面を先頭に戻す _scrollController.animateTo(0, duration: const Duration(milliseconds:600), curve: Curves.easeInQuint); return; } CommUtils.updateCookie(response, widget.cookies, widget.headers); // response.bodyをutf8でdecodeする。 String _resData = utf8.decode(response.body.runes.toList()); if (kDebugMode) { print(_resData); } // バックエンドで例外発生の場合MessageFormの値しか戻らないため、ここで確認する MessageForm _messageForm = MessageForm.initData(); _messageForm.fromJson(_resData); // SpringBootで例外発生の場合 if (_messageForm.mode =="SystemError") { // エラー画面 Navigator.of(context).push( MaterialPageRoute( builder: (context) => Error(title: widget.title, username: widget.username, headers: widget.headers, cookies: widget.cookies, resData: _resData), ), ); // 正常処理 } else { if (_messageForm.mode == 'list_back') { ・・・ } else { setState(() { _gazoForm.fromJson(_resData); _errorSuccessMsg = _gazoForm.messageForm.errorMessage + _gazoForm.messageForm.successMessage; if (_errorSuccessMsg == '' && _gazoForm.messageForm.itemErrorMessages != '') { _errorSuccessMsg = '項目エラーを確認してください'; } }); } } } catch (e) { setState(() { _errorSuccessMsg = "エラーが発生しました" + e.toString(); }); } // 画面を先頭に戻す _scrollController.animateTo(0, duration: const Duration(milliseconds:600), curve: Curves.easeInQuint); } @override void initState() { super.initState(); // displayText = ""; // 画面初期データを設定する _gazoForm = GazoForm.initData(); _gazoForm.fromJson(widget.resData); _scrollController= ScrollController(); if (_gazoForm.messageForm.mode == 'ins' || _gazoForm.messageForm.mode == 'ins_do') { _thisTitle = '画像情報登録'; } else { _thisTitle = '画像情報更新'; } }
■comm_utils.dart
/// ELEMENTSキー毎のコード一覧(LinkedHashMap)返却 /// str ELEMENTSのキー /// dflt ”▼選択してください"や"未選択"を設定する。設定しない場合""を指定する。 /// elements Elements.javaのエレメント又は、DBから読み込んだElements /// ELEMENTSキー毎のコード一覧(LinkedHashMap)を返却 static Map<Object, String> publicELEMENTS(String str, String dflt, Map<String, dynamic>? elements) { Map<String, String> map = <String, String>{}; if (dflt.isNotEmpty ) { map.addAll({"" : dflt}); } if (elements != null) { Map<String, String> dynamicMap = Map<String, String>.from(elements[str]); map.addAll(dynamicMap); } return map; } /// エレメントのドロップダウンリストを作成する static List<DropdownMenuItem<String>> getItemsFromELEMENTS(Map<Object, String> elements, String? selectItem) { List<DropdownMenuItem<String>> _items = <DropdownMenuItem<String>>[]; elements.forEach((key, value) { if (selectItem != null && selectItem == key as String) { _items.add(DropdownMenuItem( value: key, child: Text(value, style: const TextStyle(fontWeight: FontWeight.normal, color: Colors.black, backgroundColor: Colors.yellow)), )); } else { _items.add(DropdownMenuItem( value: key as String, child: Text(value, style: const TextStyle(fontWeight: FontWeight.normal, color: Colors.black)), )); } }); return _items.toList(); } ///大分類、中分類のチェンジ関数。DBデータをpostで取得して、dbELEMENTSを戻す。 /// 大分類及び中分類のchange関数。 /// 補足:大分類が選択された時は中分類がある場合、中分類が選択された時は小分類がある場合のみ使用する。 /// (選択されたテーブルに、子テーブルがない場合使用しない。子テーブルが無いと、エレメントが取れないので。) /// 中分類の値が空白の時、中分類のELEMENTS、以外は小分類のELEMENTSを作成する。 /// _context : BuildContext /// lrgValue : 大分類の値 /// midValue : 中分類の値(大分類が選択された時は空白、中分類が選択された時は値を入れる) /// midSmlTableId : 中分類の値が空白の時、中分類のテーブルID、以外は小分類のテーブルIDを設定する。 /// inopenkbn : '1'非公開を含む、'2'非公開を含まない /// urlArg : ajax先のURL /// fmElementNm : エレメント名 /// dbELEMENTS : 各フォームのdbELEMENTS /// headers : ヘッダー /// cookies : クッキー static Future<Map<String, dynamic>?> createLrgMidChange(BuildContext context, String lrgValue, String midValue, String midSmlTableId, String inopenkbn, String urlArg, String fmElementNm, Map<String, dynamic>? dbELEMENTS, Map<String, String> headers, Map<String, String> cookies) async { http.Response response; if (dbELEMENTS == null) { return dbELEMENTS; } headers["content-type"]= "application/json; charset=UTF-8"; headers["X-XSRF-TOKEN"]= "${cookies['XSRF-TOKEN']}"; Map<String, String> data = {"fmElementNm":fmElementNm, "fmLrgKey":lrgValue, "fmMidKey":midValue, "inopenkbn":inopenkbn}; final url = Uri.parse(Consts.myHttpUrl + urlArg); response = await http.post(url, headers: headers, body: json.encode(data)); if (response.statusCode != 200) { // エラー await CommUtils.openDialogOkComm(context, "通信エラーが発生しました。"); } else { // response.bodyをutf8でdecodeする。 String _resData = utf8.decode(response.body.runes.toList()); Map<String, dynamic> _resDataMap = jsonDecode(_resData); dbELEMENTS[midSmlTableId] = _resDataMap; } return dbELEMENTS; }
■gazo_form.dart
import 'dart:convert'; import 'dart:io'; import '../utils/comm_utils.dart'; import 'comm/message_form.dart'; import 'gazo_err_form.dart'; /// Gazo情報画面Form class GazoForm { //プロパティ 入出力データ用 // late Map<String, Map<String, String>>? dbELEMENTS; // DBから作成するELEMENTS late Map<String, dynamic>? dbELEMENTS; // DBから作成するELEMENTS late String seqid; // seqid late String gazoFl; // 画像名 late String gazoFlDel; // 画像削除 late File? gazoFlFile; // 画像File late String file; // ファイル名 late String fileDel; // ファイル削除 late File? fileFile; // ファイルFile late String categoryCd; late String subcategoryCd; late String extracategoryCd; //プロパティ エラーメッセージ late GazoErrForm gazoErrForm; // プロパティ エラーメッセージ // 共通・メッセージ late MessageForm messageForm; // 共通・メッセージ /// コンストラクタ // Dartでは「コンストラクタ名.任意名称」で複数のコンストラクタを定義する // 「クラス名(this.name,・・・);」コンストラクタはいらないので, // これだけにする。 // 初期値のクラスを作成している GazoForm.initData() { //入出力データ用 dbELEMENTS = null; seqid = ""; gazoFl = ""; gazoFlDel = ""; gazoFlFile = null; file = ""; fileDel = ""; fileFile = null; categoryCd = ""; subcategoryCd = ""; extracategoryCd = ""; // プロパティ エラーメッセージ gazoErrForm = GazoErrForm.initData(); // 共通・メッセージ messageForm = MessageForm.initData(); } /// HTTPレスポンスデータを反映する fromJson(String resData) { Map<String,dynamic> _resDataMap = jsonDecode(resData); //入出力データ用 if (_resDataMap["gazoForm"]==null) { dbELEMENTS = null; seqid = ""; gazoFl = ""; gazoFlDel = ""; gazoFlFile = null; file = ""; fileDel = ""; fileFile = null; categoryCd = ""; subcategoryCd = ""; extracategoryCd = ""; } else { dbELEMENTS = _resDataMap["gazoForm"]["dbELEMENTS"]; seqid = _resDataMap["gazoForm"]["seqid"] ?? 0; gazoFl = _resDataMap["gazoForm"]["gazoFl"]; gazoFlDel = _resDataMap["gazoForm"]["gazoFlDel"]; gazoFlFile = _resDataMap["gazoForm"]["gazoFlFile"]; file = _resDataMap["gazoForm"]["file"]; fileDel = _resDataMap["gazoForm"]["fileDel"]; fileFile = _resDataMap["gazoForm"]["fileFile"]; categoryCd = _resDataMap["gazoForm"]["categoryCd"]; subcategoryCd = _resDataMap["gazoForm"]["subcategoryCd"]; extracategoryCd = _resDataMap["gazoForm"]["extracategoryCd"]; } // プロパティ エラーメッセージ gazoErrForm.fromJson(resData); // 共通・メッセージ messageForm.fromJson(resData); } /// HTTPレスポンスデータを作成する Map<String,dynamic> toJsonMap (String _mode, String _csrf, int page) { Map<String,dynamic> gazoFormMap = { 'dbELEMENTS': dbELEMENTS, 'seqid': seqid, 'gazoFl': gazoFl, 'gazoFlDel': gazoFlDel, 'gazoFlFile': gazoFlFile, 'file': file, 'fileDel': fileDel, 'fileFile': fileFile, 'categoryCd': categoryCd, 'subcategoryCd': subcategoryCd, 'extracategoryCd': extracategoryCd, }; Map<String,dynamic> rtnMap = { 'gazoForm': gazoFormMap, 'formComm': messageForm.toJsonMap(_mode, _csrf, page), }; return rtnMap; } /// http.postのbodyで使用するJsonデータを返却する String toJson(String _mode, String _csrf, [int page=1]) { return json.encode(toJsonMap(_mode, _csrf, page)); } /// publicDbELEMENTSメソッド /// ELEMENTS(DB値)から指定したキーのエレメントを戻す /// str ELEMENTSのキー /// dflt ”▼選択してください"や"未選択"を設定する。設定しない場合""を指定する。 /// ELEMENTSキー毎のコード一覧(LinkedHashMap)を戻す Map<Object, String> publicDbELEMENTS(Map<String, dynamic>? dbELEMENTS, String str, String dflt) { return CommUtils.publicELEMENTS(str, dflt, dbELEMENTS); } }
■gazo_err_form.dart
import 'dart:convert'; /// ユーザー情報画面プロパティ エラーメッセージ用Form class GazoErrForm { //プロパティ エラーメッセージ用 late List<String> seqidErr; late List<String> gazoFlFileErr; late List<String> checkGazoFlHissuErr; late List<String> checkGazoFlErr; late List<String> fileFileErr; late List<String> checkFileHissuErr; late List<String> checkFileErr; late List<String> categoryCdErr; late List<String> subcategoryCdErr; late List<String> extracategoryCdErr; /// コンストラクタ // Dartでは「コンストラクタ名.任意名称」で複数のコンストラクタを定義する // 「クラス名(this.name,・・・);」コンストラクタはいらないので, // これだけにする。 // 初期値のクラスを作成している GazoErrForm.initData() { //エラーメッセージ用 seqidErr = <String>[]; gazoFlFileErr = <String>[]; checkGazoFlHissuErr = <String>[]; checkGazoFlErr = <String>[]; fileFileErr = <String>[]; checkFileHissuErr = <String>[]; checkFileErr = <String>[]; categoryCdErr = <String>[]; subcategoryCdErr = <String>[]; extracategoryCdErr = <String>[]; } /// HTTPレスポンスデータを反映する fromJson(String resData) { Map<String,dynamic> _resDataMap = jsonDecode(resData); //エラーメッセージ用 if (_resDataMap["resErrorData"]==null) { seqidErr = <String>[]; gazoFlFileErr = <String>[]; checkGazoFlHissuErr = <String>[]; checkGazoFlErr = <String>[]; fileFileErr = <String>[]; checkFileHissuErr = <String>[]; checkFileErr = <String>[]; categoryCdErr = <String>[]; subcategoryCdErr = <String>[]; extracategoryCdErr = <String>[]; } else { seqidErr = _resDataMap["resErrorData"]["gazoForm.seqid"]!=null?_resDataMap["resErrorData"]["gazoForm.seqid"].cast<String>():<String>[]; gazoFlFileErr = _resDataMap["resErrorData"]["gazoForm.gazoFlFile"]!=null?_resDataMap["resErrorData"]["gazoForm.gazoFlFile"].cast<String>():<String>[]; checkGazoFlHissuErr = _resDataMap["resErrorData"]["gazoForm.checkGazoFlHissu"]!=null?_resDataMap["resErrorData"]["gazoForm.checkGazoFlHissu"].cast<String>():<String>[]; checkGazoFlErr = _resDataMap["resErrorData"]["gazoForm.checkGazoFl"]!=null?_resDataMap["resErrorData"]["gazoForm.checkGazoFl"].cast<String>():<String>[]; fileFileErr = _resDataMap["resErrorData"]["gazoForm.fileFile"]!=null?_resDataMap["resErrorData"]["gazoForm.fileFile"].cast<String>():<String>[]; checkFileHissuErr = _resDataMap["resErrorData"]["gazoForm.checkFileHissu"]!=null?_resDataMap["resErrorData"]["gazoForm.checkFileHissu"].cast<String>():<String>[]; checkFileErr = _resDataMap["resErrorData"]["gazoForm.checkFile"]!=null?_resDataMap["resErrorData"]["gazoForm.checkFile"].cast<String>():<String>[]; categoryCdErr = _resDataMap["resErrorData"]["gazoForm.categoryCd"]!=null?_resDataMap["resErrorData"]["gazoForm.categoryCd"].cast<String>():<String>[]; subcategoryCdErr = _resDataMap["resErrorData"]["gazoForm.subcategoryCd"]!=null?_resDataMap["resErrorData"]["gazoForm.subcategoryCd"].cast<String>():<String>[]; extracategoryCdErr = _resDataMap["resErrorData"]["gazoForm.extracategoryCd"]!=null?_resDataMap["resErrorData"]["gazoForm.extracategoryCd"].cast<String>():<String>[]; } } }
②バックエンド側SpringBootはこんな感じ
■GazoCommController.java
package com.kaz02u.demo.controller.adminComm; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.MessageSource; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.util.ObjectUtils; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.RequestMapping; import com.kaz02u.demo.entity.Gazo; import com.kaz02u.demo.fForm.FormComm; import com.kaz02u.demo.form.GazoForm; import com.kaz02u.demo.form.SessionGazoSrchForm; import com.kaz02u.demo.form.SessionGazoSrchOrderForm; import com.kaz02u.demo.response.ResData; import com.kaz02u.demo.service.DbElementsService; import com.kaz02u.demo.service.GazoService; import com.kaz02u.demo.service.SeqService; import com.kaz02u.demo.utils.AppProperties; import com.kaz02u.demo.utils.Consts; import com.kaz02u.demo.utils.Functions; import lombok.extern.slf4j.Slf4j; /** * GAZO管理のコントローラー * */ @Controller //log出力用 @Slf4j public class GazoCommController implements Consts{ /** * 一覧表示時の、ソート項目の項目名(Entityの項目名。テーブル項目名ではない) */ private static String[] sortItemNames = {"seqid", "gazoFl", "file"}; /** * PageableDefaultのsize(1画面中の表示レコード数)を指定 */ public static final int pageableDefaultSize = 10; /** * Gazoのseqid項目のsizeを指定 */ public static final int gazoSeqidLen = 11; @Autowired AppProperties appProperties; @Autowired Functions functions; @Autowired GazoService gazoService; @Autowired SeqService seqService; @Autowired SessionGazoSrchForm sessionGazoSrchForm; @Autowired SessionGazoSrchOrderForm sessionGazoSrchOrderForm; @Autowired MessageSource messageSource; @Autowired DbElementsService dbElementsService; /** * checkPkForGazoFormメソッド * 主キーをチェックする。(u:すべての主キーをチェックできる。 i:主キーにシーケンスが使われていないときチェックできる。) * * @param kbn i:挿入時チェック、u:更新時チェック * @param gazoForm フォーム * @param result result。指定しないときはnullとすること。 */ protected void checkPkForGazoForm(String kbn, GazoForm gazoForm, BindingResult result) { checkPkForGazoForm(kbn, gazoForm, result, ""); } /** * checkPkForGazoFormメソッド * 主キーをチェックする。(u:すべての主キーをチェックできる。 i:主キーにシーケンスが使われていないときチェックできる。) * * @param kbn i:挿入時チェック、u:更新時チェック * @param gazoForm フォーム * @param result result。指定しないときはnullとすること。 * @param prefixes リスト配列に当オブジェクトがある時のprefixes。指定しないときは空文字とすること。 */ protected void checkPkForGazoForm(String kbn, GazoForm gazoForm, BindingResult result, String prefixes) { //更新時チェック if (kbn.equals("u")) { //主キーがテーブルになければtrue if (gazoService.findByPk(gazoForm.getSeqid()) == null) { if (result != null) { result.rejectValue(prefixes + "seqid", "validation.already-deleted", new String[] {"『シーケンス(A+yymmdd+数字)』"}, ""); } } //挿入時チェック。主キーにシーケンスが使われていないときのみチェックする。 //主キーにシーケンスを使っているときは、ここではチェックせずに、挿入時の重複例外を使う。 } else { //主キーがテーブルにあればtrue if (gazoService.findByPk(gazoForm.getSeqid()) != null) { if (result != null) { result.rejectValue(prefixes + "seqid", "validation.already-registered", new String[] {"『シーケンス(A+yymmdd+数字)』"}, ""); } } } } //======================================================= // GAZO登録 //======================================================= /** * リターン共通処理(Flutter、PC・WEB共用) * リターン共通処理 * * @param url 遷移先 * @param model モデル * @param result チェック結果 * @param flutterFlg true:Flutter用 false:PC・WEB用 * @param resFormName レスポンスForm名 * @param page 検索以外の時のページ * @return Flutter用String:jsonデータ PC・WEB用 Map<String, Object>:遷移先 */ protected Object returnComm(String url ,Model model,BindingResult result, boolean flutterFlg, FormComm formComm, String resFormName, String srchFormName, String srchOrdeFormName) { if (flutterFlg) { Map<String, Object> modelMap = model.asMap(); if (resFormName.equals("gazoForm") && modelMap!=null && modelMap.get("gazoForm")!=null) { GazoForm gazoForm = (GazoForm)modelMap.get("gazoForm"); // MultipartFileの入力があった場合、再度選び直してもらうためのメッセージを出す。 if (modelMap.get("errorMessage") !=null) { if (gazoForm.getGazoFlFile() !=null) { if (result != null) { result.rejectValue("gazoForm.checkGazoFl", null, "『画像』を再度選び直してください。"); } // modelMap.put("errorMessage", modelMap.get("errorMessage") + "\n※ 画像、ファイルは選び直してください。" ); } if (gazoForm.getFileFile() != null) { if (result != null) { result.rejectValue("gazoForm.checkFile", null, "『ファイル』を再度選び直してください。"); } // modelMap.put("errorMessage", modelMap.get("errorMessage") + "\n※ 画像、ファイルは選び直してください。" ); } } // MultipartFileは返却しないのでnullにする。 gazoForm.setGazoFlFile(null); gazoForm.setFileFile(null); } ResData resData = new ResData(model, result); return resData.getResDataMap(messageSource, formComm, resFormName, srchFormName, srchOrdeFormName); } else { return url; } } /** * gazoInsSubメソッド * GAZO登録を表示する処理のサブモジュール * * @param model モード */ protected void gazoInsSub(Model model) { GazoForm gazoForm = new GazoForm(); //DBエレメントを取得する LinkedHashMap<String, Map<Object, String>> dbELEMENTS = new LinkedHashMap<String, Map<Object, String>>(); LinkedHashMap<String, Map<Object, String>> dbELEMENTS1; dbELEMENTS1 = dbElementsService.getDbEleAryLrgmidsmlCategory(false, null, true, null, true, "categoryCd", "subcategoryCd", "extracategoryCd"); dbELEMENTS.putAll(dbELEMENTS1); gazoForm.setDbELEMENTS(dbELEMENTS); model.addAttribute("gazoForm", gazoForm); model.addAttribute("mode", "ins"); } /** * GAZO登録処理(Flutter、PC・WEB共用) * GAZO登録するする共通処理 * * @param gazoForm GAZO登録 * @param result チェック結果 * @param mode モード * @param model モデル * @param flutterFlg true:Flutter用 false:PC・WEB用 * @return Flutter用:jsonデータ PC・WEB用:遷移先 */ protected Object gazoInsDoComm(GazoForm gazoForm, BindingResult result, Model model, boolean flutterFlg) { if (flutterFlg) { model.addAttribute("gazoForm", gazoForm); } //エラーになったときのモードを設定 model.addAttribute("mode", "ins"); //大分類、中分類、小分類をチェックし、DBエレメントを作成する checkAndEditLrgmidsmlForGazoForm(gazoForm, result); if (result.hasErrors()) { model.addAttribute("errorMessage", "エラーが発生しました"); model.addAttribute("itemErrorMessages", result.toString()); return returnComm("/members/admin/gazo/gazoRegister", model, result, flutterFlg, null, "gazoForm", null, null); } String seqid=""; List<String> inslist = new ArrayList<String>(); try { String mode = "ins_do"; //主キー作成 seqid = seqService.getDbSeq("gazo", gazoSeqidLen, "あり", 6); String itemName; if (gazoForm.getGazoFlFile() != null) { if (!ObjectUtils.isEmpty(gazoForm.getGazoFlFile().getOriginalFilename()) || !ObjectUtils.isEmpty(gazoForm.getGazoFlDel())) { itemName = functions.setAndDelUploadFile(gazoForm.getGazoFlFile(), "gazo", seqid, "gazoFl", mode, gazoForm.getGazoFlDel()); gazoForm.setGazoFl(itemName); if (!ObjectUtils.isEmpty(itemName)) { inslist.add("gazo/" + seqid + "/" + itemName); } } } if (gazoForm.getFileFile() != null) { if (!ObjectUtils.isEmpty(gazoForm.getFileFile().getOriginalFilename()) || !ObjectUtils.isEmpty(gazoForm.getFileDel())) { itemName = functions.setAndDelUploadFile(gazoForm.getFileFile(), "gazo", seqid, "file", mode, gazoForm.getFileDel()); gazoForm.setFile(itemName); if (!ObjectUtils.isEmpty(itemName)) { inslist.add("gazo/" + seqid + "/" + itemName); } } } } catch(Exception e){ e.printStackTrace(); log.error("エラーが発生しました", e); //エラーになったので、リストしたファイルを削除しておく functions.delFileList(inslist); throw e; } try { gazoService.register( seqid, gazoForm.getGazoFl(), gazoForm.getFile(), gazoForm.getCategoryCd(), gazoForm.getSubcategoryCd(), gazoForm.getExtracategoryCd()); } catch(Exception e){ e.printStackTrace(); log.error("DBエラーが発生しました", e); //エラーになったので、リストしたファイルを削除しておく functions.delFileList(inslist); throw e; } Gazo gazo = null; try { //メールのためにGAZOを取得する gazo = gazoService.findByPk(seqid); if (gazo == null) { log.error("gazo not found:pk seqid={}", seqid); throw new RuntimeException("invalid pk"); } gazoService.send(appProperties.getMailTo(), appProperties.getMailFrom(), "登録メール", gazo); } catch(Exception e){ e.printStackTrace(); log.error("メール送信でエラーが発生しました。(但し、GAZOは登録完了済みです)", e); model.addAttribute("errorMessage", "エラーが発生しました。(但し、GAZOは登録完了済みです)"); return returnComm("/members/admin/gazo/gazoRegister", model, result, flutterFlg, null, "gazoForm", null, null); } gazoInsSub(model); model.addAttribute("successMessage", "GAZO登録が完了しました"); return returnComm("/members/admin/gazo/gazoRegister", model, result, flutterFlg, null, "gazoForm", null, null); } /** * checkAndEditLrgmidsmlForGazoFormメソッド * 大分類、中分類、小分類をチェックし、DBエレメントを作成する * * @param gazoForm フォーム * @param result result。指定しないときはnullとすること。 */ protected void checkAndEditLrgmidsmlForGazoForm(GazoForm gazoForm, BindingResult result) { checkAndEditLrgmidsmlForGazoForm(gazoForm, result, ""); } /** * checkAndEditLrgmidsmlForGazoFormメソッド * 大分類、中分類、小分類をチェックし、DBエレメントを作成する * * @param gazoForm フォーム * @param result result。指定しないときはnullとすること。 * @param prefixes リスト配列に当オブジェクトがある時のprefixes。指定しないときは空文字とすること。 */ protected void checkAndEditLrgmidsmlForGazoForm(GazoForm gazoForm, BindingResult result, String prefixes) { boolean categoryCdFlg = true; boolean subcategoryCdFlg = true; boolean extracategoryCdFlg = true; //『大分類』がcategoryテーブルにあればtrue(削除されているものは取得しない) if (!dbElementsService.isCheckCategoryCd(gazoForm.getCategoryCd(), false)) { categoryCdFlg = false; if (result != null) { result.rejectValue(prefixes + "categoryCd", "validation.category", new String[] {"『大分類』"}, ""); } } //『中分類』がsubcategoryテーブルにあればtrue(削除されているものは取得しない) if (!dbElementsService.isCheckSubcategoryCd(gazoForm.getCategoryCd(), gazoForm.getSubcategoryCd(), false, categoryCdFlg)) { subcategoryCdFlg = false; if (result != null) { result.rejectValue(prefixes + "subcategoryCd", "validation.category", new String[] {"『中分類』"}, ""); } } //『小分類』がextracategoryテーブルにあればtrue(削除されているものは取得しない) if (!dbElementsService.isCheckExtracategoryCd(gazoForm.getCategoryCd(), gazoForm.getSubcategoryCd(), gazoForm.getExtracategoryCd(), false, subcategoryCdFlg)) { extracategoryCdFlg = false; if (result != null) { result.rejectValue(prefixes + "extracategoryCd", "validation.category", new String[] {"『小分類』"}, ""); } } //DBエレメントを取得する LinkedHashMap<String, Map<Object, String>> dbELEMENTS = new LinkedHashMap<String, Map<Object, String>>(); LinkedHashMap<String, Map<Object, String>> dbELEMENTS1; dbELEMENTS1 = dbElementsService.getDbEleAryLrgmidsmlCategory(false, gazoForm.getCategoryCd(), categoryCdFlg, gazoForm.getSubcategoryCd(), subcategoryCdFlg, "categoryCd", "subcategoryCd", "extracategoryCd"); dbELEMENTS.putAll(dbELEMENTS1); gazoForm.setDbELEMENTS(dbELEMENTS); } }
■GazoFlutterController.java
package com.kaz02u.demo.controller.adminFlutter; import java.util.LinkedHashMap; import java.util.Map; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.MultipartFile; import com.kaz02u.demo.controller.adminComm.GazoCommController; import com.kaz02u.demo.fForm.GazoFForm; import com.kaz02u.demo.form.GazoForm; import com.kaz02u.demo.validation.GroupOrder1; import com.kaz02u.demo.validation.GroupOrder2; import lombok.extern.slf4j.Slf4j; /** * Flutter用GAZO管理のコントローラー * */ @Controller //log出力用 @Slf4j public class GazoFlutterController extends GazoCommController{ //======================================================= // GAZO登録 //======================================================= /** * GAZO登録表示処理(Flutter用) * GAZO登録を表示する処理 * * @param model モデル * @return jsonデータ */ @SuppressWarnings("unchecked") @PostMapping("/members/admin/gazo/gazo/ins") @ResponseBody public Map<String, Object> gazoInsFlutter(Model model) { gazoInsSub(model); return (Map<String, Object>)returnComm("/members/admin/gazo/gazoRegister", model, null, true, null, "gazoForm", null, null); } /** * GAZO登録処理(Flutter用) * GAZO登録する処理 * * @param gazoFForm Flutter向けGAZO登録 * @param result チェック結果 * @param model モデル * @return jsonデータ */ @SuppressWarnings("unchecked") @PostMapping("/members/admin/gazo/gazo/ins_do") @ResponseBody public Map<String, Object> gazoInsDoFlutter(@Validated({GroupOrder1.class, GroupOrder2.class}) GazoFForm gazoFForm, BindingResult result, Model model) { return (Map<String, Object>)gazoInsDoComm(gazoFForm.getGazoForm(), result, model, true); } }
■GazoForm.java
package com.kaz02u.demo.form; import java.io.Serializable; import java.util.Date; import java.util.LinkedHashMap; import java.util.Map; import javax.validation.constraints.AssertTrue; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.stereotype.Component; import org.springframework.util.ObjectUtils; import org.springframework.web.multipart.MultipartFile; import com.kaz02u.demo.utils.Elements; import com.kaz02u.demo.utils.Functions; import com.kaz02u.demo.utils.ValidCheck; import com.kaz02u.demo.validation.CheckHalfLetterDigitBig; import com.kaz02u.demo.validation.CheckHalfStartEndSpace; import com.kaz02u.demo.validation.CheckHalf; import com.kaz02u.demo.validation.GroupOrder1; import com.kaz02u.demo.validation.GroupOrder2; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor @Component public class GazoForm implements Serializable { /** * */ private static final long serialVersionUID = 1L; /** * DBから作成するELEMENTS */ private LinkedHashMap<String, Map<Object, String>> DbELEMENTS; /** * publicDbELEMENTSメソッド * ELEMENTS(DB値)から指定したキーのエレメントを戻す * * @param str ELEMENTSのキー * @param dflt ”▼選択してください"や"未選択"を設定する。設定しない場合""を指定する。 * @return ELEMENTSキー毎のコード一覧(LinkedHashMap) */ public LinkedHashMap<Object, String> publicDbELEMENTS(String str, String dflt) { return Functions.publicELEMENTS(str, dflt, DbELEMENTS); } //全項目の必須チェック及びサイズチェックを「GroupOrder1」グループで行ったのち、 //全項目の属性を「GroupOrder2」グループでチェックする。 @Size(min = 0, max = 11, groups={GroupOrder1.class}) @CheckHalfLetterDigitBig(groups={GroupOrder2.class}) @CheckHalfStartEndSpace(groups={GroupOrder2.class}) private String seqid = ""; private String gazoFl = ""; private String gazoFlDel = ""; private MultipartFile gazoFlFile; @AssertTrue(message="『画像』に指定したファイルの拡張子は、アップロード対象外でした", groups={GroupOrder1.class}) public boolean isCheckGazoFl() { return ValidCheck.isImageFile(gazoFlFile); } private String file = ""; private String fileDel = ""; private MultipartFile fileFile; @AssertTrue(message="『ファイル』に指定したファイルの拡張子は、アップロード対象外でした", groups={GroupOrder1.class}) public boolean isCheckFile() { return ValidCheck.isFile(fileFile); } //DbELEMENTSでのチェックはControllerで行う @NotBlank(groups={GroupOrder1.class}) private String categoryCd = ""; //DbELEMENTSでのチェックはControllerで行う @NotBlank(groups={GroupOrder1.class}) private String subcategoryCd = ""; //DbELEMENTSでのチェックはControllerで行う @NotBlank(groups={GroupOrder1.class}) private String extracategoryCd = ""; @AssertTrue(message="『画像』は必須入力です", groups={GroupOrder1.class}) public boolean isCheckGazoFlHissu() { return ValidCheck.isFileHissu(gazoFlFile, gazoFl, gazoFlDel); } }
22:19
①これも書いておこう
■DbElementsFlutterController.java
package com.kaz02u.demo.controller.adminFlutter; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.util.ObjectUtils; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.ResponseBody; import com.kaz02u.demo.entity.Extracategory; import com.kaz02u.demo.entity.Subcategory; import com.kaz02u.demo.form.EleForm; import com.kaz02u.demo.repository.ExtracategoryRepository; import com.kaz02u.demo.repository.SubcategoryRepository; import com.kaz02u.demo.service.DbElementsService; import com.kaz02u.demo.utils.Functions; import lombok.extern.slf4j.Slf4j; /** * ajaxから呼ばれるエレメント取得のコントローラー * */ @Controller //log出力用 @Slf4j public class DbElementsFlutterController { @Autowired SubcategoryRepository subcategoryRepository; @Autowired ExtracategoryRepository extracategoryRepository; @Autowired DbElementsService dbElementsService; @PostMapping("/elements/elem") @ResponseBody public Map<Object, Object> dbElemFlutter(@RequestBody EleForm eleForm) { LinkedHashMap<Object, Object> map = new LinkedHashMap<Object, Object>(); //非公開を取得するかのフラグをbooleanにする boolean getAllFlg = !ObjectUtils.isEmpty(eleForm.getInopenkbn()) && eleForm.getInopenkbn().equals("1") ? true:false; //エレメント名によるDB取得 if (eleForm.getFmElementNm().equals("ary_lrgmidsml_category")) { if (!Functions.isItemEmptyOrNoGood(eleForm.getFmLrgKey(), false) && Functions.isItemEmptyOrNoGood(eleForm.getFmMidKey(), false)) { //中分類エレメントを設定 List<Subcategory> subcategoryList = subcategoryRepository.findByCategoryCd(eleForm.getFmLrgKey()); for (Subcategory subcategoryRec: subcategoryList) { //公開、非公開に関係なく取得する場合 if (!(subcategoryRec.getOpenkbn().equals("2") && getAllFlg == false)) { map.put(subcategoryRec.getSubcategoryCd(), subcategoryRec.getSubcategoryCd() + ":" + subcategoryRec.getSubcatname()); } } } else if (!Functions.isItemEmptyOrNoGood(eleForm.getFmLrgKey(), false) && !Functions.isItemEmptyOrNoGood(eleForm.getFmMidKey(), false)) { //小分類エレメントを設定 List<Extracategory> extracategoryList = extracategoryRepository.findByCategoryCdAndSubcategoryCd(eleForm.getFmLrgKey(), eleForm.getFmMidKey()); for (Extracategory extracategoryRec: extracategoryList) { //公開、非公開に関係なく取得する場合 if (!(extracategoryRec.getOpenkbn().equals("2") && getAllFlg == false)) { map.put(extracategoryRec.getExtracategoryCd(), extracategoryRec.getExtracategoryCd() + ":" + extracategoryRec.getExcatname()); } } } } return map; } }
■2022/06/15に、勉強した成果:『Flutter_JavaSpringプログラム自動作成◎自動生成ツール』をVectorに載せました。Zenn本も書きました。使ってみての感想や間違いの指定や、こうやったほうがいいとかの情報があればメールください。
・Vector
www.vector.co.jp
・Zenn本(Flutter_JavaSpringプログラム自動作成)
zenn.dev