───────┬────────────────────────────────────────────────────────────────────────
       File: src/main//java/jp/ac/kobe_u/cs/itspecialist/todoapp/service/ToDoService.java
───────┼────────────────────────────────────────────────────────────────────────
   1    package jp.ac.kobe_u.cs.itspecialist.todoapp.service;
   2    
   3    import java.util.*;
   4    
   5    import java.util.function.BiFunction;
   6    import java.util.function.Function;
   7    
   8    import jp.ac.kobe_u.cs.itspecialist.todoapp.TriFunction;
   9    import org.springframework.beans.factory.annotation.Autowired;
  10    import org.springframework.data.util.Pair;
  11    import org.springframework.data.domain.Page;
  12    import org.springframework.data.domain.Pageable;
  13    import org.springframework.stereotype.Service;
  14    
  15    import jp.ac.kobe_u.cs.itspecialist.todoapp.dto.ToDoForm;
  16    import jp.ac.kobe_u.cs.itspecialist.todoapp.entity.ToDo;
  17    import jp.ac.kobe_u.cs.itspecialist.todoapp.exception.ToDoAppException;
  18    import jp.ac.kobe_u.cs.itspecialist.todoapp.repository.ToDoRepository;
  19    
  20    @Service
  21    public class ToDoService {
  22        @Autowired
  23        MemberService mService;
  24        @Autowired
  25        ToDoRepository tRepo;
  26        /**
  27         * ToDoを作成する (C)
  28         * @param mid 作成者
  29         * @param form フォーム
  30         * @return
  31         */
  32        public ToDo createToDo(String mid, ToDoForm form) {
  33            mService.getMember(mid); //実在メンバーか確認
  34            ToDo todo = form.toEntity();
  35            if(!todo.isValidDueDate()) {
  36                throw new ToDoAppException(ToDoAppException.INVALID_TODO_OPERATION,
  37                        todo.getDueAt() + ": should be after created at");
  38            }
  39            todo.setMid(mid);
  40            return tRepo.save(todo);
  41        }
  42    
  43        /**
  44         * ToDoを1つ取得する (R)
  45         * @param seq
  46         * @return
  47         */
  48        public ToDo getToDo(Long seq) {
  49            ToDo todo = tRepo.findById(seq).orElseThrow(
  50                () -> new ToDoAppException(ToDoAppException.NO_SUCH_TODO_EXISTS, 
  51                seq + ": No such ToDo exists")
  52            );
  53            return todo;
  54        }
  55    
  56        public Page<ToDo> getToDoAllList(String mid, Pageable pageable) {
  57            return tRepo.findByMidAndDone(mid, false, pageable);
  58        }
  59    
  60        /**
  61         * あるメンバーのToDoリストを取得する (R)
  62         * @param mid
  63         * @return
  64         */
  65        public Page<ToDo> getToDoList(String mid, String sortBy, String order, Pageable pageable) {
  66            TriFunction<String, Boolean, Pageable, Page<ToDo>> finder = midAndDoneFinder.getOrDefault(Pair.of(sortBy, order),
  67                    (memberId, doneFlag, pageable2) -> tRepo.findByMidAndDone(memberId, doneFlag, pageable2));
  68            return finder.apply(mid, false, pageable);
  69        }
  70    
  71        public Page<ToDo> getDoneAllList(String mid, Pageable pageable) {
  72            return tRepo.findByMidAndDone(mid, true, pageable);
  73        }
  74    
  75        /**
  76         * あるメンバーのDoneリストを取得する (R)
  77         * @param mid
  78         * @return
  79         */
  80        public Page<ToDo> getDoneList(String mid, String sortBy, String order, Pageable pageable) {
  81            TriFunction<String, Boolean, Pageable, Page<ToDo>> finder = midAndDoneFinder.getOrDefault(Pair.of(sortBy, order),
  82                    (memberId, doneFlag, pageable2) -> tRepo.findByMidAndDone(memberId, doneFlag, pageable2));
  83            return finder.apply(mid, true, pageable);
  84        }
  85    
  86        private final Map<Pair<String, String>, TriFunction<String, Boolean, Pageable, Page<ToDo>>> midAndDoneFinder = generateMidAndDoneFinder();
  87        private Map<Pair<String, String>, TriFunction<String, Boolean, Pageable, Page<ToDo>>> generateMidAndDoneFinder() {
  88            Map<Pair<String, String>, TriFunction<String, Boolean, Pageable, Page<ToDo>>> map = new HashMap<>();
  89            map.put(Pair.of("seq", "asc"), (mid, done, pageable) -> tRepo.findByMidAndDoneOrderBySeqAsc(mid, done, pageable));
  90            map.put(Pair.of("seq", "desc"), (mid, done, pageable) -> tRepo.findByMidAndDoneOrderBySeqDesc(mid, done, pageable));
  91            map.put(Pair.of("title", "asc"), (mid, done, pageable) -> tRepo.findByMidAndDoneOrderByTitleAsc(mid, done, pageable));
  92            map.put(Pair.of("title", "desc"), (mid, done, pageable) -> tRepo.findByMidAndDoneOrderByTitleDesc(mid, done, pageable));
  93            map.put(Pair.of("created_at", "asc"), (mid, done, pageable) -> tRepo.findByMidAndDoneOrderByCreatedAtAsc(mid, done, pageable));
  94            map.put(Pair.of("created_at", "desc"), (mid, done, pageable) -> tRepo.findByMidAndDoneOrderByCreatedAtDesc(mid, done, pageable));
  95            map.put(Pair.of("done_at", "asc"), (mid, done, pageable) -> tRepo.findByMidAndDoneOrderByDoneAtAsc(mid, done, pageable));
  96            map.put(Pair.of("done_at", "desc"), (mid, done, pageable) -> tRepo.findByMidAndDoneOrderByDoneAtDesc(mid, done, pageable));
  97            map.put(Pair.of("due_at", "asc"), (mid, done, pageable) -> tRepo.findByMidAndDoneOrderByDueAtAsc(mid, done, pageable));
  98            map.put(Pair.of("due_at", "desc"), (mid, done, pageable) -> tRepo.findByMidAndDoneOrderByDueAtDesc(mid, done, pageable));
  99            return map;
 100        }
 101    
 102        /**
 103         * 全員のToDoリストを取得する (R)
 104         * @return
 105         */
 106        public Page<ToDo> getToDoList(String sortBy, String order, Pageable pageable) {
 107            BiFunction<Boolean, Pageable, Page<ToDo>> finder = doneFinder.getOrDefault(Pair.of(sortBy, order),
 108                    (doneFlag, pageable2) -> tRepo.findByDone(doneFlag, pageable2));
 109            return finder.apply(false, pageable);
 110        }
 111    
 112        /**
 113         * 全員のDoneリストを取得する (R)
 114         * @return
 115         */
 116        public Page<ToDo> getDoneList(String sortBy, String order, Pageable pageable) {
 117            BiFunction<Boolean, Pageable, Page<ToDo>> finder = doneFinder.getOrDefault(Pair.of(sortBy, order),
 118                    (doneFlag, pageable2) -> tRepo.findByDone(doneFlag, pageable2));
 119            return finder.apply(true, pageable);
 120        }
 121    
 122        /**
 123         * 〆切を更新する.
 124         * @param mid
 125         * @param seq
 126         * @param due
 127         */
 128        public ToDo updateDueDate(String mid, Long seq, Date due) {
 129            ToDo todo = getToDo(seq);
 130            if (!Objects.equals(mid, todo.getMid())) {
 131                throw new ToDoAppException(ToDoAppException.INVALID_TODO_OPERATION, mid
 132                        + ": Cannot done other's todo of " + todo.getMid());
 133            }
 134            if (due != null && due.before(todo.getCreatedAt())) {
 135                throw new ToDoAppException(ToDoAppException.INVALID_TODO_OPERATION,
 136                        due + ": should be after created at.");
 137            }
 138            todo.setDueAt(due);
 139            return tRepo.save(todo);
 140        }
 141    
 142        private final Map<Pair<String, String>, BiFunction<Boolean, Pageable, Page<ToDo>>> doneFinder = generateDoneFinder();
 143        private Map<Pair<String, String>, BiFunction<Boolean, Pageable, Page<ToDo>>> generateDoneFinder() {
 144            Map<Pair<String, String>, BiFunction<Boolean, Pageable, Page<ToDo>>> map = new HashMap<>();
 145            map.put(Pair.of("seq", "asc"), (doneFlag, pageable) -> tRepo.findByDoneOrderBySeqAsc(doneFlag, pageable));
 146            map.put(Pair.of("seq", "desc"), (doneFlag, pageable) -> tRepo.findByDoneOrderBySeqDesc(doneFlag, pageable));
 147            map.put(Pair.of("title", "asc"), (doneFlag, pageable) -> tRepo.findByDoneOrderByTitleAsc(doneFlag, pageable));
 148            map.put(Pair.of("title", "desc"), (doneFlag, pageable) -> tRepo.findByDoneOrderByTitleDesc(doneFlag, pageable));
 149            map.put(Pair.of("mid", "asc"), (doneFlag, pageable) -> tRepo.findByDoneOrderByMidAsc(doneFlag, pageable));
 150            map.put(Pair.of("mid", "desc"), (doneFlag, pageable) -> tRepo.findByDoneOrderByMidDesc(doneFlag, pageable));
 151            map.put(Pair.of("created_at", "asc"), (doneFlag, pageable) -> tRepo.findByDoneOrderByCreatedAtAsc(doneFlag, pageable));
 152            map.put(Pair.of("created_at", "desc"), (doneFlag, pageable) -> tRepo.findByDoneOrderByCreatedAtDesc(doneFlag, pageable));
 153            map.put(Pair.of("done_at", "asc"), (doneFlag, pageable) -> tRepo.findByDoneOrderByDoneAtAsc(doneFlag, pageable));
 154            map.put(Pair.of("done_at", "desc"), (doneFlag, pageable) -> tRepo.findByDoneOrderByDoneAtDesc(doneFlag, pageable));
 155            map.put(Pair.of("due_at", "asc"), (doneFlag, pageable) -> tRepo.findByDoneOrderByDueAtAsc(doneFlag, pageable));
 156            map.put(Pair.of("due_at", "desc"), (doneFlag, pageable) -> tRepo.findByDoneOrderByDueAtDesc(doneFlag, pageable));
 157            return map;
 158        }
 159    
 160        /**
 161         * ToDoを完了する
 162         * @param mid 完了者
 163         * @param seq 完了するToDoの番号
 164         * @return
 165         */
 166        public ToDo done(String mid, Long seq) {
 167            ToDo todo = getToDo(seq);
 168            //Doneの認可を確認する.他人のToDoを閉めたらダメ.
 169            if (!mid.equals(todo.getMid())) {
 170                throw new ToDoAppException(ToDoAppException.INVALID_TODO_OPERATION, mid
 171                        + ": Cannot done other's todo of " + todo.getMid());
 172            }
 173            todo.setDone(true);
 174            todo.setDoneAt(new Date());
 175            return tRepo.save(todo);
 176        }
 177    
 178        /**
 179         * ToDo の完了をキャンセルする.
 180         * @param mid 完了者
 181         * @param seq 完了をキャンセルするToDoの番号
 182         * @return
 183         */
 184        public ToDo cancel(String mid, Long seq) {
 185            ToDo todo = getToDo(seq);
 186            // Doneの認可を確認する.他人のToDoを閉めたらダメ.
 187            if (!mid.equals(todo.getMid())) {
 188                throw new ToDoAppException(ToDoAppException.INVALID_TODO_OPERATION,
 189                        mid + ": Cannot cancel other's todo of " + todo.getMid());
 190            }
 191            todo.setDone(false);
 192            todo.setDoneAt(null);
 193            return tRepo.save(todo);
 194        }
 195    
 196        /**
 197         * ToDoを更新する
 198         * @param mid 更新者
 199         * @param seq 更新するToDo番号
 200         * @param form 更新フォーム
 201         * @return
 202         */
 203        public ToDo updateToDo(String mid, Long seq, ToDoForm form) {
 204            ToDo todo = getToDo(seq);
 205            //Doneの認可を確認する.他人のToDoを更新したらダメ.
 206            if (!mid.equals(todo.getMid())) {
 207                throw new ToDoAppException(ToDoAppException.INVALID_TODO_OPERATION, mid
 208                        + ": Cannot update other's todo of " + todo.getMid());
 209            }
 210            todo.setTitle(form.getTitle()); //タイトルを更新
 211            return tRepo.save(todo);
 212        }
 213    
 214        /**
 215         * 背景色を更新する.
 216         * @param mid 更新者
 217         * @param seq 更新するToDo番号
 218         * @param background 新しい背景色
 219         * @return
 220         */
 221        public ToDo updateBackground(String mid, Long seq, String background) {
 222            ToDo todo = getToDo(seq);
 223            //Doneの認可を確認する.他人のToDoを更新したらダメ.
 224            if (!mid.equals(todo.getMid())) {
 225                throw new ToDoAppException(ToDoAppException.INVALID_TODO_OPERATION, mid
 226                        + ": Cannot update other's todo of " + todo.getMid());
 227            }
 228            todo.setBackground(background);
 229            return tRepo.save(todo);
 230        }
 231    
 232        /**
 233         * ToDoを削除する
 234         * @param mid 削除者
 235         * @param seq 削除するToDo番号
 236         */
 237        public void deleteToDo(String mid, Long seq) {
 238            ToDo todo = getToDo(seq);
 239            //Doneの認可を確認する.他人のToDoを削除したらダメ.
 240            if (!mid.equals(todo.getMid())) {
 241                throw new ToDoAppException(ToDoAppException.INVALID_TODO_OPERATION, mid 
 242                + ": Cannot delete other's todo of " + todo.getMid());
 243            }
 244            tRepo.deleteById(seq);
 245        }
 246    
 247    
 248    
 249    }
───────┴────────────────────────────────────────────────────────────────────────