设备参数组
diff --git a/src/main/java/com/supwisdom/dlpay/exception/WebCheckException.java b/src/main/java/com/supwisdom/dlpay/exception/WebCheckException.java
new file mode 100644
index 0000000..d0b3b6c
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/exception/WebCheckException.java
@@ -0,0 +1,7 @@
+package com.supwisdom.dlpay.exception;
+
+public class WebCheckException extends Exception {
+  public WebCheckException(String message) {
+    super(message);
+  }
+}
diff --git a/src/main/java/com/supwisdom/dlpay/framework/util/DateUtil.java b/src/main/java/com/supwisdom/dlpay/framework/util/DateUtil.java
index a6b733e..80097c6 100644
--- a/src/main/java/com/supwisdom/dlpay/framework/util/DateUtil.java
+++ b/src/main/java/com/supwisdom/dlpay/framework/util/DateUtil.java
@@ -239,7 +239,7 @@
     try {
       java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat(pattern);
       Date d = sdf.parse(datetime);
-      return true;
+      return datetime.trim().equals(sdf.format(d));
     } catch (Exception e) {
     }
     return false;
diff --git a/src/main/java/com/supwisdom/dlpay/restaurant/controller/DeviceParamController.java b/src/main/java/com/supwisdom/dlpay/restaurant/controller/DeviceParamController.java
new file mode 100644
index 0000000..7ffc9b4
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/restaurant/controller/DeviceParamController.java
@@ -0,0 +1,115 @@
+package com.supwisdom.dlpay.restaurant.controller;
+
+import com.supwisdom.dlpay.api.bean.JsonResult;
+import com.supwisdom.dlpay.exception.WebCheckException;
+import com.supwisdom.dlpay.framework.util.NumberUtil;
+import com.supwisdom.dlpay.framework.util.PageResult;
+import com.supwisdom.dlpay.framework.util.StringUtil;
+import com.supwisdom.dlpay.framework.util.WebConstant;
+import com.supwisdom.dlpay.restaurant.domain.TDevparaGroup;
+import com.supwisdom.dlpay.restaurant.service.DeviceParamService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Map;
+
+@Controller
+public class DeviceParamController {
+  @Autowired
+  private DeviceParamService deviceParamService;
+
+  @GetMapping("/device/devpara")
+  public String devparaView() {
+    return "restaurant/devpara/devpara";
+  }
+
+  @GetMapping("/device/devparalist")
+  @PreAuthorize("hasPermission('/device/devpara','')")
+  @ResponseBody
+  public PageResult<TDevparaGroup> getDataList(@RequestParam("page") Integer pageNo,
+                                               @RequestParam("limit") Integer pageSize,
+                                               @RequestParam(value = "groupname", required = false) String groupname) {
+    try {
+      if (null == pageNo || pageNo < 1) pageNo = WebConstant.PAGENO_DEFAULT;
+      if (null == pageSize || pageSize < 1) pageSize = WebConstant.PAGESIZE_DEFAULT;
+      return deviceParamService.getDevparaGroupPage(groupname, pageNo, pageSize);
+    } catch (Exception e) {
+      e.printStackTrace();
+      return new PageResult<>(99, "系统查询错误");
+    }
+  }
+
+  @PostMapping("/device/deletedevpara")
+  @PreAuthorize("hasPermission('/device/deletedevpara','')")
+  @ResponseBody
+  public JsonResult deleteDevpara(@RequestParam("groupid") Integer groupid) {
+    if (null == groupid) {
+      return JsonResult.error("参数传递错误");
+    }
+    TDevparaGroup group = deviceParamService.getDevparaGroupByGroupid(groupid);
+    if (null == group) {
+      return JsonResult.error("参数组不存在!");
+    }
+    try {
+      if (deviceParamService.deleteDevparaGroup(group)) {
+        return JsonResult.ok("删除成功");
+      } else {
+        return JsonResult.error("删除失败");
+      }
+    } catch (WebCheckException ex) {
+      return JsonResult.error(ex.getMessage());
+    } catch (Exception e) {
+      e.printStackTrace();
+      return JsonResult.error("系统处理异常").put("exception", e);
+    }
+  }
+
+  @GetMapping("/device/load4editdevpara")
+  @PreAuthorize("hasPermission('/device/load4editdevpara','')")
+  public String editView(@RequestParam(value = "groupid", required = false) Integer groupid, Model model) {
+    TDevparaGroup group = deviceParamService.getDevparaGroupByGroupid(groupid != null ? groupid : -99);
+    if (null == group) {
+      group = new TDevparaGroup();
+      group.setGroupid(0);
+      model.addAttribute("devparaGroup", group);
+      model.addAttribute("devparaConfigList", deviceParamService.getDevparaInfo(0));
+    } else {
+      model.addAttribute("devparaGroup", group);
+      model.addAttribute("devparaConfigList", deviceParamService.getDevparaInfo(group.getGroupid()));
+    }
+    return "restaurant/devpara/devparaform";
+  }
+
+  @PostMapping("/device/editdevpara")
+  @PreAuthorize("hasPermission('/device/editdevpara','')")
+  @ResponseBody
+  public JsonResult editDevpara(@RequestBody Map<String, String> param) {
+    String groupidHtmlKey = "form_devpara_groupid";   //页面上传来groupid的KEY
+    String groupnameHtmlKey = "form_devpara_groupname"; //页面上传来groupname的KEY
+    if (null == param || !NumberUtil.isDigits(param.get(groupidHtmlKey)) || StringUtil.isEmpty(groupnameHtmlKey)) {
+      return JsonResult.error("参数传递错误");
+    }
+
+    try {
+      int groupid = Integer.parseInt(param.get(groupidHtmlKey));
+      String groupname = param.get(groupnameHtmlKey).trim();
+      param.remove(groupidHtmlKey);
+      param.remove(groupnameHtmlKey);
+
+      if (deviceParamService.saveOrUpdateDevpara(groupid, groupname, param)) {
+        return JsonResult.ok(groupid == 0 ? "新增成功" : "修改成功");
+      } else {
+        return JsonResult.ok(groupid == 0 ? "新增失败" : "修改失败");
+      }
+    } catch (WebCheckException ex) {
+      return JsonResult.error(ex.getMessage());
+    } catch (Exception e) {
+      e.printStackTrace();
+      return JsonResult.error("系统处理异常").put("exception", e);
+    }
+  }
+
+}
diff --git a/src/main/java/com/supwisdom/dlpay/restaurant/dao/DevparaBindDao.java b/src/main/java/com/supwisdom/dlpay/restaurant/dao/DevparaBindDao.java
new file mode 100644
index 0000000..67e32a5
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/restaurant/dao/DevparaBindDao.java
@@ -0,0 +1,13 @@
+package com.supwisdom.dlpay.restaurant.dao;
+
+import com.supwisdom.dlpay.restaurant.domain.TDevparaBind;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface DevparaBindDao extends JpaRepository<TDevparaBind, Integer> {
+
+  @Query("select count(t.deviceid) from TDevparaBind t where t.groupid=?1")
+  long getDevparaBindRecordcnt(int groupid);
+}
diff --git a/src/main/java/com/supwisdom/dlpay/restaurant/dao/DevparaDao.java b/src/main/java/com/supwisdom/dlpay/restaurant/dao/DevparaDao.java
new file mode 100644
index 0000000..68af28a
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/restaurant/dao/DevparaDao.java
@@ -0,0 +1,23 @@
+package com.supwisdom.dlpay.restaurant.dao;
+
+import com.supwisdom.dlpay.restaurant.domain.TDevpara;
+import com.supwisdom.dlpay.restaurant.domain.TDevparaPk;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Repository
+public interface DevparaDao extends JpaRepository<TDevpara, TDevparaPk> {
+  void deleteAllByGroupid(int groupid);
+
+  @Query("from TDevpara t where t.groupid=?1 order by t.paraname ")
+  List<TDevpara> findAllByGroupid(int groupid);
+
+  @Query("select t from TDevpara t,TDevparaGroup g where t.groupid=g.groupid and g.globalflag=true order by t.paraname ")
+  List<TDevpara> findSystemDefaultDevpara();
+
+  @Query("from TDevpara t where t.groupid=?1 and t.paraname=?2")
+  TDevpara findById(Integer groupid, String paraname);
+}
diff --git a/src/main/java/com/supwisdom/dlpay/restaurant/dao/DevparaGroupDao.java b/src/main/java/com/supwisdom/dlpay/restaurant/dao/DevparaGroupDao.java
new file mode 100644
index 0000000..d9b52fe
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/restaurant/dao/DevparaGroupDao.java
@@ -0,0 +1,22 @@
+package com.supwisdom.dlpay.restaurant.dao;
+
+import com.supwisdom.dlpay.restaurant.domain.TDevparaGroup;
+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.Query;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface DevparaGroupDao extends JpaRepository<TDevparaGroup, Integer> {
+  Page<TDevparaGroup> findAllByGroupnameIsContaining(String groupname, Pageable pageable);
+
+  TDevparaGroup findByGroupid(int groupid);
+
+  @Query("select count(t.groupid) from TDevparaGroup t where t.groupname=?1 ")
+  long checkGroupnameExists(String groupname);
+
+  @Query("select count(t.groupid) from TDevparaGroup t where t.groupname=?1 and t.groupid<>?2")
+  long checkGroupnameExists(String groupname, int groupid);
+
+}
diff --git a/src/main/java/com/supwisdom/dlpay/restaurant/domain/TDevpara.java b/src/main/java/com/supwisdom/dlpay/restaurant/domain/TDevpara.java
new file mode 100644
index 0000000..e15124c
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/restaurant/domain/TDevpara.java
@@ -0,0 +1,237 @@
+package com.supwisdom.dlpay.restaurant.domain;
+
+import com.supwisdom.dlpay.framework.util.DateUtil;
+import com.supwisdom.dlpay.framework.util.MoneyUtil;
+import com.supwisdom.dlpay.framework.util.NumberUtil;
+import com.supwisdom.dlpay.framework.util.StringUtil;
+
+import javax.persistence.*;
+
+@Entity
+@Table(name = "TB_DEVPARA")
+@IdClass(TDevparaPk.class)
+public class TDevpara {
+  @Id
+  @Column(name="GROUPID", nullable = false, precision = 9)
+  private Integer groupid;
+
+  @Id
+  @Column(name="PARANAME", nullable = false, length = 60)
+  private String paraname;
+
+  @Column(name="PARAVAL",  length = 90)
+  private String paraval;
+
+  @Column(name="PARADESC",  length = 600)
+  private String paradesc;
+
+  @Column(name="VALTYPE",  length = 10)
+  private String valtype;
+
+  @Column(name="MINVAL",  length = 10)
+  private String minval;
+
+  @Column(name="MAXVAL",  length = 10)
+  private String maxval;
+
+  @Transient
+  private String errmsg;
+
+  public Integer getGroupid() {
+    return groupid;
+  }
+
+  public void setGroupid(Integer groupid) {
+    this.groupid = groupid;
+  }
+
+  public String getParaname() {
+    return paraname;
+  }
+
+  public void setParaname(String paraname) {
+    this.paraname = paraname;
+  }
+
+  public String getParaval() {
+    return paraval;
+  }
+
+  public void setParaval(String paraval) {
+    this.paraval = paraval;
+  }
+
+  public String getParadesc() {
+    return paradesc;
+  }
+
+  public void setParadesc(String paradesc) {
+    this.paradesc = paradesc;
+  }
+
+  public String getValtype() {
+    return valtype;
+  }
+
+  public void setValtype(String valtype) {
+    this.valtype = valtype;
+  }
+
+  public String getMinval() {
+    return minval;
+  }
+
+  public void setMinval(String minval) {
+    this.minval = minval;
+  }
+
+  public String getMaxval() {
+    return maxval;
+  }
+
+  public void setMaxval(String maxval) {
+    this.maxval = maxval;
+  }
+
+  public String getErrmsg() {
+    return errmsg;
+  }
+
+  public void setErrmsg(String errmsg) {
+    this.errmsg = errmsg;
+  }
+
+  public boolean checkValue() {
+    String paraname = this.paraname == null ? "" : this.paraname;
+
+    if (StringUtil.isEmpty(this.paraval)) {
+      this.errmsg = paraname + " 参数值为空";
+      return false;
+    }
+    this.paraval = this.paraval.trim();
+
+    //B-boolean;N-number;A-amount;S-String;DT-'yyyyMMddHHmmss';D-'yyyyMMdd';T-'HHmmss'
+    if (!StringUtil.isEmpty(this.valtype) && !"S".equals(this.valtype)) {
+      //指定了值类型
+      if ("B".equals(this.valtype)) {
+        //boolean
+        if (!"0".equals(this.paraval) && !"1".equals(this.paraval)) {
+          this.errmsg = paraname + " 只能取0或1 ";
+          return false;
+        }
+      }
+
+      if ("N".equals(this.valtype)) {
+        // number
+        if (!NumberUtil.isDigits(this.paraval)) {
+          this.errmsg = paraname + " 只能取整数 ";
+          return false;
+        }
+        if (NumberUtil.isDigits(this.minval) && Long.parseLong(this.paraval) < Long.parseLong(this.minval)) {
+          this.errmsg = paraname + " 小于最小值(" + Long.parseLong(this.minval) + ") ";
+          return false;
+        }
+        if (NumberUtil.isDigits(this.maxval) && Long.parseLong(this.paraval) > Long.parseLong(this.maxval)) {
+          this.errmsg = paraname + " 大于最大值(" + Long.parseLong(this.maxval) + ") ";
+          return false;
+        }
+      }
+
+      if ("A".equals(this.valtype)) {
+        //金额amount
+        if (!NumberUtil.isAmount(this.paraval)) {
+          this.errmsg = paraname + " 为金额,最多两位小数 ";
+          return false;
+        }
+        if (NumberUtil.isAmount(this.minval) && MoneyUtil.moneyCompare(Double.parseDouble(this.paraval), Double.parseDouble(this.minval)) < 0) {
+          this.errmsg = paraname + " 小于最小值(" + MoneyUtil.formatYuan(Double.parseDouble(this.minval)) + ") ";
+          return false;
+        }
+        if (NumberUtil.isAmount(this.maxval) && MoneyUtil.moneyCompare(Double.parseDouble(this.paraval), Double.parseDouble(this.maxval)) > 0) {
+          this.errmsg = paraname + " 大于最大值(" + MoneyUtil.formatYuan(Double.parseDouble(this.maxval)) + ") ";
+          return false;
+        }
+      }
+
+      if ("DT".equals(this.valtype)) {
+        //时间 yyyyMMddHHmmss
+        if (!DateUtil.checkDatetimeValid(this.paraval, "yyyyMMddHHmmss")) {
+          this.errmsg = paraname + " 格式错误(yyyyMMddHhmmss) ";
+          return false;
+        }
+
+        if (!StringUtil.isEmpty(this.minval) && DateUtil.compareDatetime(this.paraval, this.minval, "yyyyMMddHHmmss") < 0) {
+          this.errmsg = paraname + " 小于最小值(" + this.minval + ") ";
+          return false;
+        }
+        if (!StringUtil.isEmpty(this.maxval) && DateUtil.compareDatetime(this.paraval, this.maxval, "yyyyMMddHHmmss") > 0) {
+          this.errmsg = paraname + " 大于最大值(" + this.maxval + ") ";
+          return false;
+        }
+      }
+
+      if ("D".equals(this.valtype)) {
+        //时间 yyyyMMdd
+        if (!DateUtil.checkDatetimeValid(this.paraval, "yyyyMMdd")) {
+          this.errmsg = paraname + " 格式错误(yyyyMMdd) ";
+          return false;
+        }
+
+        if (!StringUtil.isEmpty(this.minval) && DateUtil.compareDatetime(this.paraval, this.minval, "yyyyMMdd") < 0) {
+          this.errmsg = paraname + " 小于最小值(" + this.minval + ") ";
+          return false;
+        }
+        if (!StringUtil.isEmpty(this.maxval) && DateUtil.compareDatetime(this.paraval, this.maxval, "yyyyMMdd") > 0) {
+          this.errmsg = paraname + " 大于最大值(" + this.maxval + ") ";
+          return false;
+        }
+      }
+
+      if ("T".equals(this.valtype)) {
+        //时间 HHmmss
+        if (!DateUtil.checkDatetimeValid(this.paraval, "HHmmss")) {
+          this.errmsg = paraname + " 格式错误(HHmmss) ";
+          return false;
+        }
+
+        if (!StringUtil.isEmpty(this.minval) && DateUtil.compareDatetime(this.paraval, this.minval, "HHmmss") < 0) {
+          this.errmsg = paraname + " 小于最小值(" + this.minval + ") ";
+          return false;
+        }
+        if (!StringUtil.isEmpty(this.maxval) && DateUtil.compareDatetime(this.paraval, this.maxval, "HHmmss") > 0) {
+          this.errmsg = paraname + " 大于最大值(" + this.maxval + ") ";
+          return false;
+        }
+      }
+
+      if ("TS".equals(this.valtype)){
+        //时间(时分) HHmm
+        if (!DateUtil.checkDatetimeValid(this.paraval, "HHmm")) {
+          this.errmsg = paraname + " 格式错误(HHmm) ";
+          return false;
+        }
+
+        if (!StringUtil.isEmpty(this.minval) && DateUtil.compareDatetime(this.paraval, this.minval, "HHmm") < 0) {
+          this.errmsg = paraname + " 小于最小值(" + this.minval + ") ";
+          return false;
+        }
+        if (!StringUtil.isEmpty(this.maxval) && DateUtil.compareDatetime(this.paraval, this.maxval, "HHmm") > 0) {
+          this.errmsg = paraname + " 大于最大值(" + this.maxval + ") ";
+          return false;
+        }
+      }
+
+    } else {
+      //字符串判断
+      if (!StringUtil.isEmpty(this.minval) && this.paraval.compareToIgnoreCase(this.minval) < 0) {
+        this.errmsg = paraname + " 小于最小值(" + this.minval + ") ";
+        return false;
+      }
+      if (!StringUtil.isEmpty(this.maxval) && this.paraval.compareToIgnoreCase(this.maxval) > 0) {
+        this.errmsg = paraname + " 大于最大值(" + this.maxval + ") ";
+        return false;
+      }
+    }
+    return true;
+  }
+}
diff --git a/src/main/java/com/supwisdom/dlpay/restaurant/domain/TDevparaBind.java b/src/main/java/com/supwisdom/dlpay/restaurant/domain/TDevparaBind.java
new file mode 100644
index 0000000..3b704f3
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/restaurant/domain/TDevparaBind.java
@@ -0,0 +1,45 @@
+package com.supwisdom.dlpay.restaurant.domain;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "TB_DEVPARA_BIND")
+public class TDevparaBind {
+
+  @Id
+  @Column(name = "DEVICEID", nullable = false, precision = 9)
+  private Integer deviceid;
+
+  @Column(name = "GROUPID", nullable = false, precision = 9)
+  private Integer groupid;
+
+  @Column(name = "LASTSAVED", length = 14)
+  private String lastsaved;
+
+  public Integer getDeviceid() {
+    return deviceid;
+  }
+
+  public void setDeviceid(Integer deviceid) {
+    this.deviceid = deviceid;
+  }
+
+  public Integer getGroupid() {
+    return groupid;
+  }
+
+  public void setGroupid(Integer groupid) {
+    this.groupid = groupid;
+  }
+
+  public String getLastsaved() {
+    return lastsaved;
+  }
+
+  public void setLastsaved(String lastsaved) {
+    this.lastsaved = lastsaved;
+  }
+}
diff --git a/src/main/java/com/supwisdom/dlpay/restaurant/domain/TDevparaGroup.java b/src/main/java/com/supwisdom/dlpay/restaurant/domain/TDevparaGroup.java
new file mode 100644
index 0000000..24f62b1
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/restaurant/domain/TDevparaGroup.java
@@ -0,0 +1,67 @@
+package com.supwisdom.dlpay.restaurant.domain;
+
+import javax.persistence.*;
+
+@Entity
+@Table(name = "TB_DEVPARA_GROUP")
+public class TDevparaGroup {
+  @Id
+  @SequenceGenerator(name = "devpara_groupid", sequenceName = "SEQ_DEVPARAGROUPID", initialValue=2, allocationSize = 1 )
+  @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "devpara_groupid")
+  @Column(name="GROUPID", nullable = false, precision = 9)
+  private Integer groupid;
+
+  @Column(name="GROUPNAME", length = 200)
+  private String groupname;
+
+  @Column(name="GLOBALFLAG", nullable = false, length = 200)
+  private Boolean globalflag;
+
+  @Version
+  @GeneratedValue(strategy = GenerationType.AUTO)
+  @Column(name="VERNO", nullable = false, precision = 9)
+  private Long verno;
+
+  @Column(name="LASTSAVED", length = 14)
+  private String lastsaved;
+
+  public Integer getGroupid() {
+    return groupid;
+  }
+
+  public void setGroupid(Integer groupid) {
+    this.groupid = groupid;
+  }
+
+  public String getGroupname() {
+    return groupname;
+  }
+
+  public void setGroupname(String groupname) {
+    this.groupname = groupname;
+  }
+
+  public Boolean getGlobalflag() {
+    return globalflag;
+  }
+
+  public void setGlobalflag(Boolean globalflag) {
+    this.globalflag = globalflag;
+  }
+
+  public Long getVerno() {
+    return verno;
+  }
+
+  public void setVerno(Long verno) {
+    this.verno = verno;
+  }
+
+  public String getLastsaved() {
+    return lastsaved;
+  }
+
+  public void setLastsaved(String lastsaved) {
+    this.lastsaved = lastsaved;
+  }
+}
diff --git a/src/main/java/com/supwisdom/dlpay/restaurant/domain/TDevparaPk.java b/src/main/java/com/supwisdom/dlpay/restaurant/domain/TDevparaPk.java
new file mode 100644
index 0000000..f7d12ad
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/restaurant/domain/TDevparaPk.java
@@ -0,0 +1,59 @@
+package com.supwisdom.dlpay.restaurant.domain;
+
+import javax.persistence.Column;
+import javax.persistence.Id;
+import java.io.Serializable;
+
+public class TDevparaPk implements Serializable {
+  @Id
+  @Column(name = "GROUPID", nullable = false, precision = 9)
+  private Integer groupid;
+
+  @Id
+  @Column(name = "PARANAME", nullable = false, length = 60)
+  private String paraname;
+
+  public TDevparaPk() {
+  }
+
+  public TDevparaPk(Integer groupid, String paraname) {
+    this.groupid = groupid;
+    this.paraname = paraname;
+  }
+
+  public Integer getGroupid() {
+    return groupid;
+  }
+
+  public void setGroupid(Integer groupid) {
+    this.groupid = groupid;
+  }
+
+  public String getParaname() {
+    return paraname;
+  }
+
+  public void setParaname(String paraname) {
+    this.paraname = paraname;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    TDevparaPk tDevparaPk = (TDevparaPk) o;
+    if (groupid != null ? !groupid.equals(tDevparaPk.getGroupid()) : groupid != null)
+      return false;
+    if (paraname != null ? !paraname.equals(tDevparaPk.getParaname()) : paraname != null)
+      return false;
+
+    return true;
+  }
+
+  @Override
+  public int hashCode() {
+    int result = groupid != null ? groupid.hashCode() : 0;
+    result = 31 * result + (paraname != null ? paraname.hashCode() : 0);
+    return result;
+  }
+}
diff --git a/src/main/java/com/supwisdom/dlpay/restaurant/service/DeviceParamService.java b/src/main/java/com/supwisdom/dlpay/restaurant/service/DeviceParamService.java
new file mode 100644
index 0000000..9ea1508
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/restaurant/service/DeviceParamService.java
@@ -0,0 +1,30 @@
+package com.supwisdom.dlpay.restaurant.service;
+
+import com.supwisdom.dlpay.exception.WebCheckException;
+import com.supwisdom.dlpay.framework.util.PageResult;
+import com.supwisdom.dlpay.restaurant.domain.TDevpara;
+import com.supwisdom.dlpay.restaurant.domain.TDevparaGroup;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Map;
+
+public interface DeviceParamService {
+  @Transactional(rollbackFor = Exception.class, readOnly = true)
+  PageResult<TDevparaGroup> getDevparaGroupPage(String groupname,int pageNo,int pageSize);
+
+  @Transactional(rollbackFor = Exception.class, readOnly = true)
+  TDevparaGroup getDevparaGroupByGroupid(int groupid);
+
+  @Transactional(rollbackFor = Exception.class)
+  boolean deleteDevparaGroup(TDevparaGroup group) throws WebCheckException;
+
+  @Transactional(rollbackFor = Exception.class, readOnly = true)
+  List<TDevpara> getDevparaInfo(Integer groupid);
+
+  @Transactional(rollbackFor = Exception.class)
+  boolean saveOrUpdateDevpara(int groupid, String groupname, Map<String, String> param) throws WebCheckException;
+
+
+
+}
diff --git a/src/main/java/com/supwisdom/dlpay/restaurant/service/impl/DeviceParamServiceImpl.java b/src/main/java/com/supwisdom/dlpay/restaurant/service/impl/DeviceParamServiceImpl.java
new file mode 100644
index 0000000..c59cad9
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/restaurant/service/impl/DeviceParamServiceImpl.java
@@ -0,0 +1,135 @@
+package com.supwisdom.dlpay.restaurant.service.impl;
+
+import com.supwisdom.dlpay.exception.WebCheckException;
+import com.supwisdom.dlpay.framework.service.SystemUtilService;
+import com.supwisdom.dlpay.framework.util.PageResult;
+import com.supwisdom.dlpay.framework.util.StringUtil;
+import com.supwisdom.dlpay.restaurant.dao.DevparaBindDao;
+import com.supwisdom.dlpay.restaurant.dao.DevparaDao;
+import com.supwisdom.dlpay.restaurant.dao.DevparaGroupDao;
+import com.supwisdom.dlpay.restaurant.domain.TDevpara;
+import com.supwisdom.dlpay.restaurant.domain.TDevparaGroup;
+import com.supwisdom.dlpay.restaurant.service.DeviceParamService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class DeviceParamServiceImpl implements DeviceParamService {
+  @Autowired
+  private DevparaGroupDao devparaGroupDao;
+  @Autowired
+  private DevparaBindDao devparaBindDao;
+  @Autowired
+  private DevparaDao devparaDao;
+  @Autowired
+  private SystemUtilService systemUtilService;
+
+  @Override
+  public PageResult<TDevparaGroup> getDevparaGroupPage(String groupname, int pageNo, int pageSize) {
+    Pageable pageable = PageRequest.of(pageNo - 1, pageSize, Sort.by("groupid"));
+    if (StringUtil.isEmpty(groupname)) {
+      return new PageResult<>(devparaGroupDao.findAll(pageable));
+    }
+    return new PageResult<>(devparaGroupDao.findAllByGroupnameIsContaining(groupname.trim(), pageable));
+  }
+
+  @Override
+  public TDevparaGroup getDevparaGroupByGroupid(int groupid) {
+    return devparaGroupDao.findByGroupid(groupid);
+  }
+
+  @Override
+  public boolean deleteDevparaGroup(TDevparaGroup group) throws WebCheckException {
+    if (null != group) {
+      if (group.getGlobalflag()) {
+        throw new WebCheckException("默认参数组不能删除");
+      }
+      if (devparaBindDao.getDevparaBindRecordcnt(group.getGroupid()) > 0) {
+        throw new WebCheckException("该参数组下绑定了设备,请先解绑。");
+      }
+
+      devparaDao.deleteAllByGroupid(group.getGroupid());
+      devparaGroupDao.delete(group);
+      return true;
+    }
+    return false;
+  }
+
+  @Override
+  public List<TDevpara> getDevparaInfo(Integer groupid) {
+    List<TDevpara> result;
+    if (null != groupid && groupid > 0) {
+      result = devparaDao.findAllByGroupid(groupid);
+    } else {
+      result = devparaDao.findSystemDefaultDevpara(); //默认
+    }
+    if (!StringUtil.isEmpty(result)) {
+      return result;
+    } else {
+      return new ArrayList<>(0);
+    }
+  }
+
+  @Override
+  public boolean saveOrUpdateDevpara(int groupid, String groupname, Map<String, String> param) throws WebCheckException {
+    TDevparaGroup group;
+    if (groupid == 0) {
+      if (devparaGroupDao.checkGroupnameExists(groupname) > 0) {
+        throw new WebCheckException("参数名称已经存在");
+      }
+      group = new TDevparaGroup();
+      group.setGlobalflag(false);
+      group.setGroupname(groupname);
+      group.setLastsaved(systemUtilService.getSysdatetime().getHostdatetime());
+      group.setVerno(1L);
+      group = devparaGroupDao.save(group);
+
+      List<TDevpara> configList = devparaDao.findSystemDefaultDevpara(); //默认
+      for (TDevpara devpara : configList) {
+        TDevpara bean = new TDevpara();
+        bean.setGroupid(group.getGroupid());
+        bean.setParaname(devpara.getParaname());
+        bean.setParaval(param.get(devpara.getParaname()));
+        bean.setParadesc(devpara.getParadesc());
+        bean.setValtype(devpara.getValtype());
+        bean.setMinval(devpara.getMinval());
+        bean.setMaxval(devpara.getMaxval());
+        if (!bean.checkValue()) {
+          throw new WebCheckException(bean.getErrmsg());
+        }
+        devparaDao.save(bean);
+      }
+    } else {
+      group = devparaGroupDao.findByGroupid(groupid);
+      if (null == group) {
+        throw new WebCheckException("原设备参数组不存在");
+      }
+      if (devparaGroupDao.checkGroupnameExists(groupname, groupid) > 0) {
+        throw new WebCheckException("参数名称已经存在");
+      }
+      group.setGroupname(groupname);
+      group.setLastsaved(systemUtilService.getSysdatetime().getHostdatetime());
+      devparaGroupDao.save(group);
+
+      for (String key : param.keySet()) {
+        TDevpara bean = devparaDao.findById(groupid, key);
+        if (null == bean) {
+          throw new WebCheckException("设备参数[" + key + "]在参数组【" + group.getGroupname() + "】中不存在");
+        }
+        bean.setParaval(param.get(key));
+        if (!bean.checkValue()) {
+          throw new WebCheckException(bean.getErrmsg());
+        }
+        devparaDao.save(bean);
+      }
+    }
+    return true;
+  }
+}
diff --git a/src/main/resources/templates/restaurant/devpara/devpara.html b/src/main/resources/templates/restaurant/devpara/devpara.html
new file mode 100644
index 0000000..d460615
--- /dev/null
+++ b/src/main/resources/templates/restaurant/devpara/devpara.html
@@ -0,0 +1,123 @@
+<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-devparagroup-groupname" class="layui-input search-input" maxlength="20" type="text" placeholder="输入名称"/>&emsp;
+            <button id="btn-search-devparagroup" class="layui-btn icon-btn" data-type="search"><i class="layui-icon">&#xe615;</i>搜索
+            </button>
+            <button id="search-devparagroup-add" class="layui-btn icon-btn" data-type="add"><i class="layui-icon">&#xe654;</i>新 增</button>
+        </div>
+        <table class="layui-table" id="devparagroupTable" lay-filter="devparagroupTable-filter"></table>
+    </div>
+</div>
+
+
+<!-- 表格操作列 -->
+<script type="text/html" id="devparagroup-table-bar">
+    <a class="layui-btn layui-btn layui-btn-xs" lay-event="edit">修改</a>
+    {{# if(!d.globalflag){ }}
+    <a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
+    {{# } }}
+</script>
+
+<script>
+    layui.use(['form', 'table', 'layer', 'admin', 'element'], function () {
+        var form = layui.form;
+        var table = layui.table;
+        var admin = layui.admin;
+        // 渲染表格
+        table.render({
+            elem: '#devparagroupTable',
+            url: '[[@{/device/devparalist}]]',
+            page: true,
+            cols: [
+                [
+                    {field: 'groupid', title: '参数组编号', width: 150, align: 'center', fixed: 'left', sort: true},
+                    {field: 'groupname', title: '参数组名称', align: 'center', fixed: 'left'},
+                    {field: 'verno', title: '参数组版本号', align: 'center'},
+                    {field: 'globalflag', title: '默认配置', align: 'center', templet: function(d){
+                        if(d.globalflag) {
+                            return '是'
+                        }else {
+                            return '否'
+                        }
+                        }},
+                    {
+                        field: 'lastsaved', title: '更新时间', align: 'center', templet: function (d) {
+                            return admin.formatDate(d.lastsaved);
+                        }
+                    },
+                    {align: 'center', title: '操作', width: 250, toolbar: '#devparagroup-table-bar',  fixed: 'right'}
+                ]
+            ]
+        });
+
+        // 搜索按钮点击事件
+        $('#btn-search-devparagroup').click(function () {
+            var paraname = $("#search-devparagroup-groupname").val();
+            table.reload('devparagroupTable', {where: {groupname: paraname}, page: {curr: 1}});
+        });
+
+        $('#search-devparagroup-add').click(function () {
+            admin.popupCenter({
+                title: "新增设备参数组",
+                path: '[[@{/device/load4editdevpara}]]',
+                area: '1200px',
+                finish: function () {
+                    table.reload('devparagroupTable');
+                }
+            });
+        });
+
+        //监听单元格
+        table.on('tool(devparagroupTable-filter)', function (obj) {
+            var data = obj.data;
+
+            if('del' == obj.event){
+                layer.confirm('确定要删除设备参数组【'+data.groupname+'】?', {
+                    btn: ['确定', '取消']
+                },function(){
+                    layer.closeAll('dialog');
+                    layer.load(2);
+                    admin.go('[[@{/device/deletedevpara}]]', {
+                        groupid: data.groupid,
+                        _csrf: $("meta[name='_csrf_token']").attr("value")
+                    }, function (data) {
+                        console.log(data.code);
+                        layer.closeAll('loading');
+                        if (data.code == 200) {
+                            layer.msg(data.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('devparagroupTable');
+                    }, function (err) {
+                        admin.errorBack(err)
+                    });
+                });
+            }else if('edit' == obj.event){
+                admin.popupCenter({
+                    title: "修改参数组【" + data.groupid + "_" + data.groupname + "】",
+                    path: '[[@{/device/load4editdevpara}]]?groupid=' + data.groupid,
+                    area: '1200px',
+                    finish: function () {
+                        table.reload('devparagroupTable');
+                    }
+                });
+            }
+        });
+
+    });
+</script>
\ No newline at end of file
diff --git a/src/main/resources/templates/restaurant/devpara/devparaform.html b/src/main/resources/templates/restaurant/devpara/devparaform.html
new file mode 100644
index 0000000..fbc7799
--- /dev/null
+++ b/src/main/resources/templates/restaurant/devpara/devparaform.html
@@ -0,0 +1,123 @@
+<form id="devpara-form" lay-filter="devpara-form-filter" class="layui-form model-form"
+      style="padding: 30px 25px 10px 25px;">
+    <div class="layui-form-item">
+        <div class="layui-inline">
+            <label class="layui-form-label">参数组编号</label>
+            <div class="layui-input-inline">
+                <input name="form_devpara_groupid" id="form-devpara-groupid" type="text" class="layui-input" style="background-color:#fafafa;"
+                       th:value="${devparaGroup.groupid}" autocomplete="off" readonly="readonly"/>
+            </div>
+        </div>
+        <div class="layui-inline">
+            <label class="layui-form-label">参数组名称</label>
+            <div class="layui-input-inline">
+                <input name="form_devpara_groupname" id="form-devpara-groupname" type="text" maxlength="20" class="layui-input"
+                       th:value="${devparaGroup.groupname}" autocomplete="off" lay-verify="required" required/>
+            </div>
+        </div>
+    </div>
+
+    <div class="layui-form-item">
+        <table class="layui-table" lay-size="sm" style="margin-bottom: 0;">
+            <colgroup>
+                <col width="220">
+                <col width="150">
+                <col width="80">
+                <col>
+            </colgroup>
+            <thead>
+            <tr>
+                <th style="text-align: center">参数名</th>
+                <th style="text-align: center">参数值</th>
+                <th style="text-align: center">值类型</th>
+                <th style="text-align: center">参数描述</th>
+            </tr>
+            </thead>
+        </table>
+        <div style="max-height: 400px;overflow-y: auto;">
+            <table class="layui-table" lay-size="sm" style="margin-top: 0;">
+                <colgroup>
+                    <col width="220">
+                    <col width="150">
+                    <col width="80">
+                    <col>
+                </colgroup>
+                <tbody>
+                <tr th:each="config:${devparaConfigList}">
+                    <td th:text="${config.paraname}" style="text-align: left;background-color:#fafafa;">参数名</td>
+                    <td style="text-align: center"><input type="text" class="layui-input layui-table-edit" style="border: 0;box-shadow: 0px 0px 1px rgba(0,0,0,.15);"
+                                                th:name="${config.paraname}" th:value="${config.paraval}" /></td>
+                    <td style="text-align: center;background-color:#fafafa;" th:switch="${config.valtype}">
+                        <span th:case="N">整型</span>
+                        <span th:case="B">布尔型</span>
+                        <span th:case="A">浮点型</span>
+                        <span th:case="D">时间</span>
+                        <span th:case="T">时间</span>
+                        <span th:case="DT">时间</span>
+                        <span th:case="TS">时间</span>
+                        <span th:case="*">字符串</span>
+                    </td>
+                    <td style="text-align: left;background-color:#fafafa;" th:text="${config.paradesc}">参数描述</td>
+                </tr>
+                </tbody>
+            </table>
+        </div>
+    </div>
+
+    <div class="layui-form-item model-form-footer">
+        <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">取消</button>
+        <button class="layui-btn" lay-filter="form-submit" lay-submit id="submitbtn">保存</button>
+    </div>
+</form>
+
+<script>
+    layui.use(['layer', 'admin', 'form'], function () {
+        var layer = layui.layer;
+        var admin = layui.admin;
+        var form = layui.form;
+        // 表单提交事件
+        form.on('submit(form-submit)', function (data) {
+            layer.load(2);
+            var vdata = data.field;
+            if(JSON.stringify(vdata)=="{}"){
+                layer.closeAll('loading');
+                admin.closePopupCenter();
+                return; //无配置项,直接关闭
+            }
+            var token=$("meta[name='_csrf_token']").attr("value");
+            console.log('devpara:',vdata);
+            debugger
+            $.ajax({
+                type : "POST",
+                dataType : "json",
+                url : '[[@{/device/editdevpara}]]',
+                headers: {
+                    'Accept': 'application/json',
+                    'Content-Type': 'application/json',
+                    'X-CSRF-TOKEN':token,
+                },
+                data : JSON.stringify(vdata),
+                success : function(result) {
+                    layer.closeAll('loading');
+                    if (result.code == 200) {
+                        layer.msg(result.msg, {icon: 1});
+                        admin.finishPopupCenter();
+                    } else if (result.code == 401) {
+                        layer.msg(result.msg, {icon: 2, time: 1500}, function () {
+                            location.replace('[[@{/login}]]');
+                        }, 1000);
+                        return;
+                    } else {
+                        console.log('err:' + result.code);
+                        layer.msg(result.msg, {icon: 2});
+                    }
+                },
+                error : function() {
+                    layer.closeAll('loading');
+                    layer.msg("请求服务器失败!", {icon: 2});
+                }
+            });
+            return false;
+        });
+    });
+</script>
\ No newline at end of file