diff --git a/src/main/java/run/halo/app/model/dto/MenuOutputDTO.java b/src/main/java/run/halo/app/model/dto/MenuOutputDTO.java index 4bd790997..bd7923209 100644 --- a/src/main/java/run/halo/app/model/dto/MenuOutputDTO.java +++ b/src/main/java/run/halo/app/model/dto/MenuOutputDTO.java @@ -26,4 +26,6 @@ public class MenuOutputDTO implements OutputConverter { private String target; private String icon; + + private Integer parentId; } diff --git a/src/main/java/run/halo/app/model/entity/Menu.java b/src/main/java/run/halo/app/model/entity/Menu.java index 90516834f..2932a4ea5 100644 --- a/src/main/java/run/halo/app/model/entity/Menu.java +++ b/src/main/java/run/halo/app/model/entity/Menu.java @@ -58,6 +58,11 @@ public class Menu extends BaseEntity { @Column(name = "icon", columnDefinition = "varchar(50) default ''") private String icon; + /** + * Parent menu. + */ + @Column(name = "parent_id", columnDefinition = "int default 0") + private Integer parentId; @Override public void prePersist() { diff --git a/src/main/java/run/halo/app/model/params/MenuParam.java b/src/main/java/run/halo/app/model/params/MenuParam.java index 4d9221f89..55cb6266b 100644 --- a/src/main/java/run/halo/app/model/params/MenuParam.java +++ b/src/main/java/run/halo/app/model/params/MenuParam.java @@ -1,7 +1,5 @@ package run.halo.app.model.params; -import run.halo.app.model.dto.base.InputConverter; -import run.halo.app.model.entity.Menu; import lombok.Data; import lombok.ToString; import run.halo.app.model.dto.base.InputConverter; @@ -37,4 +35,6 @@ public class MenuParam implements InputConverter { @Size(max = 50, message = "Length of menu icon must not be more than {max}") private String icon; + + private Integer parentId; } diff --git a/src/main/java/run/halo/app/model/vo/MenuVO.java b/src/main/java/run/halo/app/model/vo/MenuVO.java new file mode 100644 index 000000000..2e80a0257 --- /dev/null +++ b/src/main/java/run/halo/app/model/vo/MenuVO.java @@ -0,0 +1,16 @@ +package run.halo.app.model.vo; + +import lombok.Data; +import run.halo.app.model.dto.MenuOutputDTO; + +import java.util.List; + +/** + * @author : RYAN0UP + * @date : 2019-04-07 + */ +@Data +public class MenuVO extends MenuOutputDTO { + + private List children; +} diff --git a/src/main/java/run/halo/app/service/MenuService.java b/src/main/java/run/halo/app/service/MenuService.java index 4832d0432..d96bf3b89 100644 --- a/src/main/java/run/halo/app/service/MenuService.java +++ b/src/main/java/run/halo/app/service/MenuService.java @@ -1,11 +1,11 @@ package run.halo.app.service; +import org.springframework.data.domain.Sort; +import org.springframework.lang.NonNull; import run.halo.app.model.dto.MenuOutputDTO; import run.halo.app.model.entity.Menu; import run.halo.app.model.params.MenuParam; -import run.halo.app.service.base.CrudService; -import org.springframework.data.domain.Sort; -import org.springframework.lang.NonNull; +import run.halo.app.model.vo.MenuVO; import run.halo.app.service.base.CrudService; import java.util.List; @@ -14,6 +14,7 @@ import java.util.List; * Menu service. * * @author johnniang + * @author RYAN0UP */ public interface MenuService extends CrudService { @@ -34,4 +35,12 @@ public interface MenuService extends CrudService { */ @NonNull Menu createBy(@NonNull MenuParam menuParam); + + /** + * Lists as menu tree. + * + * @param sort sort info must not be null + * @return a menu tree + */ + List listAsTree(Sort sort); } diff --git a/src/main/java/run/halo/app/service/impl/MenuServiceImpl.java b/src/main/java/run/halo/app/service/impl/MenuServiceImpl.java index 074581ee7..d22761d2d 100644 --- a/src/main/java/run/halo/app/service/impl/MenuServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/MenuServiceImpl.java @@ -1,21 +1,21 @@ package run.halo.app.service.impl; -import run.halo.app.exception.AlreadyExistsException; -import run.halo.app.model.dto.MenuOutputDTO; -import run.halo.app.model.entity.Menu; -import run.halo.app.model.params.MenuParam; -import run.halo.app.repository.MenuRepository; -import run.halo.app.service.MenuService; -import run.halo.app.service.base.AbstractCrudService; import org.springframework.data.domain.Sort; +import org.springframework.lang.NonNull; import org.springframework.stereotype.Service; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import run.halo.app.exception.AlreadyExistsException; +import run.halo.app.model.dto.MenuOutputDTO; +import run.halo.app.model.entity.Menu; +import run.halo.app.model.params.MenuParam; +import run.halo.app.model.vo.MenuVO; import run.halo.app.repository.MenuRepository; +import run.halo.app.service.MenuService; import run.halo.app.service.base.AbstractCrudService; import java.util.Collections; +import java.util.LinkedList; import java.util.List; import java.util.stream.Collectors; @@ -57,6 +57,87 @@ public class MenuServiceImpl extends AbstractCrudService implemen return create(menuParam.convertTo()); } + /** + * Lists as menu tree. + * + * @param sort sort info must not be null + * @return a menu tree + */ + @Override + public List listAsTree(Sort sort) { + Assert.notNull(sort, "Sort info must not be null"); + + // List all menu + List menus = listAll(sort); + + if (CollectionUtils.isEmpty(menus)) { + return Collections.emptyList(); + } + + // Create top menu + MenuVO topLevelMenu = createTopLevelMenu(); + + // Concrete the tree + concreteTree(topLevelMenu, menus); + + return topLevelMenu.getChildren(); + } + + /** + * Concrete menu tree. + * + * @param parentMenu parent menu vo must not be null + * @param menus a list of menu + */ + private void concreteTree(MenuVO parentMenu, List menus) { + Assert.notNull(parentMenu, "Parent menu must not be null"); + + if (CollectionUtils.isEmpty(menus)) { + return; + } + + // Create children container for removing after + List children = new LinkedList<>(); + + menus.forEach(menu -> { + if (parentMenu.getId().equals(menu.getParentId())) { + // Save child menu + children.add(menu); + + // Convert to child menu vo + MenuVO child = new MenuVO().convertFrom(menu); + + // Init children if absent + if (parentMenu.getChildren() == null) { + parentMenu.setChildren(new LinkedList<>()); + } + parentMenu.getChildren().add(child); + } + }); + + // Remove all child menus + menus.removeAll(children); + + // Foreach children vos + if (!CollectionUtils.isEmpty(parentMenu.getChildren())) { + parentMenu.getChildren().forEach(childMenu -> concreteTree(childMenu, menus)); + } + } + + /** + * Creates a top level menu. + * + * @return top level menu with id 0 + */ + @NonNull + private MenuVO createTopLevelMenu() { + MenuVO topMenu = new MenuVO(); + // Set default value + topMenu.setId(0); + topMenu.setChildren(new LinkedList<>()); + topMenu.setParentId(-1); + return topMenu; + } private List convertTo(List menus) { if (CollectionUtils.isEmpty(menus)) { diff --git a/src/main/java/run/halo/app/web/controller/admin/api/MenuController.java b/src/main/java/run/halo/app/web/controller/admin/api/MenuController.java index b49c86c2d..bf2de97f8 100644 --- a/src/main/java/run/halo/app/web/controller/admin/api/MenuController.java +++ b/src/main/java/run/halo/app/web/controller/admin/api/MenuController.java @@ -1,19 +1,19 @@ package run.halo.app.web.controller.admin.api; -import run.halo.app.model.dto.MenuOutputDTO; -import run.halo.app.model.entity.Menu; -import run.halo.app.model.params.MenuParam; -import run.halo.app.service.MenuService; import io.swagger.annotations.ApiOperation; import org.springframework.data.domain.Sort; import org.springframework.data.web.SortDefault; import org.springframework.web.bind.annotation.*; +import run.halo.app.model.dto.MenuOutputDTO; import run.halo.app.model.entity.Menu; +import run.halo.app.model.params.MenuParam; +import run.halo.app.model.vo.MenuVO; import run.halo.app.service.MenuService; import javax.validation.Valid; import java.util.List; +import static org.springframework.data.domain.Sort.Direction.ASC; import static org.springframework.data.domain.Sort.Direction.DESC; /** @@ -38,6 +38,12 @@ public class MenuController { return menuService.listDtos(sort); } + @GetMapping("tree_view") + @ApiOperation("List as category tree") + public List listAsTree(@SortDefault(sort = "name", direction = ASC) Sort sort) { + return menuService.listAsTree(sort); + } + @PostMapping @ApiOperation("Creates a menu") public MenuOutputDTO createBy(@RequestBody @Valid MenuParam menuParam) {