商户树形管理
diff --git a/src/main/java/com/supwisdom/dlpay/framework/dao/ShopDao.java b/src/main/java/com/supwisdom/dlpay/framework/dao/ShopDao.java
index fccf800..968804e 100644
--- a/src/main/java/com/supwisdom/dlpay/framework/dao/ShopDao.java
+++ b/src/main/java/com/supwisdom/dlpay/framework/dao/ShopDao.java
@@ -6,6 +6,7 @@
 import org.springframework.data.jpa.repository.Query;
 
 import javax.persistence.LockModeType;
+import java.util.List;
 
 /**
  * Created by shuwei on 2019/4/15.
@@ -20,4 +21,7 @@
     TShop getTShopByShopid(Integer shopid);
 
     TShop getTShopByShopaccno(String shopaccno);
+
+    @Query("from TShop where status='normal' and fshopid=?1 ")
+    List<TShop> getChildShopsByShopid(Integer shopid);
 }
diff --git a/src/main/java/com/supwisdom/dlpay/framework/dao/ShopaccDao.java b/src/main/java/com/supwisdom/dlpay/framework/dao/ShopaccDao.java
index 45977f7..4e1f9ef 100644
--- a/src/main/java/com/supwisdom/dlpay/framework/dao/ShopaccDao.java
+++ b/src/main/java/com/supwisdom/dlpay/framework/dao/ShopaccDao.java
@@ -40,4 +40,6 @@
   @Query("select a from TShopacc a where a.shopid=?1")
   TShopacc getShopaccWithLock(Integer shopid);
 
+  TShopacc getByShopaccno(String shopaccno);
+
 }
diff --git a/src/main/java/com/supwisdom/dlpay/system/bean/ZTreeNode.java b/src/main/java/com/supwisdom/dlpay/system/bean/ZTreeNode.java
index 9d50273..5ad5fd3 100644
--- a/src/main/java/com/supwisdom/dlpay/system/bean/ZTreeNode.java
+++ b/src/main/java/com/supwisdom/dlpay/system/bean/ZTreeNode.java
@@ -8,6 +8,8 @@
     private boolean checked;
     private boolean open;
 
+    private Integer shoptype; //商户类别,商户树用到
+
     public boolean isOpen() {
         return open;
     }
@@ -47,4 +49,12 @@
     public void setChecked(boolean checked) {
         this.checked = checked;
     }
+
+    public Integer getShoptype() {
+        return shoptype;
+    }
+
+    public void setShoptype(Integer shoptype) {
+        this.shoptype = shoptype;
+    }
 }
diff --git a/src/main/java/com/supwisdom/dlpay/system/controller/ShopController.java b/src/main/java/com/supwisdom/dlpay/system/controller/ShopController.java
new file mode 100644
index 0000000..2c8e088
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/system/controller/ShopController.java
@@ -0,0 +1,53 @@
+package com.supwisdom.dlpay.system.controller;
+
+import com.supwisdom.dlpay.api.bean.JsonResult;
+import com.supwisdom.dlpay.framework.domain.TShop;
+import com.supwisdom.dlpay.system.service.ShopDataService;
+import com.supwisdom.dlpay.util.WebCheckException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+@Controller
+public class ShopController {
+  @Autowired
+  private ShopDataService shopDataService;
+
+  @GetMapping("/shop/index")
+  public String shopView() {
+    return "system/shop/index";
+  }
+
+  @GetMapping("/shop/shoptree")
+  @PreAuthorize("hasPermission('/shop/index','')")
+  @ResponseBody
+  public JsonResult shopTreeData(){
+    return JsonResult.ok("OK").put("data", shopDataService.getAllShopNodes());
+  }
+
+  @PostMapping("/shop/deleteshop")
+  @PreAuthorize("hasPermission('/shop/deleteshop','')")
+  @ResponseBody
+  public JsonResult deleteShop(@RequestParam("shopid") Integer shopid) {
+    TShop shop = shopDataService.getShopByShopid(shopid);
+    if(null==shop){
+      return JsonResult.error("商户不存在,请重新查询");  //商户不存在,请重新查询
+    }
+
+    try{
+      if(shopDataService.deleteShop(shop)){
+        return JsonResult.ok("删除成功");
+      }else{
+        return JsonResult.error("删除失败");
+      }
+    }catch (WebCheckException ex){
+      return JsonResult.error(ex.getMessage());
+    }catch (Exception e){
+      return JsonResult.error("系统处理异常").put("exception", e);
+    }
+  }
+}
diff --git a/src/main/java/com/supwisdom/dlpay/system/service/ShopDataService.java b/src/main/java/com/supwisdom/dlpay/system/service/ShopDataService.java
new file mode 100644
index 0000000..087bec2
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/system/service/ShopDataService.java
@@ -0,0 +1,20 @@
+package com.supwisdom.dlpay.system.service;
+
+import com.supwisdom.dlpay.framework.domain.TShop;
+import com.supwisdom.dlpay.system.bean.ZTreeNode;
+import com.supwisdom.dlpay.util.WebCheckException;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+public interface ShopDataService {
+  @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class,readOnly = true)
+  List<ZTreeNode> getAllShopNodes();
+
+  @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class,readOnly = true)
+  TShop getShopByShopid(Integer shopid);
+
+  @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
+  boolean deleteShop(TShop shop) throws WebCheckException;
+}
diff --git a/src/main/java/com/supwisdom/dlpay/system/service/impl/ShopDataServiceImpl.java b/src/main/java/com/supwisdom/dlpay/system/service/impl/ShopDataServiceImpl.java
new file mode 100644
index 0000000..33d227d
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/system/service/impl/ShopDataServiceImpl.java
@@ -0,0 +1,73 @@
+package com.supwisdom.dlpay.system.service.impl;
+
+import com.supwisdom.dlpay.framework.dao.ShopDao;
+import com.supwisdom.dlpay.framework.dao.ShopaccDao;
+import com.supwisdom.dlpay.framework.domain.TShop;
+import com.supwisdom.dlpay.framework.domain.TShopacc;
+import com.supwisdom.dlpay.framework.util.StringUtil;
+import com.supwisdom.dlpay.framework.util.TradeDict;
+import com.supwisdom.dlpay.system.bean.ZTreeNode;
+import com.supwisdom.dlpay.system.service.ShopDataService;
+import com.supwisdom.dlpay.util.WebCheckException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Service
+public class ShopDataServiceImpl implements ShopDataService {
+  @Autowired
+  private ShopDao shopDao;
+  @Autowired
+  private ShopaccDao shopaccDao;
+
+  @Override
+  public List<ZTreeNode> getAllShopNodes() {
+    List<ZTreeNode> result = new ArrayList<>(0);
+    List<TShop> shoplist = shopDao.findAll();
+    if (!StringUtil.isEmpty(shoplist)) {
+      for (TShop shop : shoplist) {
+        if (null == shop || !TradeDict.STATUS_NORMAL.equals(shop.getStatus())) continue; //跳过注销商户
+        ZTreeNode node = new ZTreeNode();
+        node.setId(shop.getShopid().toString());
+        node.setName(shop.getShopname());
+        node.setpId(shop.getFshopid() == null ? "" : shop.getFshopid().toString());
+        node.setChecked(false);
+        node.setOpen(true);
+        node.setShoptype(shop.getShoptype());
+        result.add(node);
+      }
+    }
+    return result;
+  }
+
+  @Override
+  public TShop getShopByShopid(Integer shopid) {
+    if (null != shopid) {
+      return shopDao.getTShopByShopid(shopid);
+    }
+    return null;
+  }
+
+  @Override
+  public boolean deleteShop(TShop shop) throws WebCheckException{
+    if(null!=shop){
+      List<TShop> childShops = shopDao.getChildShopsByShopid(shop.getShopid());
+      if(!StringUtil.isEmpty(childShops))
+        throw new WebCheckException("请先删除下级商户");
+      shop.setStatus(TradeDict.STATUS_CLOSED);
+      shopDao.save(shop);
+      if(!StringUtil.isEmpty(shop.getShopaccno())){
+        TShopacc shopacc=shopaccDao.getByShopaccno(shop.getShopaccno());
+        if(null==shopacc) throw new WebCheckException("数据异常!对应的商户账户不存在!");
+        shopacc.setStatus(TradeDict.STATUS_CLOSED);
+        shopaccDao.save(shopacc);
+      }
+      return true;
+    }
+    return false;
+  }
+
+
+}
diff --git a/src/main/resources/static/libs/zTree/css/zTreeStyle/zTreeStyle.css b/src/main/resources/static/libs/zTree/css/zTreeStyle/zTreeStyle.css
index a6845a4..a9e1b52 100755
--- a/src/main/resources/static/libs/zTree/css/zTreeStyle/zTreeStyle.css
+++ b/src/main/resources/static/libs/zTree/css/zTreeStyle/zTreeStyle.css
@@ -75,6 +75,8 @@
 .ztree li span.button.ico_docu{margin-right:2px; background-position:-110px -32px; vertical-align:top; *vertical-align:middle}
 .ztree li span.button.edit {margin-right:2px; background-position:-110px -48px; vertical-align:top; *vertical-align:middle}
 .ztree li span.button.remove {margin-right:2px; background-position:-110px -64px; vertical-align:top; *vertical-align:middle}
+.ztree li span.button.add {margin-right:2px; background-position:-144px 0; vertical-align:top; *vertical-align:middle}
+
 
 .ztree li span.button.ico_loading{margin-right:2px; background:url(./img/loading.gif) no-repeat scroll 0 0 transparent; vertical-align:top; *vertical-align:middle}
 
diff --git a/src/main/resources/templates/system/shop/index.html b/src/main/resources/templates/system/shop/index.html
new file mode 100644
index 0000000..1211547
--- /dev/null
+++ b/src/main/resources/templates/system/shop/index.html
@@ -0,0 +1,167 @@
+<div class="layui-card">
+    <div class="layui-card-header">
+        <h2 class="header-title">商户管理</h2>
+        <span class="layui-breadcrumb pull-right">
+          <a href="#">商户中心</a>
+          <a><cite>商户管理</cite></a>
+        </span>
+    </div>
+    <div class="layui-card-body">
+        <div class="layui-row">
+            <div class="layui-col-xs5 layui-col-md3">
+                <div class="layui-card">
+                    <div class="layui-card-body layui-show"
+                         style="background-color: #D7F9F7;max-height: 560px;overflow:auto;">
+                        <ul id="shoptree" class="ztree"></ul>
+                    </div>
+                </div>
+            </div>
+            <div class="layui-col-xs7 layui-col-md9">
+                <div class="layui-card">
+                    <div class="layui-card-body">
+                        <div class="layui-form toolbar">
+                            搜索:
+                            <input id="search-value" class="layui-input search-input" type="text" placeholder="输入角色名称"/>&emsp;
+                            <button id="btn-search" class="layui-btn icon-btn" data-type="search"><i class="layui-icon">&#xe615;</i>搜索
+                            </button>
+                            <button id="btn-add" class="layui-btn icon-btn" data-type="add"><i class="layui-icon"></i>添加角色
+                            </button>
+                        </div>
+                        <table class="layui-table" id="shoptable" lay-filter="shoptable-filter"></table>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+
+<script>
+    layui.use(['form', 'table', 'layer', 'admin', 'element'], function () {
+        var form = layui.form;
+        var table = layui.table;
+        var admin = layui.admin;
+        var initTree = function (nodes) {
+            var menuSetting = {
+                view: {
+                    dblClickExpand: false,
+                    showLine: true,
+                    showIcon: false,
+                    selectedMulti: false,
+                    addHoverDom: addHoverDom,
+                    removeHoverDom: removeHoverDom
+                },
+                data: {
+                    simpleData: {
+                        enable: true
+                    }
+                },
+                edit: {
+                    enable: true,
+                    showRemoveBtn: true,
+                    removeTitle: "删除商户",
+                    showRenameBtn: false,
+                    drag: {
+                        isCopy: false,
+                        isMove: false
+                    }
+                },
+                callback: {
+                    beforeRemove: beforeRemove,
+                    onDblClick: ondblclick
+                }
+            };
+            $.fn.zTree.init($("#shoptree"), menuSetting, nodes);
+        }
+
+        function beforeRemove(treeId, treeNode) {
+            var zTree = $.fn.zTree.getZTreeObj("shoptree");
+            if (treeNode.isParent) {
+                var childrenNodes = treeNode.children;
+                if (childrenNodes && childrenNodes.length != 0) {
+                    layer.msg("请先删除下级商户", {icon: 2, time: 2000});
+                    return false;
+                }
+            }
+            zTree.selectNode(treeNode);
+            var flag = false;
+            if (confirm("确认删除商户【" + treeNode.name + "】吗?")) {
+                layer.load(2);
+                $.ajax({
+                    type: "POST",
+                    dataType: "json",
+                    url: '/shop/deleteshop',
+                    async: false,  //必须同步
+                    data: {
+                        shopid: treeNode.id,
+                        _csrf: $("meta[name='_csrf_token']").attr("value")
+                    },
+                    success : function(result) {
+                        console.log(result);
+                        layer.closeAll('loading');
+                        if (result.code == 200) {
+                            layer.msg(result.msg, {icon: 1});
+                            flag = true;
+                        } else if (result.code == 401) {
+                            layer.msg(result.msg, {icon: 2, time: 1500}, function () {
+                                location.replace('/login');
+                            }, 1000);
+                        } else {
+                            console.log('err:' + result.code);
+                            layer.msg(result.msg, {icon: 2});
+                        }
+                    },
+                    error : function() {
+                        console.log(ret);
+                        layer.closeAll('loading');
+                        layer.msg("请求服务器失败!", {icon: 2});
+                    }
+                });
+            }
+            return flag;
+        }
+
+        function removeHoverDom(treeId, treeNode) {
+            $("#addBtn_" + treeNode.tId).unbind().remove();
+        }
+
+        newCount = 10000
+        function addHoverDom(treeId, treeNode) {
+            if (treeNode.shoptype == 0) {
+                var sObj = $("#" + treeNode.tId + "_span");
+                if (treeNode.editNameFlag || $("#addBtn_" + treeNode.tId).length > 0) return;
+                var addStr = "<span class='button add' id='addBtn_" + treeNode.tId
+                    + "' title='新增下级商户' onfocus='this.blur();'></span>";
+                sObj.after(addStr);
+                var btn = $("#addBtn_" + treeNode.tId);
+                if (btn) btn.bind("click", function () {
+                    debugger
+                    var zTree = $.fn.zTree.getZTreeObj("shoptree");
+                    zTree.addNodes(treeNode, {id: (100 + newCount), pId: treeNode.id, shoptype: 0, name: "new node" + (newCount++)});
+                    return false;
+                });
+            }
+        }
+
+        function ondblclick(event, treeId, treeNode){
+            alert("treeId=["+treeId+"],nodename=["+treeNode.name+"]");
+        }
+
+        admin.dgo('/shop/shoptree', {}, function (data) {
+            if (data.code == 200) {
+                initTree(data.data);
+            } else if (data.code == 401) {
+                layer.msg(data.msg, {icon: 2, time: 1500}, function () {
+                    location.replace('/login');
+                }, 1000);
+                return;
+            } else {
+                layer.msg(data.msg, {icon: 2, time: 2000});
+            }
+        }, function (ret) {
+            console.log(ret);
+            layer.msg('查询商户树失败了,请稍后再试', {icon: 2});
+        });
+
+
+    });
+</script>
\ No newline at end of file