Browse Source

人脸设备对接

qzyReal 2 years ago
parent
commit
8ab944721f
87 changed files with 4798 additions and 1550 deletions
  1. 15 0
      business-system/car-identify/pom.xml
  2. 92 0
      business-system/car-identify/src/main/java/com/pj/biz/TbCarVisitRecordController.java
  3. 16 0
      business-system/epidemic-prevention/pom.xml
  4. 16 0
      business-system/face-identify/pom.xml
  5. 97 0
      business-system/face-identify/src/main/java/com/pj/biz/TbPersonVisitRecordController.java
  6. 16 0
      business-system/monitor-system/pom.xml
  7. 4 0
      business-system/pom.xml
  8. 1 5
      business-system/venues-system/pom.xml
  9. 96 64
      business-system/venues-system/src/main/java/com/pj/biz/TbTerminalController.java
  10. 78 64
      business-system/venues-system/src/main/java/com/pj/biz/TbVenuesController.java
  11. 0 16
      business-system/venues-system/src/main/java/com/pj/biz/UserController.java
  12. 0 92
      business-system/venues-system/src/main/java/com/pj/netty/HeartBeatHandler.java
  13. 0 22
      business-system/venues-system/src/main/java/com/pj/netty/StartNetty.java
  14. 15 1
      pom.xml
  15. 88 85
      sp-admin/src/main/java/com/pj/project4sp/admin4acc/SpAccAdminService.java
  16. 23 15
      sp-core/src/main/java/com/pj/current/SaPlusStartup.java
  17. 8 1
      sp-core/src/main/java/com/pj/current/config/MyConfig.java
  18. 21 0
      sp-core/src/main/java/com/pj/current/netty/command/config/CommandChannelInitializer.java
  19. 46 0
      sp-core/src/main/java/com/pj/current/netty/command/config/CommandNettyServer.java
  20. 150 0
      sp-core/src/main/java/com/pj/current/netty/command/config/CommandNettyServerHandler.java
  21. 40 0
      sp-core/src/main/java/com/pj/current/netty/command/constants/CommandType.java
  22. 21 0
      sp-core/src/main/java/com/pj/current/netty/command/factory/CommandFactory.java
  23. 12 0
      sp-core/src/main/java/com/pj/current/netty/command/handler/IHandler.java
  24. 24 0
      sp-core/src/main/java/com/pj/current/netty/command/handler/dto/BlackPersonDTO.java
  25. 34 0
      sp-core/src/main/java/com/pj/current/netty/command/handler/dto/ChecklogDTO.java
  26. 42 0
      sp-core/src/main/java/com/pj/current/netty/command/handler/dto/HealthlogDTO.java
  27. 35 0
      sp-core/src/main/java/com/pj/current/netty/command/handler/dto/WhitelistDTO.java
  28. 56 0
      sp-core/src/main/java/com/pj/current/netty/command/handler/impl/BlacklistHandler.java
  29. 84 0
      sp-core/src/main/java/com/pj/current/netty/command/handler/impl/CheckLogHandler.java
  30. 86 0
      sp-core/src/main/java/com/pj/current/netty/command/handler/impl/HealthlogHandler.java
  31. 44 0
      sp-core/src/main/java/com/pj/current/netty/command/handler/impl/TerminalOptionHandler.java
  32. 53 0
      sp-core/src/main/java/com/pj/current/netty/command/handler/impl/UploadCommandHandler.java
  33. 25 0
      sp-core/src/main/java/com/pj/current/netty/command/handler/impl/UploadFinishHandler.java
  34. 82 0
      sp-core/src/main/java/com/pj/current/netty/command/handler/impl/UploadUserHandler.java
  35. 25 0
      sp-core/src/main/java/com/pj/current/netty/command/handler/impl/VisitlogHandler.java
  36. 43 0
      sp-core/src/main/java/com/pj/current/netty/command/handler/impl/WhitelistHandler.java
  37. 17 0
      sp-core/src/main/java/com/pj/current/netty/dto/OKMsg.java
  38. 16 0
      sp-core/src/main/java/com/pj/current/netty/dto/ParamsDTO.java
  39. 44 0
      sp-core/src/main/java/com/pj/current/netty/dto/RequestPackageDTO.java
  40. 30 0
      sp-core/src/main/java/com/pj/current/netty/dto/ResponsePackageDTO.java
  41. 56 50
      sp-core/src/main/java/com/pj/current/netty/heart/HeartBeatHandler.java
  42. 3 3
      sp-core/src/main/java/com/pj/current/netty/heart/HeartChannelInitializer.java
  43. 13 13
      sp-core/src/main/java/com/pj/current/netty/heart/HeartNettyServer.java
  44. 940 847
      sp-core/src/main/java/com/pj/current/satoken/StpUserUtil.java
  45. 47 0
      sp-core/src/main/java/com/pj/current/task/Task.java
  46. 41 0
      sp-core/src/main/java/com/pj/current/task/TaskService.java
  47. 120 0
      sp-core/src/main/java/com/pj/project/tb_car_visit_record/TbCarVisitRecord.java
  48. 28 0
      sp-core/src/main/java/com/pj/project/tb_car_visit_record/TbCarVisitRecordMapper.java
  49. 72 0
      sp-core/src/main/java/com/pj/project/tb_car_visit_record/TbCarVisitRecordMapper.xml
  50. 118 0
      sp-core/src/main/java/com/pj/project/tb_car_visit_record/TbCarVisitRecordService.java
  51. 0 65
      sp-core/src/main/java/com/pj/project/tb_channel/TbChannelUtil.java
  52. 74 0
      sp-core/src/main/java/com/pj/project/tb_command_log/TbCommandLogMapper.xml
  53. 57 0
      sp-core/src/main/java/com/pj/project/tb_command_log/TbCommandLogService.java
  54. 28 0
      sp-core/src/main/java/com/pj/project/tb_command_log/task/ClearTask.java
  55. 1 1
      sp-core/src/main/java/com/pj/project/tb_dept/TbDept.java
  56. 27 12
      sp-core/src/main/java/com/pj/project/tb_manager/TbManager.java
  57. 17 19
      sp-core/src/main/java/com/pj/project/tb_manager/TbManagerController.java
  58. 4 4
      sp-core/src/main/java/com/pj/project/tb_manager/TbManagerMapper.java
  59. 27 18
      sp-core/src/main/java/com/pj/project/tb_manager/TbManagerMapper.xml
  60. 11 11
      sp-core/src/main/java/com/pj/project/tb_manager/TbManagerService.java
  61. 71 0
      sp-core/src/main/java/com/pj/project/tb_manager/TbManagerUtil.java
  62. 5 1
      sp-core/src/main/java/com/pj/project/tb_person_black/TbPersonBlack.java
  63. 115 0
      sp-core/src/main/java/com/pj/project/tb_person_black/TbPersonBlackService.java
  64. 8 0
      sp-core/src/main/java/com/pj/project/tb_person_filing/TbPersonFiling.java
  65. 162 2
      sp-core/src/main/java/com/pj/project/tb_person_filing/TbPersonFilingService.java
  66. 55 0
      sp-core/src/main/java/com/pj/project/tb_person_filing/task/Base64ToImage.java
  67. 172 0
      sp-core/src/main/java/com/pj/project/tb_person_visit_record/TbPersonVisitRecord.java
  68. 28 0
      sp-core/src/main/java/com/pj/project/tb_person_visit_record/TbPersonVisitRecordMapper.java
  69. 83 0
      sp-core/src/main/java/com/pj/project/tb_person_visit_record/TbPersonVisitRecordMapper.xml
  70. 153 0
      sp-core/src/main/java/com/pj/project/tb_person_visit_record/TbPersonVisitRecordService.java
  71. 54 0
      sp-core/src/main/java/com/pj/project/tb_person_visit_record/VisitAddBO.java
  72. 58 0
      sp-core/src/main/java/com/pj/project/tb_person_visit_record/task/ConvertBase64ToImage.java
  73. 29 3
      sp-core/src/main/java/com/pj/project/tb_terminal/TbTerminal.java
  74. 1 0
      sp-core/src/main/java/com/pj/project/tb_terminal/TbTerminalMapper.java
  75. 7 5
      sp-core/src/main/java/com/pj/project/tb_terminal/TbTerminalMapper.xml
  76. 258 26
      sp-core/src/main/java/com/pj/project/tb_terminal/TbTerminalService.java
  77. 33 0
      sp-core/src/main/java/com/pj/project/tb_terminal/task/CheckTask.java
  78. 36 0
      sp-core/src/main/java/com/pj/project/tb_terminal/task/CommandDelayTask.java
  79. 27 0
      sp-core/src/main/java/com/pj/project/tb_terminal/task/StateTask.java
  80. 17 2
      sp-core/src/main/java/com/pj/project/tb_venues/TbVenues.java
  81. 13 13
      sp-core/src/main/java/com/pj/project/tb_venues/TbVenuesMapper.xml
  82. 75 33
      sp-core/src/main/java/com/pj/project/tb_venues/TbVenuesService.java
  83. 0 57
      sp-core/src/main/java/com/pj/project/tb_venues/TbVenuesUtil.java
  84. 30 0
      sp-core/src/main/java/com/pj/utils/ImageUtils.java
  85. 4 0
      sp-core/src/main/java/com/pj/utils/cache/RedisUtil.java
  86. 20 0
      sp-start/pom.xml
  87. 5 0
      sp-start/src/main/resources/application-dev.yml

+ 15 - 0
business-system/car-identify/pom.xml

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>business-system</artifactId>
+        <groupId>com.pj</groupId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>car-identify</artifactId>
+
+
+</project>

+ 92 - 0
business-system/car-identify/src/main/java/com/pj/biz/TbCarVisitRecordController.java

@@ -0,0 +1,92 @@
+package com.pj.biz;
+
+import java.util.List;
+
+import com.pj.project.tb_car_visit_record.TbCarVisitRecord;
+import com.pj.project.tb_car_visit_record.TbCarVisitRecordService;
+import com.pj.project.tb_person_visit_record.TbPersonVisitRecord;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+
+import com.pj.utils.sg.*;
+import com.pj.utils.so.*;
+import com.pj.project4sp.SP;
+
+import com.pj.current.satoken.StpUserUtil;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+
+
+/**
+ * Controller: tb_car_visit_record -- 车辆出入记录
+ *
+ * @author qzyReal
+ */
+@RestController
+@RequestMapping("/TbCarVisitRecord/")
+public class TbCarVisitRecordController {
+
+    /**
+     * 底层 Service 对象
+     */
+    @Autowired
+    TbCarVisitRecordService tbCarVisitRecordService;
+
+    /**
+     * 增
+     */
+    @RequestMapping("add")
+    @SaCheckPermission(TbCarVisitRecord.PERMISSION_CODE_ADD)
+    public AjaxJson add(TbCarVisitRecord t) {
+        tbCarVisitRecordService.add(t);
+        return AjaxJson.getSuccessData(t);
+    }
+
+    /**
+     * 删
+     */
+    @RequestMapping("delete")
+    @SaCheckPermission(TbCarVisitRecord.PERMISSION_CODE_DEL)
+    public AjaxJson delete(Long id) {
+        tbCarVisitRecordService.delete(id);
+        return AjaxJson.getSuccess();
+    }
+
+
+
+    /**
+     * 改
+     */
+    @RequestMapping("update")
+    @SaCheckPermission(TbCarVisitRecord.PERMISSION_CODE_EDIT)
+    public AjaxJson update(TbCarVisitRecord t) {
+        tbCarVisitRecordService.update(t);
+        return AjaxJson.getSuccess();
+    }
+
+    /**
+     * 查 - 根据id
+     */
+    @RequestMapping("getById")
+    public AjaxJson getById(Long id) {
+        TbCarVisitRecord t = tbCarVisitRecordService.getById(id);
+        return AjaxJson.getSuccessData(t);
+    }
+
+    /**
+     * 查集合 - 根据条件(参数为空时代表忽略指定条件)
+     */
+    @RequestMapping("getList")
+    public AjaxJson getList() {
+        SoMap so = SoMap.getRequestSoMap();
+        List<TbCarVisitRecord> list = tbCarVisitRecordService.getList(so.startPage());
+        return AjaxJson.getPageData(so.getDataCount(), list);
+    }
+
+    @RequestMapping("export")
+    @SaCheckPermission(TbCarVisitRecord.PERMISSION_CODE_EXPORT)
+    public AjaxJson export() {
+        SoMap soMap = SoMap.getRequestSoMap();
+        return AjaxJson.getSuccessData(tbCarVisitRecordService.export(soMap));
+    }
+}

+ 16 - 0
business-system/epidemic-prevention/pom.xml

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>business-system</artifactId>
+        <groupId>com.pj</groupId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>jar</packaging>
+
+    <artifactId>epidemic-prevention</artifactId>
+
+
+</project>

+ 16 - 0
business-system/face-identify/pom.xml

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>business-system</artifactId>
+        <groupId>com.pj</groupId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>jar</packaging>
+
+    <artifactId>face-identify</artifactId>
+
+
+</project>

+ 97 - 0
business-system/face-identify/src/main/java/com/pj/biz/TbPersonVisitRecordController.java

@@ -0,0 +1,97 @@
+package com.pj.biz;
+
+import java.util.List;
+
+import com.pj.project.tb_person_visit_record.TbPersonVisitRecord;
+import com.pj.project.tb_person_visit_record.TbPersonVisitRecordService;
+import com.pj.project.tb_person_visit_record.VisitAddBO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+
+import com.pj.utils.sg.*;
+import com.pj.utils.so.*;
+import com.pj.project4sp.SP;
+
+import com.pj.current.satoken.StpUserUtil;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+
+
+/**
+ * Controller: tb_person_visit_record -- 人员出入记录
+ *
+ * @author qzyReal
+ */
+@RestController
+@RequestMapping("/TbPersonVisitRecord/")
+public class TbPersonVisitRecordController {
+
+    /**
+     * 底层 Service 对象
+     */
+    @Autowired
+    TbPersonVisitRecordService tbPersonVisitRecordService;
+
+    /**
+     * 增
+     */
+    @RequestMapping("add")
+    @SaCheckPermission(TbPersonVisitRecord.PERMISSION_CODE_ADD)
+    public AjaxJson add(VisitAddBO t) {
+        tbPersonVisitRecordService.add(t);
+        return AjaxJson.getSuccessData(t);
+    }
+
+    /**
+     * 删
+     */
+    @RequestMapping("delete")
+    @SaCheckPermission(TbPersonVisitRecord.PERMISSION_CODE)
+    public AjaxJson delete(Long id) {
+        tbPersonVisitRecordService.delete(id);
+        return AjaxJson.getSuccess();
+    }
+
+
+
+    /**
+     * 改
+     */
+    @RequestMapping("update")
+    @SaCheckPermission(TbPersonVisitRecord.PERMISSION_CODE)
+    public AjaxJson update(TbPersonVisitRecord t) {
+        tbPersonVisitRecordService.update(t);
+        return AjaxJson.getSuccess();
+    }
+
+    /**
+     * 查 - 根据id
+     */
+    @RequestMapping("getById")
+    public AjaxJson getById(Long id) {
+        TbPersonVisitRecord t = tbPersonVisitRecordService.getById(id);
+        return AjaxJson.getSuccessData(t);
+    }
+
+    /**
+     * 查集合 - 根据条件(参数为空时代表忽略指定条件)
+     */
+    @RequestMapping("getList")
+    public AjaxJson getList() {
+        SoMap so = SoMap.getRequestSoMap();
+        long deptId = StpUserUtil.getDeptId();
+        if (deptId != StpUserUtil.ADMIN_DEPT_ID) {
+            so.put("deptId", deptId);
+        }
+        List<TbPersonVisitRecord> list = tbPersonVisitRecordService.getList(so.startPage());
+        return AjaxJson.getPageData(so.getDataCount(), list);
+    }
+    @RequestMapping("export")
+    @SaCheckPermission(TbPersonVisitRecord.PERMISSION_CODE_EXPORT)
+    public AjaxJson export() {
+        SoMap soMap = SoMap.getRequestSoMap();
+        return AjaxJson.getSuccessData(tbPersonVisitRecordService.export(soMap));
+    }
+
+
+}

+ 16 - 0
business-system/monitor-system/pom.xml

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>business-system</artifactId>
+        <groupId>com.pj</groupId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>jar</packaging>
+
+    <artifactId>monitor-system</artifactId>
+
+
+</project>

+ 4 - 0
business-system/pom.xml

@@ -15,6 +15,10 @@
         <module>alarm-system</module>
         <module>venues-system</module>
         <module>filing-system</module>
+        <module>face-identify</module>
+        <module>car-identify</module>
+        <module>epidemic-prevention</module>
+        <module>monitor-system</module>
     </modules>
     <dependencies>
         <dependency>

+ 1 - 5
business-system/venues-system/pom.xml

@@ -15,11 +15,7 @@
 
     <dependencies>
 
-        <dependency>
-            <groupId>io.netty</groupId>
-            <artifactId>netty-all</artifactId>
-            <version>4.1.59.Final</version>
-        </dependency>
+
     </dependencies>
 
 </project>

+ 96 - 64
business-system/venues-system/src/main/java/com/pj/biz/TbTerminalController.java

@@ -17,73 +17,105 @@ import cn.dev33.satoken.annotation.SaCheckPermission;
 
 
 /**
-* Controller: tb_terminal -- 设备管理
-* @author qzyReal
-*/
+ * Controller: tb_terminal -- 设备管理
+ *
+ * @author qzyReal
+ */
 @RestController
 @RequestMapping("/TbTerminal/")
 public class TbTerminalController {
 
-/** 底层 Service 对象 */
-@Autowired
-TbTerminalService tbTerminalService;
-
-/** 增 */
-@RequestMapping("add")
-	@SaCheckPermission(TbTerminal.PERMISSION_CODE)
-@Transactional(rollbackFor = Exception.class)
-public AjaxJson add(TbTerminal t){
-tbTerminalService.add(t);
-return AjaxJson.getSuccessData(t);
-}
-
-/** 删 */
-@RequestMapping("delete")
-	@SaCheckPermission(TbTerminal.PERMISSION_CODE)
-public AjaxJson delete(Long id){
-tbTerminalService.delete(id);
-return AjaxJson.getSuccess();
-}
-
-/** 删 - 根据id列表 */
-@RequestMapping("deleteByIds")
-	@SaCheckPermission(TbTerminal.PERMISSION_CODE)
-public AjaxJson deleteByIds(){
-List<Long> ids = SoMap.getRequestSoMap().getListByComma("ids", long.class);
-	int line = SP.publicMapper.deleteByIds(TbTerminal.TABLE_NAME, ids);
-	return AjaxJson.getByLine(line);
-	}
-
-	/** 改 */
-	@RequestMapping("update")
-		@SaCheckPermission(TbTerminal.PERMISSION_CODE)
-	public AjaxJson update(TbTerminal t){
-		tbTerminalService.update(t);
-	return AjaxJson.getSuccess();
-	}
-
-	/** 查 - 根据id */
-	@RequestMapping("getById")
-	public AjaxJson getById(Long id){
-	TbTerminal t = tbTerminalService.getById(id);
-	return AjaxJson.getSuccessData(t);
-	}
-
-	/** 查集合 - 根据条件(参数为空时代表忽略指定条件) */
-	@RequestMapping("getList")
-	public AjaxJson getList() {
-	SoMap so = SoMap.getRequestSoMap();
-		List<TbTerminal> list = tbTerminalService.getList(so.startPage());
-	return AjaxJson.getPageData(so.getDataCount(), list);
-	}
-
+    /**
+     * 底层 Service 对象
+     */
+    @Autowired
+    TbTerminalService tbTerminalService;
+
+
+
+    /**
+     * 删
+     */
+    @RequestMapping("delete")
+    @SaCheckPermission(TbTerminal.PERMISSION_CODE_DEL)
+    public AjaxJson delete(Long id) {
+        tbTerminalService.delete(id);
+        return AjaxJson.getSuccess();
+    }
+
+    /**
+     * 删
+     */
+    @RequestMapping("unbind")
+    @SaCheckPermission(TbTerminal.PERMISSION_CODE_UNBIND)
+    public AjaxJson unbind(Long id) {
+        tbTerminalService.unbind(id);
+        return AjaxJson.getSuccess();
+    }
+
+    /**
+     * 删 - 根据id列表
+     */
+    @RequestMapping("deleteByIds")
+    @SaCheckPermission(TbTerminal.PERMISSION_CODE_DEL)
+    public AjaxJson deleteByIds() {
+        List<Long> ids = SoMap.getRequestSoMap().getListByComma("ids", long.class);
+        int line = SP.publicMapper.deleteByIds(TbTerminal.TABLE_NAME, ids);
+        return AjaxJson.getByLine(line);
+    }
+
+    /**
+     * 改
+     */
+    @RequestMapping("update")
+    @SaCheckPermission(TbTerminal.PERMISSION_CODE_EDIT)
+    public AjaxJson update(TbTerminal t) {
+        tbTerminalService.update(t);
+        return AjaxJson.getSuccess();
+    }
+
+    /**
+     * 查 - 根据id
+     */
+    @RequestMapping("getById")
+    public AjaxJson getById(Long id) {
+        TbTerminal t = tbTerminalService.getById(id);
+        return AjaxJson.getSuccessData(t);
+    }
+
+    /**
+     * 查集合 - 根据条件(参数为空时代表忽略指定条件)
+     */
+    @RequestMapping("getList")
+    public AjaxJson getList() {
+        SoMap so = SoMap.getRequestSoMap();
+        List<TbTerminal> list = tbTerminalService.getList(so.startPage());
+        return AjaxJson.getPageData(so.getDataCount(), list);
+    }
+
+    /**
+     * 查集合 - 根据条件(参数为空时代表忽略指定条件)
+     */
+    @RequestMapping("restart")
+    @SaCheckPermission(TbTerminal.PERMISSION_CODE_REBOOT)
+    public AjaxJson restart(String sn) {
+        tbTerminalService.restart(sn);
+        return AjaxJson.getSuccess();
+    }
+
+    @RequestMapping("clear")
+    @SaCheckPermission(TbTerminal.PERMISSION_CODE_CLEAR)
+    public AjaxJson clear(Long id) {
+        tbTerminalService.clearData(id);
+        return AjaxJson.getSuccess();
+    }
+
+    @RequestMapping("open")
+    @SaCheckPermission(TbTerminal.PERMISSION_CODE_OPEN)
+    public AjaxJson open(Long id) {
+        tbTerminalService.open(id);
+        return AjaxJson.getSuccess();
+    }
 
 
-
-
-
-
-
-
-
-			}
+}

+ 78 - 64
business-system/venues-system/src/main/java/com/pj/biz/TbVenuesController.java

@@ -17,73 +17,87 @@ import cn.dev33.satoken.annotation.SaCheckPermission;
 
 
 /**
-* Controller: tb_venues -- 场所管理
-* @author qzyReal
-*/
+ * Controller: tb_venues -- 场所管理
+ *
+ * @author qzyReal
+ */
 @RestController
 @RequestMapping("/TbVenues/")
 public class TbVenuesController {
 
-/** 底层 Service 对象 */
-@Autowired
-TbVenuesService tbVenuesService;
-
-/** 增 */
-@RequestMapping("add")
-	@SaCheckPermission(TbVenues.PERMISSION_CODE)
-@Transactional(rollbackFor = Exception.class)
-public AjaxJson add(TbVenues t){
-tbVenuesService.add(t);
-return AjaxJson.getSuccessData(t);
-}
-
-/** 删 */
-@RequestMapping("delete")
-	@SaCheckPermission(TbVenues.PERMISSION_CODE)
-public AjaxJson delete(Long id){
-tbVenuesService.delete(id);
-return AjaxJson.getSuccess();
-}
-
-/** 删 - 根据id列表 */
-@RequestMapping("deleteByIds")
-	@SaCheckPermission(TbVenues.PERMISSION_CODE)
-public AjaxJson deleteByIds(){
-List<Long> ids = SoMap.getRequestSoMap().getListByComma("ids", long.class);
-	int line = SP.publicMapper.deleteByIds(TbVenues.TABLE_NAME, ids);
-	return AjaxJson.getByLine(line);
-	}
-
-	/** 改 */
-	@RequestMapping("update")
-		@SaCheckPermission(TbVenues.PERMISSION_CODE)
-	public AjaxJson update(TbVenues t){
-		tbVenuesService.update(t);
-	return AjaxJson.getSuccess();
-	}
-
-	/** 查 - 根据id */
-	@RequestMapping("getById")
-	public AjaxJson getById(Long id){
-	TbVenues t = tbVenuesService.getById(id);
-	return AjaxJson.getSuccessData(t);
-	}
-
-	/** 查集合 - 根据条件(参数为空时代表忽略指定条件) */
-	@RequestMapping("getList")
-	public AjaxJson getList() {
-	SoMap so = SoMap.getRequestSoMap();
-		List<TbVenues> list = tbVenuesService.getList(so.startPage());
-	return AjaxJson.getPageData(so.getDataCount(), list);
-	}
-
+    /**
+     * 底层 Service 对象
+     */
+    @Autowired
+    TbVenuesService tbVenuesService;
+
+    /**
+     * 增
+     */
+    @RequestMapping("add")
+    @SaCheckPermission(TbVenues.PERMISSION_CODE_ADD)
+    public AjaxJson add(TbVenues t) {
+        tbVenuesService.add(t);
+        return AjaxJson.getSuccessData(t);
+    }
+
+    /**
+     * 删
+     */
+    @RequestMapping("delete")
+    @SaCheckPermission(TbVenues.PERMISSION_CODE_DEL)
+    public AjaxJson delete(Long id) {
+        tbVenuesService.delete(id);
+        return AjaxJson.getSuccess();
+    }
+
+
+    /**
+     * 改
+     */
+    @RequestMapping("update")
+    @SaCheckPermission(TbVenues.PERMISSION_CODE_EDIT)
+    public AjaxJson update(TbVenues t) {
+        tbVenuesService.update(t);
+        return AjaxJson.getSuccess();
+    }
+
+    /**
+     * 查 - 根据id
+     */
+    @RequestMapping("getById")
+    public AjaxJson getById(Long id) {
+        TbVenues t = tbVenuesService.getById(id);
+        return AjaxJson.getSuccessData(t);
+    }
+
+    /**
+     * 查集合 - 根据条件(参数为空时代表忽略指定条件)
+     */
+    @RequestMapping("getList")
+    public AjaxJson getList() {
+        SoMap so = SoMap.getRequestSoMap();
+        List<TbVenues> list = tbVenuesService.getList(so.startPage());
+        return AjaxJson.getPageData(so.getDataCount(), list);
+    }
+
+    /**
+     * 查集合 (整个表数据转化为tree结构返回)
+     */
+    @RequestMapping("getTree")
+    public AjaxJson getTree() {
+        // 获取记录
+        SoMap so = SoMap.getRequestSoMap();
+        long deptId = StpUserUtil.getDeptId();
+        if (deptId != StpUserUtil.ADMIN_DEPT_ID) {
+            so.put("deptId", deptId);
+        }
+        List<TbVenues> list = tbVenuesService.getList(so);
+        // 转为tree结构,并返回
+        List<SoMap> listMap = SoMap.getSoMapByList(list);
+        List<SoMap> listTree = SoMap.listToTree(listMap, "id", "pId", "children");
+        return AjaxJson.getPageData((long) listMap.size(), listTree);
+    }
 
 
-
-
-
-
-
-
-
-			}
+}

+ 0 - 16
business-system/venues-system/src/main/java/com/pj/biz/UserController.java

@@ -1,16 +0,0 @@
-package com.pj.biz;
-
-import com.pj.utils.sg.AjaxJson;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-@RequestMapping("test")
-@RestController
-public class UserController {
-
-    @RequestMapping(value = "test")
-    public AjaxJson test(){
-
-        return AjaxJson.getSuccess();
-    }
-}

+ 0 - 92
business-system/venues-system/src/main/java/com/pj/netty/HeartBeatHandler.java

@@ -1,92 +0,0 @@
-package com.pj.netty;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.channel.ChannelHandler;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelInboundHandlerAdapter;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Component;
-
-import java.nio.charset.StandardCharsets;
-
-
-@ChannelHandler.Sharable
-@Slf4j
-@Component
-public class HeartBeatHandler extends ChannelInboundHandlerAdapter {
-
-    /**
-     * 接收报文
-     */
-    @Override
-    public void channelRead(ChannelHandlerContext ctx, Object msg) {
-        ByteBuf byteBuf = (ByteBuf) msg;
-        int len = byteBuf.readableBytes();
-        log.info("---------------start process msg--------------------");
-        log.info("readable bytes is:" + len);
-        byte[] old = new byte[len];
-        for (int i = 0; i < len; i++) {
-            old[i] = byteBuf.readByte();
-        }
-        log.info(String.format("message:%s", msg));
-        //相关处理
-    }
-    public static String toBinaryString(String str) {
-        if (str == null) {
-            return null;
-        }
-
-        StringBuffer sb = new StringBuffer();
-
-        byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
-        System.out.println(bytes.length);
-        for (byte aByte : bytes) {
-            sb.append(Integer.toBinaryString(aByte));
-        }
-        return sb.toString();
-    }
-
-    /**
-     * 接收到客户端信息完成
-     */
-    @Override
-    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
-        log.info("接收到客户端信息完成");
-        ctx.flush();
-    }
-
-    /**
-     * 异常信息
-     */
-    @Override
-    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
-        if (cause instanceof Exception) {
-            log.info("异常捕获");
-            cause.printStackTrace();
-        }
-    }
-    /**
-     * 接入连接
-     */
-    @Override
-    public void channelActive(ChannelHandlerContext ctx) throws Exception {
-        log.info("CLIENT" + getRemoteAddress(ctx) + " 接入连接");
-    }
-
-    /**
-     * 断开连接
-     */
-    @Override
-    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
-        log.info("CLIENT" + getRemoteAddress(ctx) + " 断开连接");
-        ctx.close();
-    }
-
-    /**
-     * 获取设备IP地址
-     */
-    public static String getRemoteAddress(ChannelHandlerContext ctx) {
-        return ctx.channel().remoteAddress().toString();
-    }
-
-}

+ 0 - 22
business-system/venues-system/src/main/java/com/pj/netty/StartNetty.java

@@ -1,22 +0,0 @@
-package com.pj.netty;
-
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.boot.CommandLineRunner;
-import org.springframework.stereotype.Component;
-
-import javax.annotation.Resource;
-import java.net.InetSocketAddress;
-
-@Component
-@Slf4j
-public class StartNetty implements CommandLineRunner {
-    @Resource
-    private NettyServer nettyServer;
-    @Override
-    public void run(String... args) throws Exception {
-        InetSocketAddress address = new InetSocketAddress("192.168.3.27",9999);
-        log.info("neety服务器启动地址:192.168.3.27:9999 ");
-        nettyServer.start(address);
-
-    }
-}

+ 15 - 1
pom.xml

@@ -147,7 +147,21 @@
         	<artifactId>spring-boot-configuration-processor</artifactId>
         	<optional>true</optional>
         </dependency>
-        
+		<dependency>
+			<groupId>org.apache.poi</groupId>
+			<artifactId>poi-ooxml</artifactId>
+			<version>4.1.2</version>
+		</dependency>
+		<dependency>
+			<groupId>io.netty</groupId>
+			<artifactId>netty-all</artifactId>
+			<version>4.1.59.Final</version>
+		</dependency>
+		<dependency>
+			<groupId>com.google.guava</groupId>
+			<artifactId>guava</artifactId>
+			<version>20.0</version>
+		</dependency>
     </dependencies>
 
 

+ 88 - 85
sp-admin/src/main/java/com/pj/project4sp/admin4acc/SpAccAdminService.java

@@ -29,110 +29,113 @@ import eu.bitwalker.useragentutils.UserAgent;
 
 /**
  * service:admin账号相关
- * @author kong
  *
+ * @author kong
  */
 @Service
 public class SpAccAdminService {
 
-	@Resource
-	SpAdminMapper spAdminMapper;
-	
-	@Autowired
-	SpRolePermissionService spRolePermissionService;
-
-	@Autowired
-	SpAdminLoginMapper sysLoginLogMapper;
-	
-	/**
-	  * 登录 
-	 * @param key 账号 (ID / 名称 / 手机号)
-	 * @param password 密码 
-	 * @return
-	 */
-	@Transactional(rollbackFor = Exception.class)
-	public AjaxJson doLogin(String key, String password) {
-		
-		// 0、判断 way (1=ID, 2=昵称,3=手机号  )
-    	int way = 2;	
-    	if(NbUtil.isNumber(key) == true){
-    		way = 1;
-    		if(key.length() == 11){
-    			way = 3;
-    		}
-    	}
-		
-		// 2、获取admin
-        SpAdmin admin = null;	
-        if(way == 1) {
-        	admin = spAdminMapper.getById(Long.parseLong(key)); 
+    @Resource
+    SpAdminMapper spAdminMapper;
+
+    @Autowired
+    SpRolePermissionService spRolePermissionService;
+
+    @Autowired
+    SpAdminLoginMapper sysLoginLogMapper;
+
+    /**
+     * 登录
+     *
+     * @param key      账号 (ID / 名称 / 手机号)
+     * @param password 密码
+     * @return
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public AjaxJson doLogin(String key, String password) {
+
+        // 0、判断 way (1=ID, 2=昵称,3=手机号  )
+        int way = 2;
+        if (NbUtil.isNumber(key) == true) {
+            way = 1;
+            if (key.length() == 11) {
+                way = 3;
+            }
+        }
+
+        // 2、获取admin
+        SpAdmin admin = null;
+        if (way == 1) {
+            admin = spAdminMapper.getById(Long.parseLong(key));
         }
-        if(way == 2) {
-        	admin = spAdminMapper.getByName(key); 
+        if (way == 2) {
+            admin = spAdminMapper.getByName(key);
         }
-        if(way == 3) {
-        	admin = spAdminMapper.getByPhone(key); 
+        if (way == 3) {
+            admin = spAdminMapper.getByPhone(key);
         }
-        
+
 
         // 3、开始验证
-        if(admin == null){
-        	return AjaxJson.getError("无此账号");	
+        if (admin == null) {
+            return AjaxJson.getError("无此账号");
         }
-        if(NbUtil.isNull(admin.getPassword2())) {
-        	return AjaxJson.getError("此账号尚未设置密码,无法登陆");
+        if (NbUtil.isNull(admin.getPassword2())) {
+            return AjaxJson.getError("此账号尚未设置密码,无法登陆");
         }
         String md5Password = SystemObject.getPasswordMd5(admin.getId(), password);
-        if(admin.getPassword2().equals(md5Password) == false){
-        	return AjaxJson.getError("密码错误");	
+        if (admin.getPassword2().equals(md5Password) == false) {
+            return AjaxJson.getError("密码错误");
         }
-        
+
         // 4、是否禁用
-        if(admin.getStatus() == 2) {
-        	return AjaxJson.getError("此账号已被禁用,如有疑问,请联系管理员");	
+        if (admin.getStatus() == 2) {
+            return AjaxJson.getError("此账号已被禁用,如有疑问,请联系管理员");
         }
 
         // =========== 至此, 已登录成功 ============ 
-        StpUtil.login(admin.getId()); 		
+        StpUtil.login(admin.getId());
         String tokenValue = StpUtil.getTokenValue();
         successLogin(admin, tokenValue);
-		StpUserUtil.setDeptId(admin.getDeptId());
+        StpUserUtil.setDeptId(admin.getDeptId());
+        StpUserUtil.setAdmin(admin.getNickname(), admin.getPhone());
         // 组织返回参数
-		SoMap map = new SoMap();
-		map.put("admin", admin);
-		map.put("appCfg", SpCfgUtil.getAppCfg());
-		map.put("perList", spRolePermissionService.getPcodeByRid(admin.getRoleId()));
-		map.put("tokenInfo", StpUtil.getTokenInfo());
-		return AjaxJson.getSuccessData(map);	
-	}
-	
-	/**
-	 * 指定id的账号成功登录一次 (修改最后登录时间等数据 )
-	 * @param s
-	 * @return
-	 */
-	void successLogin(SpAdmin s, String tokenValue){
-		HttpServletRequest request = SpringMVCUtil.getRequest();
-		UserAgent ua = UserAgent.parseUserAgentString(request.getHeader("user-agent"));
-		String loginIp = IpUtil.getIP(request);
-		
-		// 1、修改 admin表 最后登录日志 
-		int line = spAdminMapper.updateLoginLog(s.getId(), loginIp);
-		if(line > 0) {
-	        s.setLoginIp(loginIp);
-	        s.setLoginTime(new Date());
-	        s.setLoginCount(s.getLoginCount() + 1);
-		}
-		
-		// 2、在管理员登录日志表增加记录 
-		SpAdminLogin al = new SpAdminLogin();	// 声明对象 
-		al.setAccId(s.getId());		// 管理员id 
-		al.setAccToken(tokenValue);	// 本次登录Token 
-		al.setLoginIp(loginIp);		// 登陆IP 
-		al.setAddress(IpUtil.getAddres(loginIp));		// 客户端所在地址 
-		al.setDevice(ua.getBrowser().getName());	// 客户端标识 
-		al.setSystem(ua.getOperatingSystem().getName()); 	// 客户端系统 
-		sysLoginLogMapper.add(al);
-	}
-	
+        SoMap map = new SoMap();
+        map.put("admin", admin);
+        map.put("appCfg", SpCfgUtil.getAppCfg());
+        map.put("perList", spRolePermissionService.getPcodeByRid(admin.getRoleId()));
+        map.put("tokenInfo", StpUtil.getTokenInfo());
+        return AjaxJson.getSuccessData(map);
+    }
+
+    /**
+     * 指定id的账号成功登录一次 (修改最后登录时间等数据 )
+     *
+     * @param s
+     * @return
+     */
+    void successLogin(SpAdmin s, String tokenValue) {
+        HttpServletRequest request = SpringMVCUtil.getRequest();
+        UserAgent ua = UserAgent.parseUserAgentString(request.getHeader("user-agent"));
+        String loginIp = IpUtil.getIP(request);
+
+        // 1、修改 admin表 最后登录日志
+        int line = spAdminMapper.updateLoginLog(s.getId(), loginIp);
+        if (line > 0) {
+            s.setLoginIp(loginIp);
+            s.setLoginTime(new Date());
+            s.setLoginCount(s.getLoginCount() + 1);
+        }
+
+        // 2、在管理员登录日志表增加记录
+        SpAdminLogin al = new SpAdminLogin();    // 声明对象
+        al.setAccId(s.getId());        // 管理员id
+        al.setAccToken(tokenValue);    // 本次登录Token
+        al.setLoginIp(loginIp);        // 登陆IP
+        al.setAddress(IpUtil.getAddres(loginIp));        // 客户端所在地址
+        al.setDevice(ua.getBrowser().getName());    // 客户端标识
+        al.setSystem(ua.getOperatingSystem().getName());    // 客户端系统
+        sysLoginLogMapper.add(al);
+    }
+
 }

+ 23 - 15
sp-core/src/main/java/com/pj/current/SaPlusStartup.java

@@ -1,19 +1,26 @@
 package com.pj.current;
 
-import java.net.InetAddress;
+import java.net.InetSocketAddress;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 
+import com.pj.current.config.MyConfig;
+import com.pj.current.netty.command.config.CommandNettyServer;
+import com.pj.current.netty.heart.HeartNettyServer;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.CommandLineRunner;
 import org.springframework.stereotype.Component;
 
+import javax.annotation.Resource;
+
 /**
  * springboot启动之后 
  * @author kong
  *
  */
 @Component
+@Slf4j
 public class SaPlusStartup implements CommandLineRunner {
 
 	@Value("${spring.application.name:sa-plus}")
@@ -27,22 +34,23 @@ public class SaPlusStartup implements CommandLineRunner {
 
     @Value("${spring.profiles.active:}")
     private String active;
+
+    @Resource
+    private MyConfig myConfig;
+
+
    
     @Override
-    public void run(String... args) throws Exception {
-    	 String ip = InetAddress.getLocalHost().getHostAddress();
-         String str = "\n------------- " + applicationName + " (" + active + ") 启动成功 --by " + getNow() + " -------------\n" + 
-                 "\t- Local:   http://localhost:" + port + path + "\n" +
-                 "\t- Local2:  http://127.0.0.1:" + port + path + ""
-                 ;
-         System.out.println(str);
-         // 
-         try {
-        	 System.out.println("\t- Network: http://" + ip + ":" + port + path + "\n");
-         } catch (Exception e) {
-        	 System.out.println("\t- Network: 未能成功获取\n");
-        	 System.err.println("异常:" + e.getMessage());
-         }
+    public void run(String... args) {
+        InetSocketAddress commandAddress = new InetSocketAddress(myConfig.getIp(),myConfig.getCommandPort());
+        CommandNettyServer commandNettyServer=  new CommandNettyServer(commandAddress);
+        commandNettyServer.setDaemon(true);
+        commandNettyServer.start();
+
+        InetSocketAddress hearAddress = new InetSocketAddress(myConfig.getIp(),myConfig.getHeartPort());
+        HeartNettyServer heartNettyServer=  new HeartNettyServer(hearAddress);
+        heartNettyServer.setDaemon(true);
+        heartNettyServer.start();
     }
 
 	/**

+ 8 - 1
sp-core/src/main/java/com/pj/current/config/MyConfig.java

@@ -50,5 +50,12 @@ public class MyConfig {
 	 * 是否把API请求日志记录在数据库 
 	 */
 	private Boolean logToDb = true;
-	
+
+
+	private String ip;
+	private int heartPort;
+	private int commandPort;
+	private String commandPrefix;
+	private String heartPrefix;
+
 }

+ 21 - 0
sp-core/src/main/java/com/pj/current/netty/command/config/CommandChannelInitializer.java

@@ -0,0 +1,21 @@
+package com.pj.current.netty.command.config;
+
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.FixedRecvByteBufAllocator;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
+import io.netty.util.CharsetUtil;
+
+import java.nio.charset.Charset;
+
+public class CommandChannelInitializer extends ChannelInitializer<SocketChannel> {
+    @Override
+    protected void initChannel(SocketChannel channel) throws Exception {
+        channel.config().setRecvByteBufAllocator(new FixedRecvByteBufAllocator(2056));
+        channel.pipeline().addLast("decoder",new StringDecoder(Charset.forName("GBK")));
+        channel.pipeline().addLast("encoder",new StringEncoder(Charset.forName("GBK")));
+        channel.pipeline().addLast(new CommandNettyServerHandler());
+    }
+
+}

+ 46 - 0
sp-core/src/main/java/com/pj/current/netty/command/config/CommandNettyServer.java

@@ -0,0 +1,46 @@
+package com.pj.current.netty.command.config;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.*;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import lombok.extern.slf4j.Slf4j;
+
+import java.net.InetSocketAddress;
+
+@Slf4j
+
+public class CommandNettyServer extends Thread{
+   private InetSocketAddress address;
+
+    public CommandNettyServer(InetSocketAddress address) {
+        this.address = address;
+    }
+
+    @Override
+    public void run() {
+        //配置服务端的NIO线程组
+        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
+        EventLoopGroup workerGroup = new NioEventLoopGroup();
+
+        try {
+            ServerBootstrap bootstrap = new ServerBootstrap()
+                    .group(bossGroup, workerGroup)  // 绑定线程池
+                    .channel(NioServerSocketChannel.class)
+                    .localAddress(address)
+                    .childHandler(new CommandChannelInitializer())//编码解码
+                    .option(ChannelOption.SO_BACKLOG, 258)  //服务端接受连接的队列长度,如果队列已满,客户端连接将被拒绝
+                    .childOption(ChannelOption.SO_KEEPALIVE, true);  //保持长连接,2小时无数据激活心跳机制
+
+            // 绑定端口,开始接收进来的连接
+            ChannelFuture future = bootstrap.bind(address).sync();
+            log.info("命令服务器开始监听:{}" ,address.getPort());
+            //关闭channel和块,直到它被关闭
+            future.channel().closeFuture().sync();
+        } catch (Exception e) {
+            e.printStackTrace();
+            bossGroup.shutdownGracefully();
+            workerGroup.shutdownGracefully();
+        }
+    }
+}

+ 150 - 0
sp-core/src/main/java/com/pj/current/netty/command/config/CommandNettyServerHandler.java

@@ -0,0 +1,150 @@
+package com.pj.current.netty.command.config;
+
+import cn.hutool.cache.CacheUtil;
+import cn.hutool.cache.impl.TimedCache;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.extra.spring.SpringUtil;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.pj.current.config.MyConfig;
+import com.pj.current.netty.command.factory.CommandFactory;
+import com.pj.current.netty.dto.RequestPackageDTO;
+import com.pj.project.tb_command_log.TbCommandLog;
+import com.pj.project.tb_command_log.TbCommandLogService;
+import com.pj.project.tb_terminal.TbTerminalService;
+import com.pj.utils.cache.RedisUtil;
+import io.netty.channel.*;
+import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
+import io.netty.handler.timeout.IdleState;
+import io.netty.handler.timeout.IdleStateEvent;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.net.InetSocketAddress;
+import java.nio.charset.Charset;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Slf4j
+@ChannelHandler.Sharable
+@Component
+public class CommandNettyServerHandler extends ChannelInboundHandlerAdapter {
+    /**
+     * 管理一个全局map,保存连接进服务端的通道数量
+     */
+    private static final ConcurrentHashMap<ChannelId, ChannelHandlerContext> CHANNEL_MAP = new ConcurrentHashMap<>();
+
+
+    /**
+     * @param ctx
+     * @DESCRIPTION: 有客户端连接服务器会触发此函数
+     * @return: void
+     */
+    @Override
+    public void channelActive(ChannelHandlerContext ctx) {
+
+    }
+
+    /**
+     * @param ctx
+     * @DESCRIPTION: 有客户端终止连接服务器会触发此函数
+     * @return: void
+     */
+    @Override
+    public void channelInactive(ChannelHandlerContext ctx) {
+        log.info("设备离线======");
+    }
+
+    TimedCache<String, String> CONTENT_CACHE = CacheUtil.newTimedCache(30000);
+
+    /**
+     * @param ctx
+     * @DESCRIPTION: 有客户端发消息会触发此函数
+     * @return: void
+     */
+    @Override
+    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+        String channelId = ctx.channel().id().asShortText();
+        String str = msg.toString();
+        if (!StrUtil.endWith(str, "}") && StrUtil.startWith(str, "{")) {
+            CONTENT_CACHE.put(channelId, str);
+            return;
+        } else if (!StrUtil.endWith(str, "}") && !StrUtil.startWith(str, "{")) {
+            String content = CONTENT_CACHE.get(channelId);
+            content += str;
+            CONTENT_CACHE.put(channelId, content);
+            return;
+        } else if (StrUtil.endWith(str, "}")) {
+            String content = CONTENT_CACHE.get(channelId);
+            if (StrUtil.isEmpty(content)) {
+                content = "";
+            }
+            str = content + str;
+            CONTENT_CACHE.remove(channelId);
+        }
+        log.info("终端请求:{}", str);
+        JSONObject reqObj = JSONUtil.parseObj(str);
+        Integer code = reqObj.getInt("code");
+        if (code != null) {//设备端回复服务器的消息,无需再回复设备
+            String msgId = RedisUtil.get(channelId);
+            TbCommandLogService tbCommandLogService = SpringUtil.getBean(TbCommandLogService.class);
+            TbCommandLog tbCommandLog = tbCommandLogService.findByMsgId(msgId);
+            tbCommandLog.setResponseCode(code)
+                    .setResponseContent(str)
+                    .setResponseTime(new Date());
+            tbCommandLogService.updateById(tbCommandLog);
+            RedisUtil.del(channelId);
+            return;
+        }
+        RequestPackageDTO packageDTO = JSONUtil.toBean(str, RequestPackageDTO.class);
+        String funcId = packageDTO.getFuncId();
+        CommandFactory commandFactory = SpringUtil.getBean(CommandFactory.class);
+        commandFactory.handler(funcId).handler(ctx, packageDTO);
+
+    }
+
+    /**
+     * @param msg 需要发送的消息内容
+     * @DESCRIPTION: 服务端给客户端发送消息
+     * @return: void
+     */
+    public void channelWrite(ChannelHandlerContext ctx, Object msg) throws Exception {
+        //将客户端的信息直接返回写入ctx
+        ctx.write(msg);
+        //刷新缓存区
+        ctx.flush();
+    }
+
+    @Override
+    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
+        String socketString = ctx.channel().remoteAddress().toString();
+
+        if (evt instanceof IdleStateEvent) {
+            IdleStateEvent event = (IdleStateEvent) evt;
+            if (event.state() == IdleState.READER_IDLE) {
+                log.info("Client: " + socketString + " READER_IDLE 读超时");
+                ctx.disconnect();
+            } else if (event.state() == IdleState.WRITER_IDLE) {
+                log.info("Client: " + socketString + " WRITER_IDLE 写超时");
+                ctx.disconnect();
+            } else if (event.state() == IdleState.ALL_IDLE) {
+                log.info("Client: " + socketString + " ALL_IDLE 总超时");
+                ctx.disconnect();
+            }
+        }
+    }
+
+    /**
+     * @param ctx
+     * @DESCRIPTION: 发生异常会触发此函数
+     * @return: void
+     */
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+        ctx.close();
+        log.info(ctx.channel().id() + " 发生了错误,此连接被关闭" + "此时连通数量: " + CHANNEL_MAP.size());
+        cause.printStackTrace();
+    }
+
+}

+ 40 - 0
sp-core/src/main/java/com/pj/current/netty/command/constants/CommandType.java

@@ -0,0 +1,40 @@
+package com.pj.current.netty.command.constants;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public enum CommandType {
+    /**
+     * 上传类的
+     */
+    UPLOAD_COMMAND("device.upload.command","请求下发命令"),
+    UPLOAD_USER("device.upload.user","人员信息上传"),
+    UPLOAD_CHECK_LOG("device.upload.checklog","核验记录上传"),
+    UPLOAD_WHITELIST("device.upload.whitelist","上传白名单"),
+    UPLOAD_BLACKLIST("device.upload.blacklist","上传黑名单"),
+    UPLOAD_VISIT_LOG("device.upload.visitlog","访客记录上传"),
+    UPLOAD_TERMINAL_OPTION("device.option.upload","上传设备属性"),
+    UPLOAD_HEALTH_LOG("device.upload.healthlog","健康核查记录上传"),
+    UPLOAD_FINISH("device.upload.finish","设备数据上传结束"),
+
+    /**
+     * 下发类的
+     */
+    ISSUED_CLEAR_COMMAND("cmd.clear.all","清除所有数据"),
+    ISSUED_REBOOT_COMMAND("cmd.device.reboot","重启"),
+    ISSUED_BLACKLIST_COMMAND("cmd.update.blacklist","下发黑名单"),
+    DELETE_BLACKLIST_COMMAND("cmd.delete.blacklist","删除黑名单"),
+    ISSUED_USER_COMMAND("cmd.update.user","下发人员信息"),
+     DELETE_USER_COMMAND("cmd.delete.user","删除人员信息"),
+    ISSUED_OPTION_COMMAND("cmd.device.option.upload","获取设备特性"),
+    ISSUED_FINISH_COMMAND("cmd.device.finish","后台命令下发结束"),
+    ISSUED_NORMALOPEN_COMMAND("cmd.device.normalopen","远程开门"),
+    SYNC_TIME_COMMAND("cmd.device.sync.time","同步时间"),
+
+    ;
+
+    private String funcId;
+    private String desc;
+}

+ 21 - 0
sp-core/src/main/java/com/pj/current/netty/command/factory/CommandFactory.java

@@ -0,0 +1,21 @@
+package com.pj.current.netty.command.factory;
+
+
+import com.pj.current.global.BusinessException;
+import com.pj.current.netty.command.handler.IHandler;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class CommandFactory {
+    @Autowired
+    private List<IHandler>handlers;
+
+    public IHandler handler(String funcId){
+       return handlers.stream().filter(h->h.commandType().getFuncId().equals(funcId))
+                .findAny().orElseThrow(()->new BusinessException("不支持交互"));
+    }
+
+}

+ 12 - 0
sp-core/src/main/java/com/pj/current/netty/command/handler/IHandler.java

@@ -0,0 +1,12 @@
+package com.pj.current.netty.command.handler;
+
+import com.pj.current.netty.command.constants.CommandType;
+import com.pj.current.netty.dto.RequestPackageDTO;
+import io.netty.channel.ChannelHandlerContext;
+
+public interface IHandler {
+
+    CommandType commandType();
+
+    void handler(ChannelHandlerContext ctx, RequestPackageDTO packageDTO);
+}

+ 24 - 0
sp-core/src/main/java/com/pj/current/netty/command/handler/dto/BlackPersonDTO.java

@@ -0,0 +1,24 @@
+package com.pj.current.netty.command.handler.dto;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+@Data
+@Accessors(chain = true)
+public class BlackPersonDTO implements Serializable {
+
+
+    /**
+     * identity_number : 350623xxxxxxxxxxx
+     * name : xxxxxxx
+     * sex : xxxxx
+     * nation : 汉
+     */
+
+    private String identity_number;
+    private String name;
+    private String sex;
+    private String nation;
+
+}

+ 34 - 0
sp-core/src/main/java/com/pj/current/netty/command/handler/dto/ChecklogDTO.java

@@ -0,0 +1,34 @@
+package com.pj.current.netty.command.handler.dto;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+@Accessors(chain = true)
+public class ChecklogDTO implements Serializable {
+
+
+    /**
+     * pin : xxxxxxxxxxxxx
+     * verify_type : 0
+     * date : 2012-03-01 20:01:00
+     * status : 1
+     * picture : xxxxxx
+     * direction : 1
+     * temperature : xxxx
+     * allow_through : true
+     */
+
+    private String pin;
+    private String verify_type;
+    private Date date;
+    private String status;
+    private String picture;
+    private Integer direction;
+    private String temperature;
+    private boolean allow_through;
+
+}

+ 42 - 0
sp-core/src/main/java/com/pj/current/netty/command/handler/dto/HealthlogDTO.java

@@ -0,0 +1,42 @@
+package com.pj.current.netty.command.handler.dto;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+@Accessors(chain = true)
+public class HealthlogDTO implements Serializable {
+
+
+    /**
+     * identity_number : 350623xxxxxxxxxxx
+     * name : xxxxxxx
+     * sex : xxxxxxx
+     * phone : xxxxxxx
+     * temperature : 36.5
+     * health_status : 绿码
+     * verify_type : 0
+     * picture : xxxxxxxxxxxxx
+     * date : 2012-03-01 20:01:00
+     * status : 1
+     * direction : 1
+     * allow_through : true
+     */
+
+    private String identity_number;
+    private String name;
+    private String sex;
+    private String phone;
+    private String temperature;
+    private String health_status;
+    private String verify_type;
+    private String picture;
+    private Date date;
+    private String status;
+    private Integer direction;
+    private boolean allow_through;
+
+}

+ 35 - 0
sp-core/src/main/java/com/pj/current/netty/command/handler/dto/WhitelistDTO.java

@@ -0,0 +1,35 @@
+package com.pj.current.netty.command.handler.dto;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+@Data
+@Accessors(chain = true)
+public class WhitelistDTO implements Serializable {
+
+    /**
+     * identity_number : 350623xxxxxxxxxxx
+     * name : xxxxxxx
+     * nation : xxxxxxx
+     * sex : xxxxxxx
+     * card : xxxxxxx
+     * photo : xxxxxxx
+     * start_time : xxxxxxx
+     * end_time : xxxxxxx
+     * check_num : -1
+     */
+
+    private String pin;
+    private String identity_number;
+    private String name;
+    private String nation;
+    private String sex;
+    private String card;
+    private String photo;
+    private String start_time;
+    private String end_time;
+    private String check_num;
+
+}

+ 56 - 0
sp-core/src/main/java/com/pj/current/netty/command/handler/impl/BlacklistHandler.java

@@ -0,0 +1,56 @@
+package com.pj.current.netty.command.handler.impl;
+
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.pj.current.netty.command.constants.CommandType;
+import com.pj.current.netty.command.handler.IHandler;
+import com.pj.current.netty.command.handler.dto.BlackPersonDTO;
+import com.pj.current.netty.command.handler.dto.WhitelistDTO;
+import com.pj.current.netty.dto.OKMsg;
+import com.pj.current.netty.dto.RequestPackageDTO;
+import com.pj.project.tb_person_black.TbPersonBlack;
+import com.pj.project.tb_person_black.TbPersonBlackService;
+import com.pj.project.tb_person_filing.TbPersonFiling;
+import com.pj.project.tb_terminal.TbTerminal;
+import com.pj.project.tb_terminal.TbTerminalService;
+import io.netty.channel.ChannelHandlerContext;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+
+/**
+ * 白名单上传
+ */
+@Service
+@Slf4j
+public class BlacklistHandler implements IHandler {
+    @Resource
+    private TbTerminalService tbTerminalService;
+
+    @Resource
+    private TbPersonBlackService tbPersonBlackService;
+
+    @Override
+    public CommandType commandType() {
+        return CommandType.UPLOAD_BLACKLIST;
+    }
+
+    @Override
+    public void handler(ChannelHandlerContext ctx, RequestPackageDTO packageDTO) {
+        log.info("========黑名单上传===========");
+        JSONObject params = JSONUtil.parseObj(packageDTO.getPayload().getParams());
+        String sn = params.getStr("sn");
+        TbTerminal tbTerminal = tbTerminalService.findBySN(sn);
+        if (tbTerminal == null || tbTerminal.getDeptId() == null) {
+            log.error("设备未绑定=========:{}", sn);
+            return;
+        }
+        ctx.write(OKMsg.ok());
+        ctx.flush();
+        BlackPersonDTO uploadUserDTO = JSONUtil.toBean(params.getStr("users"), BlackPersonDTO.class);
+        Long deptId = tbTerminal.getDeptId();
+        TbPersonBlack tbPersonBlack = tbPersonBlackService.findByIdCardAndDeptId(uploadUserDTO.getIdentity_number(),deptId);
+
+    }
+}

+ 84 - 0
sp-core/src/main/java/com/pj/current/netty/command/handler/impl/CheckLogHandler.java

@@ -0,0 +1,84 @@
+package com.pj.current.netty.command.handler.impl;
+
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.pj.current.netty.command.constants.CommandType;
+import com.pj.current.netty.command.handler.IHandler;
+import com.pj.current.netty.command.handler.dto.ChecklogDTO;
+import com.pj.current.netty.dto.OKMsg;
+import com.pj.current.netty.dto.RequestPackageDTO;
+import com.pj.current.task.TaskService;
+import com.pj.project.tb_dept.TbDept;
+import com.pj.project.tb_dept.TbDeptService;
+import com.pj.project.tb_person_filing.TbPersonFiling;
+import com.pj.project.tb_person_filing.TbPersonFilingService;
+import com.pj.project.tb_person_visit_record.TbPersonVisitRecord;
+import com.pj.project.tb_person_visit_record.TbPersonVisitRecordService;
+import com.pj.project.tb_person_visit_record.task.ConvertBase64ToImage;
+import com.pj.project.tb_terminal.TbTerminal;
+import com.pj.project.tb_terminal.TbTerminalService;
+import io.netty.channel.ChannelHandlerContext;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.Date;
+
+/**
+ * 人脸核验记录
+ */
+@Service
+@Slf4j
+public class CheckLogHandler implements IHandler {
+
+    @Resource
+    private TbPersonVisitRecordService tbPersonVisitRecordService;
+    @Resource
+    private TbTerminalService tbTerminalService;
+    @Resource
+    private TbPersonFilingService tbPersonFilingService;
+    @Resource
+    private TaskService taskService;
+    @Resource
+    private TbDeptService tbDeptService;
+
+    @Override
+    public CommandType commandType() {
+        return CommandType.UPLOAD_CHECK_LOG;
+    }
+
+    @Override
+    public void handler(ChannelHandlerContext ctx, RequestPackageDTO packageDTO) {
+        log.info("============核验记录上传========:{}", JSONUtil.toJsonStr(packageDTO));
+        JSONObject params = JSONUtil.parseObj(packageDTO.getPayload().getParams());
+        String str = params.getStr("log");
+        ChecklogDTO dto = JSONUtil.toBean(str, ChecklogDTO.class);
+
+        String sn = params.getStr("sn");
+        TbTerminal tbTerminal = tbTerminalService.findBySN(sn);
+        Long deptId = tbTerminal.getDeptId();
+        TbDept tbDept = tbDeptService.getById(deptId);
+        Date date = dto.getDate();
+        LocalDateTime dayTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
+        Integer direction = dto.getDirection();
+        TbPersonFiling tbPersonFiling = tbPersonFilingService.findByPin(dto.getPin());
+        String idCard = tbPersonFiling.getIdCard();
+        TbPersonVisitRecord record = tbPersonVisitRecordService.findTheNearRecord(idCard, deptId, direction, dayTime.minusSeconds(8));
+        if (record == null) {
+            record = new TbPersonVisitRecord();
+        }
+        record.setDeptId(tbTerminal.getDeptId()).setType(TbPersonVisitRecord.TypeEnum.UPLOAD.getType())
+                .setVenues(tbTerminal.getVenuesName()).setChannel(tbTerminal.getChannelName())
+                .setTerminalName(tbTerminal.getName()).setVisitDate(dto.getDate()).setCreateTime(new Date())
+                .setSn(sn).setUpdateTime(new Date()).setAllowPass(dto.isAllow_through())
+                .setDirection(dto.getDirection()).setTemperature(dto.getTemperature()).setDeptName(tbDept.getName())
+                .setName(tbPersonFiling.getName()).setPhone(tbPersonFiling.getPhone()).setIdCard(tbPersonFiling.getIdCard());
+        tbPersonVisitRecordService.saveOrUpdate(record);
+        ctx.write(OKMsg.ok());
+        ctx.flush();
+        taskService.addTask(new ConvertBase64ToImage(RandomUtil.randomNumbers(8), 2000, record.getId(), dto.getPicture()));
+    }
+}

+ 86 - 0
sp-core/src/main/java/com/pj/current/netty/command/handler/impl/HealthlogHandler.java

@@ -0,0 +1,86 @@
+package com.pj.current.netty.command.handler.impl;
+
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.pj.current.netty.command.constants.CommandType;
+import com.pj.current.netty.command.handler.IHandler;
+import com.pj.current.netty.command.handler.dto.ChecklogDTO;
+import com.pj.current.netty.command.handler.dto.HealthlogDTO;
+import com.pj.current.netty.dto.OKMsg;
+import com.pj.current.netty.dto.RequestPackageDTO;
+import com.pj.current.task.TaskService;
+import com.pj.project.tb_dept.TbDept;
+import com.pj.project.tb_dept.TbDeptService;
+import com.pj.project.tb_person_filing.TbPersonFiling;
+import com.pj.project.tb_person_filing.TbPersonFilingService;
+import com.pj.project.tb_person_visit_record.TbPersonVisitRecord;
+import com.pj.project.tb_person_visit_record.TbPersonVisitRecordService;
+import com.pj.project.tb_person_visit_record.task.ConvertBase64ToImage;
+import com.pj.project.tb_terminal.TbTerminal;
+import com.pj.project.tb_terminal.TbTerminalService;
+import io.netty.channel.ChannelHandlerContext;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.Date;
+
+/**
+ * 健康核查记录上传
+ */
+@Service
+@Slf4j
+public class HealthlogHandler implements IHandler {
+    @Resource
+    private TbPersonVisitRecordService tbPersonVisitRecordService;
+    @Resource
+    private TbTerminalService tbTerminalService;
+    @Resource
+    private TbPersonFilingService tbPersonFilingService;
+    @Resource
+    private TbDeptService tbDeptService;
+    @Resource
+    private TaskService taskService;
+
+    @Override
+    public CommandType commandType() {
+        return CommandType.UPLOAD_HEALTH_LOG;
+    }
+
+    @Override
+    public void handler(ChannelHandlerContext ctx, RequestPackageDTO packageDTO) {
+        log.info("========健康核查记录上传===========");
+        JSONObject params = JSONUtil.parseObj(packageDTO.getPayload().getParams());
+        String str = params.getStr("log");
+        HealthlogDTO dto = JSONUtil.toBean(str, HealthlogDTO.class);
+        String sn = params.getStr("sn");
+        TbTerminal tbTerminal = tbTerminalService.findBySN(sn);
+        Long deptId = tbTerminal.getDeptId();
+        String idCard = dto.getIdentity_number();
+        TbDept tbDept = tbDeptService.getById(deptId);
+        TbPersonFiling tbPersonFiling = tbPersonFilingService.findByIdCardAndDeptId(idCard, deptId);
+        Date date = dto.getDate();
+        LocalDateTime dayTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
+        Integer direction = dto.getDirection();
+        TbPersonVisitRecord record = tbPersonVisitRecordService.findTheNearRecord(idCard, deptId, direction, dayTime.minusSeconds(8));
+        if (record == null) {
+            record = new TbPersonVisitRecord();
+        }
+        record.setDeptId(tbTerminal.getDeptId()).setType(TbPersonVisitRecord.TypeEnum.UPLOAD.getType())
+                .setVenues(tbTerminal.getVenuesName()).setChannel(tbTerminal.getChannelName())
+                .setTerminalName(tbTerminal.getName()).setVisitDate(dto.getDate()).setCreateTime(new Date())
+                .setSn(sn).setUpdateTime(new Date()).setAllowPass(dto.isAllow_through()).setHealthStatus(dto.getHealth_status())
+                .setDirection(dto.getDirection()).setTemperature(dto.getTemperature()).setDeptName(tbDept.getName())
+                .setName(tbPersonFiling.getName()).setPhone(tbPersonFiling.getPhone()).setIdCard(tbPersonFiling.getIdCard());
+        tbPersonVisitRecordService.saveOrUpdate(record);
+        ctx.write(OKMsg.ok());
+        ctx.flush();
+        taskService.addTask(new ConvertBase64ToImage(RandomUtil.randomNumbers(8), 2000, record.getId(), dto.getPicture()));
+
+    }
+}

+ 44 - 0
sp-core/src/main/java/com/pj/current/netty/command/handler/impl/TerminalOptionHandler.java

@@ -0,0 +1,44 @@
+package com.pj.current.netty.command.handler.impl;
+
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.pj.current.netty.command.constants.CommandType;
+import com.pj.current.netty.command.handler.IHandler;
+import com.pj.current.netty.dto.OKMsg;
+import com.pj.current.netty.dto.RequestPackageDTO;
+import com.pj.project.tb_terminal.TbTerminal;
+import com.pj.project.tb_terminal.TbTerminalService;
+import io.netty.channel.ChannelHandlerContext;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+
+/**
+ * 设备属性处理
+ */
+@Service
+@Slf4j
+public class TerminalOptionHandler implements IHandler {
+
+    @Resource
+    private TbTerminalService tbTerminalService;
+
+    @Override
+    public CommandType commandType() {
+        return CommandType.UPLOAD_TERMINAL_OPTION;
+    }
+
+    @Override
+    public void handler(ChannelHandlerContext ctx, RequestPackageDTO packageDTO) {
+        JSONObject params = JSONUtil.parseObj(packageDTO.getPayload().getParams());
+        String sn = params.getStr("sn");
+        Long userCount = params.getLong("UserCount");
+        String ip = params.getStr("IPAddress");
+        TbTerminal tbTerminal = tbTerminalService.findBySN(sn);
+        tbTerminal.setPersonNum(userCount).setIpAddress(ip);
+        tbTerminalService.updateById(tbTerminal);
+        ctx.write(OKMsg.ok());
+        ctx.flush();
+    }
+}

+ 53 - 0
sp-core/src/main/java/com/pj/current/netty/command/handler/impl/UploadCommandHandler.java

@@ -0,0 +1,53 @@
+package com.pj.current.netty.command.handler.impl;
+
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+import com.pj.current.config.MyConfig;
+import com.pj.current.netty.command.constants.CommandType;
+import com.pj.current.netty.command.handler.IHandler;
+import com.pj.current.netty.dto.RequestPackageDTO;
+import com.pj.current.task.TaskService;
+import com.pj.project.tb_terminal.task.CommandDelayTask;
+import com.pj.utils.cache.RedisUtil;
+import io.netty.channel.ChannelHandlerContext;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+/**
+ * 设备请求下发命令
+ */
+@Service
+@Slf4j
+public class UploadCommandHandler implements IHandler {
+    @Resource
+    private MyConfig myConfig;
+    @Resource
+    private TaskService taskService;
+
+    @Override
+    public CommandType commandType() {
+        return CommandType.UPLOAD_COMMAND;
+    }
+
+    @Override
+    public void handler(ChannelHandlerContext ctx, RequestPackageDTO packageDTO) {
+        log.info("进入请求命令处理========================");
+        String sn = JSONUtil.parseObj(packageDTO.getPayload().getParams()).getStr("sn");
+        String key = myConfig.getCommandPrefix() + sn;
+        String commandListStr = RedisUtil.get(key);
+        List<String> list = JSONUtil.toList(commandListStr, String.class);
+        long delay = Long.parseLong(RandomUtil.randomNumbers(4));
+        for (String command : list) {
+            RequestPackageDTO requestPackageDTO = JSONUtil.toBean(command, RequestPackageDTO.class);
+            String taskId=RandomUtil.randomNumbers(16);
+            String msgId=requestPackageDTO.getMsgId();
+            taskService.addTask(new CommandDelayTask(taskId, delay, ctx, command,msgId));
+            delay = delay + 1000;
+        }
+        RedisUtil.del(key);
+    }
+}

+ 25 - 0
sp-core/src/main/java/com/pj/current/netty/command/handler/impl/UploadFinishHandler.java

@@ -0,0 +1,25 @@
+package com.pj.current.netty.command.handler.impl;
+
+import com.pj.current.netty.command.constants.CommandType;
+import com.pj.current.netty.command.handler.IHandler;
+import com.pj.current.netty.dto.RequestPackageDTO;
+import io.netty.channel.ChannelHandlerContext;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/**
+ * 上传结束处理---------无需响应
+ */
+@Slf4j
+@Service
+public class UploadFinishHandler implements IHandler {
+    @Override
+    public CommandType commandType() {
+        return CommandType.UPLOAD_FINISH;
+    }
+
+    @Override
+    public void handler(ChannelHandlerContext ctx, RequestPackageDTO packageDTO) {
+        log.info("==========上传结束===================");
+    }
+}

+ 82 - 0
sp-core/src/main/java/com/pj/current/netty/command/handler/impl/UploadUserHandler.java

@@ -0,0 +1,82 @@
+package com.pj.current.netty.command.handler.impl;
+
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.pj.current.netty.command.constants.CommandType;
+import com.pj.current.netty.command.handler.IHandler;
+import com.pj.current.netty.command.handler.dto.WhitelistDTO;
+import com.pj.current.netty.dto.OKMsg;
+import com.pj.current.netty.dto.RequestPackageDTO;
+import com.pj.current.task.TaskService;
+import com.pj.project.tb_person_filing.TbPersonFiling;
+import com.pj.project.tb_person_filing.TbPersonFilingService;
+import com.pj.project.tb_person_filing.task.Base64ToImage;
+import com.pj.project.tb_terminal.TbTerminal;
+import com.pj.project.tb_terminal.TbTerminalService;
+import io.netty.channel.ChannelHandlerContext;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 上传人员信息
+ */
+@Service
+@Slf4j
+public class UploadUserHandler implements IHandler {
+    @Resource
+    private TbTerminalService tbTerminalService;
+    @Resource
+    private TbPersonFilingService tbPersonFilingService;
+    @Resource
+    private TaskService taskService;
+
+
+
+    @Override
+    public CommandType commandType() {
+        return CommandType.UPLOAD_USER;
+    }
+
+    @Override
+    public void handler(ChannelHandlerContext ctx, RequestPackageDTO packageDTO) {
+        JSONObject params = JSONUtil.parseObj(packageDTO.getPayload().getParams());
+        String sn = params.getStr("sn");
+        TbTerminal tbTerminal = tbTerminalService.findBySN(sn);
+        if (tbTerminal == null || tbTerminal.getDeptId() == null) {
+            log.error("设备未绑定=========:{}", sn);
+            return;
+        }
+        ctx.write(OKMsg.ok());
+        ctx.flush();
+        Long deptId = tbTerminal.getDeptId();
+        WhitelistDTO uploadUserDTO = JSONUtil.toBean(params.getStr("users"), WhitelistDTO.class);
+        TbPersonFiling personFiling = tbPersonFilingService.findByIdCardAndDeptId(uploadUserDTO.getIdentity_number(),deptId);
+        if (personFiling == null) {
+            Date now = new Date();
+            personFiling = new TbPersonFiling();
+            personFiling.setCreateTime(now)
+                    .setUpdateTime(now)
+                    .setPin(uploadUserDTO.getPin())
+                    .setCreateBy("设备上传")
+                    .setIdCard(uploadUserDTO.getIdentity_number())
+                    .setDeptId(deptId)
+                    .setName(uploadUserDTO.getName());
+            tbPersonFilingService.save(personFiling);
+            //转换图片
+            taskService.addTask(new Base64ToImage(RandomUtil.randomNumbers(10),2000,personFiling.getId(),uploadUserDTO.getPhoto()));
+        }
+        List<TbTerminal> list = tbTerminalService.findByDeptId(deptId);
+        //分发人员信息,不分发上传设备
+        list.stream().filter(t -> !StrUtil.equals(t.getSn(), sn))
+                .forEach(t -> {
+                    tbTerminalService.setCommand(CommandType.ISSUED_USER_COMMAND.getFuncId(), params, t);
+                    tbTerminalService.startHeartCommand(sn);
+                });
+    }
+}

+ 25 - 0
sp-core/src/main/java/com/pj/current/netty/command/handler/impl/VisitlogHandler.java

@@ -0,0 +1,25 @@
+package com.pj.current.netty.command.handler.impl;
+
+import com.pj.current.netty.command.constants.CommandType;
+import com.pj.current.netty.command.handler.IHandler;
+import com.pj.current.netty.dto.RequestPackageDTO;
+import io.netty.channel.ChannelHandlerContext;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/**
+ * 访客记录
+ */
+@Service
+@Slf4j
+public class VisitlogHandler implements IHandler {
+    @Override
+    public CommandType commandType() {
+        return CommandType.UPLOAD_VISIT_LOG;
+    }
+
+    @Override
+    public void handler(ChannelHandlerContext ctx, RequestPackageDTO packageDTO) {
+        log.info("========访客记录上传===========");
+    }
+}

+ 43 - 0
sp-core/src/main/java/com/pj/current/netty/command/handler/impl/WhitelistHandler.java

@@ -0,0 +1,43 @@
+package com.pj.current.netty.command.handler.impl;
+
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.pj.current.netty.command.constants.CommandType;
+import com.pj.current.netty.command.handler.IHandler;
+import com.pj.current.netty.command.handler.dto.WhitelistDTO;
+import com.pj.current.netty.dto.OKMsg;
+import com.pj.current.netty.dto.RequestPackageDTO;
+import com.pj.current.task.TaskService;
+import com.pj.project.tb_person_filing.TbPersonFiling;
+import com.pj.project.tb_person_filing.TbPersonFilingService;
+import com.pj.project.tb_person_filing.task.Base64ToImage;
+import com.pj.project.tb_terminal.TbTerminal;
+import com.pj.project.tb_terminal.TbTerminalService;
+import io.netty.channel.ChannelHandlerContext;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 白名单上传
+ */
+@Service
+@Slf4j
+public class WhitelistHandler implements IHandler {
+
+
+    @Override
+    public CommandType commandType() {
+        return CommandType.UPLOAD_WHITELIST;
+    }
+
+    @Override
+    public void handler(ChannelHandlerContext ctx, RequestPackageDTO packageDTO) {
+        log.info("========白名单上传===========");
+    }
+}

+ 17 - 0
sp-core/src/main/java/com/pj/current/netty/dto/OKMsg.java

@@ -0,0 +1,17 @@
+package com.pj.current.netty.dto;
+
+import cn.hutool.json.JSONUtil;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class OKMsg {
+    private int code;
+    private String message;
+    public static String ok(){
+        return JSONUtil.toJsonStr(new OKMsg(0,"ok"));
+    }
+}

+ 16 - 0
sp-core/src/main/java/com/pj/current/netty/dto/ParamsDTO.java

@@ -0,0 +1,16 @@
+package com.pj.current.netty.dto;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+@Data
+@Accessors(chain = true)
+public class ParamsDTO {
+    /**
+     * 1=有命令 0 无命令
+     */
+    private int command = 0;
+    private String secret = "dahk12fldsahkjgfds";
+    private String ip;
+    private long port;
+}

+ 44 - 0
sp-core/src/main/java/com/pj/current/netty/dto/RequestPackageDTO.java

@@ -0,0 +1,44 @@
+package com.pj.current.netty.dto;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+@Data
+@Accessors(chain = true)
+public class RequestPackageDTO implements Serializable {
+
+
+    /**
+     * funcId : device.upload.heartbeat
+     * payload : {"params":{"sn":"05132021092800030682693417061020"}}
+     */
+
+    /**
+     * 自己标识
+     */
+    private String msgId;
+
+    private String funcId;
+    private PayloadBean payload;
+
+    @Data
+    public static class PayloadBean {
+        /**
+         * params : {"sn":"05132021092800030682693417061020"}
+         */
+
+        private Object params;
+    }
+
+    public static RequestPackageDTO getHeartCommandDTO() {
+        RequestPackageDTO requestPackageDTO = new RequestPackageDTO();
+        RequestPackageDTO.PayloadBean payloadBean = new RequestPackageDTO.PayloadBean();
+        ParamsDTO paramsDTO = new ParamsDTO();
+        paramsDTO.setCommand(1);
+        payloadBean.setParams(paramsDTO);
+        requestPackageDTO.setPayload(payloadBean);
+        return requestPackageDTO;
+    }
+}

+ 30 - 0
sp-core/src/main/java/com/pj/current/netty/dto/ResponsePackageDTO.java

@@ -0,0 +1,30 @@
+package com.pj.current.netty.dto;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+@Data
+@Accessors(chain = true)
+public class ResponsePackageDTO {
+
+
+    /**
+     * code : 0
+     * message : xxxx
+     * payload : {"params":{"command":0,"secret":"xxxxxxxxx","ip":"192.168.215.36","port":18092}}
+     */
+
+    private int code=0;
+    private String message="success";
+    private PayloadBean payload;
+
+    @Data
+    public static class PayloadBean {
+        /**
+         * params : {"command":0,"secret":"xxxxxxxxx","ip":"192.168.215.36","port":18092}
+         */
+        private Object params;
+
+
+    }
+}

+ 56 - 50
business-system/venues-system/src/main/java/com/pj/netty/NettyServerHandler.java → sp-core/src/main/java/com/pj/current/netty/heart/HeartBeatHandler.java

@@ -1,7 +1,18 @@
-package com.pj.netty;
-
+package com.pj.current.netty.heart;
+
+import cn.hutool.cache.CacheUtil;
+import cn.hutool.cache.impl.TimedCache;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.extra.spring.SpringUtil;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.pj.current.config.MyConfig;
+import com.pj.current.netty.dto.ParamsDTO;
+import com.pj.current.netty.dto.RequestPackageDTO;
+import com.pj.current.netty.dto.ResponsePackageDTO;
+import com.pj.project.tb_terminal.TbTerminalService;
+import com.pj.utils.cache.RedisUtil;
 import io.netty.channel.*;
-import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
 import io.netty.handler.timeout.IdleState;
 import io.netty.handler.timeout.IdleStateEvent;
 import lombok.extern.slf4j.Slf4j;
@@ -13,12 +24,13 @@ import java.util.concurrent.ConcurrentHashMap;
 @Slf4j
 @ChannelHandler.Sharable
 @Component
-public class NettyServerHandler extends ChannelInboundHandlerAdapter {
+public class HeartBeatHandler extends ChannelInboundHandlerAdapter {
     /**
      * 管理一个全局map,保存连接进服务端的通道数量
      */
     private static final ConcurrentHashMap<ChannelId, ChannelHandlerContext> CHANNEL_MAP = new ConcurrentHashMap<>();
 
+    public static final TimedCache<String, String> STATE_CACHE = CacheUtil.newTimedCache(15000);
 
 
     /**
@@ -28,26 +40,16 @@ public class NettyServerHandler extends ChannelInboundHandlerAdapter {
      */
     @Override
     public void channelActive(ChannelHandlerContext ctx) {
-
-        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
-
-        String clientIp = insocket.getAddress().getHostAddress();
-        int clientPort = insocket.getPort();
-
-        //获取连接通道唯一标识
-        ChannelId channelId = ctx.channel().id();
-
-        System.out.println();
-        //如果map中不包含此连接,就保存连接
-        if (CHANNEL_MAP.containsKey(channelId)) {
-            log.info("客户端【" + channelId + "】是连接状态,连接通道数量: " + CHANNEL_MAP.size());
-        } else {
-            //保存连接
-            CHANNEL_MAP.put(channelId, ctx);
-
-            log.info("客户端【" + channelId + "】连接netty服务器[IP:" + clientIp + "--->PORT:" + clientPort + "]");
-            log.info("连接通道数量: " + CHANNEL_MAP.size());
-        }
+        //上线时同步时间===========
+        log.info("心跳设备上线,同步时间");
+        RequestPackageDTO dto = new RequestPackageDTO();
+        RequestPackageDTO.PayloadBean payloadBean = new RequestPackageDTO.PayloadBean();
+        JSONObject params = new JSONObject();
+        params.set("time", System.currentTimeMillis() / 1000);
+        payloadBean.setParams(params);
+        dto.setPayload(payloadBean);
+        ctx.write(dto);
+        ctx.flush();
     }
 
     /**
@@ -81,38 +83,42 @@ public class NettyServerHandler extends ChannelInboundHandlerAdapter {
      */
     @Override
     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
-
-        String msgStr = String.valueOf(msg);
-        System.out.println();
-        log.info("加载客户端报文......");
-        log.info("【" + ctx.channel().id() + "】" + " :" + msg);
-
-        //响应客户端
-        log.info("服务端端返回报文......");
-        log.info("【" + ctx.channel().id() + "】" + " :" + msg);
-        this.channelWrite(ctx.channel().id(), msg);
+        String body = JSONUtil.toJsonStr(msg);
+        RequestPackageDTO dto = JSONUtil.toBean(body, RequestPackageDTO.class);
+        log.info("心跳包:{}", body);
+        MyConfig myConfig = SpringUtil.getBean(MyConfig.class);
+        //心跳包
+        String sn = JSONUtil.parseObj(dto.getPayload().getParams()).getStr("sn");
+        STATE_CACHE.put(sn, sn);
+        String commandStr = RedisUtil.get(myConfig.getHeartPrefix() + sn);
+        if (StrUtil.isNotEmpty(commandStr)) {
+            this.channelWrite(ctx, commandStr);
+            RedisUtil.del(myConfig.getHeartPrefix() + sn);
+            return;
+        }
+        //todo 检查是否存在库里
+        TbTerminalService tbTerminalService = SpringUtil.getBean(TbTerminalService.class);
+        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
+        String clientIp = insocket.getAddress().getHostAddress();
+        tbTerminalService.initTerminal(sn, clientIp);
+        ResponsePackageDTO responseDTO = new ResponsePackageDTO();
+        ParamsDTO paramsDTO = new ParamsDTO();
+        paramsDTO.setIp(myConfig.getIp()).setPort(myConfig.getCommandPort());
+        ResponsePackageDTO.PayloadBean payloadBean = new ResponsePackageDTO.PayloadBean();
+        payloadBean.setParams(paramsDTO);
+        responseDTO.setPayload(payloadBean);
+        String response = JSONUtil.toJsonStr(responseDTO);
+
+        log.info("回复心跳:{}", response);
+        this.channelWrite(ctx, response);
     }
 
     /**
-     * @param msg        需要发送的消息内容
-     * @param channelId 连接通道唯一id
+     * @param msg 需要发送的消息内容
      * @DESCRIPTION: 服务端给客户端发送消息
      * @return: void
      */
-    public void channelWrite(ChannelId channelId, Object msg) throws Exception {
-
-        ChannelHandlerContext ctx = CHANNEL_MAP.get(channelId);
-
-        if (ctx == null) {
-            log.info("通道【" + channelId + "】不存在");
-            return;
-        }
-
-        if (msg == null || msg == "") {
-            log.info("服务端响应空的消息");
-            return;
-        }
-
+    public void channelWrite(ChannelHandlerContext ctx, Object msg) throws Exception {
         //将客户端的信息直接返回写入ctx
         ctx.write(msg);
         //刷新缓存区

+ 3 - 3
business-system/venues-system/src/main/java/com/pj/netty/NettyServerChannelInitializer.java → sp-core/src/main/java/com/pj/current/netty/heart/HeartChannelInitializer.java

@@ -1,4 +1,4 @@
-package com.pj.netty;
+package com.pj.current.netty.heart;
 
 import io.netty.channel.ChannelInitializer;
 import io.netty.channel.socket.SocketChannel;
@@ -6,13 +6,13 @@ import io.netty.handler.codec.string.StringDecoder;
 import io.netty.handler.codec.string.StringEncoder;
 import io.netty.util.CharsetUtil;
 
-public class NettyServerChannelInitializer extends ChannelInitializer<SocketChannel> {
+public class HeartChannelInitializer extends ChannelInitializer<SocketChannel> {
     @Override
     protected void initChannel(SocketChannel channel) throws Exception {
 
         channel.pipeline().addLast("decoder",new StringDecoder(CharsetUtil.UTF_8));
         channel.pipeline().addLast("encoder",new StringEncoder(CharsetUtil.UTF_8));
-        channel.pipeline().addLast(new NettyServerHandler());
+        channel.pipeline().addLast(new HeartBeatHandler());
     }
 
 }

+ 13 - 13
business-system/venues-system/src/main/java/com/pj/netty/NettyServer.java → sp-core/src/main/java/com/pj/current/netty/heart/HeartNettyServer.java

@@ -1,25 +1,25 @@
-package com.pj.netty;
+package com.pj.current.netty.heart;
 
 import io.netty.bootstrap.ServerBootstrap;
 import io.netty.channel.*;
 import io.netty.channel.nio.NioEventLoopGroup;
-import io.netty.channel.socket.SocketChannel;
 import io.netty.channel.socket.nio.NioServerSocketChannel;
-import io.netty.handler.codec.FixedLengthFrameDecoder;
-import io.netty.handler.codec.http.HttpObjectAggregator;
-import io.netty.handler.codec.http.HttpServerCodec;
-import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
-import io.netty.handler.stream.ChunkedWriteHandler;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
 
 import java.net.InetSocketAddress;
 
 @Slf4j
-@Component
-public class NettyServer {
 
-    public void start(InetSocketAddress address) {
+public class HeartNettyServer extends Thread {
+    private InetSocketAddress address;
+
+    public HeartNettyServer(InetSocketAddress address) {
+        this.address = address;
+    }
+
+    @Override
+    public void run() {
         //配置服务端的NIO线程组
         EventLoopGroup bossGroup = new NioEventLoopGroup(1);
         EventLoopGroup workerGroup = new NioEventLoopGroup();
@@ -29,13 +29,13 @@ public class NettyServer {
                     .group(bossGroup, workerGroup)  // 绑定线程池
                     .channel(NioServerSocketChannel.class)
                     .localAddress(address)
-                    .childHandler(new NettyServerChannelInitializer())//编码解码
-                    .option(ChannelOption.SO_BACKLOG, 128)  //服务端接受连接的队列长度,如果队列已满,客户端连接将被拒绝
+                    .childHandler(new HeartChannelInitializer())//编码解码
+                    .option(ChannelOption.SO_BACKLOG, 258)  //服务端接受连接的队列长度,如果队列已满,客户端连接将被拒绝
                     .childOption(ChannelOption.SO_KEEPALIVE, true);  //保持长连接,2小时无数据激活心跳机制
 
             // 绑定端口,开始接收进来的连接
             ChannelFuture future = bootstrap.bind(address).sync();
-            log.info("netty服务器开始监听端口:" + address.getPort());
+            log.info("心跳服务器开始监听端口:{}" ,address.getPort());
             //关闭channel和块,直到它被关闭
             future.channel().closeFuture().sync();
         } catch (Exception e) {

+ 940 - 847
sp-core/src/main/java/com/pj/current/satoken/StpUserUtil.java

@@ -9,857 +9,950 @@ import cn.dev33.satoken.stp.SaLoginModel;
 import cn.dev33.satoken.stp.SaTokenInfo;
 import cn.dev33.satoken.stp.StpLogic;
 import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
 import com.pj.utils.cache.RedisUtil;
 
 /**
  * Sa-Token 权限认证工具类 (User版本)
- * @author kong 
+ *
+ * @author kong
  */
 public class StpUserUtil {
-	public static final long ADMIN_DEPT_ID=9999999L;
-
-	public static void setDeptId(Long deptId) {
-		String key = "dept:" + StpUtil.getLoginIdAsString();
-		RedisUtil.set(key, deptId.toString());
-	}
-
-	public static long getDeptId() {
-		String key = "dept:" + StpUtil.getLoginIdAsString();
-		String deptId = RedisUtil.get(key);
-		return Long.parseLong(deptId);
-	}
-	/**
-	 * 账号类型标识 
-	 */
-	public static final String TYPE = "login";
-	
-	/**
-	 * 底层的 StpLogic 对象  
-	 */
-	public static StpLogic stpLogic = new StpLogic(TYPE); 
-
-	/**
-	 * 获取当前 StpLogic 的账号类型
-	 * @return See Note 
-	 */
-	public static String getLoginType(){
-		return stpLogic.getLoginType();
-	}
-
-	/**
-	 * 重置 StpLogic 对象
-	 * @param stpLogic / 
-	 */
-	public static void setStpLogic(StpLogic stpLogic) {
-		StpUtil.stpLogic = stpLogic;
-		// 防止自定义 stpLogic 被覆盖 
-		SaManager.putStpLogic(stpLogic);
-	}
-	
-	
-	// =================== 获取token 相关 ===================
-
-	/**
-	 * 返回token名称 
-	 * @return 此StpLogic的token名称
-	 */
-	public static String getTokenName() {
- 		return stpLogic.getTokenName();
- 	}
-
- 	/**
- 	 * 在当前会话写入当前TokenValue 
- 	 * @param tokenValue token值 
- 	 * @param cookieTimeout Cookie存活时间(秒)
- 	 */
-	public static void setTokenValue(String tokenValue, int cookieTimeout){
-		stpLogic.setTokenValue(tokenValue, cookieTimeout);
-	}
- 	
-	/**
-	 * 获取当前TokenValue
-	 * @return 当前tokenValue
-	 */
-	public static String getTokenValue() {
-		return stpLogic.getTokenValue();
-	}
-
-	/**
-	 * 获取当前会话的Token信息 
-	 * @return token信息 
-	 */
-	public static SaTokenInfo getTokenInfo() {
-		return stpLogic.getTokenInfo();
-	}
-
-	
-	// =================== 登录相关操作 ===================
-
-	// --- 登录 
-	
-	/**
-	 * 会话登录 
-	 * @param id 账号id,建议的类型:(long | int | String)
-	 */
-	public static void login(Object id) {
-		stpLogic.login(id);
-	}
-
-	/**
-	 * 会话登录,并指定登录设备 
-	 * @param id 账号id,建议的类型:(long | int | String)
-	 * @param device 设备标识 
-	 */
-	public static void login(Object id, String device) {
-		stpLogic.login(id, device);
-	}
-
-	/**
-	 * 会话登录,并指定是否 [记住我] 
-	 * @param id 账号id,建议的类型:(long | int | String)
-	 * @param isLastingCookie 是否为持久Cookie 
-	 */
-	public static void login(Object id, boolean isLastingCookie) {
-		stpLogic.login(id, isLastingCookie);
-	}
-
-	/**
-	 * 会话登录,并指定所有登录参数Model 
-	 * @param id 登录id,建议的类型:(long | int | String)
-	 * @param loginModel 此次登录的参数Model 
-	 */
-	public static void login(Object id, SaLoginModel loginModel) {
-		stpLogic.login(id, loginModel);
-	}
-
-	// --- 注销 
-	
-	/** 
-	 * 会话注销 
-	 */
-	public static void logout() {
-		stpLogic.logout();
-	}
-
-	/**
-	 * 会话注销,根据账号id 
-	 * @param loginId 账号id 
-	 */
-	public static void logout(Object loginId) {
-		stpLogic.logout(loginId);
-	}
-
-	/**
-	 * 会话注销,根据账号id 和 设备标识 
-	 * 
-	 * @param loginId 账号id 
-	 * @param device 设备标识 (填null代表所有注销设备) 
-	 */
-	public static void logout(Object loginId, String device) {
-		stpLogic.logout(loginId, device);
-	}
-	
-	/**
-	 * 会话注销,根据指定 Token 
-	 * 
-	 * @param tokenValue 指定token
-	 */
-	public static void logoutByTokenValue(String tokenValue) {
-		stpLogic.logoutByTokenValue(tokenValue);
-	}
-	
-	/**
-	 * 踢人下线,根据账号id 
-	 * <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-5 </p>
-	 * 
-	 * @param loginId 账号id 
-	 */
-	public static void kickout(Object loginId) {
-		stpLogic.kickout(loginId);
-	}
-	
-	/**
-	 * 踢人下线,根据账号id 和 设备标识 
-	 * <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-5 </p>
-	 * 
-	 * @param loginId 账号id 
-	 * @param device 设备标识 (填null代表踢出所有设备) 
-	 */
-	public static void kickout(Object loginId, String device) {
-		stpLogic.kickout(loginId, device);
-	}
-
-	/**
-	 * 踢人下线,根据指定 Token 
-	 * <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-5 </p>
-	 * 
-	 * @param tokenValue 指定token
-	 */
-	public static void kickoutByTokenValue(String tokenValue) {
-		stpLogic.kickoutByTokenValue(tokenValue);
-	}
-	
-	/**
-	 * 顶人下线,根据账号id 和 设备标识 
-	 * <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-4 </p>
-	 * 
-	 * @param loginId 账号id 
-	 * @param device 设备标识 (填null代表顶替所有设备) 
-	 */
-	public static void replaced(Object loginId, String device) {
-		stpLogic.replaced(loginId, device);
-	}
-	
-	
-	// 查询相关
-
-	/** 
- 	 * 当前会话是否已经登录 
- 	 * @return 是否已登录 
- 	 */
-	public static boolean isLogin() {
-		return stpLogic.isLogin();
-	}
-
-	/** 
- 	 * 检验当前会话是否已经登录,如未登录,则抛出异常 
- 	 */
- 	public static void checkLogin() {
- 		stpLogic.checkLogin();
- 	}
-
- 	/** 
- 	 * 获取当前会话账号id, 如果未登录,则抛出异常 
- 	 * @return 账号id
- 	 */
-	public static Object getLoginId() {
-		return stpLogic.getLoginId();
-	}
-
-	/** 
-	 * 获取当前会话账号id, 如果未登录,则返回默认值 
-	 * @param <T> 返回类型 
-	 * @param defaultValue 默认值
-	 * @return 登录id 
-	 */
-	public static <T> T getLoginId(T defaultValue) {
-		return stpLogic.getLoginId(defaultValue);
-	}
-
-	/** 
-	 * 获取当前会话账号id, 如果未登录,则返回null 
-	 * @return 账号id 
-	 */
-	public static Object getLoginIdDefaultNull() {
-		return stpLogic.getLoginIdDefaultNull();
- 	}
-
-	/** 
-	 * 获取当前会话账号id, 并转换为String类型
-	 * @return 账号id 
-	 */
-	public static String getLoginIdAsString() {
-		return stpLogic.getLoginIdAsString();
-	}
-
-	/** 
-	 * 获取当前会话账号id, 并转换为int类型
-	 * @return 账号id 
-	 */
-	public static int getLoginIdAsInt() {
-		return stpLogic.getLoginIdAsInt();
-	}
-
-	/**
-	 * 获取当前会话账号id, 并转换为long类型 
-	 * @return 账号id 
-	 */
-	public static long getLoginIdAsLong() {
-		return stpLogic.getLoginIdAsLong();
-	}
-
-	/** 
- 	 * 获取指定Token对应的账号id,如果未登录,则返回 null 
- 	 * @param tokenValue token
- 	 * @return 账号id
- 	 */
- 	public static Object getLoginIdByToken(String tokenValue) {
- 		return stpLogic.getLoginIdByToken(tokenValue);
- 	}
-	
- 	
-	// =================== User-Session 相关 ===================
-
- 	/** 
-	 * 获取指定账号id的Session, 如果Session尚未创建,isCreate=是否新建并返回
-	 * @param loginId 账号id
-	 * @param isCreate 是否新建
-	 * @return Session对象
-	 */
-	public static SaSession getSessionByLoginId(Object loginId, boolean isCreate) {
-		return stpLogic.getSessionByLoginId(loginId, isCreate);
-	}
-
-	/** 
-	 * 获取指定key的Session, 如果Session尚未创建,则返回null
-	 * @param sessionId SessionId
-	 * @return Session对象 
-	 */
-	public static SaSession getSessionBySessionId(String sessionId) {
-		return stpLogic.getSessionBySessionId(sessionId);
-	}
-
-	/** 
-	 * 获取指定账号id的Session,如果Session尚未创建,则新建并返回 
-	 * @param loginId 账号id 
-	 * @return Session对象 
-	 */
-	public static SaSession getSessionByLoginId(Object loginId) {
-		return stpLogic.getSessionByLoginId(loginId);
-	}
-
-	/** 
-	 * 获取当前会话的Session, 如果Session尚未创建,isCreate=是否新建并返回 
-	 * @param isCreate 是否新建 
-	 * @return Session对象 
-	 */
-	public static SaSession getSession(boolean isCreate) {
-		return stpLogic.getSession(isCreate);
-	}
-
-	/** 
-	 * 获取当前会话的Session,如果Session尚未创建,则新建并返回 
-	 * @return Session对象 
-	 */
-	public static SaSession getSession() {
-		return stpLogic.getSession();
-	}
-
-	
-	// =================== Token-Session 相关 ===================  
-	
-	/** 
-	 * 获取指定Token-Session,如果Session尚未创建,则新建并返回 
-	 * @param tokenValue Token值
-	 * @return Session对象  
-	 */
-	public static SaSession getTokenSessionByToken(String tokenValue) {
-		return stpLogic.getTokenSessionByToken(tokenValue);
-	}
-	
-	/** 
-	 * 获取当前Token-Session,如果Session尚未创建,则新建并返回
-	 * @return Session对象 
-	 */
-	public static SaSession getTokenSession() {
-		return stpLogic.getTokenSession();
-	}
-
-
-	// =================== [临时过期] 验证相关 ===================  
-
-	/**
- 	 * 检查当前token 是否已经[临时过期],如果已经过期则抛出异常  
- 	 */
- 	public static void checkActivityTimeout() {
- 		stpLogic.checkActivityTimeout();
- 	}
-
- 	/**
- 	 * 续签当前token:(将 [最后操作时间] 更新为当前时间戳) 
- 	 * <h1>请注意: 即时token已经 [临时过期] 也可续签成功,
- 	 * 如果此场景下需要提示续签失败,可在此之前调用 checkActivityTimeout() 强制检查是否过期即可 </h1>
- 	 */
- 	public static void updateLastActivityToNow() {
- 		stpLogic.updateLastActivityToNow();
- 	}
- 	
-
-	// =================== 过期时间相关 ===================  
-
- 	/**
- 	 * 获取当前登录者的 token 剩余有效时间 (单位: 秒)
- 	 * @return token剩余有效时间
- 	 */
- 	public static long getTokenTimeout() {
- 		return stpLogic.getTokenTimeout();
- 	}
- 	
- 	/**
- 	 * 获取当前登录者的 User-Session 剩余有效时间 (单位: 秒)
- 	 * @return token剩余有效时间
- 	 */
- 	public static long getSessionTimeout() {
- 		return stpLogic.getSessionTimeout();
- 	}
-
- 	/**
- 	 * 获取当前 Token-Session 剩余有效时间 (单位: 秒) 
- 	 * @return token剩余有效时间
- 	 */
- 	public static long getTokenSessionTimeout() {
- 		return stpLogic.getTokenSessionTimeout();
- 	}
- 	
- 	/**
- 	 * 获取当前 token [临时过期] 剩余有效时间 (单位: 秒)
- 	 * @return token [临时过期] 剩余有效时间
- 	 */
- 	public static long getTokenActivityTimeout() {
- 		return stpLogic.getTokenActivityTimeout();
- 	}
- 	
-
- 	
-	// =================== 角色验证操作 ===================  
-
-	/**
-	 * 获取:当前账号的角色集合 
-	 * @return /
-	 */
-	public static List<String> getRoleList() {
-		return stpLogic.getRoleList();
-	}
-
-	/**
-	 * 获取:指定账号的角色集合 
-	 * @param loginId 指定账号id 
-	 * @return /
-	 */
-	public static List<String> getRoleList(Object loginId) {
-		return stpLogic.getRoleList(loginId);
-	}
-
- 	/** 
- 	 * 判断:当前账号是否拥有指定角色, 返回true或false 
- 	 * @param role 角色标识
- 	 * @return 是否含有指定角色标识
- 	 */
- 	public static boolean hasRole(String role) {
- 		return stpLogic.hasRole(role);
- 	}
-
- 	/** 
- 	 * 判断:指定账号是否含有指定角色标识, 返回true或false 
- 	 * @param loginId 账号id
- 	 * @param role 角色标识
- 	 * @return 是否含有指定角色标识
- 	 */
- 	public static boolean hasRole(Object loginId, String role) {
- 		return stpLogic.hasRole(loginId, role);
- 	}
- 	
- 	/** 
- 	 * 判断:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过] 
- 	 * @param roleArray 角色标识数组
- 	 * @return true或false
- 	 */
- 	public static boolean hasRoleAnd(String... roleArray){
- 		return stpLogic.hasRoleAnd(roleArray);
- 	}
-
- 	/** 
- 	 * 判断:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可] 
- 	 * @param roleArray 角色标识数组
- 	 * @return true或false
- 	 */
- 	public static boolean hasRoleOr(String... roleArray){
- 		return stpLogic.hasRoleOr(roleArray);
- 	}
- 	
- 	/** 
- 	 * 校验:当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException 
- 	 * @param role 角色标识
- 	 */
- 	public static void checkRole(String role) {
- 		stpLogic.checkRole(role);
- 	}
-
- 	/** 
- 	 * 校验:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过] 
- 	 * @param roleArray 角色标识数组
- 	 */
- 	public static void checkRoleAnd(String... roleArray){
- 		stpLogic.checkRoleAnd(roleArray);
- 	}
-
- 	/** 
- 	 * 校验:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可] 
- 	 * @param roleArray 角色标识数组
- 	 */
- 	public static void checkRoleOr(String... roleArray){
- 		stpLogic.checkRoleOr(roleArray);
- 	}
-
-	
-	// =================== 权限验证操作 ===================
-
-	/**
-	 * 获取:当前账号的权限码集合 
-	 * @return / 
-	 */
-	public static List<String> getPermissionList() {
-		return stpLogic.getPermissionList();
-	}
-
-	/**
-	 * 获取:指定账号的权限码集合 
-	 * @param loginId 指定账号id
-	 * @return / 
-	 */
-	public static List<String> getPermissionList(Object loginId) {
-		return stpLogic.getPermissionList(loginId);
-	}
-
- 	/** 
- 	 * 判断:当前账号是否含有指定权限, 返回true或false 
- 	 * @param permission 权限码
- 	 * @return 是否含有指定权限
- 	 */
-	public static boolean hasPermission(String permission) {
-		return stpLogic.hasPermission(permission);
-	}
-
- 	/** 
- 	 * 判断:指定账号id是否含有指定权限, 返回true或false 
- 	 * @param loginId 账号id
- 	 * @param permission 权限码
- 	 * @return 是否含有指定权限
- 	 */
-	public static boolean hasPermission(Object loginId, String permission) {
-		return stpLogic.hasPermission(loginId, permission);
-	}
-
- 	/** 
- 	 * 判断:当前账号是否含有指定权限, [指定多个,必须全部具有] 
- 	 * @param permissionArray 权限码数组
- 	 * @return true 或 false 
- 	 */
- 	public static boolean hasPermissionAnd(String... permissionArray){
- 		return stpLogic.hasPermissionAnd(permissionArray);
- 	}
-
- 	/** 
- 	 * 判断:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可] 
- 	 * @param permissionArray 权限码数组
- 	 * @return true 或 false 
- 	 */
- 	public static boolean hasPermissionOr(String... permissionArray){
- 		return stpLogic.hasPermissionOr(permissionArray);
- 	}
- 	
- 	/** 
- 	 * 校验:当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException 
- 	 * @param permission 权限码
- 	 */
-	public static void checkPermission(String permission) {
-		stpLogic.checkPermission(permission);
-	}
-
- 	/** 
- 	 * 校验:当前账号是否含有指定权限 [指定多个,必须全部验证通过] 
- 	 * @param permissionArray 权限码数组
- 	 */
-	public static void checkPermissionAnd(String... permissionArray) {
-		stpLogic.checkPermissionAnd(permissionArray);
-	}
-
- 	/** 
- 	 * 校验:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可] 
- 	 * @param permissionArray 权限码数组
- 	 */
-	public static void checkPermissionOr(String... permissionArray) {
-		stpLogic.checkPermissionOr(permissionArray);
-	}
-
-
-	// =================== id 反查token 相关操作 ===================  
-	
-	/** 
-	 * 获取指定账号id的tokenValue 
-	 * <p> 在配置为允许并发登录时,此方法只会返回队列的最后一个token,
-	 * 如果你需要返回此账号id的所有token,请调用 getTokenValueListByLoginId 
-	 * @param loginId 账号id
-	 * @return token值 
-	 */
-	public static String getTokenValueByLoginId(Object loginId) {
-		return stpLogic.getTokenValueByLoginId(loginId);
-	}
-
-	/** 
-	 * 获取指定账号id指定设备端的tokenValue 
-	 * <p> 在配置为允许并发登录时,此方法只会返回队列的最后一个token,
-	 * 如果你需要返回此账号id的所有token,请调用 getTokenValueListByLoginId 
-	 * @param loginId 账号id
-	 * @param device 设备标识 
-	 * @return token值 
-	 */
-	public static String getTokenValueByLoginId(Object loginId, String device) {
-		return stpLogic.getTokenValueByLoginId(loginId, device);
-	}
-	
-	/** 
-	 * 获取指定账号id的tokenValue集合 
-	 * @param loginId 账号id 
-	 * @return 此loginId的所有相关token 
- 	 */
-	public static List<String> getTokenValueListByLoginId(Object loginId) {
-		return stpLogic.getTokenValueListByLoginId(loginId);
-	}
-
-	/** 
-	 * 获取指定账号id指定设备端的tokenValue 集合 
-	 * @param loginId 账号id 
-	 * @param device 设备标识 
-	 * @return 此loginId的所有相关token 
- 	 */
-	public static List<String> getTokenValueListByLoginId(Object loginId, String device) {
-		return stpLogic.getTokenValueListByLoginId(loginId, device);
-	}
-	
-	/**
-	 * 返回当前会话的登录设备 
-	 * @return 当前令牌的登录设备 
-	 */
-	public static String getLoginDevice() {
-		return stpLogic.getLoginDevice(); 
-	}
-
-	
-	// =================== 会话管理 ===================  
-
-	/**
-	 * 根据条件查询Token 
-	 * @param keyword 关键字 
-	 * @param start 开始处索引 (-1代表查询所有) 
-	 * @param size 获取数量 
-	 * @return token集合 
-	 */
-	public static List<String> searchTokenValue(String keyword, int start, int size) {
-		return stpLogic.searchTokenValue(keyword, start, size);
-	}
-	
-	/**
-	 * 根据条件查询SessionId 
-	 * @param keyword 关键字 
-	 * @param start 开始处索引 (-1代表查询所有) 
-	 * @param size 获取数量 
-	 * @return sessionId集合 
-	 */
-	public static List<String> searchSessionId(String keyword, int start, int size) {
-		return stpLogic.searchSessionId(keyword, start, size);
-	}
-
-	/**
-	 * 根据条件查询Token专属Session的Id 
-	 * @param keyword 关键字 
-	 * @param start 开始处索引 (-1代表查询所有) 
-	 * @param size 获取数量 
-	 * @return sessionId集合 
-	 */
-	public static List<String> searchTokenSessionId(String keyword, int start, int size) {
-		return stpLogic.searchTokenSessionId(keyword, start, size);
-	}
-
-	
-	// ------------------- 账号封禁 -------------------  
-
-	/**
-	 * 封禁指定账号
-	 * <p> 此方法不会直接将此账号id踢下线,而是在对方再次登录时抛出`DisableLoginException`异常 
-	 * @param loginId 指定账号id 
-	 * @param disableTime 封禁时间, 单位: 秒 (-1=永久封禁)
-	 */
-	public static void disable(Object loginId, long disableTime) {
-		stpLogic.disable(loginId, disableTime);
-	}
-	
-	/**
-	 * 指定账号是否已被封禁 (true=已被封禁, false=未被封禁) 
-	 * @param loginId 账号id
-	 * @return see note
-	 */
-	public static boolean isDisable(Object loginId) {
-		return stpLogic.isDisable(loginId);
-	}
-	
-	/**
-	 * 获取指定账号剩余封禁时间,单位:秒(-1=永久封禁,-2=未被封禁)
-	 * @param loginId 账号id
-	 * @return see note 
-	 */
-	public static long getDisableTime(Object loginId) {
-		return stpLogic.getDisableTime(loginId);
-	}
-
-	/**
-	 * 解封指定账号
-	 * @param loginId 账号id 
-	 */
-	public static void untieDisable(Object loginId) {
-		stpLogic.untieDisable(loginId);
-	}
-	
-	
-	// =================== 身份切换 ===================  
-
-	/**
-	 * 临时切换身份为指定账号id 
-	 * @param loginId 指定loginId 
-	 */
-	public static void switchTo(Object loginId) {
-		stpLogic.switchTo(loginId);
-	}
-	
-	/**
-	 * 结束临时切换身份
-	 */
-	public static void endSwitch() {
-		stpLogic.endSwitch();
-	}
-
-	/**
-	 * 当前是否正处于[身份临时切换]中 
-	 * @return 是否正处于[身份临时切换]中 
-	 */
-	public static boolean isSwitch() {
-		return stpLogic.isSwitch();
-	}
-
-	/**
-	 * 在一个代码段里方法内,临时切换身份为指定账号id
-	 * @param loginId 指定账号id 
-	 * @param function 要执行的方法 
-	 */
-	public static void switchTo(Object loginId, SaFunction function) {
-		stpLogic.switchTo(loginId, function);
-	}
-	
-
-	// ------------------- 二级认证 -------------------  
-	
-	/**
-	 * 在当前会话 开启二级认证 
-	 * @param safeTime 维持时间 (单位: 秒) 
-	 */
-	public static void openSafe(long safeTime) {
-		stpLogic.openSafe(safeTime);
-	}
-
-	/**
-	 * 当前会话 是否处于二级认证时间内 
-	 * @return true=二级认证已通过, false=尚未进行二级认证或认证已超时 
-	 */
-	public static boolean isSafe() {
-		return stpLogic.isSafe();
-	}
-
-	/**
-	 * 检查当前会话是否已通过二级认证,如未通过则抛出异常 
-	 */
-	public static void checkSafe() {
-		stpLogic.checkSafe();
-	}
-	
-	/**
-	 * 获取当前会话的二级认证剩余有效时间 (单位: 秒, 返回-2代表尚未通过二级认证)
-	 * @return 剩余有效时间
-	 */
-	public static long getSafeTime() {
-		return stpLogic.getSafeTime();
-	}
-
-	/**
-	 * 在当前会话 结束二级认证 
-	 */
-	public static void closeSafe() {
-		stpLogic.closeSafe();
-	}
-
-
-	// =================== 历史API,兼容旧版本 ===================  
-
-	/**
-	 * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.getLoginType() ,使用方式保持不变 </h1>
-	 * 
-	 * 获取当前StpLogin的loginKey 
-	 * @return 当前StpLogin的loginKey
-	 */
-	@Deprecated
-	public static String getLoginKey(){
-		return stpLogic.getLoginType();
-	}
-
-	/**
-	 * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
-	 * 
-	 * 在当前会话上登录id 
-	 * @param loginId 登录id,建议的类型:(long | int | String)
-	 */
-	@Deprecated
-	public static void setLoginId(Object loginId) {
-		stpLogic.login(loginId);
-	}
-
-	/**
-	 * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
-	 * 
-	 * 在当前会话上登录id, 并指定登录设备 
-	 * @param loginId 登录id,建议的类型:(long | int | String)
-	 * @param device 设备标识 
-	 */
-	@Deprecated
-	public static void setLoginId(Object loginId, String device) {
-		stpLogic.login(loginId, device);
-	}
-
-	/**
-	 * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
-	 * 
-	 * 在当前会话上登录id, 并指定登录设备 
-	 * @param loginId 登录id,建议的类型:(long | int | String)
-	 * @param isLastingCookie 是否为持久Cookie 
-	 */
-	@Deprecated
-	public static void setLoginId(Object loginId, boolean isLastingCookie) {
-		stpLogic.login(loginId, isLastingCookie);
-	}
-	
-	/**
-	 * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
-	 * 
-	 * 在当前会话上登录id, 并指定所有登录参数Model 
-	 * @param loginId 登录id,建议的类型:(long | int | String)
-	 * @param loginModel 此次登录的参数Model 
-	 */
-	@Deprecated
-	public static void setLoginId(Object loginId, SaLoginModel loginModel) {
-		stpLogic.login(loginId, loginModel);
-	}
-	
-	/**
-	 * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.kickout() ,使用方式保持不变 </h1>
-	 * 
-	 * 会话注销,根据账号id (踢人下线)
-	 * <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2
-	 * @param loginId 账号id 
-	 */
-	@Deprecated
-	public static void logoutByLoginId(Object loginId) {
-		stpLogic.kickout(loginId);
-	}
-	
-	/**
-	 * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.kickout() ,使用方式保持不变 </h1>
-	 * 
-	 * 会话注销,根据账号id and 设备标识 (踢人下线)
-	 * <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2 </p>
-	 * @param loginId 账号id 
-	 * @param device 设备标识 (填null代表所有注销设备) 
-	 */
-	@Deprecated
-	public static void logoutByLoginId(Object loginId, String device) {
-		stpLogic.kickout(loginId, device);
-	}
-	
+    public static final long ADMIN_DEPT_ID = 9999999L;
+
+    public static void setDeptId(Long deptId) {
+        String key = "dept:" + StpUtil.getLoginIdAsString();
+        RedisUtil.set(key, deptId.toString());
+    }
+
+    public static long getDeptId() {
+        String key = "dept:" + StpUtil.getLoginIdAsString();
+        String deptId = RedisUtil.get(key);
+        return Long.parseLong(deptId);
+    }
+
+    public static void setAdmin(String name, String phone) {
+        String key = "login_user:" + StpUtil.getLoginIdAsString();
+        JSONObject jsonObject = new JSONObject();
+        jsonObject.set("name", name).set("phone", phone);
+        RedisUtil.set(key, JSONUtil.toJsonStr(jsonObject));
+    }
+
+    public static String getAdminName() {
+        try {
+            String key = "login_user:" + StpUtil.getLoginIdAsString();
+            String cacheObJ = RedisUtil.get(key);
+            return JSONUtil.parseObj(cacheObJ).getStr("name");
+        }catch (Exception e){
+            return "";
+        }
+    }
+
+    /**
+     * 账号类型标识
+     */
+    public static final String TYPE = "login";
+
+    /**
+     * 底层的 StpLogic 对象
+     */
+    public static StpLogic stpLogic = new StpLogic(TYPE);
+
+    /**
+     * 获取当前 StpLogic 的账号类型
+     *
+     * @return See Note
+     */
+    public static String getLoginType() {
+        return stpLogic.getLoginType();
+    }
+
+    /**
+     * 重置 StpLogic 对象
+     *
+     * @param stpLogic /
+     */
+    public static void setStpLogic(StpLogic stpLogic) {
+        StpUtil.stpLogic = stpLogic;
+        // 防止自定义 stpLogic 被覆盖
+        SaManager.putStpLogic(stpLogic);
+    }
+
+
+    // =================== 获取token 相关 ===================
+
+    /**
+     * 返回token名称
+     *
+     * @return 此StpLogic的token名称
+     */
+    public static String getTokenName() {
+        return stpLogic.getTokenName();
+    }
+
+    /**
+     * 在当前会话写入当前TokenValue
+     *
+     * @param tokenValue    token值
+     * @param cookieTimeout Cookie存活时间(秒)
+     */
+    public static void setTokenValue(String tokenValue, int cookieTimeout) {
+        stpLogic.setTokenValue(tokenValue, cookieTimeout);
+    }
+
+    /**
+     * 获取当前TokenValue
+     *
+     * @return 当前tokenValue
+     */
+    public static String getTokenValue() {
+        return stpLogic.getTokenValue();
+    }
+
+    /**
+     * 获取当前会话的Token信息
+     *
+     * @return token信息
+     */
+    public static SaTokenInfo getTokenInfo() {
+        return stpLogic.getTokenInfo();
+    }
+
+
+    // =================== 登录相关操作 ===================
+
+    // --- 登录
+
+    /**
+     * 会话登录
+     *
+     * @param id 账号id,建议的类型:(long | int | String)
+     */
+    public static void login(Object id) {
+        stpLogic.login(id);
+    }
+
+    /**
+     * 会话登录,并指定登录设备
+     *
+     * @param id     账号id,建议的类型:(long | int | String)
+     * @param device 设备标识
+     */
+    public static void login(Object id, String device) {
+        stpLogic.login(id, device);
+    }
+
+    /**
+     * 会话登录,并指定是否 [记住我]
+     *
+     * @param id              账号id,建议的类型:(long | int | String)
+     * @param isLastingCookie 是否为持久Cookie
+     */
+    public static void login(Object id, boolean isLastingCookie) {
+        stpLogic.login(id, isLastingCookie);
+    }
+
+    /**
+     * 会话登录,并指定所有登录参数Model
+     *
+     * @param id         登录id,建议的类型:(long | int | String)
+     * @param loginModel 此次登录的参数Model
+     */
+    public static void login(Object id, SaLoginModel loginModel) {
+        stpLogic.login(id, loginModel);
+    }
+
+    // --- 注销
+
+    /**
+     * 会话注销
+     */
+    public static void logout() {
+        stpLogic.logout();
+    }
+
+    /**
+     * 会话注销,根据账号id
+     *
+     * @param loginId 账号id
+     */
+    public static void logout(Object loginId) {
+        stpLogic.logout(loginId);
+    }
+
+    /**
+     * 会话注销,根据账号id 和 设备标识
+     *
+     * @param loginId 账号id
+     * @param device  设备标识 (填null代表所有注销设备)
+     */
+    public static void logout(Object loginId, String device) {
+        stpLogic.logout(loginId, device);
+    }
+
+    /**
+     * 会话注销,根据指定 Token
+     *
+     * @param tokenValue 指定token
+     */
+    public static void logoutByTokenValue(String tokenValue) {
+        stpLogic.logoutByTokenValue(tokenValue);
+    }
+
+    /**
+     * 踢人下线,根据账号id
+     * <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-5 </p>
+     *
+     * @param loginId 账号id
+     */
+    public static void kickout(Object loginId) {
+        stpLogic.kickout(loginId);
+    }
+
+    /**
+     * 踢人下线,根据账号id 和 设备标识
+     * <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-5 </p>
+     *
+     * @param loginId 账号id
+     * @param device  设备标识 (填null代表踢出所有设备)
+     */
+    public static void kickout(Object loginId, String device) {
+        stpLogic.kickout(loginId, device);
+    }
+
+    /**
+     * 踢人下线,根据指定 Token
+     * <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-5 </p>
+     *
+     * @param tokenValue 指定token
+     */
+    public static void kickoutByTokenValue(String tokenValue) {
+        stpLogic.kickoutByTokenValue(tokenValue);
+    }
+
+    /**
+     * 顶人下线,根据账号id 和 设备标识
+     * <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-4 </p>
+     *
+     * @param loginId 账号id
+     * @param device  设备标识 (填null代表顶替所有设备)
+     */
+    public static void replaced(Object loginId, String device) {
+        stpLogic.replaced(loginId, device);
+    }
+
+
+    // 查询相关
+
+    /**
+     * 当前会话是否已经登录
+     *
+     * @return 是否已登录
+     */
+    public static boolean isLogin() {
+        return stpLogic.isLogin();
+    }
+
+    /**
+     * 检验当前会话是否已经登录,如未登录,则抛出异常
+     */
+    public static void checkLogin() {
+        stpLogic.checkLogin();
+    }
+
+    /**
+     * 获取当前会话账号id, 如果未登录,则抛出异常
+     *
+     * @return 账号id
+     */
+    public static Object getLoginId() {
+        return stpLogic.getLoginId();
+    }
+
+    /**
+     * 获取当前会话账号id, 如果未登录,则返回默认值
+     *
+     * @param <T>          返回类型
+     * @param defaultValue 默认值
+     * @return 登录id
+     */
+    public static <T> T getLoginId(T defaultValue) {
+        return stpLogic.getLoginId(defaultValue);
+    }
+
+    /**
+     * 获取当前会话账号id, 如果未登录,则返回null
+     *
+     * @return 账号id
+     */
+    public static Object getLoginIdDefaultNull() {
+        return stpLogic.getLoginIdDefaultNull();
+    }
+
+    /**
+     * 获取当前会话账号id, 并转换为String类型
+     *
+     * @return 账号id
+     */
+    public static String getLoginIdAsString() {
+        return stpLogic.getLoginIdAsString();
+    }
+
+    /**
+     * 获取当前会话账号id, 并转换为int类型
+     *
+     * @return 账号id
+     */
+    public static int getLoginIdAsInt() {
+        return stpLogic.getLoginIdAsInt();
+    }
+
+    /**
+     * 获取当前会话账号id, 并转换为long类型
+     *
+     * @return 账号id
+     */
+    public static long getLoginIdAsLong() {
+        return stpLogic.getLoginIdAsLong();
+    }
+
+    /**
+     * 获取指定Token对应的账号id,如果未登录,则返回 null
+     *
+     * @param tokenValue token
+     * @return 账号id
+     */
+    public static Object getLoginIdByToken(String tokenValue) {
+        return stpLogic.getLoginIdByToken(tokenValue);
+    }
+
+
+    // =================== User-Session 相关 ===================
+
+    /**
+     * 获取指定账号id的Session, 如果Session尚未创建,isCreate=是否新建并返回
+     *
+     * @param loginId  账号id
+     * @param isCreate 是否新建
+     * @return Session对象
+     */
+    public static SaSession getSessionByLoginId(Object loginId, boolean isCreate) {
+        return stpLogic.getSessionByLoginId(loginId, isCreate);
+    }
+
+    /**
+     * 获取指定key的Session, 如果Session尚未创建,则返回null
+     *
+     * @param sessionId SessionId
+     * @return Session对象
+     */
+    public static SaSession getSessionBySessionId(String sessionId) {
+        return stpLogic.getSessionBySessionId(sessionId);
+    }
+
+    /**
+     * 获取指定账号id的Session,如果Session尚未创建,则新建并返回
+     *
+     * @param loginId 账号id
+     * @return Session对象
+     */
+    public static SaSession getSessionByLoginId(Object loginId) {
+        return stpLogic.getSessionByLoginId(loginId);
+    }
+
+    /**
+     * 获取当前会话的Session, 如果Session尚未创建,isCreate=是否新建并返回
+     *
+     * @param isCreate 是否新建
+     * @return Session对象
+     */
+    public static SaSession getSession(boolean isCreate) {
+        return stpLogic.getSession(isCreate);
+    }
+
+    /**
+     * 获取当前会话的Session,如果Session尚未创建,则新建并返回
+     *
+     * @return Session对象
+     */
+    public static SaSession getSession() {
+        return stpLogic.getSession();
+    }
+
+
+    // =================== Token-Session 相关 ===================
+
+    /**
+     * 获取指定Token-Session,如果Session尚未创建,则新建并返回
+     *
+     * @param tokenValue Token值
+     * @return Session对象
+     */
+    public static SaSession getTokenSessionByToken(String tokenValue) {
+        return stpLogic.getTokenSessionByToken(tokenValue);
+    }
+
+    /**
+     * 获取当前Token-Session,如果Session尚未创建,则新建并返回
+     *
+     * @return Session对象
+     */
+    public static SaSession getTokenSession() {
+        return stpLogic.getTokenSession();
+    }
+
+
+    // =================== [临时过期] 验证相关 ===================
+
+    /**
+     * 检查当前token 是否已经[临时过期],如果已经过期则抛出异常
+     */
+    public static void checkActivityTimeout() {
+        stpLogic.checkActivityTimeout();
+    }
+
+    /**
+     * 续签当前token:(将 [最后操作时间] 更新为当前时间戳)
+     * <h1>请注意: 即时token已经 [临时过期] 也可续签成功,
+     * 如果此场景下需要提示续签失败,可在此之前调用 checkActivityTimeout() 强制检查是否过期即可 </h1>
+     */
+    public static void updateLastActivityToNow() {
+        stpLogic.updateLastActivityToNow();
+    }
+
+
+    // =================== 过期时间相关 ===================
+
+    /**
+     * 获取当前登录者的 token 剩余有效时间 (单位: 秒)
+     *
+     * @return token剩余有效时间
+     */
+    public static long getTokenTimeout() {
+        return stpLogic.getTokenTimeout();
+    }
+
+    /**
+     * 获取当前登录者的 User-Session 剩余有效时间 (单位: 秒)
+     *
+     * @return token剩余有效时间
+     */
+    public static long getSessionTimeout() {
+        return stpLogic.getSessionTimeout();
+    }
+
+    /**
+     * 获取当前 Token-Session 剩余有效时间 (单位: 秒)
+     *
+     * @return token剩余有效时间
+     */
+    public static long getTokenSessionTimeout() {
+        return stpLogic.getTokenSessionTimeout();
+    }
+
+    /**
+     * 获取当前 token [临时过期] 剩余有效时间 (单位: 秒)
+     *
+     * @return token [临时过期] 剩余有效时间
+     */
+    public static long getTokenActivityTimeout() {
+        return stpLogic.getTokenActivityTimeout();
+    }
+
+
+    // =================== 角色验证操作 ===================
+
+    /**
+     * 获取:当前账号的角色集合
+     *
+     * @return /
+     */
+    public static List<String> getRoleList() {
+        return stpLogic.getRoleList();
+    }
+
+    /**
+     * 获取:指定账号的角色集合
+     *
+     * @param loginId 指定账号id
+     * @return /
+     */
+    public static List<String> getRoleList(Object loginId) {
+        return stpLogic.getRoleList(loginId);
+    }
+
+    /**
+     * 判断:当前账号是否拥有指定角色, 返回true或false
+     *
+     * @param role 角色标识
+     * @return 是否含有指定角色标识
+     */
+    public static boolean hasRole(String role) {
+        return stpLogic.hasRole(role);
+    }
+
+    /**
+     * 判断:指定账号是否含有指定角色标识, 返回true或false
+     *
+     * @param loginId 账号id
+     * @param role    角色标识
+     * @return 是否含有指定角色标识
+     */
+    public static boolean hasRole(Object loginId, String role) {
+        return stpLogic.hasRole(loginId, role);
+    }
+
+    /**
+     * 判断:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过]
+     *
+     * @param roleArray 角色标识数组
+     * @return true或false
+     */
+    public static boolean hasRoleAnd(String... roleArray) {
+        return stpLogic.hasRoleAnd(roleArray);
+    }
+
+    /**
+     * 判断:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可]
+     *
+     * @param roleArray 角色标识数组
+     * @return true或false
+     */
+    public static boolean hasRoleOr(String... roleArray) {
+        return stpLogic.hasRoleOr(roleArray);
+    }
+
+    /**
+     * 校验:当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException
+     *
+     * @param role 角色标识
+     */
+    public static void checkRole(String role) {
+        stpLogic.checkRole(role);
+    }
+
+    /**
+     * 校验:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过]
+     *
+     * @param roleArray 角色标识数组
+     */
+    public static void checkRoleAnd(String... roleArray) {
+        stpLogic.checkRoleAnd(roleArray);
+    }
+
+    /**
+     * 校验:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可]
+     *
+     * @param roleArray 角色标识数组
+     */
+    public static void checkRoleOr(String... roleArray) {
+        stpLogic.checkRoleOr(roleArray);
+    }
+
+
+    // =================== 权限验证操作 ===================
+
+    /**
+     * 获取:当前账号的权限码集合
+     *
+     * @return /
+     */
+    public static List<String> getPermissionList() {
+        return stpLogic.getPermissionList();
+    }
+
+    /**
+     * 获取:指定账号的权限码集合
+     *
+     * @param loginId 指定账号id
+     * @return /
+     */
+    public static List<String> getPermissionList(Object loginId) {
+        return stpLogic.getPermissionList(loginId);
+    }
+
+    /**
+     * 判断:当前账号是否含有指定权限, 返回true或false
+     *
+     * @param permission 权限码
+     * @return 是否含有指定权限
+     */
+    public static boolean hasPermission(String permission) {
+        return stpLogic.hasPermission(permission);
+    }
+
+    /**
+     * 判断:指定账号id是否含有指定权限, 返回true或false
+     *
+     * @param loginId    账号id
+     * @param permission 权限码
+     * @return 是否含有指定权限
+     */
+    public static boolean hasPermission(Object loginId, String permission) {
+        return stpLogic.hasPermission(loginId, permission);
+    }
+
+    /**
+     * 判断:当前账号是否含有指定权限, [指定多个,必须全部具有]
+     *
+     * @param permissionArray 权限码数组
+     * @return true 或 false
+     */
+    public static boolean hasPermissionAnd(String... permissionArray) {
+        return stpLogic.hasPermissionAnd(permissionArray);
+    }
+
+    /**
+     * 判断:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可]
+     *
+     * @param permissionArray 权限码数组
+     * @return true 或 false
+     */
+    public static boolean hasPermissionOr(String... permissionArray) {
+        return stpLogic.hasPermissionOr(permissionArray);
+    }
+
+    /**
+     * 校验:当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException
+     *
+     * @param permission 权限码
+     */
+    public static void checkPermission(String permission) {
+        stpLogic.checkPermission(permission);
+    }
+
+    /**
+     * 校验:当前账号是否含有指定权限 [指定多个,必须全部验证通过]
+     *
+     * @param permissionArray 权限码数组
+     */
+    public static void checkPermissionAnd(String... permissionArray) {
+        stpLogic.checkPermissionAnd(permissionArray);
+    }
+
+    /**
+     * 校验:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可]
+     *
+     * @param permissionArray 权限码数组
+     */
+    public static void checkPermissionOr(String... permissionArray) {
+        stpLogic.checkPermissionOr(permissionArray);
+    }
+
+
+    // =================== id 反查token 相关操作 ===================
+
+    /**
+     * 获取指定账号id的tokenValue
+     * <p> 在配置为允许并发登录时,此方法只会返回队列的最后一个token,
+     * 如果你需要返回此账号id的所有token,请调用 getTokenValueListByLoginId
+     *
+     * @param loginId 账号id
+     * @return token值
+     */
+    public static String getTokenValueByLoginId(Object loginId) {
+        return stpLogic.getTokenValueByLoginId(loginId);
+    }
+
+    /**
+     * 获取指定账号id指定设备端的tokenValue
+     * <p> 在配置为允许并发登录时,此方法只会返回队列的最后一个token,
+     * 如果你需要返回此账号id的所有token,请调用 getTokenValueListByLoginId
+     *
+     * @param loginId 账号id
+     * @param device  设备标识
+     * @return token值
+     */
+    public static String getTokenValueByLoginId(Object loginId, String device) {
+        return stpLogic.getTokenValueByLoginId(loginId, device);
+    }
+
+    /**
+     * 获取指定账号id的tokenValue集合
+     *
+     * @param loginId 账号id
+     * @return 此loginId的所有相关token
+     */
+    public static List<String> getTokenValueListByLoginId(Object loginId) {
+        return stpLogic.getTokenValueListByLoginId(loginId);
+    }
+
+    /**
+     * 获取指定账号id指定设备端的tokenValue 集合
+     *
+     * @param loginId 账号id
+     * @param device  设备标识
+     * @return 此loginId的所有相关token
+     */
+    public static List<String> getTokenValueListByLoginId(Object loginId, String device) {
+        return stpLogic.getTokenValueListByLoginId(loginId, device);
+    }
+
+    /**
+     * 返回当前会话的登录设备
+     *
+     * @return 当前令牌的登录设备
+     */
+    public static String getLoginDevice() {
+        return stpLogic.getLoginDevice();
+    }
+
+
+    // =================== 会话管理 ===================
+
+    /**
+     * 根据条件查询Token
+     *
+     * @param keyword 关键字
+     * @param start   开始处索引 (-1代表查询所有)
+     * @param size    获取数量
+     * @return token集合
+     */
+    public static List<String> searchTokenValue(String keyword, int start, int size) {
+        return stpLogic.searchTokenValue(keyword, start, size);
+    }
+
+    /**
+     * 根据条件查询SessionId
+     *
+     * @param keyword 关键字
+     * @param start   开始处索引 (-1代表查询所有)
+     * @param size    获取数量
+     * @return sessionId集合
+     */
+    public static List<String> searchSessionId(String keyword, int start, int size) {
+        return stpLogic.searchSessionId(keyword, start, size);
+    }
+
+    /**
+     * 根据条件查询Token专属Session的Id
+     *
+     * @param keyword 关键字
+     * @param start   开始处索引 (-1代表查询所有)
+     * @param size    获取数量
+     * @return sessionId集合
+     */
+    public static List<String> searchTokenSessionId(String keyword, int start, int size) {
+        return stpLogic.searchTokenSessionId(keyword, start, size);
+    }
+
+
+    // ------------------- 账号封禁 -------------------
+
+    /**
+     * 封禁指定账号
+     * <p> 此方法不会直接将此账号id踢下线,而是在对方再次登录时抛出`DisableLoginException`异常
+     *
+     * @param loginId     指定账号id
+     * @param disableTime 封禁时间, 单位: 秒 (-1=永久封禁)
+     */
+    public static void disable(Object loginId, long disableTime) {
+        stpLogic.disable(loginId, disableTime);
+    }
+
+    /**
+     * 指定账号是否已被封禁 (true=已被封禁, false=未被封禁)
+     *
+     * @param loginId 账号id
+     * @return see note
+     */
+    public static boolean isDisable(Object loginId) {
+        return stpLogic.isDisable(loginId);
+    }
+
+    /**
+     * 获取指定账号剩余封禁时间,单位:秒(-1=永久封禁,-2=未被封禁)
+     *
+     * @param loginId 账号id
+     * @return see note
+     */
+    public static long getDisableTime(Object loginId) {
+        return stpLogic.getDisableTime(loginId);
+    }
+
+    /**
+     * 解封指定账号
+     *
+     * @param loginId 账号id
+     */
+    public static void untieDisable(Object loginId) {
+        stpLogic.untieDisable(loginId);
+    }
+
+
+    // =================== 身份切换 ===================
+
+    /**
+     * 临时切换身份为指定账号id
+     *
+     * @param loginId 指定loginId
+     */
+    public static void switchTo(Object loginId) {
+        stpLogic.switchTo(loginId);
+    }
+
+    /**
+     * 结束临时切换身份
+     */
+    public static void endSwitch() {
+        stpLogic.endSwitch();
+    }
+
+    /**
+     * 当前是否正处于[身份临时切换]中
+     *
+     * @return 是否正处于[身份临时切换]中
+     */
+    public static boolean isSwitch() {
+        return stpLogic.isSwitch();
+    }
+
+    /**
+     * 在一个代码段里方法内,临时切换身份为指定账号id
+     *
+     * @param loginId  指定账号id
+     * @param function 要执行的方法
+     */
+    public static void switchTo(Object loginId, SaFunction function) {
+        stpLogic.switchTo(loginId, function);
+    }
+
+
+    // ------------------- 二级认证 -------------------
+
+    /**
+     * 在当前会话 开启二级认证
+     *
+     * @param safeTime 维持时间 (单位: 秒)
+     */
+    public static void openSafe(long safeTime) {
+        stpLogic.openSafe(safeTime);
+    }
+
+    /**
+     * 当前会话 是否处于二级认证时间内
+     *
+     * @return true=二级认证已通过, false=尚未进行二级认证或认证已超时
+     */
+    public static boolean isSafe() {
+        return stpLogic.isSafe();
+    }
+
+    /**
+     * 检查当前会话是否已通过二级认证,如未通过则抛出异常
+     */
+    public static void checkSafe() {
+        stpLogic.checkSafe();
+    }
+
+    /**
+     * 获取当前会话的二级认证剩余有效时间 (单位: 秒, 返回-2代表尚未通过二级认证)
+     *
+     * @return 剩余有效时间
+     */
+    public static long getSafeTime() {
+        return stpLogic.getSafeTime();
+    }
+
+    /**
+     * 在当前会话 结束二级认证
+     */
+    public static void closeSafe() {
+        stpLogic.closeSafe();
+    }
+
+
+    // =================== 历史API,兼容旧版本 ===================
+
+    /**
+     * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.getLoginType() ,使用方式保持不变 </h1>
+     * <p>
+     * 获取当前StpLogin的loginKey
+     *
+     * @return 当前StpLogin的loginKey
+     */
+    @Deprecated
+    public static String getLoginKey() {
+        return stpLogic.getLoginType();
+    }
+
+    /**
+     * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
+     * <p>
+     * 在当前会话上登录id
+     *
+     * @param loginId 登录id,建议的类型:(long | int | String)
+     */
+    @Deprecated
+    public static void setLoginId(Object loginId) {
+        stpLogic.login(loginId);
+    }
+
+    /**
+     * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
+     * <p>
+     * 在当前会话上登录id, 并指定登录设备
+     *
+     * @param loginId 登录id,建议的类型:(long | int | String)
+     * @param device  设备标识
+     */
+    @Deprecated
+    public static void setLoginId(Object loginId, String device) {
+        stpLogic.login(loginId, device);
+    }
+
+    /**
+     * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
+     * <p>
+     * 在当前会话上登录id, 并指定登录设备
+     *
+     * @param loginId         登录id,建议的类型:(long | int | String)
+     * @param isLastingCookie 是否为持久Cookie
+     */
+    @Deprecated
+    public static void setLoginId(Object loginId, boolean isLastingCookie) {
+        stpLogic.login(loginId, isLastingCookie);
+    }
+
+    /**
+     * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
+     * <p>
+     * 在当前会话上登录id, 并指定所有登录参数Model
+     *
+     * @param loginId    登录id,建议的类型:(long | int | String)
+     * @param loginModel 此次登录的参数Model
+     */
+    @Deprecated
+    public static void setLoginId(Object loginId, SaLoginModel loginModel) {
+        stpLogic.login(loginId, loginModel);
+    }
+
+    /**
+     * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.kickout() ,使用方式保持不变 </h1>
+     * <p>
+     * 会话注销,根据账号id (踢人下线)
+     * <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2
+     *
+     * @param loginId 账号id
+     */
+    @Deprecated
+    public static void logoutByLoginId(Object loginId) {
+        stpLogic.kickout(loginId);
+    }
+
+    /**
+     * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.kickout() ,使用方式保持不变 </h1>
+     * <p>
+     * 会话注销,根据账号id and 设备标识 (踢人下线)
+     * <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2 </p>
+     *
+     * @param loginId 账号id
+     * @param device  设备标识 (填null代表所有注销设备)
+     */
+    @Deprecated
+    public static void logoutByLoginId(Object loginId, String device) {
+        stpLogic.kickout(loginId, device);
+    }
+
 }

+ 47 - 0
sp-core/src/main/java/com/pj/current/task/Task.java

@@ -0,0 +1,47 @@
+package com.pj.current.task;
+
+import com.google.common.primitives.Ints;
+
+import java.util.concurrent.Delayed;
+import java.util.concurrent.TimeUnit;
+
+public abstract class Task implements Delayed, Runnable{
+    private String id = "";
+    private long start = 0;
+
+    public Task(String id, long delayInMilliseconds){
+        this.id = id;
+        this.start = System.currentTimeMillis() + delayInMilliseconds;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    @Override
+    public long getDelay(TimeUnit unit) {
+        long diff = this.start - System.currentTimeMillis();
+        return unit.convert(diff, TimeUnit.MILLISECONDS);
+    }
+
+    @Override
+    public int compareTo(Delayed o) {
+        return Ints.saturatedCast(this.start - ((Task) o).start);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null) return false;
+        if (!(o instanceof Task)) {
+            return false;
+        }
+        Task t = (Task)o;
+        return this.id.equals(t.getId());
+    }
+
+    @Override
+    public int hashCode() {
+        return this.id.hashCode();
+    }
+}

+ 41 - 0
sp-core/src/main/java/com/pj/current/task/TaskService.java

@@ -0,0 +1,41 @@
+package com.pj.current.task;
+
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import java.util.concurrent.DelayQueue;
+import java.util.concurrent.Executors;
+
+@Component
+public class TaskService {
+    private TaskService taskService;
+    private DelayQueue<Task> delayQueue =  new DelayQueue<Task>();
+
+    @PostConstruct
+    private void init() {
+        taskService = this;
+
+        Executors.newSingleThreadExecutor().execute(() -> {
+            while (true) {
+                try {
+                    Task task = delayQueue.take();
+                    task.run();
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        });
+    }
+
+    public void addTask(Task task){
+        if(delayQueue.contains(task)){
+            return;
+        }
+        delayQueue.add(task);
+    }
+
+    public void removeTask(Task task){
+        delayQueue.remove(task);
+    }
+
+}

+ 120 - 0
sp-core/src/main/java/com/pj/project/tb_car_visit_record/TbCarVisitRecord.java

@@ -0,0 +1,120 @@
+package com.pj.project.tb_car_visit_record;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import lombok.EqualsAndHashCode;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+/**
+ * Model: tb_car_visit_record -- 车辆出入记录
+ * @author qzyReal 
+ */
+@Data
+@Accessors(chain = true)
+@TableName(TbCarVisitRecord.TABLE_NAME)
+@EqualsAndHashCode(callSuper = false)
+public class TbCarVisitRecord extends Model<TbCarVisitRecord> implements Serializable {
+
+	// ---------- 模块常量 ----------
+	/**
+	 * 序列化版本id 
+	 */
+	private static final long serialVersionUID = 1L;	
+	/**
+	 * 此模块对应的表名 
+	 */
+	public static final String TABLE_NAME = "tb_car_visit_record";	
+	/**
+	 * 此模块对应的权限码 
+	 */
+	public static final String PERMISSION_CODE = "tb-car-visit-record";	
+	public static final String PERMISSION_CODE_ADD = "tb-car-visit-record-add";
+	public static final String PERMISSION_CODE_EDIT = "tb-car-visit-record-edit";
+	public static final String PERMISSION_CODE_DEL = "tb-car-visit-record-del";
+	public static final String PERMISSION_CODE_EXPORT = "tb-car-visit-record-export";
+
+
+	// ---------- 表中字段 ----------
+	/**
+	 * 主键 
+	 */
+	@TableId(type = IdType.AUTO)
+	private Long id;	
+
+	/**
+	 * 通道 
+	 */
+	private Long channelId;
+	private Long deptId;
+	private String deptName;
+	private String channel;
+
+	/**
+	 * 车牌 
+	 */
+	private String carNo;	
+
+	/**
+	 * 通过时间 
+	 */
+	private String visitTime;	
+
+	/**
+	 * 方向(1=入,2=出) 
+	 */
+	private Integer direction;	
+
+	/**
+	 * 图片 
+	 */
+	private String image;	
+
+	/**
+	 * 类型(1=临时,2=内部) 
+	 */
+	private Integer type=1;
+
+	/**
+	 * 联系人 
+	 */
+	private String driverName;	
+
+	/**
+	 * 联系电话 
+	 */
+	private String driverContact;	
+
+	/**
+	 * 联系人身份证 
+	 */
+	private String driverIdCard;	
+
+	/**
+	 * 方式(1=手动,2=自动) 
+	 */
+	private Integer recordType=1;
+
+	private Date createTime;
+	private Date updateTime;
+
+	@TableField(exist = false)
+	private String typeStr;
+	@TableField(exist = false)
+	private String directionStr;
+
+
+	public String getTypeStr() {
+		return recordType == 1 ? "补录" : "设备上传";
+	}
+
+	public String getDirectionStr() {
+		return direction == 1 ? "入场" : "离场";
+	}
+
+
+}

+ 28 - 0
sp-core/src/main/java/com/pj/project/tb_car_visit_record/TbCarVisitRecordMapper.java

@@ -0,0 +1,28 @@
+package com.pj.project.tb_car_visit_record;
+
+import java.util.List;
+
+import org.apache.ibatis.annotations.Mapper;
+
+import com.pj.utils.so.*;
+	import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.springframework.stereotype.Repository;
+
+/**
+* Mapper: tb_car_visit_record -- 车辆出入记录
+* @author qzyReal
+*/
+
+@Mapper
+@Repository
+public interface TbCarVisitRecordMapper extends BaseMapper <TbCarVisitRecord> {
+
+/**
+* 查集合 - 根据条件(参数为空时代表忽略指定条件)
+* @param so 参数集合
+* @return 数据列表
+*/
+List<TbCarVisitRecord> getList(SoMap so);
+
+
+}

+ 72 - 0
sp-core/src/main/java/com/pj/project/tb_car_visit_record/TbCarVisitRecordMapper.xml

@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.pj.project.tb_car_visit_record.TbCarVisitRecordMapper">
+
+
+	<!-- ================================== 查询相关 ================================== -->
+			<!-- select id, channel, car_no, visit_time, direction, image, type, driver_name, driver_contact, driver_id_card, record_type from tb_car_visit_record  -->
+
+		<!-- 通用映射:手动模式 -->
+		<resultMap id="model" type="com.pj.project.tb_car_visit_record.TbCarVisitRecord">
+				<result property="id" column="id" />
+				<result property="channel" column="channel" />
+				<result property="carNo" column="car_no" />
+				<result property="visitTime" column="visit_time" />
+				<result property="direction" column="direction" />
+				<result property="image" column="image" />
+				<result property="type" column="type" />
+				<result property="driverName" column="driver_name" />
+				<result property="driverContact" column="driver_contact" />
+				<result property="driverIdCard" column="driver_id_card" />
+				<result property="recordType" column="record_type" />
+		</resultMap>
+
+	<!-- 公共查询sql片段 -->
+	<sql id="select_sql">
+		select *
+		from tb_car_visit_record
+	</sql>
+
+
+
+	<!-- 查集合 - 根据条件(参数为空时代表忽略指定条件) [G] -->
+	<select id="getList" resultMap="model">
+		<include refid="select_sql"></include>
+		<where>
+						<if test=' this.has("id") '> and id = #{id} </if>
+			<if test=' this.has("channel") '> and channel = #{channel} </if>
+			<if test=' this.has("carNo") '> and car_no like concat('%',#{carNo},'%') </if>
+			<if test=' this.has("visitTime") '> and visit_time = #{visitTime} </if>
+			<if test=' this.has("direction") '> and direction = #{direction} </if>
+			<if test=' this.has("type") '> and type = #{type} </if>
+			<if test=' this.has("driverName") '> and driver_name like concat('%',#{driverName} '%')</if>
+			<if test=' this.has("driverContact") '> and driver_contact like concat('%',#{driverContact},'%')  </if>
+			<if test=' this.has("driverIdCard") '> and driver_id_card = #{driverIdCard} </if>
+			<if test=' this.has("recordType") '> and record_type = #{recordType} </if>
+		</where>
+		order by
+		<choose>
+						<when test='sortType == 1'> id desc </when>
+			<when test='sortType == 2'> channel desc </when>
+			<when test='sortType == 3'> car_no desc </when>
+			<when test='sortType == 4'> visit_time desc </when>
+			<when test='sortType == 5'> direction desc </when>
+			<when test='sortType == 6'> type desc </when>
+			<when test='sortType == 7'> driver_name desc </when>
+			<when test='sortType == 8'> driver_contact desc </when>
+			<when test='sortType == 9'> driver_id_card desc </when>
+			<when test='sortType == 10'> record_type desc </when>
+			<otherwise> id desc </otherwise>
+		</choose>
+	</select>
+
+
+
+
+
+
+
+
+
+
+</mapper>

+ 118 - 0
sp-core/src/main/java/com/pj/project/tb_car_visit_record/TbCarVisitRecordService.java

@@ -0,0 +1,118 @@
+package com.pj.project.tb_car_visit_record;
+
+import java.io.File;
+import java.util.Date;
+import java.util.List;
+
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.poi.excel.ExcelUtil;
+import cn.hutool.poi.excel.ExcelWriter;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.pj.current.config.SystemObject;
+import com.pj.project.tb_person_visit_record.TbPersonVisitRecord;
+import com.pj.project.tb_venues.TbVenues;
+import com.pj.project.tb_venues.TbVenuesService;
+import com.pj.project4sp.uploadfile.UploadConfig;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import com.pj.utils.so.*;
+
+import javax.annotation.Resource;
+
+/**
+ * Service: tb_car_visit_record -- 车辆出入记录
+ *
+ * @author qzyReal
+ */
+@Service
+@Transactional(rollbackFor = Exception.class)
+public class TbCarVisitRecordService extends ServiceImpl<TbCarVisitRecordMapper, TbCarVisitRecord> implements IService<TbCarVisitRecord> {
+
+    /**
+     * 底层 Mapper 对象
+     */
+    @Autowired
+    TbCarVisitRecordMapper tbCarVisitRecordMapper;
+    @Resource
+    private TbVenuesService tbVenuesService;
+
+    @Resource
+    private UploadConfig uploadConfig;
+
+    /**
+     * 增
+     */
+    public void add(TbCarVisitRecord t) {
+        Date now = new Date();
+        TbVenues tbVenues = tbVenuesService.getById(t.getChannelId());
+        t.setRecordType(1).setCreateTime(now).setCarNo(t.getCarNo().toUpperCase())
+                .setUpdateTime(now).setDeptId(tbVenues.getDeptId()).setDeptName(tbVenues.getDeptName())
+                .setChannel(tbVenues.getName());
+        this.save(t);
+    }
+
+    /**
+     * 删
+     */
+    public void delete(Long id) {
+        this.removeById(id);
+    }
+
+    /**
+     * 改
+     */
+    public void update(TbCarVisitRecord t) {
+        Date now=new Date();
+        t.setUpdateTime(new Date());
+        TbVenues tbVenues = tbVenuesService.getById(t.getChannelId());
+        t.setCarNo(t.getCarNo().toUpperCase())
+                .setUpdateTime(now)
+                .setDeptId(tbVenues.getDeptId())
+                .setDeptName(tbVenues.getDeptName())
+                .setChannel(tbVenues.getName());
+        this.updateById(t);
+    }
+
+    /**
+     * 查
+     */
+    public TbCarVisitRecord getById(Long id) {
+        return super.getById(id);
+    }
+
+    /**
+     * 查集合 - 根据条件(参数为空时代表忽略指定条件)
+     */
+    public List<TbCarVisitRecord> getList(SoMap so) {
+        return tbCarVisitRecordMapper.getList(so);
+    }
+
+
+    public String export(SoMap soMap) {
+        List<TbCarVisitRecord> list = this.getList(soMap);
+        String flieTypeFolder = "/export/";
+        String currDateFolder = DateUtil.today();
+        String fileName = "carRecord-" + DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".xlsx";
+        String fileFolder = new File(uploadConfig.rootFolder).getAbsolutePath() +
+                uploadConfig.httpPrefix + flieTypeFolder + currDateFolder + "/";
+        if (!FileUtil.exist(fileFolder)) {
+            FileUtil.mkdir(fileFolder);
+        }
+        ExcelWriter writer = ExcelUtil.getWriter(fileFolder + fileName);
+        writer.addHeaderAlias("deptName", "部门");
+        writer.addHeaderAlias("driverName", "联系人");
+        writer.addHeaderAlias("driverContact", "联系号码");
+        writer.addHeaderAlias("channel", "通道");
+        writer.addHeaderAlias("carNo", "车牌");
+        writer.addHeaderAlias("visitTime", "时间");
+        writer.addHeaderAlias("typeStr", "补录/自动");
+        writer.addHeaderAlias("directionStr", "入场/离场");
+        writer.setOnlyAlias(true);
+        writer.write(list, true);
+        writer.close();
+        return SystemObject.config.getDomain() + uploadConfig.httpPrefix + flieTypeFolder + currDateFolder + "/" + fileName;
+    }
+}

+ 0 - 65
sp-core/src/main/java/com/pj/project/tb_channel/TbChannelUtil.java

@@ -1,65 +0,0 @@
-package com.pj.project.tb_channel;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-import com.pj.utils.sg.*;
-
-/**
- * 工具类:tb_channel -- 通道管理
- * @author qzyReal 
- *
- */
-@Component
-public class TbChannelUtil {
-
-	
-	/** 底层 Mapper 对象 */
-	public static TbChannelMapper tbChannelMapper;
-	@Autowired
-	private void setTbChannelMapper(TbChannelMapper tbChannelMapper) {
-		TbChannelUtil.tbChannelMapper = tbChannelMapper;
-	}
-	
-	
-	/** 
-	 * 将一个 TbChannel 对象进行进行数据完整性校验 (方便add/update等接口数据校验) [G] 
-	 */
-	static void check(TbChannel t) {
-		AjaxError.throwByIsNull(t.getId(), "[] 不能为空");		// 验证:  
-		AjaxError.throwByIsNull(t.getDeptId(), "[部门ID] 不能为空");		// 验证: 部门ID 
-		AjaxError.throwByIsNull(t.getName(), "[名称] 不能为空");		// 验证: 名称 
-		AjaxError.throwByIsNull(t.getVenuesId(), "[场所ID] 不能为空");		// 验证: 场所ID 
-		AjaxError.throwByIsNull(t.getVenuesName(), "[场所名称] 不能为空");		// 验证: 场所名称 
-		AjaxError.throwByIsNull(t.getTerminalNum(), "[设备数] 不能为空");		// 验证: 设备数 
-		AjaxError.throwByIsNull(t.getRemark(), "[备注] 不能为空");		// 验证: 备注 
-		AjaxError.throwByIsNull(t.getCreateTime(), "[创建时间] 不能为空");		// 验证: 创建时间 
-		AjaxError.throwByIsNull(t.getCreateBy(), "[创建人] 不能为空");		// 验证: 创建人 
-		AjaxError.throwByIsNull(t.getUpdateTime(), "[更新时间] 不能为空");		// 验证: 更新时间 
-		AjaxError.throwByIsNull(t.getUpdateBy(), "[更新人] 不能为空");		// 验证: 更新人 
-	}
-
-	/** 
-	 * 获取一个TbChannel (方便复制代码用) [G] 
-	 */ 
-	static TbChannel getTbChannel() {
-		TbChannel t = new TbChannel();	// 声明对象 
-		t.setId(0L);		//  
-		t.setDeptId(0L);		// 部门ID 
-		t.setName("");		// 名称 
-		t.setVenuesId(0L);		// 场所ID 
-		t.setVenuesName("");		// 场所名称 
-		t.setTerminalNum(0);		// 设备数 
-		t.setRemark("");		// 备注 
-		t.setCreateTime("");		// 创建时间 
-		t.setCreateBy("");		// 创建人 
-		t.setUpdateTime("");		// 更新时间 
-		t.setUpdateBy("");		// 更新人 
-		return t;
-	}
-	
-	
-	
-	
-	
-}

+ 74 - 0
sp-core/src/main/java/com/pj/project/tb_command_log/TbCommandLogMapper.xml

@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.pj.project.tb_command_log.TbCommandLogMapper">
+
+
+	<!-- ================================== 查询相关 ================================== -->
+			<!-- select id, dept_id, command, sn, terminal_name, channel_name, create_time, response_time, msg_id, response_content, response_code from tb_command_log  -->
+
+		<!-- 通用映射:手动模式 -->
+		<resultMap id="model" type="com.pj.project.tb_command_log.TbCommandLog">
+				<result property="id" column="id" />
+				<result property="deptId" column="dept_id" />
+				<result property="command" column="command" />
+				<result property="sn" column="sn" />
+				<result property="terminalName" column="terminal_name" />
+				<result property="channelName" column="channel_name" />
+				<result property="createTime" column="create_time" />
+				<result property="responseTime" column="response_time" />
+				<result property="msgId" column="msg_id" />
+				<result property="responseContent" column="response_content" />
+				<result property="responseCode" column="response_code" />
+		</resultMap>
+
+	<!-- 公共查询sql片段 -->
+	<sql id="select_sql">
+		select *
+		from tb_command_log
+	</sql>
+
+
+
+	<!-- 查集合 - 根据条件(参数为空时代表忽略指定条件) [G] -->
+	<select id="getList" resultMap="model">
+		<include refid="select_sql"></include>
+		<where>
+						<if test=' this.has("id") '> and id = #{id} </if>
+			<if test=' this.has("deptId") '> and dept_id = #{deptId} </if>
+			<if test=' this.has("command") '> and command = #{command} </if>
+			<if test=' this.has("sn") '> and sn = #{sn} </if>
+			<if test=' this.has("terminalName") '> and terminal_name = #{terminalName} </if>
+			<if test=' this.has("channelName") '> and channel_name = #{channelName} </if>
+			<if test=' this.has("createTime") '> and create_time = #{createTime} </if>
+			<if test=' this.has("responseTime") '> and response_time = #{responseTime} </if>
+			<if test=' this.has("msgId") '> and msg_id = #{msgId} </if>
+			<if test=' this.has("responseContent") '> and response_content = #{responseContent} </if>
+			<if test=' this.has("responseCode") '> and response_code = #{responseCode} </if>
+		</where>
+		order by
+		<choose>
+						<when test='sortType == 1'> id desc </when>
+			<when test='sortType == 2'> dept_id desc </when>
+			<when test='sortType == 3'> command desc </when>
+			<when test='sortType == 4'> sn desc </when>
+			<when test='sortType == 5'> terminal_name desc </when>
+			<when test='sortType == 6'> channel_name desc </when>
+			<when test='sortType == 7'> create_time desc </when>
+			<when test='sortType == 8'> response_time desc </when>
+			<when test='sortType == 9'> msg_id desc </when>
+			<when test='sortType == 10'> response_content desc </when>
+			<when test='sortType == 11'> response_code desc </when>
+			<otherwise> id desc </otherwise>
+		</choose>
+	</select>
+
+
+
+
+
+
+
+
+
+
+</mapper>

+ 57 - 0
sp-core/src/main/java/com/pj/project/tb_command_log/TbCommandLogService.java

@@ -0,0 +1,57 @@
+package com.pj.project.tb_command_log;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import com.pj.utils.so.*;
+
+/**
+ * Service: tb_command_log -- 
+ * @author qzyReal 
+ */
+@Service
+@Transactional(rollbackFor = Exception.class)
+public class TbCommandLogService extends ServiceImpl<TbCommandLogMapper, TbCommandLog> implements IService<TbCommandLog>{
+
+	/** 底层 Mapper 对象 */
+	@Autowired
+	TbCommandLogMapper tbCommandLogMapper;
+
+
+	/** 增 */
+	public void add(TbCommandLog t){
+	this.save( t);
+	}
+
+	/** 删 */
+	public void delete(Long id){
+	this.removeById(id);
+	}
+
+	/** 改 */
+	public void update(TbCommandLog t){
+	this.updateById(t);
+	}
+
+	/** 查 */
+	public TbCommandLog getById(Long id){
+	return super.getById(id);
+	}
+
+	/** 查集合 - 根据条件(参数为空时代表忽略指定条件) */
+	public List<TbCommandLog> getList(SoMap so) {
+		return tbCommandLogMapper.getList(so);	
+	}
+
+
+	public TbCommandLog findByMsgId(String msgId) {
+		QueryWrapper<TbCommandLog>ew=new QueryWrapper<>();
+		ew.eq("msg_id",msgId);
+		return getOne(ew);
+	}
+}

+ 28 - 0
sp-core/src/main/java/com/pj/project/tb_command_log/task/ClearTask.java

@@ -0,0 +1,28 @@
+package com.pj.project.tb_command_log.task;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
+import com.pj.project.tb_command_log.TbCommandLog;
+import com.pj.project.tb_command_log.TbCommandLogService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+
+@Component
+@Slf4j
+public class ClearTask {
+    @Resource
+    private TbCommandLogService tbCommandLogService;
+
+    /**
+     * 凌晨开始删除前一天的下发日志
+     */
+    @Scheduled(cron = "5 0 0 * * ?")
+    @Async
+    public void clear() {
+        tbCommandLogService.remove(new QueryWrapper<>());
+    }
+}

+ 1 - 1
sp-core/src/main/java/com/pj/project/tb_dept/TbDept.java

@@ -53,7 +53,7 @@ public class TbDept extends Model<TbDept> implements Serializable {
 	/**
 	 * 人数 
 	 */
-	private int peopleNum;
+	private Integer peopleNum;
 
 	/**
 	 * 描述 

+ 27 - 12
sp-core/src/main/java/com/pj/project/tb_channel/TbChannel.java → sp-core/src/main/java/com/pj/project/tb_manager/TbManager.java

@@ -1,4 +1,4 @@
-package com.pj.project.tb_channel;
+package com.pj.project.tb_manager;
 
 import java.io.Serializable;
 import com.baomidou.mybatisplus.annotation.*;
@@ -9,14 +9,14 @@ import lombok.Data;
 import lombok.experimental.Accessors;
 
 /**
- * Model: tb_channel -- 通道管理
+ * Model: tb_manager -- 防疫人员
  * @author qzyReal 
  */
 @Data
 @Accessors(chain = true)
-@TableName(TbChannel.TABLE_NAME)
+@TableName(TbManager.TABLE_NAME)
 @EqualsAndHashCode(callSuper = false)
-public class TbChannel extends Model<TbChannel> implements Serializable {
+public class TbManager extends Model<TbManager> implements Serializable {
 
 	// ---------- 模块常量 ----------
 	/**
@@ -26,29 +26,39 @@ public class TbChannel extends Model<TbChannel> implements Serializable {
 	/**
 	 * 此模块对应的表名 
 	 */
-	public static final String TABLE_NAME = "tb_channel";	
+	public static final String TABLE_NAME = "tb_manager";	
 	/**
 	 * 此模块对应的权限码 
 	 */
-	public static final String PERMISSION_CODE = "tb-channel";	
+	public static final String PERMISSION_CODE = "tb-manager";	
 
 
 	// ---------- 表中字段 ----------
 	/**
-	 *  
+	 * 主键 
 	 */
 	@TableId(type = IdType.AUTO)
 	private Long id;	
 
 	/**
+	 * 姓名 
+	 */
+	private String name;	
+
+	/**
+	 * 联系号码 
+	 */
+	private String phone;	
+
+	/**
 	 * 部门ID 
 	 */
 	private Long deptId;	
 
 	/**
-	 * 名称 
+	 * 职能 
 	 */
-	private String name;	
+	private String position;	
 
 	/**
 	 * 场所ID 
@@ -56,14 +66,19 @@ public class TbChannel extends Model<TbChannel> implements Serializable {
 	private Long venuesId;	
 
 	/**
-	 * 场所名称 
+	 * 值班地点 
 	 */
 	private String venuesName;	
 
 	/**
-	 * 设备数 
+	 * 值班开始时间 
+	 */
+	private String workStartTime;	
+
+	/**
+	 * 值班结束时间 
 	 */
-	private Integer terminalNum;	
+	private String workEndTime;	
 
 	/**
 	 * 备注 

+ 17 - 19
business-system/venues-system/src/main/java/com/pj/biz/TbChannelController.java → sp-core/src/main/java/com/pj/project/tb_manager/TbManagerController.java

@@ -1,9 +1,7 @@
-package com.pj.biz;
+package com.pj.project.tb_manager;
 
 import java.util.List;
 
-import com.pj.project.tb_channel.TbChannel;
-import com.pj.project.tb_channel.TbChannelService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.bind.annotation.*;
@@ -17,55 +15,55 @@ import cn.dev33.satoken.annotation.SaCheckPermission;
 
 
 /**
-* Controller: tb_channel -- 通道管理
+* Controller: tb_manager -- 防疫人员
 * @author qzyReal
 */
 @RestController
-@RequestMapping("/TbChannel/")
-public class TbChannelController {
+@RequestMapping("/TbManager/")
+public class TbManagerController {
 
 /** 底层 Service 对象 */
 @Autowired
-TbChannelService tbChannelService;
+TbManagerService tbManagerService;
 
 /** 增 */
 @RequestMapping("add")
-	@SaCheckPermission(TbChannel.PERMISSION_CODE)
+	@SaCheckPermission(TbManager.PERMISSION_CODE)
 @Transactional(rollbackFor = Exception.class)
-public AjaxJson add(TbChannel t){
-tbChannelService.add(t);
+public AjaxJson add(TbManager t){
+tbManagerService.add(t);
 return AjaxJson.getSuccessData(t);
 }
 
 /** 删 */
 @RequestMapping("delete")
-	@SaCheckPermission(TbChannel.PERMISSION_CODE)
+	@SaCheckPermission(TbManager.PERMISSION_CODE)
 public AjaxJson delete(Long id){
-tbChannelService.delete(id);
+tbManagerService.delete(id);
 return AjaxJson.getSuccess();
 }
 
 /** 删 - 根据id列表 */
 @RequestMapping("deleteByIds")
-	@SaCheckPermission(TbChannel.PERMISSION_CODE)
+	@SaCheckPermission(TbManager.PERMISSION_CODE)
 public AjaxJson deleteByIds(){
 List<Long> ids = SoMap.getRequestSoMap().getListByComma("ids", long.class);
-	int line = SP.publicMapper.deleteByIds(TbChannel.TABLE_NAME, ids);
+	int line = SP.publicMapper.deleteByIds(TbManager.TABLE_NAME, ids);
 	return AjaxJson.getByLine(line);
 	}
 
 	/** 改 */
 	@RequestMapping("update")
-		@SaCheckPermission(TbChannel.PERMISSION_CODE)
-	public AjaxJson update(TbChannel t){
-		tbChannelService.update(t);
+		@SaCheckPermission(TbManager.PERMISSION_CODE)
+	public AjaxJson update(TbManager t){
+		tbManagerService.update(t);
 	return AjaxJson.getSuccess();
 	}
 
 	/** 查 - 根据id */
 	@RequestMapping("getById")
 	public AjaxJson getById(Long id){
-	TbChannel t = tbChannelService.getById(id);
+	TbManager t = tbManagerService.getById(id);
 	return AjaxJson.getSuccessData(t);
 	}
 
@@ -73,7 +71,7 @@ List<Long> ids = SoMap.getRequestSoMap().getListByComma("ids", long.class);
 	@RequestMapping("getList")
 	public AjaxJson getList() {
 	SoMap so = SoMap.getRequestSoMap();
-		List<TbChannel> list = tbChannelService.getList(so.startPage());
+		List<TbManager> list = tbManagerService.getList(so.startPage());
 	return AjaxJson.getPageData(so.getDataCount(), list);
 	}
 

+ 4 - 4
sp-core/src/main/java/com/pj/project/tb_channel/TbChannelMapper.java → sp-core/src/main/java/com/pj/project/tb_manager/TbManagerMapper.java

@@ -1,4 +1,4 @@
-package com.pj.project.tb_channel;
+package com.pj.project.tb_manager;
 
 import java.util.List;
 
@@ -9,20 +9,20 @@ import com.pj.utils.so.*;
 import org.springframework.stereotype.Repository;
 
 /**
-* Mapper: tb_channel -- 通道管理
+* Mapper: tb_manager -- 防疫人员
 * @author qzyReal
 */
 
 @Mapper
 @Repository
-public interface TbChannelMapper extends BaseMapper <TbChannel> {
+public interface TbManagerMapper extends BaseMapper <TbManager> {
 
 /**
 * 查集合 - 根据条件(参数为空时代表忽略指定条件)
 * @param so 参数集合
 * @return 数据列表
 */
-List<TbChannel> getList(SoMap so);
+List<TbManager> getList(SoMap so);
 
 
 }

+ 27 - 18
sp-core/src/main/java/com/pj/project/tb_channel/TbChannelMapper.xml → sp-core/src/main/java/com/pj/project/tb_manager/TbManagerMapper.xml

@@ -1,19 +1,22 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="com.pj.project.tb_channel.TbChannelMapper">
+<mapper namespace="com.pj.project.tb_manager.TbManagerMapper">
 
 
 	<!-- ================================== 查询相关 ================================== -->
-			<!-- select id, dept_id, name, venues_id, venues_name, terminal_num, remark, create_time, create_by, update_time, update_by from tb_channel  -->
+			<!-- select id, name, phone, dept_id, position, venues_id, venues_name, work_start_time, work_end_time, remark, create_time, create_by, update_time, update_by from tb_manager  -->
 
 		<!-- 通用映射:手动模式 -->
-		<resultMap id="model" type="com.pj.project.tb_channel.TbChannel">
+		<resultMap id="model" type="com.pj.project.tb_manager.TbManager">
 				<result property="id" column="id" />
-				<result property="deptId" column="dept_id" />
 				<result property="name" column="name" />
+				<result property="phone" column="phone" />
+				<result property="deptId" column="dept_id" />
+				<result property="position" column="position" />
 				<result property="venuesId" column="venues_id" />
 				<result property="venuesName" column="venues_name" />
-				<result property="terminalNum" column="terminal_num" />
+				<result property="workStartTime" column="work_start_time" />
+				<result property="workEndTime" column="work_end_time" />
 				<result property="remark" column="remark" />
 				<result property="createTime" column="create_time" />
 				<result property="createBy" column="create_by" />
@@ -24,7 +27,7 @@
 	<!-- 公共查询sql片段 -->
 	<sql id="select_sql">
 		select *
-		from tb_channel
+		from tb_manager
 	</sql>
 
 
@@ -34,11 +37,14 @@
 		<include refid="select_sql"></include>
 		<where>
 						<if test=' this.has("id") '> and id = #{id} </if>
-			<if test=' this.has("deptId") '> and dept_id = #{deptId} </if>
 			<if test=' this.has("name") '> and name = #{name} </if>
+			<if test=' this.has("phone") '> and phone = #{phone} </if>
+			<if test=' this.has("deptId") '> and dept_id = #{deptId} </if>
+			<if test=' this.has("position") '> and position = #{position} </if>
 			<if test=' this.has("venuesId") '> and venues_id = #{venuesId} </if>
 			<if test=' this.has("venuesName") '> and venues_name = #{venuesName} </if>
-			<if test=' this.has("terminalNum") '> and terminal_num = #{terminalNum} </if>
+			<if test=' this.has("workStartTime") '> and work_start_time = #{workStartTime} </if>
+			<if test=' this.has("workEndTime") '> and work_end_time = #{workEndTime} </if>
 			<if test=' this.has("remark") '> and remark = #{remark} </if>
 			<if test=' this.has("createTime") '> and create_time = #{createTime} </if>
 			<if test=' this.has("createBy") '> and create_by = #{createBy} </if>
@@ -48,16 +54,19 @@
 		order by
 		<choose>
 						<when test='sortType == 1'> id desc </when>
-			<when test='sortType == 2'> dept_id desc </when>
-			<when test='sortType == 3'> name desc </when>
-			<when test='sortType == 4'> venues_id desc </when>
-			<when test='sortType == 5'> venues_name desc </when>
-			<when test='sortType == 6'> terminal_num desc </when>
-			<when test='sortType == 7'> remark desc </when>
-			<when test='sortType == 8'> create_time desc </when>
-			<when test='sortType == 9'> create_by desc </when>
-			<when test='sortType == 10'> update_time desc </when>
-			<when test='sortType == 11'> update_by desc </when>
+			<when test='sortType == 2'> name desc </when>
+			<when test='sortType == 3'> phone desc </when>
+			<when test='sortType == 4'> dept_id desc </when>
+			<when test='sortType == 5'> position desc </when>
+			<when test='sortType == 6'> venues_id desc </when>
+			<when test='sortType == 7'> venues_name desc </when>
+			<when test='sortType == 8'> work_start_time desc </when>
+			<when test='sortType == 9'> work_end_time desc </when>
+			<when test='sortType == 10'> remark desc </when>
+			<when test='sortType == 11'> create_time desc </when>
+			<when test='sortType == 12'> create_by desc </when>
+			<when test='sortType == 13'> update_time desc </when>
+			<when test='sortType == 14'> update_by desc </when>
 			<otherwise> id desc </otherwise>
 		</choose>
 	</select>

+ 11 - 11
sp-core/src/main/java/com/pj/project/tb_channel/TbChannelService.java → sp-core/src/main/java/com/pj/project/tb_manager/TbManagerService.java

@@ -1,4 +1,4 @@
-package com.pj.project.tb_channel;
+package com.pj.project.tb_manager;
 
 import java.util.List;
 import com.baomidou.mybatisplus.extension.service.IService;
@@ -9,40 +9,40 @@ import org.springframework.transaction.annotation.Transactional;
 import com.pj.utils.so.*;
 
 /**
- * Service: tb_channel -- 通道管理
+ * Service: tb_manager -- 防疫人员
  * @author qzyReal 
  */
 @Service
-public class TbChannelService extends ServiceImpl<TbChannelMapper, TbChannel> implements IService<TbChannel>{
+public class TbManagerService extends ServiceImpl<TbManagerMapper, TbManager> implements IService<TbManager>{
 
 	/** 底层 Mapper 对象 */
 	@Autowired
-	TbChannelMapper tbChannelMapper;
+	TbManagerMapper tbManagerMapper;
 
 
 	/** 增 */
-    public void add(TbChannel t){
+	void add(TbManager t){
 	this.save( t);
 	}
 
 	/** 删 */
-	public void delete(Long id){
+	void delete(Long id){
 	this.removeById(id);
 	}
 
 	/** 改 */
-	public void update(TbChannel t){
+	void update(TbManager t){
 	this.updateById(t);
 	}
 
 	/** 查 */
-	TbChannel getById(Long id){
+	TbManager getById(Long id){
 	return super.getById(id);
 	}
 
-	/** 查集合 - 根据条件(参数为空时代表忽略指定条件) */
-	public List<TbChannel> getList(SoMap so) {
-		return tbChannelMapper.getList(so);	
+	/** 查集合 - 根据条件(参数为空时代表忽略指定条件) */  
+	List<TbManager> getList(SoMap so) { 
+		return tbManagerMapper.getList(so);	
 	}
 	
 

+ 71 - 0
sp-core/src/main/java/com/pj/project/tb_manager/TbManagerUtil.java

@@ -0,0 +1,71 @@
+package com.pj.project.tb_manager;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.pj.utils.sg.*;
+
+/**
+ * 工具类:tb_manager -- 防疫人员
+ * @author qzyReal 
+ *
+ */
+@Component
+public class TbManagerUtil {
+
+	
+	/** 底层 Mapper 对象 */
+	public static TbManagerMapper tbManagerMapper;
+	@Autowired
+	private void setTbManagerMapper(TbManagerMapper tbManagerMapper) {
+		TbManagerUtil.tbManagerMapper = tbManagerMapper;
+	}
+	
+	
+	/** 
+	 * 将一个 TbManager 对象进行进行数据完整性校验 (方便add/update等接口数据校验) [G] 
+	 */
+	static void check(TbManager t) {
+		AjaxError.throwByIsNull(t.getId(), "[主键] 不能为空");		// 验证: 主键 
+		AjaxError.throwByIsNull(t.getName(), "[姓名] 不能为空");		// 验证: 姓名 
+		AjaxError.throwByIsNull(t.getPhone(), "[联系号码] 不能为空");		// 验证: 联系号码 
+		AjaxError.throwByIsNull(t.getDeptId(), "[部门ID] 不能为空");		// 验证: 部门ID 
+		AjaxError.throwByIsNull(t.getPosition(), "[职能] 不能为空");		// 验证: 职能 
+		AjaxError.throwByIsNull(t.getVenuesId(), "[场所ID] 不能为空");		// 验证: 场所ID 
+		AjaxError.throwByIsNull(t.getVenuesName(), "[值班地点] 不能为空");		// 验证: 值班地点 
+		AjaxError.throwByIsNull(t.getWorkStartTime(), "[值班开始时间] 不能为空");		// 验证: 值班开始时间 
+		AjaxError.throwByIsNull(t.getWorkEndTime(), "[值班结束时间] 不能为空");		// 验证: 值班结束时间 
+		AjaxError.throwByIsNull(t.getRemark(), "[备注] 不能为空");		// 验证: 备注 
+		AjaxError.throwByIsNull(t.getCreateTime(), "[创建时间] 不能为空");		// 验证: 创建时间 
+		AjaxError.throwByIsNull(t.getCreateBy(), "[创建人] 不能为空");		// 验证: 创建人 
+		AjaxError.throwByIsNull(t.getUpdateTime(), "[更新时间] 不能为空");		// 验证: 更新时间 
+		AjaxError.throwByIsNull(t.getUpdateBy(), "[更新人] 不能为空");		// 验证: 更新人 
+	}
+
+	/** 
+	 * 获取一个TbManager (方便复制代码用) [G] 
+	 */ 
+	static TbManager getTbManager() {
+		TbManager t = new TbManager();	// 声明对象 
+		t.setId(0L);		// 主键 
+		t.setName("");		// 姓名 
+		t.setPhone("");		// 联系号码 
+		t.setDeptId(0L);		// 部门ID 
+		t.setPosition("");		// 职能 
+		t.setVenuesId(0L);		// 场所ID 
+		t.setVenuesName("");		// 值班地点 
+		t.setWorkStartTime("");		// 值班开始时间 
+		t.setWorkEndTime("");		// 值班结束时间 
+		t.setRemark("");		// 备注 
+		t.setCreateTime("");		// 创建时间 
+		t.setCreateBy("");		// 创建人 
+		t.setUpdateTime("");		// 更新时间 
+		t.setUpdateBy("");		// 更新人 
+		return t;
+	}
+	
+	
+	
+	
+	
+}

+ 5 - 1
sp-core/src/main/java/com/pj/project/tb_person_black/TbPersonBlack.java

@@ -55,7 +55,11 @@ public class TbPersonBlack extends Model<TbPersonBlack> implements Serializable
 	/**
 	 * 名字 
 	 */
-	private String name;	
+	private String name;
+	/**
+	 * 图片
+	 */
+	private String photo;
 
 	/**
 	 * 身份证 

+ 115 - 0
sp-core/src/main/java/com/pj/project/tb_person_black/TbPersonBlackService.java

@@ -1,18 +1,30 @@
 package com.pj.project.tb_person_black;
 
+import java.util.Collections;
 import java.util.Date;
 import java.util.List;
 
 import cn.dev33.satoken.stp.StpUtil;
 import cn.hutool.core.util.IdcardUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.pj.current.global.BusinessException;
+import com.pj.current.netty.command.constants.CommandType;
+import com.pj.current.netty.command.handler.dto.BlackPersonDTO;
+import com.pj.project.tb_person_filing.TbPersonFiling;
+import com.pj.project.tb_terminal.TbTerminal;
+import com.pj.project.tb_terminal.TbTerminalService;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import com.pj.utils.so.*;
 
+import javax.annotation.Resource;
+
 /**
  * Service: tb_person_black -- 人员黑名单
  *
@@ -27,6 +39,10 @@ public class TbPersonBlackService extends ServiceImpl<TbPersonBlackMapper, TbPer
     @Autowired
     TbPersonBlackMapper tbPersonBlackMapper;
 
+    @Resource
+    @Lazy
+    private TbTerminalService tbTerminalService;
+
 
     /**
      * 增
@@ -35,17 +51,26 @@ public class TbPersonBlackService extends ServiceImpl<TbPersonBlackMapper, TbPer
         if (!IdcardUtil.isValidCard(t.getIdCard())) {
             throw new BusinessException("身份证不正确");
         }
+        TbPersonBlack db = findByIdCardAndDeptId(t.getIdCard(), t.getDeptId());
+        if (db != null) {
+            throw new BusinessException("人员已经存在");
+        }
+
         Date now = new Date();
         String loginId = StpUtil.getLoginIdAsString();
         t.setCreateTime(now).setCreateBy(loginId).setUpdateBy(loginId).setUpdateTime(now);
         this.save(t);
+        // todo 下发黑名单信息
+        issued(t);
     }
 
     /**
      * 删
      */
     public void delete(Long id) {
+        TbPersonBlack db = this.getById(id);
         this.removeById(id);
+        deleteTerminalBlack(db);
     }
 
     /**
@@ -55,13 +80,43 @@ public class TbPersonBlackService extends ServiceImpl<TbPersonBlackMapper, TbPer
         if (!IdcardUtil.isValidCard(t.getIdCard())) {
             throw new BusinessException("身份证不正确");
         }
+        TbPersonBlack idCardDb = findByIdCardAndDeptId(t.getIdCard(), t.getDeptId());
+        if (idCardDb != null && !idCardDb.getId().equals(t.getId())) {
+            throw new BusinessException("人员已存在");
+        }
         Date now = new Date();
         String loginId = StpUtil.getLoginIdAsString();
         t.setUpdateBy(loginId).setUpdateTime(now);
+        TbPersonBlack db = this.getById(t.getId());
+        //改了身份证或者名字,部门,要把旧的人员信息清除,下发新的
+        if (!StrUtil.equals(t.getIdCard(), db.getIdCard())
+                || !StrUtil.equals(t.getName(), db.getName())
+                || !t.getDeptId().equals(db.getDeptId())
+        ) {
+            deleteTerminalBlack(db);
+            issued(t);
+        }
         this.updateById(t);
     }
 
     /**
+     * 删除设备黑名单
+     *
+     * @param t
+     */
+    public void deleteTerminalBlack(TbPersonBlack t) {
+        List<TbTerminal> tbTerminalList = tbTerminalService.findByDeptId(t.getDeptId());
+        JSONObject params = new JSONObject();
+        params.putIfAbsent("identity_numbers", Collections.singleton(t.getIdCard()));
+        tbTerminalList.forEach(tbTerminal -> {
+            tbTerminalService.setCommand(CommandType.DELETE_BLACKLIST_COMMAND.getFuncId(), params, tbTerminal);
+            tbTerminalService.startHeartCommand(tbTerminal.getSn());
+        });
+
+    }
+
+
+    /**
      * 查
      */
     public TbPersonBlack getById(Long id) {
@@ -75,5 +130,65 @@ public class TbPersonBlackService extends ServiceImpl<TbPersonBlackMapper, TbPer
         return tbPersonBlackMapper.getList(so);
     }
 
+    /**
+     * 下发所有黑名单到某一个设备
+     *
+     * @param t
+     */
+    public void issued(TbTerminal t) {
+        Long deptId = t.getDeptId();
+        List<TbPersonBlack> list = this.findByDeptId(deptId);
+        list.forEach(tbPersonBlack -> {
+            JSONObject users = builderData(tbPersonBlack);
+            tbTerminalService.setCommand(CommandType.ISSUED_BLACKLIST_COMMAND.getFuncId(), users, t);
+        });
+    }
+
+    /**
+     * 下发单个黑名单到部门所有设备
+     *
+     * @param tbPersonBlack
+     */
+    public void issued(TbPersonBlack tbPersonBlack) {
+        Long deptId = tbPersonBlack.getDeptId();
+        List<TbTerminal> terminals = tbTerminalService.findByDeptId(deptId);
+        JSONObject users = builderData(tbPersonBlack);
+        terminals.forEach(tbTerminal -> {
+            tbTerminalService.setCommand(CommandType.ISSUED_BLACKLIST_COMMAND.getFuncId(), users, tbTerminal);
+            tbTerminalService.startHeartCommand(tbTerminal.getSn());
+        });
+    }
+
+    /**
+     * 构建黑名单数据格式
+     *
+     * @param tbPersonBlack
+     * @return
+     */
+    private JSONObject builderData(TbPersonBlack tbPersonBlack) {
+        BlackPersonDTO dto = new BlackPersonDTO();
+        dto.setIdentity_number(tbPersonBlack.getIdCard()).setName(tbPersonBlack.getName());
+        JSONObject users = new JSONObject();
+        users.putIfAbsent("users", dto);
+        return users;
+    }
+
+    /**
+     * 根据部门查找
+     *
+     * @param deptId
+     * @return
+     */
+    private List<TbPersonBlack> findByDeptId(Long deptId) {
+        QueryWrapper<TbPersonBlack> ew = new QueryWrapper<>();
+        ew.eq("dept_id", deptId);
+        return list(ew);
+    }
 
+    public TbPersonBlack findByIdCardAndDeptId(String idCard, Long deptId) {
+        QueryWrapper<TbPersonBlack> ew = new QueryWrapper<>();
+        ew.eq("dept_id", deptId);
+        ew.eq("id_card", idCard);
+        return getOne(ew);
+    }
 }

+ 8 - 0
sp-core/src/main/java/com/pj/project/tb_person_filing/TbPersonFiling.java

@@ -57,8 +57,13 @@ public class TbPersonFiling extends Model<TbPersonFiling> implements Serializabl
      * 姓名
      */
     private String name;
+    /**
+     * 工号
+     */
+    private String pin;
     private String phone;
 
+
     /**
      * 类型(1=临时,2=内部)
      */
@@ -89,6 +94,8 @@ public class TbPersonFiling extends Model<TbPersonFiling> implements Serializabl
      */
     private String visit;
 
+    private String photo;
+
     /**
      * 备注
      */
@@ -116,6 +123,7 @@ public class TbPersonFiling extends Model<TbPersonFiling> implements Serializabl
 
     @TableField(exist = false)
     private String deptName;
+
     @TableField(exist = false)
     private String idCadStr;
 

+ 162 - 2
sp-core/src/main/java/com/pj/project/tb_person_filing/TbPersonFilingService.java

@@ -1,16 +1,41 @@
 package com.pj.project.tb_person_filing;
 
+import java.io.File;
+import java.util.Collections;
 import java.util.Date;
 import java.util.List;
 
 import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.core.codec.Base64;
+import cn.hutool.core.codec.Base64Encoder;
+import cn.hutool.core.img.ImgUtil;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.util.IdcardUtil;
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.pj.current.config.SystemObject;
+import com.pj.current.global.BusinessException;
+import com.pj.current.netty.command.constants.CommandType;
+import com.pj.current.netty.command.handler.dto.WhitelistDTO;
+import com.pj.current.task.TaskService;
+import com.pj.project.tb_person_black.TbPersonBlack;
+import com.pj.project.tb_terminal.TbTerminal;
+import com.pj.project.tb_terminal.TbTerminalService;
+import com.pj.project4sp.uploadfile.UploadConfig;
+import com.sun.imageio.plugins.common.ImageUtil;
+import org.apache.poi.ss.util.ImageUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import com.pj.utils.so.*;
 
+import javax.annotation.Resource;
+
 /**
  * Service: tb_person_filing -- 人员备案
  *
@@ -25,26 +50,44 @@ public class TbPersonFilingService extends ServiceImpl<TbPersonFilingMapper, TbP
      */
     @Autowired
     TbPersonFilingMapper tbPersonFilingMapper;
+    @Resource
+    @Lazy
+    private TbTerminalService tbTerminalService;
+
+    @Resource
+    private UploadConfig uploadConfig;
 
 
     /**
      * 增
      */
     public void add(TbPersonFiling t) {
+        String idCard = t.getIdCard();
+        if (!IdcardUtil.isValidCard(idCard)) {
+            throw new BusinessException("身份证不正确");
+        }
+        TbPersonFiling db = findByIdCardAndDeptId(idCard, t.getDeptId());
+        if (db != null) {
+            throw new BusinessException("人员已存在");
+        }
+        String pin = RandomUtil.randomNumbers(9);
+        t.setPin(pin);
         this.handlerTime(t);
         t.setCreateBy(StpUtil.getLoginIdAsString()).setCreateTime(new Date());
         this.save(t);
+        issued(t);
     }
 
     private void handlerTime(TbPersonFiling t) {
         t.setUpdateTime(new Date()).setUpdateBy(StpUtil.getLoginIdAsString());
-
     }
 
     /**
      * 删
      */
     public void delete(Integer id) {
+        TbPersonFiling db = this.getById(id);
+        deleteTerminalWhilte(db);
         this.removeById(id);
     }
 
@@ -52,7 +95,26 @@ public class TbPersonFilingService extends ServiceImpl<TbPersonFilingMapper, TbP
      * 改
      */
     public void update(TbPersonFiling t) {
+        String idCard = t.getIdCard();
+        if (!IdcardUtil.isValidCard(idCard)) {
+            throw new BusinessException("身份证不正确");
+        }
+        TbPersonFiling idCardDb = findByIdCardAndDeptId(idCard, t.getDeptId());
+        if (idCardDb != null && !idCardDb.getId().equals(t.getId())) {
+            throw new BusinessException("人员已存在");
+        }
+        TbPersonFiling db = this.getById(t.getId());
         handlerTime(t);
+        //改了身份证或者名字,部门,要把旧的人员信息清除,下发新的
+        if (!StrUtil.equals(t.getIdCard(), db.getIdCard())
+                || !StrUtil.equals(t.getName(), db.getName())
+                || !t.getDeptId().equals(db.getDeptId())
+        ) {
+            //删除旧的
+            deleteTerminalWhilte(db);
+            //下发新的
+            issued(t);
+        }
         this.updateById(t);
     }
 
@@ -60,7 +122,9 @@ public class TbPersonFilingService extends ServiceImpl<TbPersonFilingMapper, TbP
      * 查
      */
     public TbPersonFiling getById(Integer id) {
-        return tbPersonFilingMapper.getById(id);
+        TbPersonFiling db = tbPersonFilingMapper.getById(id);
+
+        return db;
     }
 
     /**
@@ -70,5 +134,101 @@ public class TbPersonFilingService extends ServiceImpl<TbPersonFilingMapper, TbP
         return tbPersonFilingMapper.getList(so);
     }
 
+    /**
+     * 根据身份证查找
+     *
+     * @param idCard
+     * @return
+     */
+    public TbPersonFiling findByIdCardAndDeptId(String idCard, Long deptId) {
+        QueryWrapper<TbPersonFiling> ew = new QueryWrapper<>();
+        ew.eq("id_card", idCard);
+        ew.eq("dept_id", deptId);
+        return getOne(ew);
+    }
+
+    /**
+     * 下发单个白名单到所有设备
+     *
+     * @param filing
+     */
+    public void issued(TbPersonFiling filing) {
+        List<TbTerminal> tbTerminalList = tbTerminalService.findByDeptId(filing.getDeptId());
+        JSONObject params = builderParams(filing);
+        tbTerminalList.forEach(tbTerminal -> {
+            tbTerminalService.setCommand(CommandType.ISSUED_USER_COMMAND.getFuncId(), params, tbTerminal);
+            tbTerminalService.startHeartCommand(tbTerminal.getSn());
+        });
+    }
+
+
+    /**
+     * 下发所有人员白名单到单个设备
+     *
+     * @param t
+     */
+    public void issued(TbTerminal t) {
+        Long deptId = t.getDeptId();
+        List<TbPersonFiling> list = this.findByDeptId(deptId);
+        list.forEach(tbPersonFiling -> {
+            JSONObject params = builderParams(tbPersonFiling);
+            tbTerminalService.setCommand(CommandType.ISSUED_USER_COMMAND.getFuncId(), params, t);
+        });
+    }
 
+    /**
+     * 删除设备某个人的白名单
+     *
+     * @param t
+     */
+    public void deleteTerminalWhilte(TbPersonFiling t) {
+        List<TbTerminal> tbTerminalList = tbTerminalService.findByDeptId(t.getDeptId());
+        JSONObject params = new JSONObject();
+        params.putIfAbsent("pin", Collections.singleton(t.getPin()));
+        tbTerminalList.forEach(tbTerminal -> {
+            tbTerminalService.setCommand(CommandType.DELETE_USER_COMMAND.getFuncId(), params, tbTerminal);
+            tbTerminalService.startHeartCommand(tbTerminal.getSn());
+        });
+
+    }
+
+    /**
+     * 构建人员下发数据
+     *
+     * @param tbPersonFiling
+     * @return
+     */
+    private JSONObject builderParams(TbPersonFiling tbPersonFiling) {
+        WhitelistDTO dto = new WhitelistDTO();
+        dto.setIdentity_number(tbPersonFiling.getIdCard())
+                .setPin(tbPersonFiling.getPin())
+                .setName(tbPersonFiling.getName());
+        String photo = tbPersonFiling.getPhoto();
+        if (StrUtil.isNotEmpty(photo)) {
+            String filePath = new File(uploadConfig.rootFolder + tbPersonFiling.getPhoto().replace(SystemObject.config.getDomain(), "")).getAbsolutePath();
+            dto.setPhoto(Base64Encoder.encode(FileUtil.readBytes(filePath)));
+        }
+        JSONObject params = new JSONObject();
+        params.putIfAbsent("users", dto);
+        return params;
+    }
+
+
+    /**
+     * 查找部门的白名单
+     *
+     * @param deptId
+     * @return
+     */
+    public List<TbPersonFiling> findByDeptId(Long deptId) {
+        QueryWrapper<TbPersonFiling> ew = new QueryWrapper<>();
+        ew.eq("dept_id", deptId);
+        return list(ew);
+    }
+
+    public TbPersonFiling findByPin(String pin) {
+        QueryWrapper<TbPersonFiling> ew = new QueryWrapper<>();
+        ew.eq("pin", pin);
+        return getOne(ew);
+    }
 }

+ 55 - 0
sp-core/src/main/java/com/pj/project/tb_person_filing/task/Base64ToImage.java

@@ -0,0 +1,55 @@
+package com.pj.project.tb_person_filing.task;
+
+import cn.hutool.core.codec.Base64;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.extra.spring.SpringUtil;
+import com.pj.current.config.SystemObject;
+import com.pj.current.task.Task;
+import com.pj.project.tb_person_filing.TbPersonFiling;
+import com.pj.project.tb_person_filing.TbPersonFilingService;
+import com.pj.project4sp.uploadfile.UploadConfig;
+import com.pj.utils.ImageUtils;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.File;
+
+@Slf4j
+public class Base64ToImage extends Task {
+
+    private String base64;
+    private Integer personId;
+
+    public Base64ToImage(String id, long delayInMilliseconds, Integer personId, String base64) {
+        super(id, delayInMilliseconds);
+        this.base64 = base64;
+        this.personId = personId;
+    }
+
+    @Override
+    public void run() {
+        if (StrUtil.isEmpty(base64)) {
+            log.info("人员信息无图片,{}", personId);
+            return;
+        }
+        UploadConfig uploadConfig = SpringUtil.getBean(UploadConfig.class);
+        TbPersonFilingService tbPersonFilingService = SpringUtil.getBean(TbPersonFilingService.class);
+        TbPersonFiling tbPersonFiling = tbPersonFilingService.getById(personId);
+        String flieTypeFolder = "/image/";
+        String currDateFolder = DateUtil.today();
+        String fileName = RandomUtil.randomString(16) + ".jpg";
+        String imgFilePath = new File(uploadConfig.rootFolder).getAbsolutePath() +
+                uploadConfig.httpPrefix + flieTypeFolder + currDateFolder + "/";
+        if (!FileUtil.exist(imgFilePath)) {
+            FileUtil.mkdir(imgFilePath);
+        }
+        // 对外暴露的http路径
+        String httpUrl = SystemObject.config.getDomain() + uploadConfig.httpPrefix + flieTypeFolder + currDateFolder + "/" + fileName;
+       // ImageUtils.generateImage(base64, imgFilePath + fileName);
+        Base64.decodeToFile(base64,new File(imgFilePath + fileName));
+        tbPersonFiling.setPhoto(httpUrl);
+        tbPersonFilingService.updateById(tbPersonFiling);
+    }
+}

+ 172 - 0
sp-core/src/main/java/com/pj/project/tb_person_visit_record/TbPersonVisitRecord.java

@@ -0,0 +1,172 @@
+package com.pj.project.tb_person_visit_record;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import cn.hutool.core.util.IdcardUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.annotation.*;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import lombok.AllArgsConstructor;
+import lombok.EqualsAndHashCode;
+
+import lombok.Data;
+import lombok.Getter;
+import lombok.experimental.Accessors;
+
+/**
+ * Model: tb_person_visit_record -- 人员出入记录
+ *
+ * @author qzyReal
+ */
+@Data
+@Accessors(chain = true)
+@TableName(TbPersonVisitRecord.TABLE_NAME)
+@EqualsAndHashCode(callSuper = false)
+public class TbPersonVisitRecord extends Model<TbPersonVisitRecord> implements Serializable {
+
+    // ---------- 模块常量 ----------
+    /**
+     * 序列化版本id
+     */
+    private static final long serialVersionUID = 1L;
+    /**
+     * 此模块对应的表名
+     */
+    public static final String TABLE_NAME = "tb_person_visit_record";
+    /**
+     * 此模块对应的权限码
+     */
+    public static final String PERMISSION_CODE = "tb-person-visit-record";
+    public static final String PERMISSION_CODE_EDIT = "tb-person-visit-record-edit";
+    public static final String PERMISSION_CODE_ADD = "tb-person-visit-record-add";
+    public static final String PERMISSION_CODE_EXPORT = "tb-person-visit-record-export";
+
+
+    // ---------- 表中字段 ----------
+    /**
+     * 主键
+     */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 出入(1=入场,2=离场)
+     */
+    private Integer direction;
+    /**
+     * [1=补录,2=设备上传]
+     */
+    private Integer type;
+
+    /**
+     * 部门ID
+     */
+    private Long deptId;
+
+
+
+    /**
+     * 姓名
+     */
+    private String name;
+    private String phone;
+
+    /**
+     * 图片
+     */
+    private String picture;
+
+    /**
+     * 身份证
+     */
+    private String idCard;
+
+    /**
+     * 通过通道
+     */
+    private String channel;
+
+    /**
+     * 场所
+     */
+    private String venues;
+
+    /**
+     * 体温
+     */
+    private String temperature;
+
+    /**
+     * 健康码
+     */
+    private String healthStatus;
+
+    /**
+     * 上传设备
+     */
+    private String sn;
+    private String terminalName;
+
+    /**
+     * 时间
+     */
+    private Date visitDate;
+
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+    private Date updateTime;
+    private Boolean allowPass;
+
+    /**
+     * 部门名称
+     */
+    private String deptName;
+
+    @TableField(exist = false)
+    private String idCadStr;
+
+    @TableField(exist = false)
+    private String typeStr;
+    @TableField(exist = false)
+    private String directionStr;
+
+    @Getter
+    @AllArgsConstructor
+    public static enum TypeEnum {
+        UPLOAD(2, "设备上传"),
+        ADD(1, "补录");
+
+        private Integer type;
+        private String desc;
+    }
+
+    @Getter
+    @AllArgsConstructor
+    public static enum DirectionEnum {
+        OUT(2, "离场"),
+        IN(1, "入场");
+
+        private Integer direction;
+        private String desc;
+    }
+
+
+    public String getIdCadStr() {
+        String str = IdcardUtil.getBirthByIdCard(idCard);
+        if (StrUtil.isNotEmpty(str)) {
+            return idCard.replace(str, "********");
+        }
+        return idCard;
+    }
+
+    public String getTypeStr() {
+        return type == TypeEnum.ADD.type ? "补录" : "设备上传";
+    }
+
+    public String getDirectionStr() {
+        return direction == 1 ? "入场" : "离场";
+    }
+}

+ 28 - 0
sp-core/src/main/java/com/pj/project/tb_person_visit_record/TbPersonVisitRecordMapper.java

@@ -0,0 +1,28 @@
+package com.pj.project.tb_person_visit_record;
+
+import java.util.List;
+
+import org.apache.ibatis.annotations.Mapper;
+
+import com.pj.utils.so.*;
+	import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.springframework.stereotype.Repository;
+
+/**
+* Mapper: tb_person_visit_record -- 人员出入记录
+* @author qzyReal
+*/
+
+@Mapper
+@Repository
+public interface TbPersonVisitRecordMapper extends BaseMapper <TbPersonVisitRecord> {
+
+/**
+* 查集合 - 根据条件(参数为空时代表忽略指定条件)
+* @param so 参数集合
+* @return 数据列表
+*/
+List<TbPersonVisitRecord> getList(SoMap so);
+
+
+}

+ 83 - 0
sp-core/src/main/java/com/pj/project/tb_person_visit_record/TbPersonVisitRecordMapper.xml

@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.pj.project.tb_person_visit_record.TbPersonVisitRecordMapper">
+
+
+	<!-- ================================== 查询相关 ================================== -->
+			<!-- select id, direction, dept_id, dept_name, name, picture, id_card, channel, venues, temperature, health_status, sn, visit_date, create_time from tb_person_visit_record  -->
+
+		<!-- 通用映射:手动模式 -->
+		<resultMap id="model" type="com.pj.project.tb_person_visit_record.TbPersonVisitRecord">
+				<result property="id" column="id" />
+				<result property="direction" column="direction" />
+				<result property="deptId" column="dept_id" />
+				<result property="deptName" column="dept_name" />
+				<result property="name" column="name" />
+				<result property="picture" column="picture" />
+				<result property="idCard" column="id_card" />
+				<result property="channel" column="channel" />
+				<result property="venues" column="venues" />
+				<result property="temperature" column="temperature" />
+				<result property="healthStatus" column="health_status" />
+				<result property="sn" column="sn" />
+				<result property="visitDate" column="visit_date" />
+				<result property="createTime" column="create_time" />
+		</resultMap>
+
+	<!-- 公共查询sql片段 -->
+	<sql id="select_sql">
+		select *
+		from tb_person_visit_record
+	</sql>
+
+
+
+	<!-- 查集合 - 根据条件(参数为空时代表忽略指定条件) [G] -->
+	<select id="getList" resultMap="model">
+		<include refid="select_sql"></include>
+		<where>
+						<if test=' this.has("id") '> and id = #{id} </if>
+			<if test=' this.has("direction") '> and direction = #{direction} </if>
+			<if test=' this.has("deptId") '> and dept_id = #{deptId} </if>
+			<if test=' this.has("deptName") '> and dept_name = #{deptName} </if>
+			<if test=' this.has("name") '> and name like concat('%',#{name},'%') </if>
+			<if test=' this.has("idCard") '> and id_card = #{idCard} </if>
+			<if test=' this.has("channel") '> and channel = #{channel} </if>
+			<if test=' this.has("venues") '> and venues = #{venues} </if>
+			<if test=' this.has("temperature") '> and temperature = #{temperature} </if>
+			<if test=' this.has("healthStatus") '> and health_status = #{healthStatus} </if>
+			<if test=' this.has("type") '> and type = #{type} </if>
+			<if test=' this.has("visitDate") '> and visit_date = #{visitDate} </if>
+			<if test=' this.has("createTime") '> and create_time = #{createTime} </if>
+			<if test=' this.has("startTime") '> and date_format(visit_date,'%Y-%m-%d %H:%i:%s') &gt;= #{startTime} </if>
+			<if test=' this.has("endTime") '> and date_format(visit_date,'%Y-%m-%d %H:%i:%s') &lt;= #{endTime} </if>
+		</where>
+		order by
+		<choose>
+						<when test='sortType == 1'> id desc </when>
+			<when test='sortType == 2'> direction desc </when>
+			<when test='sortType == 3'> dept_id desc </when>
+			<when test='sortType == 4'> dept_name desc </when>
+			<when test='sortType == 5'> name desc </when>
+			<when test='sortType == 6'> id_card desc </when>
+			<when test='sortType == 7'> channel desc </when>
+			<when test='sortType == 8'> venues desc </when>
+			<when test='sortType == 9'> temperature desc </when>
+			<when test='sortType == 10'> health_status desc </when>
+			<when test='sortType == 11'> sn desc </when>
+			<when test='sortType == 12'> visit_date desc </when>
+			<when test='sortType == 13'> create_time desc </when>
+			<otherwise> id desc </otherwise>
+		</choose>
+	</select>
+
+
+
+
+
+
+
+
+
+
+</mapper>

+ 153 - 0
sp-core/src/main/java/com/pj/project/tb_person_visit_record/TbPersonVisitRecordService.java

@@ -0,0 +1,153 @@
+package com.pj.project.tb_person_visit_record;
+
+import java.io.File;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Date;
+import java.util.List;
+
+import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.poi.excel.ExcelUtil;
+import cn.hutool.poi.excel.ExcelWriter;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.pj.current.config.SystemObject;
+import com.pj.current.global.BusinessException;
+import com.pj.project.tb_venues.TbVenues;
+import com.pj.project.tb_venues.TbVenuesService;
+import com.pj.project4sp.uploadfile.UploadConfig;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import com.pj.utils.so.*;
+
+import javax.annotation.Resource;
+
+/**
+ * Service: tb_person_visit_record -- 人员出入记录
+ *
+ * @author qzyReal
+ */
+@Service
+@Transactional(rollbackFor = Exception.class)
+public class TbPersonVisitRecordService extends ServiceImpl<TbPersonVisitRecordMapper, TbPersonVisitRecord> implements IService<TbPersonVisitRecord> {
+
+    /**
+     * 底层 Mapper 对象
+     */
+    @Autowired
+    TbPersonVisitRecordMapper tbPersonVisitRecordMapper;
+    @Resource
+    private UploadConfig uploadConfig;
+    @Resource
+    private TbVenuesService tbVenuesService;
+
+
+    /**
+     * 增
+     */
+    public void add(VisitAddBO t) {
+        String inChannel = t.getInChannel();
+        String outChannel = t.getOutChannel();
+        if (StrUtil.isEmpty(inChannel) && StrUtil.isEmpty(outChannel)) {
+            throw new BusinessException("缺少出入场信息");
+        }
+        if (StrUtil.isNotEmpty(inChannel)) {
+            handler(t, TbPersonVisitRecord.DirectionEnum.IN.getDirection(), inChannel);
+        }
+        if (StrUtil.isNotEmpty(outChannel)) {
+            handler(t, TbPersonVisitRecord.DirectionEnum.OUT.getDirection(), outChannel);
+        }
+    }
+
+    private void handler(VisitAddBO t, Integer direction, String channelId) {
+        Date now = new Date();
+        TbPersonVisitRecord record = new TbPersonVisitRecord();
+        record.setType(TbPersonVisitRecord.TypeEnum.ADD.getType())
+                .setCreateTime(now).setUpdateTime(now).setDirection(direction);
+        BeanUtil.copyProperties(t, record);
+        TbVenues tbVenues = tbVenuesService.getById(channelId);
+        record.setChannel(channelId).setVenues(tbVenues.getName()).setDeptId(tbVenues.getDeptId()).setDeptName(tbVenues.getDeptName());
+        if (direction.equals(TbPersonVisitRecord.DirectionEnum.IN.getDirection())) {
+            record.setVisitDate(t.getInTime())
+                    .setTemperature(t.getInTemperature());
+        } else if (direction.equals(TbPersonVisitRecord.DirectionEnum.OUT.getDirection())) {
+            record.setVisitDate(t.getOutTime()).setTemperature(t.getOutTemperature());
+        }
+        this.save(record);
+    }
+
+    /**
+     * 删
+     */
+    public void delete(Long id) {
+        this.removeById(id);
+    }
+
+    /**
+     * 改
+     */
+    public void update(TbPersonVisitRecord t) {
+        this.updateById(t);
+    }
+
+    /**
+     * 查
+     */
+    public TbPersonVisitRecord getById(Long id) {
+        return super.getById(id);
+    }
+
+    /**
+     * 查集合 - 根据条件(参数为空时代表忽略指定条件)
+     */
+    public List<TbPersonVisitRecord> getList(SoMap so) {
+        return tbPersonVisitRecordMapper.getList(so);
+    }
+
+
+    public String export(SoMap soMap) {
+        List<TbPersonVisitRecord> list = tbPersonVisitRecordMapper.getList(soMap);
+        String flieTypeFolder = "/export/";
+        String currDateFolder = DateUtil.today();
+        String fileName = "record-" + DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".xlsx";
+        String fileFolder = new File(uploadConfig.rootFolder).getAbsolutePath() +
+                uploadConfig.httpPrefix + flieTypeFolder + currDateFolder + "/";
+        if (!FileUtil.exist(fileFolder)) {
+            FileUtil.mkdir(fileFolder);
+        }
+        ExcelWriter writer = ExcelUtil.getWriter(fileFolder + fileName);
+        writer.addHeaderAlias("name", "姓名");
+        writer.addHeaderAlias("deptName", "部门");
+        writer.addHeaderAlias("idCard", "身份证");
+        writer.addHeaderAlias("venues", "场所");
+        writer.addHeaderAlias("channel", "通道");
+        writer.addHeaderAlias("temperature", "体温");
+        writer.addHeaderAlias("healthStatus", "健康码");
+        writer.addHeaderAlias("visitDate", "时间");
+        writer.addHeaderAlias("terminalName", "识别设备");
+        writer.addHeaderAlias("typeStr", "补录/自动");
+        writer.addHeaderAlias("directionStr", "入场/离场");
+        writer.setOnlyAlias(true);
+        writer.write(list, true);
+        writer.close();
+        return SystemObject.config.getDomain() + uploadConfig.httpPrefix + flieTypeFolder + currDateFolder + "/" + fileName;
+    }
+
+    public TbPersonVisitRecord findTheNearRecord(String idCard, Long deptId, Integer direction, LocalDateTime checkDate) {
+        QueryWrapper<TbPersonVisitRecord> ew = new QueryWrapper<>();
+        ew.eq("dept_id", deptId)
+                .eq("id_card", idCard)
+                .eq("direction", direction)
+                .ge("date_format(visit_date,'%Y-%m-%d %H:%i:%s')", checkDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
+        ew.orderByDesc("id");
+        List<TbPersonVisitRecord> list = list(ew);
+        return list.isEmpty() ? null : list.get(0);
+    }
+}

+ 54 - 0
sp-core/src/main/java/com/pj/project/tb_person_visit_record/VisitAddBO.java

@@ -0,0 +1,54 @@
+package com.pj.project.tb_person_visit_record;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Date;
+
+
+@Data
+@EqualsAndHashCode
+public class VisitAddBO {
+
+
+
+    /**
+     * 姓名
+     */
+    private String name;
+    private String phone;
+
+
+    /**
+     * 身份证
+     */
+    private String idCard;
+
+    /**
+     * 通过通道
+     */
+    private String inChannel;
+
+    private String outChannel;
+
+
+
+    /**
+     * 体温
+     */
+    private String inTemperature;
+    private String outTemperature;
+
+    /**
+     * 健康码
+     */
+    private String healthStatus;
+
+
+
+    /**
+     * 时间
+     */
+    private Date inTime;
+    private Date outTime;
+}

+ 58 - 0
sp-core/src/main/java/com/pj/project/tb_person_visit_record/task/ConvertBase64ToImage.java

@@ -0,0 +1,58 @@
+package com.pj.project.tb_person_visit_record.task;
+
+
+import cn.hutool.core.codec.Base64;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.extra.spring.SpringUtil;
+import com.pj.current.config.SystemObject;
+import com.pj.current.task.Task;
+import com.pj.project.tb_person_filing.TbPersonFiling;
+import com.pj.project.tb_person_filing.TbPersonFilingService;
+import com.pj.project.tb_person_visit_record.TbPersonVisitRecord;
+import com.pj.project.tb_person_visit_record.TbPersonVisitRecordService;
+import com.pj.project4sp.uploadfile.UploadConfig;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.File;
+
+@Slf4j
+public class ConvertBase64ToImage extends Task {
+
+    private Long recordId;
+    private String base64;
+
+    public ConvertBase64ToImage(String id, long delayInMilliseconds, Long recordId, String base64) {
+        super(id, delayInMilliseconds);
+        this.recordId = recordId;
+        this.base64 = base64;
+    }
+
+
+    @Override
+    public void run() {
+        if (StrUtil.isEmpty(base64)) {
+            log.info("核验记录无图片,{}", recordId);
+            return;
+        }
+        UploadConfig uploadConfig = SpringUtil.getBean(UploadConfig.class);
+        TbPersonVisitRecordService tbPersonVisitRecordService = SpringUtil.getBean(TbPersonVisitRecordService.class);
+        TbPersonVisitRecord record = tbPersonVisitRecordService.getById(recordId);
+        String flieTypeFolder = "/image/";
+        String currDateFolder = DateUtil.today();
+        String fileName = RandomUtil.randomString(16) + ".jpg";
+        String imgFilePath = new File(uploadConfig.rootFolder).getAbsolutePath() +
+                uploadConfig.httpPrefix + flieTypeFolder + currDateFolder + "/";
+        if (!FileUtil.exist(imgFilePath)) {
+            FileUtil.mkdir(imgFilePath);
+        }
+        // 对外暴露的http路径
+        String httpUrl = SystemObject.config.getDomain() + uploadConfig.httpPrefix + flieTypeFolder + currDateFolder + "/" + fileName;
+        Base64.decodeToFile(base64,new File(imgFilePath + fileName));
+        record.setPicture(httpUrl);
+        tbPersonVisitRecordService.updateById(record);
+
+    }
+}

+ 29 - 3
sp-core/src/main/java/com/pj/project/tb_terminal/TbTerminal.java

@@ -1,11 +1,15 @@
 package com.pj.project.tb_terminal;
 
 import java.io.Serializable;
+import java.util.Date;
+
 import com.baomidou.mybatisplus.annotation.*;
 import com.baomidou.mybatisplus.extension.activerecord.Model;
+import lombok.AllArgsConstructor;
 import lombok.EqualsAndHashCode;
 
 import lombok.Data;
+import lombok.Getter;
 import lombok.experimental.Accessors;
 
 /**
@@ -31,6 +35,12 @@ public class TbTerminal extends Model<TbTerminal> implements Serializable {
 	 * 此模块对应的权限码 
 	 */
 	public static final String PERMISSION_CODE = "tb-terminal";	
+	public static final String PERMISSION_CODE_UNBIND = "tb-terminal-unbind";
+	public static final String PERMISSION_CODE_EDIT = "tb-terminal-edit";
+	public static final String PERMISSION_CODE_DEL = "tb-terminal-del";
+	public static final String PERMISSION_CODE_REBOOT = "tb-terminal-reboot";
+	public static final String PERMISSION_CODE_CLEAR = "tb-terminal-clear";
+	public static final String PERMISSION_CODE_OPEN= "tb-terminal-open";
 
 
 	// ---------- 表中字段 ----------
@@ -98,7 +108,7 @@ public class TbTerminal extends Model<TbTerminal> implements Serializable {
 	/**
 	 * 创建时间 
 	 */
-	private String createTime;	
+	private Date createTime;
 
 	/**
 	 * 创建人 
@@ -108,16 +118,32 @@ public class TbTerminal extends Model<TbTerminal> implements Serializable {
 	/**
 	 * 更新时间 
 	 */
-	private String updateTime;	
+	private Date updateTime;
 
 	/**
 	 * 更新人 
 	 */
-	private String updateBy;	
+	private String updateBy;
+	/**
+	 * 人数
+	 */
+	private Long personNum;
+
+
+	@TableField(exist = false)
+	private String deptName;
+
 
 
+	@Getter
+	@AllArgsConstructor
+	public static enum StateType{
+		ONLINE("在线"),
+		OFFLINE("离线");
+		private String state;
 
 
+	}
 
 	
 

+ 1 - 0
sp-core/src/main/java/com/pj/project/tb_terminal/TbTerminalMapper.java

@@ -25,4 +25,5 @@ public interface TbTerminalMapper extends BaseMapper <TbTerminal> {
 List<TbTerminal> getList(SoMap so);
 
 
+    void unbind(Long id);
 }

+ 7 - 5
sp-core/src/main/java/com/pj/project/tb_terminal/TbTerminalMapper.xml

@@ -28,19 +28,21 @@
 
 	<!-- 公共查询sql片段 -->
 	<sql id="select_sql">
-		select *
+		select *,(select name from tb_dept where tb_dept.id=tb_terminal.dept_id) as deptName
 		from tb_terminal
 	</sql>
+    <update id="unbind">
+		update  tb_terminal set channel_id=null,channel_name=null where id=#{id}
+	</update>
 
 
-
-	<!-- 查集合 - 根据条件(参数为空时代表忽略指定条件) [G] -->
+    <!-- 查集合 - 根据条件(参数为空时代表忽略指定条件) [G] -->
 	<select id="getList" resultMap="model">
 		<include refid="select_sql"></include>
 		<where>
-						<if test=' this.has("id") '> and id = #{id} </if>
+			<if test=' this.has("id") '> and id = #{id} </if>
 			<if test=' this.has("deptId") '> and dept_id = #{deptId} </if>
-			<if test=' this.has("name") '> and name = #{name} </if>
+			<if test=' this.has("name") '> and name like concat('%',#{name},'%') </if>
 			<if test=' this.has("ipAddress") '> and ip_address = #{ipAddress} </if>
 			<if test=' this.has("state") '> and state = #{state} </if>
 			<if test=' this.has("sn") '> and sn = #{sn} </if>

+ 258 - 26
sp-core/src/main/java/com/pj/project/tb_terminal/TbTerminalService.java

@@ -1,49 +1,281 @@
 package com.pj.project.tb_terminal;
 
+import java.util.Date;
 import java.util.List;
+import java.util.Set;
+
+import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.Update;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.pj.current.config.MyConfig;
+import com.pj.current.global.BusinessException;
+import com.pj.current.netty.command.constants.CommandType;
+import com.pj.current.netty.dto.ParamsDTO;
+import com.pj.current.netty.dto.RequestPackageDTO;
+import com.pj.current.netty.dto.ResponsePackageDTO;
+import com.pj.current.netty.heart.HeartBeatHandler;
+import com.pj.current.satoken.StpUserUtil;
+import com.pj.project.tb_command_log.TbCommandLog;
+import com.pj.project.tb_command_log.TbCommandLogService;
+import com.pj.project.tb_person_black.TbPersonBlackService;
+import com.pj.project.tb_person_filing.TbPersonFilingService;
+import com.pj.project.tb_venues.TbVenues;
+import com.pj.project.tb_venues.TbVenuesService;
+import com.pj.utils.cache.RedisUtil;
+import io.netty.channel.ChannelHandlerContext;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import com.pj.utils.so.*;
 
+import javax.annotation.Resource;
+
 /**
  * Service: tb_terminal -- 设备管理
- * @author qzyReal 
+ *
+ * @author qzyReal
  */
 @Service
-public class TbTerminalService extends ServiceImpl<TbTerminalMapper, TbTerminal> implements IService<TbTerminal>{
+@Transactional(rollbackFor = Exception.class)
+@Slf4j
+public class TbTerminalService extends ServiceImpl<TbTerminalMapper, TbTerminal> implements IService<TbTerminal> {
+
+    /**
+     * 底层 Mapper 对象
+     */
+    @Autowired
+    TbTerminalMapper tbTerminalMapper;
+    @Resource
+    private MyConfig myConfig;
+
+    @Resource
+    private TbCommandLogService tbCommandLogService;
+
+    @Resource
+    private TbVenuesService tbVenuesService;
+
+    @Resource
+    private TbPersonFilingService tbPersonFilingService;
+
+    @Resource
+    private TbPersonBlackService tbPersonBlackService;
+
+
+    /**
+     * 增
+     */
+    public void add(TbTerminal t) {
+        this.save(t);
+    }
+
+    /**
+     * 删
+     */
+    public void delete(Long id) {
+        this.removeById(id);
+    }
+
+    /**
+     * 改
+     */
+    public void update(TbTerminal t) {
+        Long channelId = t.getChannelId();
+        TbVenues tbVenues = tbVenuesService.getById(channelId);
+        t.setChannelName(tbVenues.getName()).setDeptId(tbVenues.getDeptId())
+                .setUpdateBy(StpUtil.getLoginIdAsString()).setUpdateTime(new Date());
+        TbTerminal db = this.getById(t.getId());
+        if (!t.getDeptId().equals(db.getDeptId())) {
+            //更换组织,先清除所有数据再下发
+            this.setCommand(CommandType.ISSUED_CLEAR_COMMAND.getFuncId(), null, t);
+            //下发新的白名单
+            tbPersonFilingService.issued(t);
+            //下发新的黑名单
+            tbPersonBlackService.issued(t);
+            //开始设置心跳command=1
+            startHeartCommand(t.getSn());
+        }
+        this.updateById(t);
+    }
+
+
+    /**
+     * 查
+     */
+    TbTerminal getById(Long id) {
+        return super.getById(id);
+    }
+
+    /**
+     * 查集合 - 根据条件(参数为空时代表忽略指定条件)
+     */
+    public List<TbTerminal> getList(SoMap so) {
+        return tbTerminalMapper.getList(so);
+    }
+
+    /**
+     * 初始化设备
+     *
+     * @param sn
+     * @param clientIp
+     */
+    public void initTerminal(String sn, String clientIp) {
+        TbTerminal tbTerminal = findBySN(sn);
+        if (tbTerminal == null) {
+            tbTerminal = new TbTerminal();
+            tbTerminal.setIpAddress(clientIp).setState(TbTerminal.StateType.ONLINE.getState())
+                    .setSn(sn).setCreateTime(new Date())
+                    .setDeptId(StpUserUtil.ADMIN_DEPT_ID);
+            this.save(tbTerminal);
+            return;
+        }
+        if (!StrUtil.equals(tbTerminal.getState(), TbTerminal.StateType.ONLINE.getState())) {
+            tbTerminal.setState(TbTerminal.StateType.ONLINE.getState());
+            this.updateById(tbTerminal);
+        }
+    }
+
+    /**
+     * 根据sn查找
+     *
+     * @param sn
+     * @return
+     */
+    public TbTerminal findBySN(String sn) {
+        QueryWrapper<TbTerminal> ew = new QueryWrapper<>();
+        ew.eq("sn", sn);
+        return getOne(ew);
+    }
+
+    /**
+     * 重启
+     *
+     * @param sn
+     */
+    public void restart(String sn) {
+        if (HeartBeatHandler.STATE_CACHE.get(sn) == null) {
+            throw new BusinessException("设备已离线");
+        }
+        TbTerminal tbTerminal = this.findBySN(sn);
+        //缓存命令
+        setCommand(CommandType.ISSUED_REBOOT_COMMAND.getFuncId(), null, tbTerminal);
+        //心跳缓存
+        startHeartCommand(sn);
+    }
+
+    /**
+     * 设置命令
+     *
+     * @param funcId func id
+     * @param params 具体参数
+     * @return
+     */
+    public void setCommand(String funcId, Object params, TbTerminal tbTerminal) {
+        String sn = tbTerminal.getSn();
+        String msgId = RandomUtil.randomNumbers(8);
+        String key = myConfig.getCommandPrefix() + sn;
+        String commandStr = RedisUtil.get(key);
+        List<RequestPackageDTO> commandList = JSONUtil.toList(commandStr, RequestPackageDTO.class);
+        RequestPackageDTO commandDTO = new RequestPackageDTO();
+        RequestPackageDTO.PayloadBean payloadBean = new RequestPackageDTO.PayloadBean();
 
-	/** 底层 Mapper 对象 */
-	@Autowired
-	TbTerminalMapper tbTerminalMapper;
+        payloadBean.setParams(params);
+        commandDTO.setFuncId(funcId).setPayload(payloadBean)
+                .setMsgId(msgId);
+        commandList.add(commandDTO);
+        TbCommandLog tbCommandLog = new TbCommandLog();
+        tbCommandLog.setChannelName(tbTerminal.getChannelName()).setCommand(JSONUtil.toJsonStr(commandDTO))
+                .setDeptId(tbTerminal.getDeptId()).setMsgId(msgId).setSn(sn)
+                .setTerminalName(tbTerminal.getName()).setCreateTime(new Date()).setCreateBy(StpUserUtil.getAdminName());
+        //日志记录
+        tbCommandLogService.save(tbCommandLog);
+        RedisUtil.set(myConfig.getCommandPrefix() + sn, JSONUtil.toJsonStr(commandList));
+    }
 
+    /**
+     * 设置心跳command=1,设备开始请求命令服务器接口
+     *
+     * @param sn sn码
+     */
+    public void startHeartCommand(String sn) {
+        log.info("设置心跳命令===========:{}", sn);
+        RequestPackageDTO requestPackageDTO = RequestPackageDTO.getHeartCommandDTO();
+        RedisUtil.set(myConfig.getHeartPrefix() + sn, JSONUtil.toJsonStr(requestPackageDTO));
+    }
 
-	/** 增 */
-    public void add(TbTerminal t){
-	this.save( t);
-	}
+    /**
+     * 解绑设备
+     *
+     * @param id
+     */
+    public void unbind(Long id) {
+        TbTerminal tbTerminal = this.getById(id);
+        if (StrUtil.equals(tbTerminal.getState(),TbTerminal.StateType.OFFLINE.getState())){
+            throw new BusinessException("设备已离线");
+        }
+        tbTerminalMapper.unbind(id);
+    }
 
-	/** 删 */
-	public void delete(Long id){
-	this.removeById(id);
-	}
+    /**
+     * 清除数据
+     *
+     * @param id
+     */
+    public void clearData(Long id) {
+        TbTerminal tbTerminal = this.getById(id);
+        if (StrUtil.equals(tbTerminal.getState(),TbTerminal.StateType.OFFLINE.getState())){
+            throw new BusinessException("设备已离线");
+        }
+        this.clearData(tbTerminal);
+    }
 
-	/** 改 */
-	public void update(TbTerminal t){
-	this.updateById(t);
-	}
+    /**
+     * 将离线设备设置为下线
+     *
+     * @param snList
+     */
+    public void offlineTerminal(List<String> snList) {
+        UpdateWrapper<TbTerminal> ew = new UpdateWrapper<>();
+        ew.notIn(!snList.isEmpty(), "sn", snList);
+        ew.setSql("state='" + TbTerminal.StateType.OFFLINE.getState() + "'");
+        this.update(ew);
+    }
 
-	/** 查 */
-	TbTerminal getById(Long id){
-	return super.getById(id);
-	}
+    /**
+     * 根据部门id查找设备
+     *
+     * @param deptId
+     * @return
+     */
+    public List<TbTerminal> findByDeptId(Long deptId) {
+        QueryWrapper<TbTerminal> ew = new QueryWrapper<>();
+        ew.eq("dept_id", deptId);
+        return list(ew);
+    }
 
-	/** 查集合 - 根据条件(参数为空时代表忽略指定条件) */
-	public List<TbTerminal> getList(SoMap so) {
-		return tbTerminalMapper.getList(so);	
-	}
-	
+    /**
+     * 清空所有数据
+     *
+     * @param tbTerminal
+     */
+    public void clearData(TbTerminal tbTerminal) {
+        setCommand(CommandType.ISSUED_CLEAR_COMMAND.getFuncId(), null, tbTerminal);
+        startHeartCommand(tbTerminal.getSn());
+    }
 
+    public void open(Long id) {
+        TbTerminal tbTerminal = this.getById(id);
+        if (StrUtil.equals(tbTerminal.getState(),TbTerminal.StateType.OFFLINE.getState())){
+            throw new BusinessException("设备已离线");
+        }
+        setCommand(CommandType.ISSUED_NORMALOPEN_COMMAND.getFuncId(), null, tbTerminal);
+        startHeartCommand(tbTerminal.getSn());
+    }
 }

+ 33 - 0
sp-core/src/main/java/com/pj/project/tb_terminal/task/CheckTask.java

@@ -0,0 +1,33 @@
+package com.pj.project.tb_terminal.task;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.pj.current.netty.command.constants.CommandType;
+import com.pj.project.tb_terminal.TbTerminal;
+import com.pj.project.tb_terminal.TbTerminalService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+/**
+ * 检查终端人脸数量
+ */
+@Component
+@Slf4j
+public class CheckTask {
+
+    @Resource
+    private TbTerminalService tbTerminalService;
+
+    @Scheduled(fixedDelay = 300000)
+    public void check() {
+        List<TbTerminal> tbTerminalList = tbTerminalService.list(new QueryWrapper<>());
+        tbTerminalList.forEach(tbTerminal -> {
+            tbTerminalService.setCommand(CommandType.ISSUED_OPTION_COMMAND.getFuncId(),null,tbTerminal);
+            tbTerminalService.startHeartCommand(tbTerminal.getSn());
+        });
+
+    }
+}

+ 36 - 0
sp-core/src/main/java/com/pj/project/tb_terminal/task/CommandDelayTask.java

@@ -0,0 +1,36 @@
+package com.pj.project.tb_terminal.task;
+
+import cn.hutool.extra.spring.SpringUtil;
+
+import com.pj.current.task.Task;
+
+import com.pj.utils.cache.RedisUtil;
+import io.netty.channel.ChannelHandlerContext;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class CommandDelayTask extends Task {
+
+    private ChannelHandlerContext cx;
+    private String content;
+    private String msgId;
+
+
+
+    public CommandDelayTask(String id, long delayInMilliseconds, ChannelHandlerContext cx, String content, String msgId) {
+        super(id, delayInMilliseconds);
+        this.cx = cx;
+        this.content = content;
+        this.msgId = msgId;
+
+    }
+
+    @Override
+    public void run() {
+        log.info("延迟发送命令:{}",content);
+        String channelId = cx.channel().id().asShortText();
+        RedisUtil.setBySecond(channelId, msgId, 10);
+        cx.write(content);
+        cx.flush();
+    }
+}

+ 27 - 0
sp-core/src/main/java/com/pj/project/tb_terminal/task/StateTask.java

@@ -0,0 +1,27 @@
+package com.pj.project.tb_terminal.task;
+
+import com.pj.current.netty.heart.HeartBeatHandler;
+import com.pj.project.tb_terminal.TbTerminalService;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+@Component
+public class StateTask {
+    @Resource
+    private TbTerminalService tbTerminalService;
+
+    @Scheduled(fixedDelay = 20000)
+    @Async
+    public void stateHandler() {
+        List<String>snList=new ArrayList<>();
+         HeartBeatHandler.STATE_CACHE.forEach(k->snList.add(HeartBeatHandler.STATE_CACHE.get(k,false)));
+        tbTerminalService.offlineTerminal(snList);
+
+    }
+}

+ 17 - 2
sp-core/src/main/java/com/pj/project/tb_venues/TbVenues.java

@@ -1,6 +1,8 @@
 package com.pj.project.tb_venues;
 
 import java.io.Serializable;
+import java.util.Date;
+
 import com.baomidou.mybatisplus.annotation.*;
 import com.baomidou.mybatisplus.extension.activerecord.Model;
 import lombok.EqualsAndHashCode;
@@ -31,6 +33,9 @@ public class TbVenues extends Model<TbVenues> implements Serializable {
 	 * 此模块对应的权限码 
 	 */
 	public static final String PERMISSION_CODE = "tb-venues";	
+	public static final String PERMISSION_CODE_ADD = "tb-venues-add";
+	public static final String PERMISSION_CODE_EDIT = "tb-venues-edit";
+	public static final String PERMISSION_CODE_DEL = "tb-venues-del";
 
 
 	// ---------- 表中字段 ----------
@@ -46,6 +51,16 @@ public class TbVenues extends Model<TbVenues> implements Serializable {
 	private Long deptId;	
 
 	/**
+	 * 部门 
+	 */
+	private String deptName;	
+
+	/**
+	 * 父ID 
+	 */
+	private Long pId;	
+
+	/**
 	 * 名称 
 	 */
 	private String name;	
@@ -53,7 +68,7 @@ public class TbVenues extends Model<TbVenues> implements Serializable {
 	/**
 	 * 创建时间 
 	 */
-	private String createTime;	
+	private Date createTime;
 
 	/**
 	 * 创建人 
@@ -63,7 +78,7 @@ public class TbVenues extends Model<TbVenues> implements Serializable {
 	/**
 	 * 更新时间 
 	 */
-	private String updateTime;	
+	private Date updateTime;
 
 	/**
 	 * 更新人 

+ 13 - 13
sp-core/src/main/java/com/pj/project/tb_venues/TbVenuesMapper.xml

@@ -4,12 +4,14 @@
 
 
 	<!-- ================================== 查询相关 ================================== -->
-			<!-- select id, dept_id, name, create_time, create_by, update_time, update_by from tb_venues  -->
+			<!-- select id, dept_id, dept_name, p_id, name, create_time, create_by, update_time, update_by from tb_venues  -->
 
 		<!-- 通用映射:手动模式 -->
 		<resultMap id="model" type="com.pj.project.tb_venues.TbVenues">
 				<result property="id" column="id" />
 				<result property="deptId" column="dept_id" />
+				<result property="deptName" column="dept_name" />
+				<result property="pId" column="p_id" />
 				<result property="name" column="name" />
 				<result property="createTime" column="create_time" />
 				<result property="createBy" column="create_by" />
@@ -29,24 +31,22 @@
 	<select id="getList" resultMap="model">
 		<include refid="select_sql"></include>
 		<where>
-						<if test=' this.has("id") '> and id = #{id} </if>
+			<if test=' this.has("deptName") '> and dept_name = #{deptName} </if>
 			<if test=' this.has("deptId") '> and dept_id = #{deptId} </if>
-			<if test=' this.has("name") '> and name = #{name} </if>
-			<if test=' this.has("createTime") '> and create_time = #{createTime} </if>
-			<if test=' this.has("createBy") '> and create_by = #{createBy} </if>
-			<if test=' this.has("updateTime") '> and update_time = #{updateTime} </if>
-			<if test=' this.has("updateBy") '> and update_by = #{updateBy} </if>
+			<if test=' this.has("name") '> and name like concat('%', #{name},'%') and p_id=-1 </if>
 		</where>
 		order by
 		<choose>
 						<when test='sortType == 1'> id desc </when>
 			<when test='sortType == 2'> dept_id desc </when>
-			<when test='sortType == 3'> name desc </when>
-			<when test='sortType == 4'> create_time desc </when>
-			<when test='sortType == 5'> create_by desc </when>
-			<when test='sortType == 6'> update_time desc </when>
-			<when test='sortType == 7'> update_by desc </when>
-			<otherwise> id desc </otherwise>
+			<when test='sortType == 3'> dept_name desc </when>
+			<when test='sortType == 4'> p_id desc </when>
+			<when test='sortType == 5'> name desc </when>
+			<when test='sortType == 6'> create_time desc </when>
+			<when test='sortType == 7'> create_by desc </when>
+			<when test='sortType == 8'> update_time desc </when>
+			<when test='sortType == 9'> update_by desc </when>
+			<otherwise> id asc </otherwise>
 		</choose>
 	</select>
 

+ 75 - 33
sp-core/src/main/java/com/pj/project/tb_venues/TbVenuesService.java

@@ -1,49 +1,91 @@
 package com.pj.project.tb_venues;
 
+import java.util.Date;
 import java.util.List;
+
+import cn.dev33.satoken.stp.StpUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.pj.current.global.BusinessException;
+import com.pj.project.tb_dept.TbDept;
+import com.pj.project.tb_dept.TbDeptService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import com.pj.utils.so.*;
 
+import javax.annotation.Resource;
+
 /**
  * Service: tb_venues -- 场所管理
- * @author qzyReal 
+ *
+ * @author qzyReal
  */
 @Service
-public class TbVenuesService extends ServiceImpl<TbVenuesMapper, TbVenues> implements IService<TbVenues>{
-
-	/** 底层 Mapper 对象 */
-	@Autowired
-	TbVenuesMapper tbVenuesMapper;
-
-
-	/** 增 */
-	public void add(TbVenues t){
-	this.save( t);
-	}
-
-	/** 删 */
-	public void delete(Long id){
-	this.removeById(id);
-	}
-
-	/** 改 */
-	public void update(TbVenues t){
-	this.updateById(t);
-	}
-
-	/** 查 */
-	TbVenues getById(Long id){
-	return super.getById(id);
-	}
-
-	/** 查集合 - 根据条件(参数为空时代表忽略指定条件) */
-	public List<TbVenues> getList(SoMap so) {
-		return tbVenuesMapper.getList(so);	
-	}
-	
+@Transactional(rollbackFor = Exception.class)
+public class TbVenuesService extends ServiceImpl<TbVenuesMapper, TbVenues> implements IService<TbVenues> {
+
+    /**
+     * 底层 Mapper 对象
+     */
+    @Autowired
+    TbVenuesMapper tbVenuesMapper;
+    @Resource
+    private TbDeptService tbDeptService;
+
+    /**
+     * 增
+     */
+    public void add(TbVenues t) {
+        t.setCreateBy(StpUtil.getLoginIdAsString()).setCreateTime(new Date());
+        handler(t);
+        this.save(t);
+    }
+
+    private void handler(TbVenues t) {
+        TbDept tbDept = tbDeptService.getById(t.getDeptId());
+        t.setDeptName(tbDept.getName()).setUpdateBy(StpUtil.getLoginIdAsString()).setUpdateTime(new Date());
+    }
+
+    /**
+     * 删
+     */
+    public void delete(Long id) {
+        List<TbVenues> list = this.findByPid(id);
+        if (!list.isEmpty()){
+            throw new BusinessException("该场所存在通道,不能删除");
+        }
+        this.removeById(id);
+    }
+
+    private List<TbVenues> findByPid(Long id) {
+        QueryWrapper<TbVenues>ew=new QueryWrapper<>();
+        ew.eq("p_id",id);
+        return list(ew);
+    }
+
+    /**
+     * 改
+     */
+    public void update(TbVenues t) {
+        t.setUpdateBy(StpUtil.getLoginIdAsString()).setUpdateTime(new Date());
+        this.updateById(t);
+    }
+
+    /**
+     * 查
+     */
+    public TbVenues getById(Long id) {
+        return super.getById(id);
+    }
+
+    /**
+     * 查集合 - 根据条件(参数为空时代表忽略指定条件)
+     */
+    public List<TbVenues> getList(SoMap so) {
+        return tbVenuesMapper.getList(so);
+    }
+
 
 }

+ 0 - 57
sp-core/src/main/java/com/pj/project/tb_venues/TbVenuesUtil.java

@@ -1,57 +0,0 @@
-package com.pj.project.tb_venues;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-import com.pj.utils.sg.*;
-
-/**
- * 工具类:tb_venues -- 场所管理
- * @author qzyReal 
- *
- */
-@Component
-public class TbVenuesUtil {
-
-	
-	/** 底层 Mapper 对象 */
-	public static TbVenuesMapper tbVenuesMapper;
-	@Autowired
-	private void setTbVenuesMapper(TbVenuesMapper tbVenuesMapper) {
-		TbVenuesUtil.tbVenuesMapper = tbVenuesMapper;
-	}
-	
-	
-	/** 
-	 * 将一个 TbVenues 对象进行进行数据完整性校验 (方便add/update等接口数据校验) [G] 
-	 */
-	static void check(TbVenues t) {
-		AjaxError.throwByIsNull(t.getId(), "[] 不能为空");		// 验证:  
-		AjaxError.throwByIsNull(t.getDeptId(), "[部门ID] 不能为空");		// 验证: 部门ID 
-		AjaxError.throwByIsNull(t.getName(), "[名称] 不能为空");		// 验证: 名称 
-		AjaxError.throwByIsNull(t.getCreateTime(), "[创建时间] 不能为空");		// 验证: 创建时间 
-		AjaxError.throwByIsNull(t.getCreateBy(), "[创建人] 不能为空");		// 验证: 创建人 
-		AjaxError.throwByIsNull(t.getUpdateTime(), "[更新时间] 不能为空");		// 验证: 更新时间 
-		AjaxError.throwByIsNull(t.getUpdateBy(), "[更新人] 不能为空");		// 验证: 更新人 
-	}
-
-	/** 
-	 * 获取一个TbVenues (方便复制代码用) [G] 
-	 */ 
-	static TbVenues getTbVenues() {
-		TbVenues t = new TbVenues();	// 声明对象 
-		t.setId(0L);		//  
-		t.setDeptId(0L);		// 部门ID 
-		t.setName("");		// 名称 
-		t.setCreateTime("");		// 创建时间 
-		t.setCreateBy("");		// 创建人 
-		t.setUpdateTime("");		// 更新时间 
-		t.setUpdateBy("");		// 更新人 
-		return t;
-	}
-	
-	
-	
-	
-	
-}

+ 30 - 0
sp-core/src/main/java/com/pj/utils/ImageUtils.java

@@ -0,0 +1,30 @@
+package com.pj.utils;
+
+import sun.misc.BASE64Decoder;
+
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+
+public class ImageUtils {
+    public static boolean generateImage(String imgStr, String imgFilePath) {
+        BASE64Decoder decoder = new BASE64Decoder();
+        try {
+            // Base64解码
+            byte[] bytes = decoder.decodeBuffer(imgStr);
+            for (int i = 0; i < bytes.length; ++i) {
+                if (bytes[i] < 0) {// 调整异常数据
+                    bytes[i] += 256;
+                }
+            }
+            // 生成jpeg图片
+            OutputStream out = new FileOutputStream(imgFilePath);
+            out.write(bytes);
+            out.flush();
+            out.close();
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+}

+ 4 - 0
sp-core/src/main/java/com/pj/utils/cache/RedisUtil.java

@@ -66,6 +66,10 @@ public class RedisUtil {
 	public static void setByMINUTES(String key, String value, int timeout) {
 		stringRedisTemplate.opsForValue().set(key, value, timeout, TimeUnit.MINUTES);
 	}
+	// 写入,并设置时长,单位 秒
+	public static void setBySecond(String key, String value, int timeout) {
+		stringRedisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
+	}
 
 	// 写入,永久有效 
 	public static void setByForever(String key, String value) {

+ 20 - 0
sp-start/pom.xml

@@ -50,6 +50,26 @@
             <artifactId>filing-system</artifactId>
             <version>${project.parent.version}</version>
         </dependency>
+        <dependency>
+            <groupId>com.pj</groupId>
+            <artifactId>face-identify</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.pj</groupId>
+            <artifactId>car-identify</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.pj</groupId>
+            <artifactId>epidemic-prevention</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.pj</groupId>
+            <artifactId>monitor-system</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
 
         <!-- ============== 项目依赖 end =============== -->
 

+ 5 - 0
sp-start/src/main/resources/application-dev.yml

@@ -46,3 +46,8 @@ spring:
     myconfig:
         # 本项目部署到的服务器域名(文件上传等等模块  要用到)
         domain: http://127.0.0.1:8099
+        ip: 192.168.3.27
+        heart-port: 9999
+        command-port: 8888
+        command-prefix: "command:"
+        heart-prefix: "heart:"