全局参数管理
diff --git a/src/main/java/com/supwisdom/dlpay/framework/dao/BusinessparaDao.java b/src/main/java/com/supwisdom/dlpay/framework/dao/BusinessparaDao.java
index 5bf13d9..8f8be9c 100644
--- a/src/main/java/com/supwisdom/dlpay/framework/dao/BusinessparaDao.java
+++ b/src/main/java/com/supwisdom/dlpay/framework/dao/BusinessparaDao.java
@@ -1,6 +1,8 @@
 package com.supwisdom.dlpay.framework.dao;
 
 import com.supwisdom.dlpay.framework.domain.TBusinesspara;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.Lock;
 import org.springframework.data.jpa.repository.Query;
@@ -22,4 +24,6 @@
   @QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value ="0")})
   @Query(value = " from TBusinesspara where parakey=?1 ")
   TBusinesspara findByParakeyForUpdateNowait(String parakey);
+
+  Page<TBusinesspara> findAllByParakeyContaining(String parakey, Pageable pageable);
 }
diff --git a/src/main/java/com/supwisdom/dlpay/framework/dao/SysparaDao.java b/src/main/java/com/supwisdom/dlpay/framework/dao/SysparaDao.java
index 7e20856..b4f5da3 100644
--- a/src/main/java/com/supwisdom/dlpay/framework/dao/SysparaDao.java
+++ b/src/main/java/com/supwisdom/dlpay/framework/dao/SysparaDao.java
@@ -1,17 +1,14 @@
 package com.supwisdom.dlpay.framework.dao;
 
 import com.supwisdom.dlpay.framework.domain.TSyspara;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.Lock;
-import org.springframework.data.jpa.repository.Query;
-import org.springframework.data.jpa.repository.QueryHints;
+import org.springframework.data.jpa.repository.*;
 import org.springframework.stereotype.Repository;
 
 import javax.persistence.LockModeType;
 import javax.persistence.QueryHint;
 
 @Repository
-public interface SysparaDao extends JpaRepository<TSyspara, Integer> {
+public interface SysparaDao extends JpaRepository<TSyspara, Integer>, JpaSpecificationExecutor<TSyspara> {
   TSyspara findByParaid(int paraid);
 
   @Lock(LockModeType.PESSIMISTIC_WRITE)
@@ -22,4 +19,6 @@
   @QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value ="0")})
   @Query(value = "from TSyspara where paraid=?1 ")
   TSyspara findByParaidWithLockNowait(int paraid);
+
+
 }
diff --git a/src/main/java/com/supwisdom/dlpay/system/controller/ParamController.java b/src/main/java/com/supwisdom/dlpay/system/controller/ParamController.java
new file mode 100644
index 0000000..1224c6e
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/system/controller/ParamController.java
@@ -0,0 +1,106 @@
+package com.supwisdom.dlpay.system.controller;
+
+import com.supwisdom.dlpay.api.bean.JsonResult;
+import com.supwisdom.dlpay.framework.domain.TBusinesspara;
+import com.supwisdom.dlpay.framework.domain.TSyspara;
+import com.supwisdom.dlpay.framework.service.SystemUtilService;
+import com.supwisdom.dlpay.framework.util.PageResult;
+import com.supwisdom.dlpay.framework.util.WebConstant;
+import com.supwisdom.dlpay.system.service.ParamService;
+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 ParamController {
+  @Autowired
+  private ParamService paramService;
+  @Autowired
+  private SystemUtilService systemUtilService;
+
+  @GetMapping("/param/syspara")
+  public String sysparaView() {
+    return "system/param/syspara";
+  }
+
+  @GetMapping("/param/sysparalist")
+  @PreAuthorize("hasPermission('/param/syspara','')")
+  @ResponseBody
+  public PageResult<TSyspara> getDataList(@RequestParam("page") Integer pageNo,
+                                          @RequestParam("limit") Integer pageSize,
+                                          @RequestParam(value = "paraid", required = false) Integer paraid,
+                                          @RequestParam(value = "paraname", required = false) String paraname) {
+    try {
+      if (null == pageNo || pageNo < 1) pageNo = WebConstant.PAGENO_DEFAULT;
+      if (null == pageSize || pageSize < 1) pageSize = WebConstant.PAGESIZE_DEFAULT;
+      return paramService.getSysparaPage(paraid, paraname, pageNo, pageSize);
+    } catch (Exception e) {
+      e.printStackTrace();
+      return new PageResult<>(99, "系统查询错误");
+    }
+  }
+
+  @PostMapping("/param/sysparaupdate")
+  @PreAuthorize("hasPermission('/param/sysparaupdate','')")
+  @ResponseBody
+  public JsonResult updateSyspara(@RequestParam("paraid") Integer paraid,
+                                  @RequestParam("paraval") String paraval) {
+    if (null == paraid || null == paraval) {
+      return JsonResult.error("参数传递错误");
+    } else if (paraval.length() > 30) {
+      return JsonResult.error("参数值过长");
+    }
+    TSyspara syspara = paramService.getSysparaByParaid(paraid);
+    if (null == syspara) {
+      return JsonResult.error("全局参数不存在");
+    }
+    syspara.setParaval(paraval);
+    syspara.setLastsaved(systemUtilService.getSysdatetime().getHostdatetime());
+    if (syspara.checkValueInvalid()) {
+      return JsonResult.error("参数值格式错误");
+    }
+    try {
+      if (paramService.updateSyspara(syspara)) {
+        return JsonResult.ok("修改成功");
+      } else {
+        return JsonResult.error("修改失败");
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      return JsonResult.error("系统处理异常");
+    }
+  }
+
+
+  /**
+   * ====================================================
+   *              业务参数配置
+   * ====================================================
+   */
+  @GetMapping("/param/businesspara")
+  public String busiparaView() {
+    return "system/param/businesspara";
+  }
+
+  @PostMapping("/param/businessparalist")
+  @PreAuthorize("hasPermission('/param/businesspara','')")
+  @ResponseBody
+  public PageResult<TBusinesspara> getDataList(@RequestParam("page") Integer pageNo,
+                                               @RequestParam("limit") Integer pageSize,
+                                               @RequestParam(value = "paraname", required = false) String paraname) {
+    try {
+      if (null == pageNo || pageNo < 1) pageNo = WebConstant.PAGENO_DEFAULT;
+      if (null == pageSize || pageSize < 1) pageSize = WebConstant.PAGESIZE_DEFAULT;
+      return paramService.getBusinessparaPage(paraname, pageNo, pageSize);
+    } catch (Exception e) {
+      e.printStackTrace();
+      return new PageResult<>(99, "系统查询错误");
+    }
+  }
+
+
+}
diff --git a/src/main/java/com/supwisdom/dlpay/system/service/ParamService.java b/src/main/java/com/supwisdom/dlpay/system/service/ParamService.java
new file mode 100644
index 0000000..a255c4c
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/system/service/ParamService.java
@@ -0,0 +1,22 @@
+package com.supwisdom.dlpay.system.service;
+
+import com.supwisdom.dlpay.framework.domain.TBusinesspara;
+import com.supwisdom.dlpay.framework.domain.TSyspara;
+import com.supwisdom.dlpay.framework.util.PageResult;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+
+public interface ParamService {
+  @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class, readOnly = true)
+  PageResult<TSyspara> getSysparaPage(Integer paraid, String paraname, int pageNo, int pageSize);
+
+  @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class, readOnly = true)
+  TSyspara getSysparaByParaid(int paraid);
+
+  @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
+  boolean updateSyspara(TSyspara syspara);
+
+  @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class, readOnly = true)
+  PageResult<TBusinesspara> getBusinessparaPage(String paraname, int pageNo, int pageSize);
+}
diff --git a/src/main/java/com/supwisdom/dlpay/system/service/impl/ParamServiceImpl.java b/src/main/java/com/supwisdom/dlpay/system/service/impl/ParamServiceImpl.java
new file mode 100644
index 0000000..199682b
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/system/service/impl/ParamServiceImpl.java
@@ -0,0 +1,77 @@
+package com.supwisdom.dlpay.system.service.impl;
+
+import com.supwisdom.dlpay.framework.dao.BusinessparaDao;
+import com.supwisdom.dlpay.framework.dao.SysparaDao;
+import com.supwisdom.dlpay.framework.domain.TBusinesspara;
+import com.supwisdom.dlpay.framework.domain.TSyspara;
+import com.supwisdom.dlpay.framework.util.PageResult;
+import com.supwisdom.dlpay.framework.util.StringUtil;
+import com.supwisdom.dlpay.system.service.ParamService;
+import com.supwisdom.dlpay.util.ConstantUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.stereotype.Service;
+
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+import java.util.ArrayList;
+import java.util.List;
+
+
+@Service
+public class ParamServiceImpl implements ParamService {
+  @Autowired
+  private SysparaDao sysparaDao;
+  @Autowired
+  private BusinessparaDao businessparaDao;
+
+  @Override
+  public PageResult<TSyspara> getSysparaPage(Integer paraid, String paraname, int pageNo, int pageSize) {
+    Pageable pageable = PageRequest.of(pageNo - 1, pageSize, Sort.by("paraid"));
+    Page<TSyspara> page = sysparaDao.findAll(new Specification<TSyspara>() {
+      @Override
+      public Predicate toPredicate(Root<TSyspara> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
+        List<Predicate> predicates = new ArrayList<>();
+        if (null != paraid) {
+          predicates.add(criteriaBuilder.equal(root.get("paraid").as(Integer.class), paraid));
+        }
+        if (!StringUtil.isEmpty(paraname)) {
+          predicates.add(criteriaBuilder.like(root.get("paraname").as(String.class), "%" + paraname.trim() + "%"));
+        }
+        predicates.add(criteriaBuilder.equal(root.get("displayflag").as(String.class), ConstantUtil.ENABLE_YES));
+        return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
+      }
+    }, pageable);
+    return new PageResult<>(page);
+  }
+
+  @Override
+  public TSyspara getSysparaByParaid(int paraid) {
+    return sysparaDao.findByParaid(paraid);
+  }
+
+  @Override
+  public boolean updateSyspara(TSyspara syspara) {
+    if (null != syspara) {
+      sysparaDao.save(syspara);
+      return true;
+    }
+    return false;
+  }
+
+  @Override
+  public PageResult<TBusinesspara> getBusinessparaPage(String paraname, int pageNo, int pageSize) {
+    Pageable pageable = PageRequest.of(pageNo - 1, pageSize, Sort.by("parakey"));
+    if (!StringUtil.isEmpty(paraname)) {
+      return new PageResult<>(businessparaDao.findAllByParakeyContaining(paraname.trim(), pageable));
+    }
+    return new PageResult<>(businessparaDao.findAll(pageable));
+  }
+
+}
diff --git a/src/main/resources/templates/system/param/businesspara.html b/src/main/resources/templates/system/param/businesspara.html
new file mode 100644
index 0000000..57b1e76
--- /dev/null
+++ b/src/main/resources/templates/system/param/businesspara.html
@@ -0,0 +1,94 @@
+<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-form toolbar">
+            搜索:
+            <input id="search-paraname" class="layui-input search-input" type="text" maxlength="40" placeholder="输入参数名称"/>&emsp;
+            <button id="btn-search" class="layui-btn icon-btn" data-type="search"><i class="layui-icon">&#xe615;</i>搜索
+            </button>
+        </div>
+        <table class="layui-table" id="businessparaTable" lay-filter="businessparaTable-filter"></table>
+    </div>
+</div>
+<script>
+    layui.use(['form', 'table', 'layer', 'admin', 'element'], function () {
+        var form = layui.form;
+        var table = layui.table;
+        var admin = layui.admin;
+        // 渲染表格
+        table.render({
+            elem: '#sysparaTable',
+            url: '/param/sysparalist',
+            page: true,
+            cols: [
+                [
+                    {field: 'paraid', title: 'ID', width: 80, align: 'center', fixed: 'left', sort: true},
+                    {field: 'paraname', title: '参数名称', width: 250, align: 'center', fixed: 'left'},
+                    {field: 'paraval', title: '参数值', fixed: 'left', edit: 'text'},
+                    {
+                        field: 'valueType', title: '格式', width: 75, align: 'center', templet: function (item) {
+                            if("amount"==item["valueType"]) return "金额";
+                            else if("switch"==item["valueType"]) return "开关";
+                            else if("date"==item["valueType"]) return "yyyyMMdd";
+                            else if("datetime"==item["valueType"]) return "yyyyMMddHHmmss";
+                            else if("decimal"==item["valueType"]) return "数值";
+                            else if("number"==item["valueType"]) return "整数";
+                            else  return "字符串";
+                        }
+                    },
+                    {field: 'paraunit', title: '单位', width: 75, align: 'center'},
+                    {field: 'remark', title: '备注'}
+                ]
+            ]
+        });
+        // 搜索按钮点击事件
+        $('#btn-search').click(function () {
+            var paraid = $("#search-paraid").val();
+            var paraname = $("#search-paraname").val();
+            if (null != paraid && paraid.length > 0 && !(/^\d+$/.test(paraid))) {
+                layer.msg("参数ID请输入数字", {icon: 2});
+            }else{
+                table.reload('sysparaTable', {where: {paraid: paraid, paraname: paraname}, page: {curr: 1}});
+            }
+        });
+
+        $('#btn-reset').click(function () {
+            $("#search-paraid").val("");
+            $("#search-paraname").val("");
+        });
+
+
+        //监听单元格编辑
+        table.on('edit(sysparaTable-filter)', function (obj) {
+            var row = obj.data; //得到所在行所有键值
+            var newval = obj.value; //得到修改后的值
+            admin.go('/param/sysparaupdate', {
+                paraid: row.paraid,
+                paraval: newval,
+                _csrf: $("meta[name='_csrf_token']").attr("value"),
+            }, function (data) {
+                if (data.code == 200) {
+                    layer.msg("修改成功", {icon: 1});
+                } 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});
+                    table.reload('sysparaTable');
+                }
+            },function () {
+                layer.msg('修改失败了,请稍后再试', {icon: 2});
+                table.reload('sysparaTable');
+            });
+        });
+
+    });
+</script>
\ No newline at end of file
diff --git a/src/main/resources/templates/system/param/syspara.html b/src/main/resources/templates/system/param/syspara.html
new file mode 100644
index 0000000..ae17c1d
--- /dev/null
+++ b/src/main/resources/templates/system/param/syspara.html
@@ -0,0 +1,96 @@
+<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-form toolbar">
+            搜索:
+            <input id="search-paraid" class="layui-input search-input" maxlength="9" type="text" placeholder="输入参数ID"/>&emsp;
+            <input id="search-paraname" class="layui-input search-input" type="text" maxlength="30" placeholder="输入参数名称"/>&emsp;
+            <button id="btn-search" class="layui-btn icon-btn" data-type="search"><i class="layui-icon">&#xe615;</i>搜索
+            </button>
+            <button id="btn-reset" class="layui-btn layui-btn-primary" data-type="reset"><i class="layui-icon"></i>清 空</button>
+        </div>
+        <table class="layui-table" id="sysparaTable" lay-filter="sysparaTable-filter"></table>
+    </div>
+</div>
+<script>
+    layui.use(['form', 'table', 'layer', 'admin', 'element'], function () {
+        var form = layui.form;
+        var table = layui.table;
+        var admin = layui.admin;
+        // 渲染表格
+        table.render({
+            elem: '#sysparaTable',
+            url: '/param/sysparalist',
+            page: true,
+            cols: [
+                [
+                    {field: 'paraid', title: 'ID', width: 80, align: 'center', fixed: 'left', sort: true},
+                    {field: 'paraname', title: '参数名称', width: 250, align: 'center', fixed: 'left'},
+                    {field: 'paraval', title: '参数值', fixed: 'left', edit: 'text'},
+                    {
+                        field: 'valueType', title: '格式', width: 75, align: 'center', templet: function (item) {
+                            if("amount"==item["valueType"]) return "金额";
+                            else if("switch"==item["valueType"]) return "开关";
+                            else if("date"==item["valueType"]) return "yyyyMMdd";
+                            else if("datetime"==item["valueType"]) return "yyyyMMddHHmmss";
+                            else if("decimal"==item["valueType"]) return "数值";
+                            else if("number"==item["valueType"]) return "整数";
+                            else  return "字符串";
+                        }
+                    },
+                    {field: 'paraunit', title: '单位', width: 75, align: 'center'},
+                    {field: 'remark', title: '备注'}
+                ]
+            ]
+        });
+        // 搜索按钮点击事件
+        $('#btn-search').click(function () {
+            var paraid = $("#search-paraid").val();
+            var paraname = $("#search-paraname").val();
+            if (null != paraid && paraid.length > 0 && !(/^\d+$/.test(paraid))) {
+                layer.msg("参数ID请输入数字", {icon: 2});
+            }else{
+                table.reload('sysparaTable', {where: {paraid: paraid, paraname: paraname}, page: {curr: 1}});
+            }
+        });
+
+        $('#btn-reset').click(function () {
+            $("#search-paraid").val("");
+            $("#search-paraname").val("");
+        });
+
+
+        //监听单元格编辑
+        table.on('edit(sysparaTable-filter)', function (obj) {
+            var row = obj.data; //得到所在行所有键值
+            var newval = obj.value; //得到修改后的值
+            admin.go('/param/sysparaupdate', {
+                paraid: row.paraid,
+                paraval: newval,
+                _csrf: $("meta[name='_csrf_token']").attr("value"),
+            }, function (data) {
+                if (data.code == 200) {
+                    layer.msg("修改成功", {icon: 1});
+                } 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});
+                    table.reload('sysparaTable');
+                }
+            },function () {
+                layer.msg('修改失败了,请稍后再试', {icon: 2});
+                table.reload('sysparaTable');
+            });
+        });
+
+    });
+</script>
\ No newline at end of file
diff --git a/src/test/kotlin/com/supwisdom/dlpay/controller/ShopControllerTest.kt b/src/test/kotlin/com/supwisdom/dlpay/controller/ShopControllerTest.kt
index b4347fa..c8fd5ec 100644
--- a/src/test/kotlin/com/supwisdom/dlpay/controller/ShopControllerTest.kt
+++ b/src/test/kotlin/com/supwisdom/dlpay/controller/ShopControllerTest.kt
@@ -29,7 +29,6 @@
     @Test
     fun open() {
         val shopParam = OpenShopParam()
-        shopParam.syscode = "ykt"//random req
         shopParam.shopUniqueId = "ykt_shop_20001"
         shopParam.shopname = "测试名称"
         val gson = Gson()
@@ -50,7 +49,6 @@
     @Test
     fun get() {
         val shopParam = QueryShopParam()
-        shopParam.syscode = "ykt"//random req
         shopParam.shopUniqueId = "ykt_shop_20001"
         val gson = Gson()
         val ret = mvc.perform(post("/shop/open").content(gson.toJson(shopParam))
diff --git a/src/test/kotlin/com/supwisdom/dlpay/controller/UserControllerTest.kt b/src/test/kotlin/com/supwisdom/dlpay/controller/UserControllerTest.kt
index 44724a3..c76eb14 100644
--- a/src/test/kotlin/com/supwisdom/dlpay/controller/UserControllerTest.kt
+++ b/src/test/kotlin/com/supwisdom/dlpay/controller/UserControllerTest.kt
@@ -26,7 +26,6 @@
     @Test
     fun open() {
         val userParam = OpenUserParam()
-        userParam.syscode = "ykt"
         userParam.uid = ""//random req
         userParam.name = "测试名称"
 
@@ -48,7 +47,6 @@
     fun get() {
 
         val userParam = QueryUserParam()
-        userParam.syscode = "ykt"
         userParam.uid = "testuseruniqueId"//测试用
         val gson = Gson()
         val ret = mvc.perform(MockMvcRequestBuilders.post("/api/user/open").content(gson.toJson(userParam))