ソースを参照

客户端开发

yingp 7 年 前
親
コミット
2af54ec423
52 ファイル変更、3009 行追加、107 行削除
  1. 110 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/control/IntegerField.java
  2. 1 4
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/CameraController.java
  3. 69 3
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/GateController.java
  4. 13 8
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/GateEditController.java
  5. 92 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/GateIssueController.java
  6. 126 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/GateIssueStatusController.java
  7. 159 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/GateSettingController.java
  8. 50 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/PersonController.java
  9. 20 4
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/PersonEditController.java
  10. 15 12
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/po/AccessType.java
  11. 30 14
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/po/FaceGate.java
  12. 75 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/po/GatePerson.java
  13. 154 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/po/GatePersonView.java
  14. 39 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/po/HealthStatus.java
  15. 134 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/po/PersonGateView.java
  16. 27 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/po/Selectable.java
  17. 6 6
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/repository/FaceGateRepository.java
  18. 91 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/repository/GatePersonRepository.java
  19. 69 12
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/service/FaceGateService.java
  20. 169 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/service/GatePersonService.java
  21. 4 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/service/PersonService.java
  22. 46 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/task/GateScheduler.java
  23. 31 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/util/NotifyUtils.java
  24. 2 2
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/util/RandomUtils.java
  25. 13 1
      applications/device/device-client-biometric/src/main/resources/schema.sql
  26. 4 0
      applications/device/device-client-biometric/src/main/resources/style/main.css
  27. 14 6
      applications/device/device-client-biometric/src/main/resources/view/gate.fxml
  28. 4 4
      applications/device/device-client-biometric/src/main/resources/view/gate_edit.fxml
  29. 22 0
      applications/device/device-client-biometric/src/main/resources/view/gate_issue.fxml
  30. 33 0
      applications/device/device-client-biometric/src/main/resources/view/gate_issue_status.fxml
  31. 109 0
      applications/device/device-client-biometric/src/main/resources/view/gate_setting.fxml
  32. 5 0
      applications/device/device-client-biometric/src/main/resources/view/person.fxml
  33. 4 4
      applications/device/device-client-biometric/src/main/resources/view/person_edit.fxml
  34. 14 0
      applications/device/device-client-biometric/src/main/resources/view/person_issue.fxml
  35. 161 0
      applications/device/device-core/src/main/java/com/usoftchina/smartschool/device/util/DateUtils.java
  36. 87 0
      applications/device/device-core/src/main/java/com/usoftchina/smartschool/device/util/DirtyBean.java
  37. 9 0
      applications/device/device-sdk-biometric/pom.xml
  38. 3 1
      applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/AccessLog.java
  39. 219 19
      applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/BiometricDeviceService.java
  40. 17 2
      applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/DeviceManageController.java
  41. 384 0
      applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/DeviceSetting.java
  42. 44 0
      applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/FaceLevel.java
  43. 2 0
      applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/HealthInfo.java
  44. 20 0
      applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/HeartbeatEvent.java
  45. 43 0
      applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/LivingLevel.java
  46. 139 0
      applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/PersonPo.java
  47. 23 0
      applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/ReqListPo.java
  48. 51 0
      applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/ResultListPo.java
  49. 7 1
      applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/ResultPo.java
  50. 34 0
      applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/Setting.java
  51. 6 2
      applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/VerificationController.java
  52. 6 2
      applications/device/device-sdk/src/main/java/com/usoftchina/smartschool/device/api/DeviceApi.java

+ 110 - 0
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/control/IntegerField.java

@@ -0,0 +1,110 @@
+package com.usoftchina.smartschool.device.client.biometric.control;
+
+import javafx.beans.property.IntegerProperty;
+import javafx.beans.property.SimpleIntegerProperty;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.scene.control.TextField;
+
+import java.math.BigDecimal;
+import java.text.NumberFormat;
+import java.text.ParseException;
+
+/**
+ * @author yingp
+ * @date 2019/11/19
+ */
+public class IntegerField extends TextField {
+    private final NumberFormat nf;
+    private IntegerProperty value;
+    private IntegerProperty maxValue;
+
+    public final int getValue() {
+        return valueProperty().get();
+    }
+
+    public final void setValue(int value) {
+        valueProperty().set(value);
+    }
+
+    public IntegerProperty valueProperty() {
+        if (null == value) {
+            value = new SimpleIntegerProperty(this, "value");
+        }
+        return value;
+    }
+
+    public final int getMaxValue() {
+        return maxValueProperty().get();
+    }
+
+    public final void setMaxValue(int maxVal) {
+        this.maxValueProperty().set(maxVal);
+    }
+
+    public final IntegerProperty maxValueProperty() {
+        if (null == maxValue) {
+            maxValue = new SimpleIntegerProperty(this, "maxValue", Integer.MAX_VALUE);
+        }
+        return maxValue;
+    }
+
+    public IntegerField() {
+        this(0);
+    }
+
+    public IntegerField(int value) {
+        this(value, NumberFormat.getInstance());
+    }
+
+    public IntegerField(int value, NumberFormat nf) {
+        super();
+        getStyleClass().add("integer-field");
+        this.nf = nf;
+        initHandlers();
+        setValue(value);
+    }
+
+    private void initHandlers() {
+
+        // try to parse when focus is lost or RETURN is hit
+        setOnAction(e -> parseAndFormatInput());
+
+        focusedProperty().addListener(new ChangeListener<Boolean>() {
+
+            @Override
+            public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
+                if (!newValue.booleanValue()) {
+                    parseAndFormatInput();
+                }
+            }
+        });
+
+        // Set text in field if BigDecimal property is changed from outside.
+        valueProperty().addListener(new ChangeListener<Number>() {
+            @Override
+            public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
+                setText(nf.format(newValue));
+            }
+        });
+    }
+
+    /**
+     * Tries to parse the user input to a number according to the provided
+     * NumberFormat
+     */
+    private void parseAndFormatInput() {
+        try {
+            String input = getText();
+            if (input == null || input.length() == 0) {
+                return;
+            }
+            Number parsedNumber = nf.parse(input);
+            BigDecimal newValue = new BigDecimal(parsedNumber.toString());
+            setValue(newValue.intValue());
+            selectAll();
+        } catch (ParseException ex) {
+            setText(nf.format(getValue()));
+        }
+    }
+}

+ 1 - 4
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/CameraController.java

@@ -6,13 +6,11 @@ import com.github.sarxos.webcam.WebcamResolution;
 import com.usoftchina.smartschool.device.client.biometric.control.ParameterizedScene;
 import com.usoftchina.smartschool.device.client.biometric.control.ParameterizedScene;
 import javafx.application.Platform;
 import javafx.application.Platform;
 import javafx.beans.property.ObjectProperty;
 import javafx.beans.property.ObjectProperty;
-import javafx.embed.swing.SwingFXUtils;
 import javafx.embed.swing.SwingNode;
 import javafx.embed.swing.SwingNode;
 import javafx.event.ActionEvent;
 import javafx.event.ActionEvent;
 import javafx.fxml.FXML;
 import javafx.fxml.FXML;
 import javafx.scene.Node;
 import javafx.scene.Node;
 import javafx.scene.Scene;
 import javafx.scene.Scene;
-import javafx.scene.image.Image;
 import org.springframework.util.CollectionUtils;
 import org.springframework.util.CollectionUtils;
 
 
 import javax.imageio.ImageIO;
 import javax.imageio.ImageIO;
@@ -90,12 +88,11 @@ public class CameraController extends ViewController {
                     Scene scene = getScene();
                     Scene scene = getScene();
                     if (scene instanceof ParameterizedScene) {
                     if (scene instanceof ParameterizedScene) {
                         ObjectProperty<File> fileProperty = (ObjectProperty<File>) ((ParameterizedScene) scene).getParams();
                         ObjectProperty<File> fileProperty = (ObjectProperty<File>) ((ParameterizedScene) scene).getParams();
-//                        imageObjectProperty.setValue(SwingFXUtils.toFXImage(webcam.getImage(), null));
                         File tempImageFile = Files.createTempFile("snapshot", "png").toFile();
                         File tempImageFile = Files.createTempFile("snapshot", "png").toFile();
                         ImageIO.write(webcam.getImage(), "PNG", tempImageFile);
                         ImageIO.write(webcam.getImage(), "PNG", tempImageFile);
                         fileProperty.setValue(tempImageFile);
                         fileProperty.setValue(tempImageFile);
                     }
                     }
-                    cameraNode.getScene().getWindow().hide();
+                    scene.getWindow().hide();
                 } catch (Exception ex) {
                 } catch (Exception ex) {
                     ex.printStackTrace();
                     ex.printStackTrace();
                 }
                 }

+ 69 - 3
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/GateController.java

@@ -3,8 +3,10 @@ package com.usoftchina.smartschool.device.client.biometric.controller;
 import com.usoftchina.smartschool.device.client.biometric.DeviceClient;
 import com.usoftchina.smartschool.device.client.biometric.DeviceClient;
 import com.usoftchina.smartschool.device.client.biometric.po.AccessType;
 import com.usoftchina.smartschool.device.client.biometric.po.AccessType;
 import com.usoftchina.smartschool.device.client.biometric.po.FaceGate;
 import com.usoftchina.smartschool.device.client.biometric.po.FaceGate;
+import com.usoftchina.smartschool.device.client.biometric.po.HealthStatus;
 import com.usoftchina.smartschool.device.client.biometric.service.FaceGateService;
 import com.usoftchina.smartschool.device.client.biometric.service.FaceGateService;
 import com.usoftchina.smartschool.device.client.biometric.util.AlertUtils;
 import com.usoftchina.smartschool.device.client.biometric.util.AlertUtils;
+import com.usoftchina.smartschool.device.client.biometric.util.NotifyUtils;
 import com.usoftchina.smartschool.device.client.biometric.util.ViewUtils;
 import com.usoftchina.smartschool.device.client.biometric.util.ViewUtils;
 import com.usoftchina.smartschool.device.context.SpringContextHolder;
 import com.usoftchina.smartschool.device.context.SpringContextHolder;
 import javafx.application.Platform;
 import javafx.application.Platform;
@@ -15,6 +17,7 @@ import javafx.collections.ObservableList;
 import javafx.event.ActionEvent;
 import javafx.event.ActionEvent;
 import javafx.fxml.FXML;
 import javafx.fxml.FXML;
 import javafx.fxml.Initializable;
 import javafx.fxml.Initializable;
+import javafx.geometry.Pos;
 import javafx.scene.Scene;
 import javafx.scene.Scene;
 import javafx.scene.control.Button;
 import javafx.scene.control.Button;
 import javafx.scene.control.TableCell;
 import javafx.scene.control.TableCell;
@@ -55,6 +58,10 @@ public class GateController implements Initializable {
     public Button editButton;
     public Button editButton;
     @FXML
     @FXML
     public Button delButton;
     public Button delButton;
+    @FXML
+    public Button settingButton;
+    @FXML
+    public TableColumn codeCol;
     private FaceGateService faceGateService;
     private FaceGateService faceGateService;
     private ObservableList<FaceGate> faceGateObservableList = FXCollections.observableArrayList();
     private ObservableList<FaceGate> faceGateObservableList = FXCollections.observableArrayList();
 
 
@@ -68,8 +75,9 @@ public class GateController implements Initializable {
         nameCol.setCellValueFactory(new PropertyValueFactory<>("name"));
         nameCol.setCellValueFactory(new PropertyValueFactory<>("name"));
         placeCol.setCellValueFactory(new PropertyValueFactory<>("place"));
         placeCol.setCellValueFactory(new PropertyValueFactory<>("place"));
         ipCol.setCellValueFactory(new PropertyValueFactory<>("ip"));
         ipCol.setCellValueFactory(new PropertyValueFactory<>("ip"));
+        codeCol.setCellValueFactory(new PropertyValueFactory<>("code"));
         accessTypeCol.setCellValueFactory(new PropertyValueFactory<>("accessType"));
         accessTypeCol.setCellValueFactory(new PropertyValueFactory<>("accessType"));
-        accessTypeCol.setCellFactory((col) -> {
+        accessTypeCol.setCellFactory(col -> {
                     TableCell<FaceGate, AccessType> cell = new TableCell<FaceGate, AccessType>() {
                     TableCell<FaceGate, AccessType> cell = new TableCell<FaceGate, AccessType>() {
 
 
                         @Override
                         @Override
@@ -88,6 +96,22 @@ public class GateController implements Initializable {
         startTimeCol.setCellValueFactory(new PropertyValueFactory<>("startTime"));
         startTimeCol.setCellValueFactory(new PropertyValueFactory<>("startTime"));
         lastActiveCol.setCellValueFactory(new PropertyValueFactory<>("lastActiveTime"));
         lastActiveCol.setCellValueFactory(new PropertyValueFactory<>("lastActiveTime"));
         statusCol.setCellValueFactory(new PropertyValueFactory<>("status"));
         statusCol.setCellValueFactory(new PropertyValueFactory<>("status"));
+        statusCol.setCellFactory((col) -> {
+                    TableCell<FaceGate, HealthStatus> cell = new TableCell<FaceGate, HealthStatus>() {
+
+                        @Override
+                        public void updateItem(HealthStatus item, boolean empty) {
+                            super.updateItem(item, empty);
+                            if (null != item) {
+                                this.setText(item.getText());
+                            } else {
+                                this.setText(null);
+                            }
+                        }
+                    };
+                    return cell;
+                }
+        );
 
 
         tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
         tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
         tableView.setItems(faceGateObservableList);
         tableView.setItems(faceGateObservableList);
@@ -99,9 +123,11 @@ public class GateController implements Initializable {
                 if (null == newValue) {
                 if (null == newValue) {
                     editButton.setDisable(true);
                     editButton.setDisable(true);
                     delButton.setDisable(true);
                     delButton.setDisable(true);
+                    settingButton.setDisable(true);
                 } else {
                 } else {
                     editButton.setDisable(false);
                     editButton.setDisable(false);
                     delButton.setDisable(false);
                     delButton.setDisable(false);
+                    settingButton.setDisable(false);
                 }
                 }
             }
             }
         });
         });
@@ -165,7 +191,47 @@ public class GateController implements Initializable {
             AlertUtils.warn("请先选择一行数据");
             AlertUtils.warn("请先选择一行数据");
             return;
             return;
         }
         }
-        faceGateService.delete(faceGate.getId());
-        loadData();
+        Platform.runLater(() -> {
+            faceGateService.delete(faceGate.getId());
+            NotifyUtils.info("删除成功");
+            loadData();
+        });
+    }
+
+    @FXML
+    public void handleIssue(ActionEvent event) {
+    }
+
+    @FXML
+    public void handleIssueStatus(ActionEvent event) {
+    }
+
+    @FXML
+    public void handleSetting(ActionEvent event) {
+        FaceGate faceGate = tableView.getSelectionModel().getSelectedItem();
+        if (null == faceGate) {
+            AlertUtils.warn("请先选择一行数据");
+            return;
+        }
+        try {
+            Scene scene = ViewUtils.load("/view/gate_setting.fxml", faceGate);
+            Stage stage = new Stage();
+            stage.setScene(scene);
+            stage.setResizable(false);
+            stage.setTitle("人脸闸机设置");
+            stage.getIcons().addAll(DeviceClient.defaultIcons);
+            stage.centerOnScreen();
+            stage.show();
+            stage.showingProperty().addListener(new ChangeListener<Boolean>() {
+                @Override
+                public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
+                    if (null == newValue || !newValue.booleanValue()) {
+                        loadData();
+                    }
+                }
+            });
+        } catch (Exception e) {
+            logger.error("Set Device Error", e);
+        }
     }
     }
 }
 }

+ 13 - 8
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/GateEditController.java

@@ -5,7 +5,9 @@ import com.usoftchina.smartschool.device.client.biometric.po.AccessType;
 import com.usoftchina.smartschool.device.client.biometric.po.FaceGate;
 import com.usoftchina.smartschool.device.client.biometric.po.FaceGate;
 import com.usoftchina.smartschool.device.client.biometric.service.FaceGateService;
 import com.usoftchina.smartschool.device.client.biometric.service.FaceGateService;
 import com.usoftchina.smartschool.device.client.biometric.util.AlertUtils;
 import com.usoftchina.smartschool.device.client.biometric.util.AlertUtils;
+import com.usoftchina.smartschool.device.client.biometric.util.NotifyUtils;
 import com.usoftchina.smartschool.device.context.SpringContextHolder;
 import com.usoftchina.smartschool.device.context.SpringContextHolder;
+import javafx.application.Platform;
 import javafx.beans.value.ChangeListener;
 import javafx.beans.value.ChangeListener;
 import javafx.beans.value.ObservableValue;
 import javafx.beans.value.ObservableValue;
 import javafx.collections.FXCollections;
 import javafx.collections.FXCollections;
@@ -17,6 +19,7 @@ import javafx.scene.Scene;
 import javafx.scene.control.ComboBox;
 import javafx.scene.control.ComboBox;
 import javafx.scene.control.TextField;
 import javafx.scene.control.TextField;
 import javafx.stage.Stage;
 import javafx.stage.Stage;
+import org.controlsfx.control.Notifications;
 import org.springframework.util.StringUtils;
 import org.springframework.util.StringUtils;
 
 
 import java.net.URL;
 import java.net.URL;
@@ -39,10 +42,7 @@ public class GateEditController implements Initializable {
     private FaceGateService faceGateService;
     private FaceGateService faceGateService;
 
 
     private ObservableList<AccessType> accessOptions =
     private ObservableList<AccessType> accessOptions =
-            FXCollections.observableArrayList(
-                    new AccessType(1, "进入"),
-                    new AccessType(2, "出去")
-            );
+            FXCollections.observableArrayList(AccessType.values());
 
 
     private FaceGate faceGate;
     private FaceGate faceGate;
 
 
@@ -94,9 +94,14 @@ public class GateEditController implements Initializable {
             AlertUtils.warn("请填写设备IP").ifPresent(b -> ipField.requestFocus());
             AlertUtils.warn("请填写设备IP").ifPresent(b -> ipField.requestFocus());
             return;
             return;
         }
         }
-        faceGateService.save(faceGate);
-
-        Stage stage = (Stage) accessTypeBox.getScene().getWindow();
-        stage.close();
+        Platform.runLater(() -> {
+            try {
+                faceGateService.save(faceGate);
+                accessTypeBox.getScene().getWindow().hide();
+                NotifyUtils.info("保存成功");
+            } catch (Exception e) {
+                AlertUtils.error(e.getMessage());
+            }
+        });
     }
     }
 }
 }

+ 92 - 0
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/GateIssueController.java

@@ -0,0 +1,92 @@
+package com.usoftchina.smartschool.device.client.biometric.controller;
+
+import com.usoftchina.smartschool.device.client.biometric.control.ParameterizedScene;
+import com.usoftchina.smartschool.device.client.biometric.po.FaceGate;
+import com.usoftchina.smartschool.device.client.biometric.service.FaceGateService;
+import com.usoftchina.smartschool.device.client.biometric.service.GatePersonService;
+import com.usoftchina.smartschool.device.client.biometric.util.AlertUtils;
+import com.usoftchina.smartschool.device.client.biometric.util.NotifyUtils;
+import com.usoftchina.smartschool.device.context.SpringContextHolder;
+import javafx.application.Platform;
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.fxml.Initializable;
+import javafx.scene.control.CheckBox;
+import javafx.scene.control.DatePicker;
+import javafx.scene.layout.HBox;
+import org.springframework.util.CollectionUtils;
+
+import java.net.URL;
+import java.sql.Date;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.util.List;
+import java.util.ResourceBundle;
+import java.util.stream.Collectors;
+
+/**
+ * @author yingp
+ * @date 2019/11/18
+ */
+public class GateIssueController implements Initializable {
+    @FXML
+    public HBox gateList;
+    @FXML
+    public DatePicker endTimePicker;
+    private FaceGateService faceGateService;
+    private GatePersonService gatePersonService;
+
+    @Override
+    public void initialize(URL location, ResourceBundle resources) {
+        faceGateService = SpringContextHolder.getContext().getBean(FaceGateService.class);
+        gatePersonService = SpringContextHolder.getContext().getBean(GatePersonService.class);
+        // 默认100年
+        endTimePicker.setValue(LocalDate.now().plusYears(100));
+        Platform.runLater(() -> {
+            List<FaceGate> faceGates = faceGateService.findAll();
+            if (!CollectionUtils.isEmpty(faceGates)) {
+                faceGates.forEach(gate -> {
+                    CheckBox checkBox = new CheckBox(gate.getName());
+                    checkBox.setSelected(true);
+                    checkBox.setPrefWidth(120);
+                    checkBox.setUserData(gate.getId());
+                    gateList.getChildren().add(checkBox);
+                });
+            }
+        });
+    }
+
+    @FXML
+    public void handleIssue(ActionEvent event) {
+        List<String> gateIds = gateList.getChildren().stream()
+                .filter(node -> ((CheckBox) node).isSelected())
+                .map(node -> (String) ((CheckBox) node).getUserData())
+                .collect(Collectors.toList());
+        if (CollectionUtils.isEmpty(gateIds)) {
+            AlertUtils.warn("请先选择设备");
+            return;
+        }
+        LocalDate endTime = endTimePicker.getValue();
+        if (null == endTime) {
+            AlertUtils.warn("请指定有效截止时间");
+            return;
+        }
+        Platform.runLater(() -> {
+            ParameterizedScene scene = (ParameterizedScene) endTimePicker.getScene();
+            Object params = scene.getParams();
+            try {
+                if (params instanceof List) {
+                    gatePersonService.issue(((List<String>) params).toArray(new String[]{}), gateIds.toArray(new String[]{}),
+                            Date.from(endTime.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()));
+                } else {
+                    gatePersonService.issue((String) params, gateIds.toArray(new String[]{}),
+                            Date.from(endTime.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()));
+                }
+                scene.getWindow().hide();
+                NotifyUtils.info("下发成功");
+            } catch (Exception e) {
+                AlertUtils.warn(e.getMessage());
+            }
+        });
+    }
+}

+ 126 - 0
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/GateIssueStatusController.java

@@ -0,0 +1,126 @@
+package com.usoftchina.smartschool.device.client.biometric.controller;
+
+import com.usoftchina.smartschool.device.client.biometric.control.ParameterizedScene;
+import com.usoftchina.smartschool.device.client.biometric.po.PersonGateView;
+import com.usoftchina.smartschool.device.client.biometric.repository.GatePersonRepository;
+import com.usoftchina.smartschool.device.client.biometric.service.GatePersonService;
+import com.usoftchina.smartschool.device.client.biometric.util.AlertUtils;
+import com.usoftchina.smartschool.device.client.biometric.util.NotifyUtils;
+import com.usoftchina.smartschool.device.context.SpringContextHolder;
+import javafx.application.Platform;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.fxml.Initializable;
+import javafx.scene.Scene;
+import javafx.scene.control.Button;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
+import javafx.scene.control.cell.CheckBoxTableCell;
+import javafx.scene.control.cell.PropertyValueFactory;
+
+import java.net.URL;
+import java.util.ResourceBundle;
+
+/**
+ * @author yingp
+ * @date 2019/11/19
+ */
+public class GateIssueStatusController implements Initializable {
+    @FXML
+    public TableColumn gateNameCol;
+    @FXML
+    public TableColumn gatePlaceCol;
+    @FXML
+    public TableColumn gateIpCol;
+    @FXML
+    public TableColumn issueTimeCol;
+    @FXML
+    public TableColumn endTimeCol;
+    @FXML
+    public TableColumn selectedCol;
+    @FXML
+    public TableView<PersonGateView> tableView;
+    @FXML
+    public Button cancelIssueButton;
+
+    private GatePersonService gatePersonService;
+    private GatePersonRepository gatePersonRepository;
+
+    private ObservableList<PersonGateView> observableList = FXCollections.observableArrayList();
+
+    @Override
+    public void initialize(URL location, ResourceBundle resources) {
+        gatePersonService = SpringContextHolder.getContext().getBean(GatePersonService.class);
+        gatePersonRepository = SpringContextHolder.getContext().getBean(GatePersonRepository.class);
+        initTable();
+
+        cancelIssueButton.sceneProperty().addListener(new ChangeListener<Scene>() {
+            @Override
+            public void changed(ObservableValue<? extends Scene> observable, Scene oldValue, Scene newValue) {
+                if (null != newValue) {
+                    loadData();
+                    ((ParameterizedScene) newValue).paramsProperty().addListener(new ChangeListener() {
+                        @Override
+                        public void changed(ObservableValue observable, Object oldValue, Object newValue) {
+                            loadData();
+                        }
+                    });
+                }
+            }
+        });
+    }
+
+    private void loadData() {
+        Platform.runLater(() -> {
+            Object params = ((ParameterizedScene) tableView.getScene()).getParams();
+            if (null != params) {
+                observableList.setAll(gatePersonRepository.findByPerson((String) params));
+            }
+        });
+    }
+
+    private void initTable() {
+        selectedCol.setCellFactory(CheckBoxTableCell.forTableColumn(selectedCol));
+        selectedCol.setCellValueFactory(new PropertyValueFactory<>("selected"));
+        gateNameCol.setCellValueFactory(new PropertyValueFactory<>("gateName"));
+        gatePlaceCol.setCellValueFactory(new PropertyValueFactory<>("gatePlace"));
+        gateIpCol.setCellValueFactory(new PropertyValueFactory<>("gateIp"));
+        issueTimeCol.setCellValueFactory(new PropertyValueFactory<>("issueTime"));
+        endTimeCol.setCellValueFactory(new PropertyValueFactory<>("endTime"));
+
+        tableView.setItems(observableList);
+
+        tableView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<PersonGateView>() {
+            @Override
+            public void changed(ObservableValue<? extends PersonGateView> observable, PersonGateView oldValue, PersonGateView newValue) {
+                if (null == newValue) {
+                    cancelIssueButton.setDisable(true);
+                } else {
+                    cancelIssueButton.setDisable(false);
+                }
+            }
+        });
+    }
+
+    @FXML
+    public void handleCancelIssue(ActionEvent event) {
+        PersonGateView personGateView = tableView.getSelectionModel().getSelectedItem();
+        if (null == personGateView) {
+            AlertUtils.warn("请先选择一行数据");
+            return;
+        }
+        Platform.runLater(() -> {
+            try {
+                gatePersonService.cancelIssue(personGateView.getId());
+                NotifyUtils.info("取消成功");
+                loadData();
+            } catch (Exception e) {
+                AlertUtils.warn(e.getMessage());
+            }
+        });
+    }
+}

+ 159 - 0
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/GateSettingController.java

@@ -0,0 +1,159 @@
+package com.usoftchina.smartschool.device.client.biometric.controller;
+
+import com.usoftchina.smartschool.device.biometric.BiometricDeviceService;
+import com.usoftchina.smartschool.device.biometric.DeviceSetting;
+import com.usoftchina.smartschool.device.biometric.FaceLevel;
+import com.usoftchina.smartschool.device.biometric.LivingLevel;
+import com.usoftchina.smartschool.device.client.biometric.control.IntegerField;
+import com.usoftchina.smartschool.device.client.biometric.control.ParameterizedScene;
+import com.usoftchina.smartschool.device.client.biometric.po.FaceGate;
+import com.usoftchina.smartschool.device.client.biometric.service.FaceGateService;
+import com.usoftchina.smartschool.device.client.biometric.util.AlertUtils;
+import com.usoftchina.smartschool.device.client.biometric.util.NotifyUtils;
+import com.usoftchina.smartschool.device.context.SpringContextHolder;
+import javafx.application.Platform;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.fxml.Initializable;
+import javafx.scene.Scene;
+import javafx.scene.control.*;
+import javafx.util.converter.NumberStringConverter;
+
+import java.net.URL;
+import java.util.ResourceBundle;
+
+/**
+ * @author yingp
+ * @date 2019/11/19
+ */
+public class GateSettingController implements Initializable {
+    @FXML
+    public Label softwareVersionLabel;
+    @FXML
+    public Label systemVersionLabel;
+    @FXML
+    public Label deviceCodeLabel;
+    @FXML
+    public TextField ipAddressField;
+    @FXML
+    public TextField maskField;
+    @FXML
+    public TextField gatewayField;
+    @FXML
+    public TextField dnsField;
+    @FXML
+    public DatePicker authorizeDateField;
+    @FXML
+    public ComboBox<FaceLevel> faceLevelBox;
+    @FXML
+    public ComboBox<LivingLevel> livingLevelBox;
+    @FXML
+    public IntegerField faceSizeField;
+    @FXML
+    public TextArea dataServiceUrlField;
+    @FXML
+    public TextArea deviceServiceUrlField;
+    @FXML
+    public IntegerField informIntervalField;
+    @FXML
+    public Label capacityLabel;
+    @FXML
+    public Button saveButton;
+
+    private BiometricDeviceService deviceService;
+    private FaceGateService faceGateService;
+
+    private ObservableList<FaceLevel> faceLevelOptions =
+            FXCollections.observableArrayList(FaceLevel.values());
+
+    private ObservableList<LivingLevel> livingLevelOptions =
+            FXCollections.observableArrayList(LivingLevel.values());
+
+    private FaceGate faceGate;
+    private DeviceSetting deviceSetting;
+
+    @Override
+    public void initialize(URL location, ResourceBundle resources) {
+        deviceService = SpringContextHolder.getContext().getBean(BiometricDeviceService.class);
+        faceGateService = SpringContextHolder.getContext().getBean(FaceGateService.class);
+        softwareVersionLabel.sceneProperty().addListener(new ChangeListener<Scene>() {
+            @Override
+            public void changed(ObservableValue<? extends Scene> observable, Scene oldValue, Scene newValue) {
+                if (null != newValue) {
+                    deviceSetting = new DeviceSetting();
+                    if (newValue instanceof ParameterizedScene) {
+                        // 监听传过来需要编辑的FaceGate对象
+                        ((ParameterizedScene) newValue).paramsProperty().addListener(new ChangeListener() {
+                            @Override
+                            public void changed(ObservableValue observable, Object oldValue, Object newValue) {
+                                if (null != newValue) {
+                                    faceGate = (FaceGate) newValue;
+                                    Platform.runLater(() -> {
+                                        deviceSetting = deviceService.getSetting(faceGate);
+                                        if (null != deviceSetting) {
+                                            initFields();
+                                        }
+                                    });
+                                }
+                            }
+                        });
+                    }
+                    initFields();
+                }
+            }
+        });
+    }
+
+    private void initFields() {
+        softwareVersionLabel.textProperty().bind(deviceSetting.softwareVersionProperty());
+        systemVersionLabel.textProperty().bind(deviceSetting.systemVersionProperty());
+        deviceCodeLabel.textProperty().bind(deviceSetting.deviceCodeProperty());
+        ipAddressField.textProperty().bindBidirectional(deviceSetting.ipAddressProperty());
+        maskField.textProperty().bindBidirectional(deviceSetting.maskProperty());
+        gatewayField.textProperty().bindBidirectional(deviceSetting.gatewayProperty());
+        dnsField.textProperty().bindBidirectional(deviceSetting.dnsProperty());
+        authorizeDateField.valueProperty().bindBidirectional(deviceSetting.authorizeDateProperty());
+        faceLevelBox.valueProperty().bindBidirectional(deviceSetting.faceLevelProperty());
+        faceLevelBox.setItems(faceLevelOptions);
+        if (null == deviceSetting.getFaceLevel()) {
+            faceLevelBox.getSelectionModel().selectFirst();
+        }
+        livingLevelBox.valueProperty().bindBidirectional(deviceSetting.livingLevelProperty());
+        livingLevelBox.setItems(livingLevelOptions);
+        if (null == deviceSetting.getFaceLevel()) {
+            livingLevelBox.getSelectionModel().selectFirst();
+        }
+        faceSizeField.valueProperty().bindBidirectional(deviceSetting.faceSizeProperty());
+        dataServiceUrlField.textProperty().bindBidirectional(deviceSetting.dataServiceUrlProperty());
+        deviceServiceUrlField.textProperty().bindBidirectional(deviceSetting.deviceServiceUrlProperty());
+        informIntervalField.valueProperty().bindBidirectional(deviceSetting.informIntervalProperty());
+        capacityLabel.textProperty().bindBidirectional(deviceSetting.capacityProperty(), new NumberStringConverter());
+    }
+
+    @FXML
+    public void handleSave(ActionEvent event) {
+        if (null != deviceSetting && deviceSetting.isDirty()) {
+            Platform.runLater(() -> {
+                try {
+                    deviceService.saveSetting(faceGate, deviceSetting);
+                    boolean ipChanged = !faceGate.getIp().equals(deviceSetting.getIpAddress())
+                            && !"-".equals(deviceSetting.getIpAddress());
+                    if (ipChanged) {
+                        faceGate.setIp(deviceSetting.getIpAddress());
+                        faceGateService.save(faceGate);
+                    }
+                    NotifyUtils.info("设置成功");
+                    softwareVersionLabel.getScene().getWindow().hide();
+                } catch (Exception e) {
+                    AlertUtils.error(e.getMessage());
+                }
+            });
+        } else {
+            AlertUtils.warn("没有修改");
+        }
+    }
+}

+ 50 - 0
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/PersonController.java

@@ -51,6 +51,10 @@ public class PersonController implements Initializable {
     public Button editButton;
     public Button editButton;
     @FXML
     @FXML
     public Button delButton;
     public Button delButton;
+    @FXML
+    public Button issueButton;
+    @FXML
+    public Button issueStatusButton;
 
 
     private PersonService personService;
     private PersonService personService;
 
 
@@ -95,9 +99,13 @@ public class PersonController implements Initializable {
                 if (null == newValue) {
                 if (null == newValue) {
                     editButton.setDisable(true);
                     editButton.setDisable(true);
                     delButton.setDisable(true);
                     delButton.setDisable(true);
+                    issueButton.setDisable(true);
+                    issueStatusButton.setDisable(true);
                 } else {
                 } else {
                     editButton.setDisable(false);
                     editButton.setDisable(false);
                     delButton.setDisable(false);
                     delButton.setDisable(false);
+                    issueButton.setDisable(false);
+                    issueStatusButton.setDisable(false);
                 }
                 }
             }
             }
         });
         });
@@ -168,4 +176,46 @@ public class PersonController implements Initializable {
     @FXML
     @FXML
     public void handleImport(ActionEvent event) {
     public void handleImport(ActionEvent event) {
     }
     }
+
+    @FXML
+    public void handleIssue(ActionEvent event) {
+        Person person = tableView.getSelectionModel().getSelectedItem();
+        if (null == person) {
+            AlertUtils.warn("请先选择一行数据");
+            return;
+        }
+        try {
+            Scene scene = ViewUtils.load("/view/gate_issue.fxml", person.getId());
+            Stage stage = new Stage();
+            stage.setScene(scene);
+            stage.setResizable(false);
+            stage.setTitle("人员下发");
+            stage.getIcons().addAll(DeviceClient.defaultIcons);
+            stage.centerOnScreen();
+            stage.show();
+        } catch (Exception e) {
+            logger.error("Issue Person Error", e);
+        }
+    }
+
+    @FXML
+    public void handleIssueStatus(ActionEvent event) {
+        Person person = tableView.getSelectionModel().getSelectedItem();
+        if (null == person) {
+            AlertUtils.warn("请先选择一行数据");
+            return;
+        }
+        try {
+            Scene scene = ViewUtils.load("/view/gate_issue_status.fxml", person.getId());
+            Stage stage = new Stage();
+            stage.setScene(scene);
+            stage.setResizable(false);
+            stage.setTitle("人员下发状态");
+            stage.getIcons().addAll(DeviceClient.defaultIcons);
+            stage.centerOnScreen();
+            stage.show();
+        } catch (Exception e) {
+            logger.error("Issue Status Error", e);
+        }
+    }
 }
 }

+ 20 - 4
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/PersonEditController.java

@@ -7,8 +7,10 @@ import com.usoftchina.smartschool.device.client.biometric.po.SexType;
 import com.usoftchina.smartschool.device.client.biometric.service.FileService;
 import com.usoftchina.smartschool.device.client.biometric.service.FileService;
 import com.usoftchina.smartschool.device.client.biometric.service.PersonService;
 import com.usoftchina.smartschool.device.client.biometric.service.PersonService;
 import com.usoftchina.smartschool.device.client.biometric.util.AlertUtils;
 import com.usoftchina.smartschool.device.client.biometric.util.AlertUtils;
+import com.usoftchina.smartschool.device.client.biometric.util.NotifyUtils;
 import com.usoftchina.smartschool.device.client.biometric.util.ViewUtils;
 import com.usoftchina.smartschool.device.client.biometric.util.ViewUtils;
 import com.usoftchina.smartschool.device.context.SpringContextHolder;
 import com.usoftchina.smartschool.device.context.SpringContextHolder;
+import javafx.application.Platform;
 import javafx.beans.property.ObjectProperty;
 import javafx.beans.property.ObjectProperty;
 import javafx.beans.property.SimpleObjectProperty;
 import javafx.beans.property.SimpleObjectProperty;
 import javafx.beans.value.ChangeListener;
 import javafx.beans.value.ChangeListener;
@@ -113,7 +115,6 @@ public class PersonEditController implements Initializable {
             sexBox.getSelectionModel().selectFirst();
             sexBox.getSelectionModel().selectFirst();
         }
         }
         lastSnapshot = null;
         lastSnapshot = null;
-        lastSelectedImage = null;
         unsavedImage = null;
         unsavedImage = null;
         if (null != person.getFaceImage()) {
         if (null != person.getFaceImage()) {
             File imageFile = fileService.getById(person.getFaceImage());
             File imageFile = fileService.getById(person.getFaceImage());
@@ -136,6 +137,13 @@ public class PersonEditController implements Initializable {
         }
         }
         if (null != unsavedImage) {
         if (null != unsavedImage) {
             try {
             try {
+                // 删除旧的文件
+                if (null != person.getFaceImage()) {
+                    File oldImageFile = fileService.getById(person.getFaceImage());
+                    if (oldImageFile.exists()) {
+                        oldImageFile.delete();
+                    }
+                }
                 person.setFaceImage(fileService.save(unsavedImage));
                 person.setFaceImage(fileService.save(unsavedImage));
                 // 清除临时文件
                 // 清除临时文件
                 if (null != lastSnapshot) {
                 if (null != lastSnapshot) {
@@ -144,10 +152,18 @@ public class PersonEditController implements Initializable {
             } catch (IOException e) {
             } catch (IOException e) {
                 logger.error("Save image error", e);
                 logger.error("Save image error", e);
             }
             }
-        } else if (null == imageView.getImage()){
+        } else if (null == imageView.getImage()) {
             person.setFaceImage(null);
             person.setFaceImage(null);
         }
         }
-        personService.save(person);
+
+        Platform.runLater(() -> {
+            try {
+                personService.save(person);
+                NotifyUtils.info("保存成功");
+            } catch (Exception e) {
+                AlertUtils.error(e.getMessage());
+            }
+        });
     }
     }
 
 
     @FXML
     @FXML
@@ -170,7 +186,7 @@ public class PersonEditController implements Initializable {
     public void handleChoosePicture(ActionEvent event) {
     public void handleChoosePicture(ActionEvent event) {
         if (null == fileChooser) {
         if (null == fileChooser) {
             fileChooser = new FileChooser();
             fileChooser = new FileChooser();
-            fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("图片文件", "*.png", "*.jpg", "*.bmp", "*.gif"));
+            fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("图片文件", "*.png", "*.jpg", "*.bmp"));
         }
         }
         // 打开上次文件夹
         // 打开上次文件夹
         if (null != lastSelectedImage) {
         if (null != lastSelectedImage) {

+ 15 - 12
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/po/AccessType.java

@@ -4,11 +4,13 @@ package com.usoftchina.smartschool.device.client.biometric.po;
  * @author yingp
  * @author yingp
  * @date 2019/11/14
  * @date 2019/11/14
  */
  */
-public class AccessType {
-    private Integer value;
-    private String text;
+public enum AccessType {
+    IN(1, "进入"),
+    OUT(2, "出去");
+    private final Integer value;
+    private final String text;
 
 
-    public AccessType(Integer value, String text) {
+    AccessType(Integer value, String text) {
         this.value = value;
         this.value = value;
         this.text = text;
         this.text = text;
     }
     }
@@ -17,20 +19,21 @@ public class AccessType {
         return value;
         return value;
     }
     }
 
 
-    public void setValue(Integer value) {
-        this.value = value;
-    }
-
     public String getText() {
     public String getText() {
         return text;
         return text;
     }
     }
 
 
-    public void setText(String text) {
-        this.text = text;
-    }
-
     @Override
     @Override
     public String toString() {
     public String toString() {
         return text;
         return text;
     }
     }
+
+    public static AccessType of(Integer value) {
+        for (AccessType type : values()) {
+            if (type.getValue().equals(value)) {
+                return type;
+            }
+        }
+        return IN;
+    }
 }
 }

+ 30 - 14
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/po/FaceGate.java

@@ -19,6 +19,10 @@ public class FaceGate implements DeviceInfo {
      */
      */
     private StringProperty place;
     private StringProperty place;
     private StringProperty ip;
     private StringProperty ip;
+    /**
+     * 设备编号
+     */
+    private StringProperty code;
     private IntegerProperty port;
     private IntegerProperty port;
     private StringProperty username;
     private StringProperty username;
     private StringProperty password;
     private StringProperty password;
@@ -37,7 +41,7 @@ public class FaceGate implements DeviceInfo {
     /**
     /**
      * 健康状况
      * 健康状况
      */
      */
-    private StringProperty status;
+    private ObjectProperty<HealthStatus> status;
 
 
     @Override
     @Override
     public String getId() {
     public String getId() {
@@ -48,6 +52,21 @@ public class FaceGate implements DeviceInfo {
         this.id = id;
         this.id = id;
     }
     }
 
 
+    public String getCode() {
+        return codeProperty().get();
+    }
+
+    public StringProperty codeProperty() {
+        if (null == code) {
+            code = new SimpleStringProperty();
+        }
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.codeProperty().set(code);
+    }
+
     public String getName() {
     public String getName() {
         return nameProperty().get();
         return nameProperty().get();
     }
     }
@@ -149,7 +168,7 @@ public class FaceGate implements DeviceInfo {
 
 
     public void setAccessType(Integer accessType) {
     public void setAccessType(Integer accessType) {
         if (null != accessType) {
         if (null != accessType) {
-            accessTypeProperty().setValue(new AccessType(accessType, 1 == accessType ? "进入" : "出去"));
+            accessTypeProperty().setValue(AccessType.of(accessType));
         }
         }
     }
     }
 
 
@@ -195,27 +214,24 @@ public class FaceGate implements DeviceInfo {
     }
     }
 
 
     public String getStatus() {
     public String getStatus() {
-        return statusProperty().get();
+        HealthStatus status = statusProperty().get();
+        return null == status ? null : status.getValue();
     }
     }
 
 
-    public StringProperty statusProperty() {
+    public ObjectProperty<HealthStatus> statusProperty() {
         if (null == status) {
         if (null == status) {
-            status = new SimpleStringProperty();
+            status = new SimpleObjectProperty<>();
         }
         }
         return status;
         return status;
     }
     }
 
 
-    public void setStatus(String status) {
+    public void setStatus(HealthStatus status) {
         this.statusProperty().set(status);
         this.statusProperty().set(status);
     }
     }
 
 
-    /**
-     * 是否在线
-     *
-     * @return
-     */
-    public boolean isActive() {
-        Date time = getLastActiveTime();
-        return null != time && System.currentTimeMillis() - time.getTime() < 60000;
+    public void setStatus(String status) {
+        if (null != status) {
+            this.statusProperty().set(HealthStatus.of(status));
+        }
     }
     }
 }
 }

+ 75 - 0
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/po/GatePerson.java

@@ -0,0 +1,75 @@
+package com.usoftchina.smartschool.device.client.biometric.po;
+
+import java.util.Date;
+
+/**
+ * 人员下发到闸机设备,绑定关系
+ *
+ * @author yingp
+ * @date 2019/11/18
+ */
+public class GatePerson {
+    private String id;
+    private String personId;
+    private String gateId;
+    /**
+     * 下发后,返回的ID
+     */
+    private Integer devicePersonId;
+    /**
+     * 有效截止时间
+     */
+    private Date endTime;
+    /**
+     * 下发时间
+     */
+    private Date issueTime;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getPersonId() {
+        return personId;
+    }
+
+    public void setPersonId(String personId) {
+        this.personId = personId;
+    }
+
+    public String getGateId() {
+        return gateId;
+    }
+
+    public void setGateId(String gateId) {
+        this.gateId = gateId;
+    }
+
+    public Integer getDevicePersonId() {
+        return devicePersonId;
+    }
+
+    public void setDevicePersonId(Integer devicePersonId) {
+        this.devicePersonId = devicePersonId;
+    }
+
+    public Date getEndTime() {
+        return endTime;
+    }
+
+    public void setEndTime(Date endTime) {
+        this.endTime = endTime;
+    }
+
+    public Date getIssueTime() {
+        return issueTime;
+    }
+
+    public void setIssueTime(Date issueTime) {
+        this.issueTime = issueTime;
+    }
+}

+ 154 - 0
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/po/GatePersonView.java

@@ -0,0 +1,154 @@
+package com.usoftchina.smartschool.device.client.biometric.po;
+
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+
+import java.util.Date;
+
+/**
+ * @author yingp
+ * @date 2019/11/18
+ */
+public class GatePersonView extends Selectable{
+    private String id;
+    private String personId;
+    private String gateId;
+    private Integer devicePersonId;
+    private StringProperty personClass;
+    private StringProperty personName;
+    private ObjectProperty<SexType> personSex;
+    private StringProperty personCardNo;
+    private ObjectProperty<Date> issueTime;
+    private ObjectProperty<Date> endTime;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getPersonId() {
+        return personId;
+    }
+
+    public void setPersonId(String personId) {
+        this.personId = personId;
+    }
+
+    public String getGateId() {
+        return gateId;
+    }
+
+    public void setGateId(String gateId) {
+        this.gateId = gateId;
+    }
+
+    public Integer getDevicePersonId() {
+        return devicePersonId;
+    }
+
+    public void setDevicePersonId(Integer devicePersonId) {
+        this.devicePersonId = devicePersonId;
+    }
+
+    public String getPersonClass() {
+        return personClassProperty().get();
+    }
+
+    public StringProperty personClassProperty() {
+        if (null == personClass) {
+            personClass = new SimpleStringProperty();
+        }
+        return personClass;
+    }
+
+    public void setPersonClass(String clazz) {
+        this.personClassProperty().set(clazz);
+    }
+
+    public String getPersonName() {
+        return personNameProperty().get();
+    }
+
+    public StringProperty personNameProperty() {
+        if (null == personName) {
+            personName = new SimpleStringProperty();
+        }
+        return personName;
+    }
+
+    public void setPersonName(String name) {
+        this.personNameProperty().set(name);
+    }
+
+    public Integer getPersonSex() {
+        SexType type = personSexProperty().get();
+        return null == type ? null : type.getValue();
+    }
+
+    public void setPersonSex(Integer sex) {
+        if (null != sex) {
+            personSexProperty().set(new SexType(sex, 1 == sex ? "男" : "女"));
+        }
+    }
+
+    public ObjectProperty<SexType> personSexProperty() {
+        if (null == personSex) {
+            personSex = new SimpleObjectProperty<>();
+        }
+        return personSex;
+    }
+
+    public void setPersonSex(SexType sex) {
+        this.personSexProperty().set(sex);
+    }
+
+    public String getPersonCardNo() {
+        return personCardNoProperty().get();
+    }
+
+    public StringProperty personCardNoProperty() {
+        if (null == personCardNo) {
+            personCardNo = new SimpleStringProperty();
+        }
+        return personCardNo;
+    }
+
+    public void setPersonCardNo(String cardNo) {
+        this.personCardNoProperty().set(cardNo);
+    }
+
+    public Date getIssueTime() {
+        return issueTimeProperty().get();
+    }
+
+    public ObjectProperty<Date> issueTimeProperty() {
+        if (null == issueTime) {
+            issueTime = new SimpleObjectProperty<>();
+        }
+        return issueTime;
+    }
+
+    public void setIssueTime(Date issueTime) {
+        this.issueTimeProperty().set(issueTime);
+    }
+
+    public Date getEndTime() {
+        return endTimeProperty().get();
+    }
+
+    public ObjectProperty<Date> endTimeProperty() {
+        if (null == endTime) {
+            endTime = new SimpleObjectProperty<>();
+        }
+        return endTime;
+    }
+
+    public void setEndTime(Date endTime) {
+        this.endTimeProperty().set(endTime);
+    }
+}

+ 39 - 0
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/po/HealthStatus.java

@@ -0,0 +1,39 @@
+package com.usoftchina.smartschool.device.client.biometric.po;
+
+/**
+ * @author yingp
+ * @date 2019/11/19
+ */
+public enum HealthStatus {
+    HEALTHY("0", "健康"),
+    UN_HEALTHY("1", "异常");
+    private final String value;
+    private final String text;
+
+    HealthStatus(String value, String text) {
+        this.value = value;
+        this.text = text;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public String getText() {
+        return text;
+    }
+
+    public static HealthStatus of(String value) {
+        for (HealthStatus status : values()) {
+            if (status.getValue().equals(value)) {
+                return status;
+            }
+        }
+        return UN_HEALTHY;
+    }
+
+    @Override
+    public String toString() {
+        return text;
+    }
+}

+ 134 - 0
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/po/PersonGateView.java

@@ -0,0 +1,134 @@
+package com.usoftchina.smartschool.device.client.biometric.po;
+
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+
+import java.util.Date;
+
+/**
+ * @author yingp
+ * @date 2019/11/18
+ */
+public class PersonGateView extends Selectable{
+    private String id;
+    private String personId;
+    private String gateId;
+    private Integer devicePersonId;
+    private StringProperty gateName;
+    /**
+     * 位置
+     */
+    private StringProperty gatePlace;
+    private StringProperty gateIp;
+    private ObjectProperty<Date> issueTime;
+    private ObjectProperty<Date> endTime;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getPersonId() {
+        return personId;
+    }
+
+    public void setPersonId(String personId) {
+        this.personId = personId;
+    }
+
+    public String getGateId() {
+        return gateId;
+    }
+
+    public void setGateId(String gateId) {
+        this.gateId = gateId;
+    }
+
+    public Integer getDevicePersonId() {
+        return devicePersonId;
+    }
+
+    public void setDevicePersonId(Integer devicePersonId) {
+        this.devicePersonId = devicePersonId;
+    }
+
+    public String getGateName() {
+        return gateNameProperty().get();
+    }
+
+    public StringProperty gateNameProperty() {
+        if (null == gateName) {
+            gateName = new SimpleStringProperty();
+        }
+        return gateName;
+    }
+
+    public void setGateName(String gateName) {
+        this.gateNameProperty().set(gateName);
+    }
+
+    public String getGatePlace() {
+        return gatePlaceProperty().get();
+    }
+
+    public StringProperty gatePlaceProperty() {
+        if (null == gatePlace) {
+            gatePlace = new SimpleStringProperty();
+        }
+        return gatePlace;
+    }
+
+    public void setGatePlace(String gatePlace) {
+        this.gatePlaceProperty().set(gatePlace);
+    }
+
+    public String getGateIp() {
+        return gateIpProperty().get();
+    }
+
+    public StringProperty gateIpProperty() {
+        if (null == gateIp) {
+            gateIp = new SimpleStringProperty();
+        }
+        return gateIp;
+    }
+
+    public void setGateIp(String gateIp) {
+        this.gateIpProperty().set(gateIp);
+    }
+
+    public Date getIssueTime() {
+        return issueTimeProperty().get();
+    }
+
+    public ObjectProperty<Date> issueTimeProperty() {
+        if (null == issueTime) {
+            issueTime = new SimpleObjectProperty<>();
+        }
+        return issueTime;
+    }
+
+    public void setIssueTime(Date issueTime) {
+        this.issueTimeProperty().set(issueTime);
+    }
+
+    public Date getEndTime() {
+        return endTimeProperty().get();
+    }
+
+    public ObjectProperty<Date> endTimeProperty() {
+        if (null == endTime) {
+            endTime = new SimpleObjectProperty<>();
+        }
+        return endTime;
+    }
+
+    public void setEndTime(Date endTime) {
+        this.endTimeProperty().set(endTime);
+    }
+}

+ 27 - 0
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/po/Selectable.java

@@ -0,0 +1,27 @@
+package com.usoftchina.smartschool.device.client.biometric.po;
+
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.SimpleBooleanProperty;
+
+/**
+ * @author yingp
+ * @date 2019/11/19
+ */
+public abstract class Selectable {
+    private BooleanProperty selected;
+
+    public boolean isSelected() {
+        return selected.get();
+    }
+
+    public BooleanProperty selectedProperty() {
+        if (null == selected) {
+            selected = new SimpleBooleanProperty();
+        }
+        return selected;
+    }
+
+    public void setSelected(boolean selected) {
+        this.selected.set(selected);
+    }
+}

+ 6 - 6
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/repository/FaceGateRepository.java

@@ -51,17 +51,17 @@ public class FaceGateRepository {
 
 
     @CacheEvict(value = "faceGate", key = "#faceGate.id")
     @CacheEvict(value = "faceGate", key = "#faceGate.id")
     public boolean save(FaceGate faceGate) {
     public boolean save(FaceGate faceGate) {
-        int ret = jdbcTemplate.update("insert into face_gate(id,name,ip,port,place,accessType,startTime,lastActiveTime) values " +
-                        "(?,?,?,?,?,?,?,?)", faceGate.getId(), faceGate.getName(), faceGate.getIp(), faceGate.getPort(),
-                faceGate.getPlace(), faceGate.getAccessType(), faceGate.getStartTime(), faceGate.getLastActiveTime());
+        int ret = jdbcTemplate.update("insert into face_gate(id,name,ip,port,code,place,accessType,startTime,status,lastActiveTime) values " +
+                        "(?,?,?,?,?,?,?,?,?,?)", faceGate.getId(), faceGate.getName(), faceGate.getIp(), faceGate.getPort(),
+                faceGate.getCode(), faceGate.getPlace(), faceGate.getAccessType(), faceGate.getStartTime(), faceGate.getStatus(), faceGate.getLastActiveTime());
         return ret > 0;
         return ret > 0;
     }
     }
 
 
     @CacheEvict(value = "faceGate", key = "#faceGate.id")
     @CacheEvict(value = "faceGate", key = "#faceGate.id")
     public boolean update(FaceGate faceGate) {
     public boolean update(FaceGate faceGate) {
-        int ret = jdbcTemplate.update("update face_gate set name=?,ip=?,port=?,place=?,accessType=?,lastActiveTime=? where id=?",
-                faceGate.getName(), faceGate.getIp(), faceGate.getPort(),
-                faceGate.getPlace(), faceGate.getAccessType(), faceGate.getLastActiveTime(), faceGate.getId());
+        int ret = jdbcTemplate.update("update face_gate set name=?,ip=?,port=?,code=?,place=?,accessType=?,lastActiveTime=?,status=? where id=?",
+                faceGate.getName(), faceGate.getIp(), faceGate.getPort(), faceGate.getCode(),
+                faceGate.getPlace(), faceGate.getAccessType(), faceGate.getLastActiveTime(), faceGate.getStatus(), faceGate.getId());
         return ret > 0;
         return ret > 0;
     }
     }
 
 

+ 91 - 0
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/repository/GatePersonRepository.java

@@ -0,0 +1,91 @@
+package com.usoftchina.smartschool.device.client.biometric.repository;
+
+import com.usoftchina.smartschool.device.client.biometric.po.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+/**
+ * @author yingp
+ * @date 2019/11/14
+ */
+@Repository
+public class GatePersonRepository {
+
+    @Autowired
+    private JdbcTemplate jdbcTemplate;
+
+    public GatePerson findById(String id) {
+        try {
+            return jdbcTemplate.queryForObject("select * from gate_person where id=?",
+                    new BeanPropertyRowMapper<>(GatePerson.class), id);
+        } catch (EmptyResultDataAccessException e) {
+            return null;
+        }
+    }
+
+
+    public GatePerson findByGateAndPerson(String gateId, String personId) {
+        try {
+            return jdbcTemplate.queryForObject("select * from gate_person where gateId=? and personId=?",
+                    new BeanPropertyRowMapper<>(GatePerson.class), gateId, personId);
+        } catch (EmptyResultDataAccessException e) {
+            return null;
+        }
+    }
+
+    public List<GatePersonView> findByGate(String gateId) {
+        try {
+            return jdbcTemplate.query("select gate_person.id,gate_person.personId,gate_person.gateId,gate_person.devicePersonId," +
+                            "gate_person.issueTime,person.name personName,person.cardNo personCardNo,person.sex personSex," +
+                            "person.clazz personClass,gate_person.endTime from face_gate,person,gate_person " +
+                            "where face_gate.id=gate_person.gateId and person.id=gate_person.personId and face_gate.id=? " +
+                            "order by person.createTime",
+                    new BeanPropertyRowMapper<>(GatePersonView.class), gateId);
+        } catch (EmptyResultDataAccessException e) {
+            return null;
+        }
+    }
+
+    public List<PersonGateView> findByPerson(String personId) {
+        try {
+            return jdbcTemplate.query("select gate_person.id,gate_person.personId,gate_person.gateId,gate_person.devicePersonId," +
+                            "gate_person.issueTime,face_gate.name gateName,face_gate.place gatePlace,face_gate.ip gateIp,gate_person.endTime " +
+                            "from face_gate,person,gate_person " +
+                            "where face_gate.id=gate_person.gateId and person.id=gate_person.personId and person.id=? " +
+                            "order by face_gate.startTime",
+                    new BeanPropertyRowMapper<>(PersonGateView.class), personId);
+        } catch (EmptyResultDataAccessException e) {
+            return null;
+        }
+    }
+
+    public boolean save(GatePerson gatePerson) {
+        int ret = jdbcTemplate.update("insert into gate_person(id,personId,gateId,devicePersonId,issueTime,endTime) values " +
+                        "(?,?,?,?,?,?)", gatePerson.getId(), gatePerson.getPersonId(), gatePerson.getGateId(), gatePerson.getDevicePersonId(),
+                gatePerson.getIssueTime(), gatePerson.getEndTime());
+        return ret > 0;
+    }
+
+    public boolean update(GatePerson gatePerson) {
+        int ret = jdbcTemplate.update("update gate_person set issueTime=?,endTime=? where id=?",
+                gatePerson.getIssueTime(), gatePerson.getEndTime(), gatePerson.getId());
+        return ret > 0;
+    }
+
+    public boolean delete(String id) {
+        return jdbcTemplate.update("delete from gate_person where id=?", id) > 0;
+    }
+
+    public boolean deleteByPerson(String personId) {
+        return jdbcTemplate.update("delete from gate_person where personId=?", personId) > 0;
+    }
+
+    public boolean deleteByGate(String gateId) {
+        return jdbcTemplate.update("delete from gate_person where gateId=?", gateId) > 0;
+    }
+}

+ 69 - 12
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/service/FaceGateService.java

@@ -1,14 +1,20 @@
 package com.usoftchina.smartschool.device.client.biometric.service;
 package com.usoftchina.smartschool.device.client.biometric.service;
 
 
-import com.usoftchina.smartschool.device.api.DeviceApi;
 import com.usoftchina.smartschool.device.base.Result;
 import com.usoftchina.smartschool.device.base.Result;
+import com.usoftchina.smartschool.device.biometric.BiometricDeviceService;
+import com.usoftchina.smartschool.device.biometric.DeviceSetting;
 import com.usoftchina.smartschool.device.client.biometric.config.DeviceServerProperties;
 import com.usoftchina.smartschool.device.client.biometric.config.DeviceServerProperties;
 import com.usoftchina.smartschool.device.client.biometric.po.FaceGate;
 import com.usoftchina.smartschool.device.client.biometric.po.FaceGate;
+import com.usoftchina.smartschool.device.client.biometric.po.HealthStatus;
 import com.usoftchina.smartschool.device.client.biometric.repository.FaceGateRepository;
 import com.usoftchina.smartschool.device.client.biometric.repository.FaceGateRepository;
+import com.usoftchina.smartschool.device.client.biometric.repository.GatePersonRepository;
 import com.usoftchina.smartschool.device.client.biometric.util.RandomUtils;
 import com.usoftchina.smartschool.device.client.biometric.util.RandomUtils;
 import com.usoftchina.smartschool.device.dto.AccessControlInfo;
 import com.usoftchina.smartschool.device.dto.AccessControlInfo;
 import com.usoftchina.smartschool.device.exception.ExceptionCode;
 import com.usoftchina.smartschool.device.exception.ExceptionCode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
@@ -16,6 +22,7 @@ import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.StringUtils;
 import org.springframework.util.StringUtils;
 import org.springframework.web.client.RestTemplate;
 import org.springframework.web.client.RestTemplate;
 
 
+import java.net.InetAddress;
 import java.util.Date;
 import java.util.Date;
 import java.util.List;
 import java.util.List;
 
 
@@ -25,9 +32,13 @@ import java.util.List;
  */
  */
 @Service
 @Service
 public class FaceGateService {
 public class FaceGateService {
+    private final Logger logger = LoggerFactory.getLogger(FaceGateService.class);
     @Autowired
     @Autowired
     private FaceGateRepository faceGateRepository;
     private FaceGateRepository faceGateRepository;
 
 
+    @Autowired
+    private GatePersonRepository gatePersonRepository;
+
     @Autowired
     @Autowired
     private RestTemplate restTemplate;
     private RestTemplate restTemplate;
 
 
@@ -35,10 +46,13 @@ public class FaceGateService {
     private DeviceServerProperties deviceServerProperties;
     private DeviceServerProperties deviceServerProperties;
 
 
     @Autowired
     @Autowired
-    private DeviceApi deviceApi;
+    private SchoolService schoolService;
 
 
     @Autowired
     @Autowired
-    private SchoolService schoolService;
+    private BiometricDeviceService deviceService;
+
+    @Value("${server.port:8080}")
+    private Integer serverPort;
 
 
     public List<FaceGate> findAll() {
     public List<FaceGate> findAll() {
         return faceGateRepository.findAll();
         return faceGateRepository.findAll();
@@ -68,32 +82,75 @@ public class FaceGateService {
 
 
         if (isNew) {
         if (isNew) {
             faceGate.setStartTime(new Date());
             faceGate.setStartTime(new Date());
-            // 调用设备接口监听
-            deviceApi.add(faceGate);
+            bindDevice(faceGate);
             faceGateRepository.save(faceGate);
             faceGateRepository.save(faceGate);
         } else {
         } else {
             oldOne = faceGateRepository.findById(faceGate.getId());
             oldOne = faceGateRepository.findById(faceGate.getId());
             if (!oldOne.getIp().equals(faceGate.getIp()) || oldOne.getPort() != faceGate.getPort()) {
             if (!oldOne.getIp().equals(faceGate.getIp()) || oldOne.getPort() != faceGate.getPort()) {
-                // 调用设备接口监听
-                deviceApi.add(faceGate);
-                // 调用设备接口停止监听
-                deviceApi.remove(oldOne);
+                try {
+                    unbindDevice(oldOne);
+                } catch (Exception e) {
+                    logger.warn("unbind device error", e);
+                }
+                bindDevice(faceGate);
             }
             }
             faceGateRepository.update(faceGate);
             faceGateRepository.update(faceGate);
         }
         }
     }
     }
 
 
+    /**
+     * 连接到设备
+     *
+     * @param faceGate
+     */
+    private void bindDevice(FaceGate faceGate) {
+        DeviceSetting deviceSetting = deviceService.getSetting(faceGate);
+        try {
+            // 设置数据回调接口
+            String serverIp = InetAddress.getLocalHost().getHostAddress();
+            deviceSetting.setDataServiceUrl(String.format("http://%s:%s/verificationInterface/passlog/passFullLog", serverIp, serverPort));
+            deviceSetting.setDeviceServiceUrl(String.format("http://%s:%s/DeviceManageService/ACSServer", serverIp, serverPort));
+        } catch (Exception e) {
+            logger.error("Inet error", e);
+        }
+        if (deviceSetting.isDirty()) {
+            deviceService.saveSetting(faceGate, deviceSetting);
+        }
+        faceGate.setCode(deviceSetting.getDeviceCode());
+        faceGate.setStatus(HealthStatus.HEALTHY.getValue());
+        faceGate.setLastActiveTime(new Date());
+    }
+
+    /**
+     * 取消连接到设备
+     *
+     * @param faceGate
+     */
+    private void unbindDevice(FaceGate faceGate) {
+        DeviceSetting deviceSetting = deviceService.getSetting(faceGate);
+        try {
+            // 取消数据回调
+            deviceSetting.setDataServiceUrl(null);
+            deviceSetting.setDeviceServiceUrl(null);
+        } catch (Exception e) {
+            logger.error("Inet error", e);
+        }
+        if (deviceSetting.isDirty()) {
+            deviceService.saveSetting(faceGate, deviceSetting);
+        }
+    }
+
     @Transactional(rollbackFor = Exception.class)
     @Transactional(rollbackFor = Exception.class)
     public void delete(String id) {
     public void delete(String id) {
         FaceGate oldOne = faceGateRepository.findById(id);
         FaceGate oldOne = faceGateRepository.findById(id);
         if (null != oldOne) {
         if (null != oldOne) {
             try {
             try {
-                // 先调用设备接口停止监听
-                deviceApi.remove(oldOne);
+                unbindDevice(oldOne);
             } catch (Exception e) {
             } catch (Exception e) {
-                e.printStackTrace();
+                logger.warn("unbind device error", e);
             }
             }
             faceGateRepository.delete(id);
             faceGateRepository.delete(id);
+            gatePersonRepository.deleteByGate(id);
         }
         }
     }
     }
 
 

+ 169 - 0
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/service/GatePersonService.java

@@ -0,0 +1,169 @@
+package com.usoftchina.smartschool.device.client.biometric.service;
+
+import com.usoftchina.smartschool.device.biometric.BiometricDeviceService;
+import com.usoftchina.smartschool.device.biometric.PersonPo;
+import com.usoftchina.smartschool.device.client.biometric.po.FaceGate;
+import com.usoftchina.smartschool.device.client.biometric.po.GatePerson;
+import com.usoftchina.smartschool.device.client.biometric.po.Person;
+import com.usoftchina.smartschool.device.client.biometric.po.PersonGateView;
+import com.usoftchina.smartschool.device.client.biometric.repository.FaceGateRepository;
+import com.usoftchina.smartschool.device.client.biometric.repository.GatePersonRepository;
+import com.usoftchina.smartschool.device.client.biometric.repository.PersonRepository;
+import com.usoftchina.smartschool.device.client.biometric.util.RandomUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.util.Base64;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author yingp
+ * @date 2019/11/18
+ */
+@Service
+public class GatePersonService {
+
+    @Autowired
+    private GatePersonRepository gatePersonRepository;
+
+    @Autowired
+    private FaceGateRepository faceGateRepository;
+
+    @Autowired
+    private PersonRepository personRepository;
+
+    @Autowired
+    private BiometricDeviceService deviceService;
+
+    @Autowired
+    private FileService fileService;
+
+    /**
+     * 人员信息下发到指定设备
+     *
+     * @param personId
+     * @param gateId
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public void issue(String personId, String gateId, Date endTime) {
+        boolean isNew = false;
+        GatePerson gatePerson = gatePersonRepository.findByGateAndPerson(gateId, personId);
+        if (null == gatePerson) {
+            gatePerson = new GatePerson();
+            gatePerson.setId(RandomUtils.randomString());
+            gatePerson.setDevicePersonId(Math.abs((gateId + personId).hashCode()));
+            isNew = true;
+        }
+        gatePerson.setGateId(gateId);
+        gatePerson.setPersonId(personId);
+        gatePerson.setIssueTime(new Date());
+        gatePerson.setEndTime(endTime);
+
+        // 先下发信息到设备上
+        FaceGate faceGate = faceGateRepository.findById(gateId);
+        Person person = personRepository.findById(personId);
+        PersonPo personPo = new PersonPo();
+        personPo.setAccessCardNum(person.getCardNo());
+        personPo.setDataType(0);
+        personPo.setEndTime(endTime);
+        personPo.setName(person.getName());
+        personPo.setGenderCode(String.valueOf(person.getSex()));
+        personPo.setId(gatePerson.getDevicePersonId());
+        try {
+            File imageFile = fileService.getById(person.getFaceImage());
+            String imageBase64 = Base64.getEncoder().encodeToString(Files.readAllBytes(imageFile.toPath()));
+            personPo.setPicture(imageBase64);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        deviceService.issue(faceGate, personPo);
+        if (isNew) {
+            gatePersonRepository.save(gatePerson);
+        } else {
+            gatePersonRepository.update(gatePerson);
+        }
+    }
+
+    /**
+     * 人员信息批量下发
+     *
+     * @param personId
+     * @param gateIds
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public void issue(String personId, String[] gateIds, Date endTime) {
+        for (String gateId : gateIds) {
+            issue(personId, gateId, endTime);
+        }
+    }
+
+    /**
+     * 人员信息批量下发
+     *
+     * @param personIds
+     * @param gateIds
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public void issue(String[] personIds, String[] gateIds, Date endTime) {
+        for (String personId : personIds) {
+            issue(personId, gateIds, endTime);
+        }
+    }
+
+    /**
+     * 人员信息下发到全部设备
+     *
+     * @param personId
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public void issue(String personId) {
+        List<FaceGate> gates = faceGateRepository.findAll();
+        if (!CollectionUtils.isEmpty(gates)) {
+            // 默认100年
+            Date endTime = Date.from(LocalDate.now().plusYears(100).atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
+            gates.forEach(gate -> issue(personId, gate.getId(), endTime));
+        }
+    }
+
+    /**
+     * 人员信息下发到指定设备
+     *
+     * @param id
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public void cancelIssue(String id) {
+        // 先删除设备上的记录
+        GatePerson gatePerson = gatePersonRepository.findById(id);
+        FaceGate faceGate = faceGateRepository.findById(gatePerson.getGateId());
+        deviceService.cancelIssue(faceGate, gatePerson.getDevicePersonId());
+
+        gatePersonRepository.delete(id);
+    }
+
+    /**
+     * 人员信息下发到指定设备
+     *
+     * @param personId
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public void cancelIssueByPerson(String personId) {
+        List<PersonGateView> gatePersonList = gatePersonRepository.findByPerson(personId);
+        if (!CollectionUtils.isEmpty(gatePersonList)) {
+            // 先删除设备上的记录
+            for (PersonGateView personGate : gatePersonList) {
+                FaceGate faceGate = faceGateRepository.findById(personGate.getGateId());
+                deviceService.cancelIssue(faceGate, personGate.getDevicePersonId());
+            }
+
+            gatePersonRepository.deleteByPerson(personId);
+        }
+    }
+}

+ 4 - 0
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/service/PersonService.java

@@ -22,6 +22,9 @@ public class PersonService {
     @Autowired
     @Autowired
     private PersonRepository personRepository;
     private PersonRepository personRepository;
 
 
+    @Autowired
+    private GatePersonService gatePersonService;
+
     public List<Person> findAll() {
     public List<Person> findAll() {
         return personRepository.findAll();
         return personRepository.findAll();
     }
     }
@@ -58,6 +61,7 @@ public class PersonService {
 
 
     @Transactional(rollbackFor = Exception.class)
     @Transactional(rollbackFor = Exception.class)
     public void delete(String id) {
     public void delete(String id) {
+        gatePersonService.cancelIssueByPerson(id);
         personRepository.delete(id);
         personRepository.delete(id);
     }
     }
 }
 }

+ 46 - 0
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/task/GateScheduler.java

@@ -0,0 +1,46 @@
+package com.usoftchina.smartschool.device.client.biometric.task;
+
+import com.usoftchina.smartschool.device.biometric.BiometricDeviceService;
+import com.usoftchina.smartschool.device.client.biometric.po.FaceGate;
+import com.usoftchina.smartschool.device.client.biometric.po.HealthStatus;
+import com.usoftchina.smartschool.device.client.biometric.service.FaceGateService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author yingp
+ * @date 2019/11/19
+ */
+@Component
+public class GateScheduler {
+    @Autowired
+    private FaceGateService faceGateService;
+    @Autowired
+    private BiometricDeviceService deviceService;
+
+    @Scheduled(fixedDelay = 120000)
+    public void pingDevice() {
+        List<FaceGate> faceGates = faceGateService.findAll();
+        if (!CollectionUtils.isEmpty(faceGates)) {
+            try {
+                faceGates.parallelStream().forEach(faceGate -> {
+                    boolean success = deviceService.ping(faceGate);
+                    if (success) {
+                        faceGate.setLastActiveTime(new Date());
+                        faceGate.setStatus(HealthStatus.HEALTHY.getValue());
+                    } else {
+                        faceGate.setStatus(HealthStatus.UN_HEALTHY.getValue());
+                    }
+                    faceGateService.save(faceGate);
+                });
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+}

+ 31 - 0
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/util/NotifyUtils.java

@@ -0,0 +1,31 @@
+package com.usoftchina.smartschool.device.client.biometric.util;
+
+import javafx.geometry.Pos;
+import javafx.util.Duration;
+import org.controlsfx.control.Notifications;
+
+/**
+ * @author yingp
+ * @date 2019/11/19
+ */
+public class NotifyUtils {
+
+    public static void info(String message) {
+        notify("提示", message).hideAfter(Duration.seconds(3)).showInformation();
+    }
+
+    public static void warn(String message) {
+        notify("警告", message).hideAfter(Duration.seconds(5)).showWarning();
+    }
+
+    public static void error(String message) {
+        notify("错误", message).hideAfter(Duration.seconds(5)).showError();
+    }
+
+    public static Notifications notify(String title, String message) {
+        return Notifications.create()
+                .title(title)
+                .text(message)
+                .position(Pos.TOP_CENTER);
+    }
+}

+ 2 - 2
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/util/RandomUtils.java

@@ -1,6 +1,6 @@
 package com.usoftchina.smartschool.device.client.biometric.util;
 package com.usoftchina.smartschool.device.client.biometric.util;
 
 
-import java.util.Random;
+import java.util.UUID;
 
 
 /**
 /**
  * @author yingp
  * @author yingp
@@ -9,7 +9,7 @@ import java.util.Random;
 public class RandomUtils {
 public class RandomUtils {
 
 
     public static String randomString() {
     public static String randomString() {
-        return String.valueOf(new Random().nextLong());
+        return UUID.randomUUID().toString();
     }
     }
 
 
 }
 }

+ 13 - 1
applications/device/device-client-biometric/src/main/resources/schema.sql

@@ -1,13 +1,15 @@
 create table if not exists face_gate
 create table if not exists face_gate
 (
 (
 id varchar(50) primary key not null,
 id varchar(50) primary key not null,
+code varchar(50),
 name varchar(300) not null,
 name varchar(300) not null,
 ip varchar(20) not null,
 ip varchar(20) not null,
 port int not null,
 port int not null,
 place varchar(30),
 place varchar(30),
 accessType int,
 accessType int,
 startTime datetime,
 startTime datetime,
-lastActiveTime datetime
+lastActiveTime datetime,
+status varchar(10)
 );
 );
 
 
 create table if not exists school
 create table if not exists school
@@ -35,4 +37,14 @@ cardNo varchar(20) not null,
 sex int,
 sex int,
 createTime datetime,
 createTime datetime,
 faceImage varchar(100)
 faceImage varchar(100)
+);
+
+create table if not exists gate_person
+(
+id varchar(50) primary key not null,
+gateId varchar(50) not null,
+personId varchar(50) not null,
+devicePersonId int,
+issueTime datetime,
+endTime datetime
 );
 );

+ 4 - 0
applications/device/device-client-biometric/src/main/resources/style/main.css

@@ -169,6 +169,10 @@ AnchorPane, BorderPane, VBox {
     -fx-font-size: 14px;
     -fx-font-size: 14px;
 }
 }
 
 
+.field-control .field-label {
+    -fx-font-weight: bold;
+}
+
 .image-view {
 .image-view {
     -fx-effect: dropshadow(three-pass-box, rgba(0, 0, 0, 0.8), 10, 0, 0, 0);
     -fx-effect: dropshadow(three-pass-box, rgba(0, 0, 0, 0.8), 10, 0, 0, 0);
     -fx-border-color: #bfcfda;
     -fx-border-color: #bfcfda;

+ 14 - 6
applications/device/device-client-biometric/src/main/resources/view/gate.fxml

@@ -8,6 +8,7 @@
 <?import javafx.scene.control.Button?>
 <?import javafx.scene.control.Button?>
 <?import javafx.scene.control.Separator?>
 <?import javafx.scene.control.Separator?>
 <BorderPane xmlns="http://javafx.com/javafx/8.0.172-ea"
 <BorderPane xmlns="http://javafx.com/javafx/8.0.172-ea"
+            stylesheets="/style/main.css"
             fx:controller="com.usoftchina.smartschool.device.client.biometric.controller.GateController"
             fx:controller="com.usoftchina.smartschool.device.client.biometric.controller.GateController"
             xmlns:fx="http://javafx.com/fxml/1">
             xmlns:fx="http://javafx.com/fxml/1">
     <top>
     <top>
@@ -19,6 +20,12 @@
                 <Separator/>
                 <Separator/>
                 <Button fx:id="delButton" text="删除" onAction="#handleDelete" disable="true"/>
                 <Button fx:id="delButton" text="删除" onAction="#handleDelete" disable="true"/>
                 <Separator/>
                 <Separator/>
+                <Button fx:id="settingButton" text="参数设置" onAction="#handleSetting" disable="true"/>
+                <Separator/>
+                <Button fx:id="issueButton" text="批量绑定" onAction="#handleIssue" disable="true"/>
+                <Separator/>
+                <Button fx:id="issueStatusButton" text="绑定状态" onAction="#handleIssueStatus" disable="true"/>
+                <Separator/>
                 <Button text="刷新" onAction="#handleRefresh"/>
                 <Button text="刷新" onAction="#handleRefresh"/>
             </items>
             </items>
         </ToolBar>
         </ToolBar>
@@ -26,13 +33,14 @@
     <center>
     <center>
         <TableView fx:id="tableView" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
         <TableView fx:id="tableView" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
             <columns>
             <columns>
-                <TableColumn fx:id="nameCol" prefWidth="100.0" text="设备名称"/>
+                <TableColumn fx:id="nameCol" prefWidth="100.0" minWidth="100" text="设备名称"/>
                 <TableColumn fx:id="placeCol" prefWidth="100.0" text="设备位置"/>
                 <TableColumn fx:id="placeCol" prefWidth="100.0" text="设备位置"/>
-                <TableColumn fx:id="ipCol" prefWidth="100.0" text="设备IP"/>
-                <TableColumn fx:id="accessTypeCol" prefWidth="100.0" text="出入类型"/>
-                <TableColumn fx:id="startTimeCol" prefWidth="200.0" text="注册时间"/>
-                <TableColumn fx:id="lastActiveCol" prefWidth="150.0" text="上次心跳"/>
-                <TableColumn fx:id="statusCol" prefWidth="100.0" text="状态"/>
+                <TableColumn fx:id="ipCol" prefWidth="100.0" minWidth="100" text="设备IP" style="-fx-alignment: CENTER"/>
+                <TableColumn fx:id="codeCol" prefWidth="100.0" minWidth="100" text="设备编号" style="-fx-alignment: CENTER"/>
+                <TableColumn fx:id="accessTypeCol" prefWidth="100.0" text="出入类型" style="-fx-alignment: CENTER"/>
+                <TableColumn fx:id="startTimeCol" prefWidth="160.0" minWidth="160" text="注册时间" style="-fx-alignment: CENTER"/>
+                <TableColumn fx:id="lastActiveCol" prefWidth="160.0" minWidth="160" text="上次心跳" style="-fx-alignment: CENTER"/>
+                <TableColumn fx:id="statusCol" prefWidth="100.0" text="状态" style="-fx-alignment: CENTER"/>
             </columns>
             </columns>
         </TableView>
         </TableView>
     </center>
     </center>

+ 4 - 4
applications/device/device-client-biometric/src/main/resources/view/gate_edit.fxml

@@ -14,25 +14,25 @@
     <children>
     <children>
         <HBox styleClass="field-control" layoutX="10" layoutY="30">
         <HBox styleClass="field-control" layoutX="10" layoutY="30">
             <children>
             <children>
-                <Label text="设备名称" prefWidth="100" prefHeight="40" alignment="CENTER_RIGHT" style="-fx-padding: 0 5px 0 0"/>
+                <Label text="设备名称" styleClass="field-label" prefWidth="100" prefHeight="40" alignment="CENTER_RIGHT" style="-fx-padding: 0 5px 0 0"/>
                 <TextField fx:id="nameField" prefWidth="200" prefHeight="40"/>
                 <TextField fx:id="nameField" prefWidth="200" prefHeight="40"/>
             </children>
             </children>
         </HBox>
         </HBox>
         <HBox styleClass="field-control" layoutX="10" layoutY="80">
         <HBox styleClass="field-control" layoutX="10" layoutY="80">
             <children>
             <children>
-                <Label text="设备IP" prefWidth="100" prefHeight="40" alignment="CENTER_RIGHT" style="-fx-padding: 0 5px 0 0"/>
+                <Label text="设备IP" styleClass="field-label" prefWidth="100" prefHeight="40" alignment="CENTER_RIGHT" style="-fx-padding: 0 5px 0 0"/>
                 <TextField fx:id="ipField" prefWidth="200" prefHeight="40"/>
                 <TextField fx:id="ipField" prefWidth="200" prefHeight="40"/>
             </children>
             </children>
         </HBox>
         </HBox>
         <HBox styleClass="field-control" layoutX="10" layoutY="130">
         <HBox styleClass="field-control" layoutX="10" layoutY="130">
             <children>
             <children>
-                <Label text="安装位置" prefWidth="100" prefHeight="40" alignment="CENTER_RIGHT" style="-fx-padding: 0 5px 0 0"/>
+                <Label text="安装位置" styleClass="field-label" prefWidth="100" prefHeight="40" alignment="CENTER_RIGHT" style="-fx-padding: 0 5px 0 0"/>
                 <TextField fx:id="placeField" prefWidth="200" prefHeight="40"/>
                 <TextField fx:id="placeField" prefWidth="200" prefHeight="40"/>
             </children>
             </children>
         </HBox>
         </HBox>
         <HBox styleClass="field-control" layoutX="10" layoutY="180">
         <HBox styleClass="field-control" layoutX="10" layoutY="180">
             <children>
             <children>
-                <Label text="出入类型" prefWidth="100" prefHeight="40" alignment="CENTER_RIGHT" style="-fx-padding: 0 5px 0 0"/>
+                <Label text="出入类型" styleClass="field-label" prefWidth="100" prefHeight="40" alignment="CENTER_RIGHT" style="-fx-padding: 0 5px 0 0"/>
                 <ComboBox fx:id="accessTypeBox" prefWidth="200" prefHeight="40"/>
                 <ComboBox fx:id="accessTypeBox" prefWidth="200" prefHeight="40"/>
             </children>
             </children>
         </HBox>
         </HBox>

+ 22 - 0
applications/device/device-client-biometric/src/main/resources/view/gate_issue.fxml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.scene.control.Label?>
+<?import javafx.scene.layout.HBox?>
+
+<?import javafx.scene.layout.VBox?>
+<?import javafx.scene.control.DatePicker?>
+<?import javafx.scene.control.Button?>
+<VBox prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.172-ea"
+      xmlns:fx="http://javafx.com/fxml/1"
+      stylesheets="/style/main.css"
+      spacing="20"
+      style="-fx-padding: 20px"
+      fx:controller="com.usoftchina.smartschool.device.client.biometric.controller.GateIssueController">
+    <children>
+        <Label text="选择人脸闸机:" style="-fx-font-weight: bold;-fx-font-size: 14px"/>
+        <HBox spacing="20" fx:id="gateList"/>
+        <Label text="有效截止时间:" style="-fx-font-weight: bold;-fx-font-size: 14px"/>
+        <DatePicker fx:id="endTimePicker" prefWidth="200" prefHeight="40"/>
+        <Button text="下发" onAction="#handleIssue" prefWidth="200" prefHeight="40"/>
+    </children>
+</VBox>

+ 33 - 0
applications/device/device-client-biometric/src/main/resources/view/gate_issue_status.fxml

@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import java.lang.*?>
+<?import java.util.*?>
+<?import javafx.scene.*?>
+<?import javafx.scene.control.*?>
+<?import javafx.scene.layout.*?>
+
+<BorderPane xmlns="http://javafx.com/javafx"
+            xmlns:fx="http://javafx.com/fxml"
+            stylesheets="/style/main.css"
+            fx:controller="com.usoftchina.smartschool.device.client.biometric.controller.GateIssueStatusController"
+            prefHeight="600.0" prefWidth="800.0">
+    <top>
+        <ToolBar BorderPane.alignment="CENTER">
+            <items>
+                <Button fx:id="cancelIssueButton" text="取消下发" onAction="#handleCancelIssue" disable="true"/>
+            </items>
+        </ToolBar>
+    </top>
+    <center>
+        <TableView fx:id="tableView" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
+            <columns>
+                <TableColumn fx:id="selectedCol" prefWidth="50.0" text="勾选"/>
+                <TableColumn fx:id="gateNameCol" prefWidth="120.0" text="设备名称"/>
+                <TableColumn fx:id="gatePlaceCol" prefWidth="120.0" text="设备位置"/>
+                <TableColumn fx:id="gateIpCol" prefWidth="120.0" text="设备IP"/>
+                <TableColumn fx:id="issueTimeCol" prefWidth="200.0" text="下发时间"/>
+                <TableColumn fx:id="endTimeCol" prefWidth="200.0" text="截止时间"/>
+            </columns>
+        </TableView>
+    </center>
+</BorderPane>

+ 109 - 0
applications/device/device-client-biometric/src/main/resources/view/gate_setting.fxml

@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.scene.control.*?>
+<?import javafx.scene.layout.*?>
+
+<?import com.usoftchina.smartschool.device.client.biometric.control.IntegerField?>
+<AnchorPane xmlns="http://javafx.com/javafx"
+            xmlns:fx="http://javafx.com/fxml"
+            stylesheets="/style/main.css"
+            fx:controller="com.usoftchina.smartschool.device.client.biometric.controller.GateSettingController"
+            prefHeight="600.0" prefWidth="900.0">
+    <children>
+        <HBox styleClass="field-control" layoutX="10" layoutY="30">
+            <children>
+                <Label text="软件版本:" styleClass="field-label" prefWidth="120" prefHeight="40" alignment="CENTER_RIGHT" style="-fx-padding: 0 5px 0 0"/>
+                <Label fx:id="softwareVersionLabel" prefWidth="200" prefHeight="40"/>
+            </children>
+        </HBox>
+        <HBox styleClass="field-control" layoutX="10" layoutY="80">
+            <children>
+                <Label text="固件版本:" styleClass="field-label" prefWidth="120" prefHeight="40" alignment="CENTER_RIGHT" style="-fx-padding: 0 5px 0 0"/>
+                <Label fx:id="systemVersionLabel" prefWidth="300" prefHeight="40"/>
+            </children>
+        </HBox>
+        <HBox styleClass="field-control" layoutX="10" layoutY="130">
+            <children>
+                <Label text="设备编号:" styleClass="field-label" prefWidth="120" prefHeight="40" alignment="CENTER_RIGHT" style="-fx-padding: 0 5px 0 0"/>
+                <Label fx:id="deviceCodeLabel" prefWidth="300" prefHeight="40"/>
+            </children>
+        </HBox>
+        <HBox styleClass="field-control" layoutX="10" layoutY="180">
+            <children>
+                <Label text="IP 地址:" styleClass="field-label" prefWidth="120" prefHeight="40" alignment="CENTER_RIGHT" style="-fx-padding: 0 5px 0 0"/>
+                <TextField fx:id="ipAddressField" prefWidth="300" prefHeight="40" disable="true"/>
+            </children>
+        </HBox>
+        <HBox styleClass="field-control" layoutX="10" layoutY="230">
+            <children>
+                <Label text="子网掩码:" styleClass="field-label" prefWidth="120" prefHeight="40" alignment="CENTER_RIGHT" style="-fx-padding: 0 5px 0 0"/>
+                <TextField fx:id="maskField" prefWidth="300" prefHeight="40" disable="true"/>
+            </children>
+        </HBox>
+        <HBox styleClass="field-control" layoutX="10" layoutY="280">
+            <children>
+                <Label text="网关:" styleClass="field-label" prefWidth="120" prefHeight="40" alignment="CENTER_RIGHT" style="-fx-padding: 0 5px 0 0"/>
+                <TextField fx:id="gatewayField" prefWidth="300" prefHeight="40" disable="true"/>
+            </children>
+        </HBox>
+        <HBox styleClass="field-control" layoutX="10" layoutY="330">
+            <children>
+                <Label text="DNS:" styleClass="field-label" prefWidth="120" prefHeight="40" alignment="CENTER_RIGHT" style="-fx-padding: 0 5px 0 0"/>
+                <TextField fx:id="dnsField" prefWidth="300" prefHeight="40" disable="true"/>
+            </children>
+        </HBox>
+        <HBox styleClass="field-control" layoutX="10" layoutY="380">
+            <children>
+                <Label text="授权截止日期:" styleClass="field-label" prefWidth="120" prefHeight="40" alignment="CENTER_RIGHT" style="-fx-padding: 0 5px 0 0"/>
+                <DatePicker fx:id="authorizeDateField" prefWidth="300" prefHeight="40"/>
+            </children>
+        </HBox>
+        <HBox styleClass="field-control" layoutX="10" layoutY="430">
+            <children>
+                <Label text="已入库人像容量:" styleClass="field-label" prefWidth="120" prefHeight="40" alignment="CENTER_RIGHT" style="-fx-padding: 0 5px 0 0"/>
+                <Label fx:id="capacityLabel" prefWidth="300" prefHeight="40"/>
+            </children>
+        </HBox>
+        <HBox styleClass="field-control" layoutX="440" layoutY="30">
+            <children>
+                <Label text="人脸阈值级别:" styleClass="field-label" prefWidth="120" prefHeight="40" alignment="CENTER_RIGHT" style="-fx-padding: 0 5px 0 0"/>
+                <ComboBox fx:id="faceLevelBox" prefWidth="300" prefHeight="40"/>
+            </children>
+        </HBox>
+        <HBox styleClass="field-control" layoutX="440" layoutY="80">
+            <children>
+                <Label text="活体阈值级别:" styleClass="field-label" prefWidth="120" prefHeight="40" alignment="CENTER_RIGHT" style="-fx-padding: 0 5px 0 0"/>
+                <ComboBox fx:id="livingLevelBox" prefWidth="300" prefHeight="40"/>
+            </children>
+        </HBox>
+        <HBox styleClass="field-control" layoutX="440" layoutY="130">
+            <children>
+                <Label text="人脸框像素大小:" styleClass="field-label" prefWidth="120" prefHeight="40" alignment="CENTER_RIGHT" style="-fx-padding: 0 5px 0 0"/>
+                <IntegerField fx:id="faceSizeField" prefWidth="300" prefHeight="40"/>
+            </children>
+        </HBox>
+        <HBox styleClass="field-control" layoutX="440" layoutY="180">
+            <children>
+                <Label text="数据上传地址:" styleClass="field-label" prefWidth="120" prefHeight="40" alignment="CENTER_RIGHT" style="-fx-padding: 0 5px 0 0"/>
+                <TextArea fx:id="dataServiceUrlField" wrapText="true" prefWidth="300" prefHeight="90" disable="true"/>
+            </children>
+        </HBox>
+        <HBox styleClass="field-control" layoutX="440" layoutY="280">
+            <children>
+                <Label text="心跳地址:" styleClass="field-label" prefWidth="120" prefHeight="40" alignment="CENTER_RIGHT" style="-fx-padding: 0 5px 0 0"/>
+                <TextArea fx:id="deviceServiceUrlField" wrapText="true" prefWidth="300" prefHeight="90" disable="true"/>
+            </children>
+        </HBox>
+        <HBox styleClass="field-control" layoutX="440" layoutY="380">
+            <children>
+                <Label text="心跳间隔:" styleClass="field-label" prefWidth="120" prefHeight="40" alignment="CENTER_RIGHT" style="-fx-padding: 0 5px 0 0"/>
+                <IntegerField fx:id="informIntervalField" prefWidth="300" prefHeight="40"/>
+            </children>
+        </HBox>
+        <AnchorPane styleClass="field-control" layoutX="10" layoutY="480">
+            <children>
+                <Button fx:id="saveButton" layoutX="120" layoutY="0" text="保存" prefWidth="300" prefHeight="40" onAction="#handleSave"/>
+            </children>
+        </AnchorPane>
+    </children>
+</AnchorPane>

+ 5 - 0
applications/device/device-client-biometric/src/main/resources/view/person.fxml

@@ -8,6 +8,7 @@
 <?import javafx.scene.control.Button?>
 <?import javafx.scene.control.Button?>
 <?import javafx.scene.control.Separator?>
 <?import javafx.scene.control.Separator?>
 <BorderPane xmlns="http://javafx.com/javafx/8.0.172-ea"
 <BorderPane xmlns="http://javafx.com/javafx/8.0.172-ea"
+            stylesheets="/style/main.css"
             fx:controller="com.usoftchina.smartschool.device.client.biometric.controller.PersonController"
             fx:controller="com.usoftchina.smartschool.device.client.biometric.controller.PersonController"
             xmlns:fx="http://javafx.com/fxml/1">
             xmlns:fx="http://javafx.com/fxml/1">
     <top>
     <top>
@@ -21,6 +22,10 @@
                 <Separator/>
                 <Separator/>
                 <Button text="导入" onAction="#handleImport"/>
                 <Button text="导入" onAction="#handleImport"/>
                 <Separator/>
                 <Separator/>
+                <Button fx:id="issueButton" text="批量下发" onAction="#handleIssue" disable="true"/>
+                <Separator/>
+                <Button fx:id="issueStatusButton" text="下发状态" onAction="#handleIssueStatus" disable="true"/>
+                <Separator/>
                 <Button text="刷新" onAction="#handleRefresh"/>
                 <Button text="刷新" onAction="#handleRefresh"/>
             </items>
             </items>
         </ToolBar>
         </ToolBar>

+ 4 - 4
applications/device/device-client-biometric/src/main/resources/view/person_edit.fxml

@@ -12,25 +12,25 @@
     <children>
     <children>
         <HBox layoutX="10" layoutY="30" styleClass="field-control">
         <HBox layoutX="10" layoutY="30" styleClass="field-control">
             <children>
             <children>
-                <Label alignment="CENTER_RIGHT" prefHeight="40" prefWidth="100" style="-fx-padding: 0 5px 0 0" text="班级" />
+                <Label alignment="CENTER_RIGHT" styleClass="field-label" prefHeight="40" prefWidth="100" style="-fx-padding: 0 5px 0 0" text="班级" />
                 <TextField fx:id="classField" prefHeight="40" prefWidth="200" />
                 <TextField fx:id="classField" prefHeight="40" prefWidth="200" />
             </children>
             </children>
         </HBox>
         </HBox>
         <HBox layoutX="10" layoutY="80" styleClass="field-control">
         <HBox layoutX="10" layoutY="80" styleClass="field-control">
             <children>
             <children>
-                <Label alignment="CENTER_RIGHT" prefHeight="40" prefWidth="100" style="-fx-padding: 0 5px 0 0" text="姓名" />
+                <Label alignment="CENTER_RIGHT" styleClass="field-label" prefHeight="40" prefWidth="100" style="-fx-padding: 0 5px 0 0" text="姓名" />
                 <TextField fx:id="nameField" prefHeight="40" prefWidth="200" />
                 <TextField fx:id="nameField" prefHeight="40" prefWidth="200" />
             </children>
             </children>
         </HBox>
         </HBox>
         <HBox layoutX="10" layoutY="130" styleClass="field-control">
         <HBox layoutX="10" layoutY="130" styleClass="field-control">
             <children>
             <children>
-                <Label alignment="CENTER_RIGHT" prefHeight="40" prefWidth="100" style="-fx-padding: 0 5px 0 0" text="卡号" />
+                <Label alignment="CENTER_RIGHT" styleClass="field-label" prefHeight="40" prefWidth="100" style="-fx-padding: 0 5px 0 0" text="卡号" />
                 <TextField fx:id="cardNoField" prefHeight="40" prefWidth="200" />
                 <TextField fx:id="cardNoField" prefHeight="40" prefWidth="200" />
             </children>
             </children>
         </HBox>
         </HBox>
         <HBox layoutX="10" layoutY="180" styleClass="field-control">
         <HBox layoutX="10" layoutY="180" styleClass="field-control">
             <children>
             <children>
-                <Label alignment="CENTER_RIGHT" prefHeight="40" prefWidth="100" style="-fx-padding: 0 5px 0 0" text="性别" />
+                <Label alignment="CENTER_RIGHT" styleClass="field-label" prefHeight="40" prefWidth="100" style="-fx-padding: 0 5px 0 0" text="性别" />
                 <ComboBox fx:id="sexBox" prefHeight="40" prefWidth="200" />
                 <ComboBox fx:id="sexBox" prefHeight="40" prefWidth="200" />
             </children>
             </children>
         </HBox>
         </HBox>

+ 14 - 0
applications/device/device-client-biometric/src/main/resources/view/person_issue.fxml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import java.lang.*?>
+<?import java.util.*?>
+<?import javafx.scene.*?>
+<?import javafx.scene.control.*?>
+<?import javafx.scene.layout.*?>
+
+<AnchorPane xmlns="http://javafx.com/javafx"
+            xmlns:fx="http://javafx.com/fxml"
+            fx:controller="view.PersonIssue"
+            prefHeight="400.0" prefWidth="600.0">
+
+</AnchorPane>

+ 161 - 0
applications/device/device-core/src/main/java/com/usoftchina/smartschool/device/util/DateUtils.java

@@ -0,0 +1,161 @@
+package com.usoftchina.smartschool.device.util;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.*;
+import java.time.temporal.TemporalAdjusters;
+import java.time.temporal.TemporalField;
+import java.time.temporal.WeekFields;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Pattern;
+
+/**
+ * @author yingp
+ * @date 2019/9/11
+ */
+public class DateUtils {
+
+    private final static Map<String, DateFormat> formats = new ConcurrentHashMap<>();
+    private final static Pattern P_YMD = Pattern.compile("\\d{4}-\\d{2}-\\d{2}");
+    private final static Pattern P_YMD_HMS = Pattern.compile("\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}");
+    private final static Pattern P_YMD_HMS_TZ = Pattern.compile("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z");
+
+    private static DateFormat getFormat(String pattern, TimeZone timeZone) {
+        DateFormat format = formats.get(pattern + "_" + timeZone.getID());
+        if (null == format) {
+            format = new SimpleDateFormat(pattern);
+            format.setTimeZone(timeZone);
+            formats.put(pattern + "_" + timeZone.getID(), format);
+        }
+        return format;
+    }
+
+    public static String format(Date date, String pattern) {
+        return getFormat(pattern, TimeZone.getDefault()).format(date);
+    }
+
+    public static String format(Date date) {
+        return format(date, "yyyy-MM-dd");
+    }
+
+    public static String formatDateTime(Date date) {
+        return format(date, "yyyy-MM-dd HH:mm:ss");
+    }
+
+    public static Date parse(String date) {
+        try {
+            if (P_YMD.matcher(date).matches()) {
+                return getFormat("yyyy-MM-dd", TimeZone.getDefault()).parse(date);
+            } else if (P_YMD_HMS.matcher(date).matches()) {
+                return getFormat("yyyy-MM-dd HH:mm:ss", TimeZone.getDefault()).parse(date);
+            } else if (P_YMD_HMS_TZ.matcher(date).matches()) {
+                return getFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", TimeZone.getTimeZone("UTC")).parse(date);
+            }
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    public static LocalDate toLocal(Date date) {
+        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
+    }
+
+    /**
+     * 今天日期开始(00:00:00)
+     *
+     * @return
+     */
+    public static Date nowDateStart() {
+        return Date.from(LocalDate.now().atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
+    }
+
+    /**
+     * 今天日期截止(23:59:59)
+     *
+     * @return
+     */
+    public static Date nowDateEnd() {
+        return Date.from(LocalDateTime.of(LocalDate.now(), LocalTime.MAX)
+                .atZone(ZoneId.systemDefault()).toInstant());
+    }
+
+    /**
+     * 今天(含时间)
+     *
+     * @return
+     */
+    public static Date nowDateTime() {
+        return new Date();
+    }
+
+    /**
+     * 日期起始(00:00:00)
+     *
+     * @return
+     */
+    public static Date startOfDate(Date date) {
+        return Date.from(date.toInstant().atZone(ZoneId.systemDefault())
+                .toLocalDate().atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
+    }
+
+    /**
+     * 日期截止(23:59:59)
+     *
+     * @return
+     */
+    public static Date endOfDate(Date date) {
+        return Date.from(
+                LocalDateTime.of(date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(), LocalTime.MAX)
+                        .atZone(ZoneId.systemDefault()).toInstant());
+    }
+
+    /**
+     * 周一
+     *
+     * @return
+     */
+    public static Date nowWeekStart() {
+        LocalDate now = LocalDate.now();
+        TemporalField fieldISO = WeekFields.of(Locale.FRANCE).dayOfWeek();
+        return Date.from(now.with(fieldISO, 1).atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
+    }
+
+    /**
+     * 周天
+     *
+     * @return
+     */
+    public static Date nowWeekEnd() {
+        LocalDate now = LocalDate.now();
+        TemporalField fieldISO = WeekFields.of(Locale.FRANCE).dayOfWeek();
+        return Date.from(
+                LocalDateTime.of(now.with(fieldISO, 7), LocalTime.MAX)
+                        .atZone(ZoneId.systemDefault()).toInstant());
+    }
+
+    /**
+     * 月初
+     *
+     * @return
+     */
+    public static Date nowMonthStart() {
+        LocalDate date = LocalDate.now().with(TemporalAdjusters.firstDayOfMonth());
+        return Date.from(date.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
+    }
+
+    /**
+     * 月末
+     *
+     * @return
+     */
+    public static Date nowMonthEnd() {
+        LocalDate date = LocalDate.now().with(TemporalAdjusters.lastDayOfMonth());
+        return Date.from(LocalDateTime.of(date, LocalTime.MAX).atZone(ZoneId.systemDefault()).toInstant());
+    }
+}

+ 87 - 0
applications/device/device-core/src/main/java/com/usoftchina/smartschool/device/util/DirtyBean.java

@@ -0,0 +1,87 @@
+package com.usoftchina.smartschool.device.util;
+
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.SimpleBooleanProperty;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * @author yingp
+ * @date 2019/11/19
+ */
+public abstract class DirtyBean {
+
+    private boolean initialized = false;
+    private Map<String, DirtyProperty> modified;
+    private BooleanProperty dirty;
+
+    protected void checkDirty(String property, Object oldValue, Object newValue) {
+        // 标记为init完成后,才开始记录dirty状态
+        if (!initialized) {
+            return;
+        }
+        if (null == modified) {
+            modified = new HashMap<>(1);
+        }
+        if (!modified.containsKey(property)) {
+            modified.put(property, new DirtyProperty(oldValue, newValue));
+        } else {
+            DirtyProperty dirtyProperty = modified.get(property);
+            if (Objects.equals(dirtyProperty.getOriginalValue(), newValue)) {
+                modified.remove(property);
+            } else {
+                dirtyProperty.setValue(newValue);
+            }
+        }
+        dirtyProperty().setValue(!modified.isEmpty());
+    }
+
+    public boolean isDirty() {
+        return dirtyProperty().get();
+    }
+
+    public BooleanProperty dirtyProperty() {
+        if (null == dirty) {
+            dirty = new SimpleBooleanProperty(false);
+        }
+        return dirty;
+    }
+
+    public Map<String, DirtyProperty> getModified() {
+        return modified;
+    }
+
+    public void ready() {
+        initialized = true;
+        modified = null;
+        dirtyProperty().setValue(false);
+    }
+
+    public class DirtyProperty {
+        private Object originalValue;
+        private Object value;
+
+        public DirtyProperty(Object originalValue, Object value) {
+            this.originalValue = originalValue;
+            this.value = value;
+        }
+
+        public Object getOriginalValue() {
+            return originalValue;
+        }
+
+        public void setOriginalValue(Object originalValue) {
+            this.originalValue = originalValue;
+        }
+
+        public Object getValue() {
+            return value;
+        }
+
+        public void setValue(Object value) {
+            this.value = value;
+        }
+    }
+}

+ 9 - 0
applications/device/device-sdk-biometric/pom.xml

@@ -33,5 +33,14 @@
             <groupId>org.slf4j</groupId>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
             <artifactId>slf4j-api</artifactId>
         </dependency>
         </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
     </dependencies>
     </dependencies>
 </project>
 </project>

+ 3 - 1
applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/AccessPo.java → applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/AccessLog.java

@@ -3,10 +3,12 @@ package com.usoftchina.smartschool.device.biometric;
 import java.util.Date;
 import java.util.Date;
 
 
 /**
 /**
+ * 过人记录
+ *
  * @author yingp
  * @author yingp
  * @date 2019/11/14
  * @date 2019/11/14
  */
  */
-public class AccessPo {
+public class AccessLog {
     /**
     /**
      * 设备代码
      * 设备代码
      */
      */

+ 219 - 19
applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/BiometricDeviceService.java

@@ -1,15 +1,25 @@
 package com.usoftchina.smartschool.device.biometric;
 package com.usoftchina.smartschool.device.biometric;
 
 
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.TypeReference;
 import com.usoftchina.smartschool.device.api.DeviceApi;
 import com.usoftchina.smartschool.device.api.DeviceApi;
 import com.usoftchina.smartschool.device.dto.DeviceInfo;
 import com.usoftchina.smartschool.device.dto.DeviceInfo;
+import com.usoftchina.smartschool.device.exception.ExceptionCode;
+import com.usoftchina.smartschool.device.util.DateUtils;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Value;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
+import org.springframework.util.ObjectUtils;
+import org.springframework.util.StringUtils;
+import org.springframework.web.client.RestTemplate;
 
 
-import java.net.DatagramPacket;
-import java.net.DatagramSocket;
-import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.List;
 
 
 /**
 /**
  * @author yingp
  * @author yingp
@@ -20,27 +30,217 @@ public class BiometricDeviceService implements DeviceApi {
 
 
     private final Logger logger = LoggerFactory.getLogger(BiometricDeviceService.class);
     private final Logger logger = LoggerFactory.getLogger(BiometricDeviceService.class);
 
 
-    @Value("${server.port:8080}")
-    private Integer serverPort;
+    @Autowired
+    private RestTemplate restTemplate;
 
 
     @Override
     @Override
     public void add(DeviceInfo info) {
     public void add(DeviceInfo info) {
-        // 广播 服务端IP:端口,让设备拿到服务端的http接口地址
-        try {
-            String serverIp = InetAddress.getLocalHost().getHostAddress();
-            DatagramSocket socket = new DatagramSocket();
-            byte[] message = (serverIp + ":" + serverPort).getBytes("utf-8");
-            DatagramPacket packet = new DatagramPacket(message, message.length,
-                    InetAddress.getByName("255.255.255.255"), 28888);
-            logger.debug("broadcast " + serverIp + ":" + serverPort);
-            socket.send(packet);
-            socket.close();
-        } catch (Exception e) {
-            logger.error("broadcast to " + info.getIp() + " error", e);
-        }
     }
     }
 
 
     @Override
     @Override
     public void remove(DeviceInfo info) {
     public void remove(DeviceInfo info) {
     }
     }
+
+    /**
+     * 清除设备数据
+     *
+     * @param deviceInfo
+     */
+    public void clearData(DeviceInfo deviceInfo) {
+//        String url = String.format("http://%s:%s/DeviceInterface/ClearDeviceData",
+//                deviceInfo.getIp(), deviceInfo.getPort());
+//        personPo.setDelFlag(0);
+//        ResponseEntity<String> response = restTemplate.postForEntity(
+//                url, personPo, String.class);
+//        if (response.getStatusCode() == HttpStatus.OK) {
+//            ResultPo result = JSON.parseObject(response.getBody(), ResultPo.class);
+//            if (!result.wasSuccess()) {
+//                ExceptionCode.ERROR_UNKNOWN.occur(result.getMessage());
+//            }
+//        } else {
+//            ExceptionCode.ERROR_UNKNOWN.occur("下发失败");
+//        }
+    }
+
+    public Object issue(DeviceInfo deviceInfo, PersonPo personPo) {
+        String url = String.format("http://%s:%s/DeviceInterface/UpdatePersonInfo",
+                deviceInfo.getIp(), deviceInfo.getPort());
+        personPo.setDelFlag(0);
+        ResponseEntity<String> response = restTemplate.postForEntity(
+                url, personPo, String.class);
+        if (response.getStatusCode() == HttpStatus.OK) {
+            ResultPo result = JSON.parseObject(response.getBody(), ResultPo.class);
+            if (!result.wasSuccess()) {
+                ExceptionCode.ERROR_UNKNOWN.occur(result.getMessage());
+            }
+        } else {
+            ExceptionCode.ERROR_UNKNOWN.occur("下发失败");
+        }
+        return null;
+    }
+
+    public void cancelIssue(DeviceInfo deviceInfo, Integer issueId) {
+        String url = String.format("http://%s:%s/DeviceInterface/UpdatePersonInfo",
+                deviceInfo.getIp(), deviceInfo.getPort());
+        PersonPo personPo = new PersonPo();
+        personPo.setId(issueId);
+        personPo.setDelFlag(1);
+        ResponseEntity<String> response = restTemplate.postForEntity(
+                url, personPo, String.class);
+        if (response.getStatusCode() == HttpStatus.OK) {
+            ResultPo result = JSON.parseObject(response.getBody(), ResultPo.class);
+            if (!result.wasSuccess()) {
+                ExceptionCode.ERROR_UNKNOWN.occur(result.getMessage());
+            }
+        } else {
+            ExceptionCode.ERROR_UNKNOWN.occur("取消下发失败");
+        }
+    }
+
+    /**
+     * 读取参数设置
+     *
+     * @param deviceInfo
+     * @return
+     */
+    public DeviceSetting getSetting(DeviceInfo deviceInfo) {
+        String url = String.format("http://%s:%s/DeviceInterface/GetParameterValues",
+                deviceInfo.getIp(), deviceInfo.getPort());
+        List<String> items = new ArrayList<>();
+        items.add("DeviceInfo.SoftwareVersion");
+        items.add("DeviceInfo.SystemVersion");
+        items.add("DeviceInfo.DeviceCode");
+        items.add("DeviceInfo.IpAddress");
+        items.add("DeviceInfo.mask");
+        items.add("DeviceInfo.Gateway");
+        items.add("DeviceInfo.DNS");
+        items.add("DeviceInfo.AuthorizeDate");
+        items.add("DeviceInfo.InformInterval");
+        items.add("CompareOption.FaceLevel");
+        items.add("CompareOption.LivingLevel");
+        items.add("CompareOption.FaceSize");
+        items.add("ServerInfo.DataServiceUrl");
+        items.add("ServerInfo.DeviceServiceUrl");
+        items.add("DeviceInfo.capacity");
+        ResponseEntity<String> response = restTemplate.postForEntity(
+                url, new ReqListPo<>(items), String.class);
+        if (response.getStatusCode() == HttpStatus.OK) {
+            ResultListPo<Setting> result = JSON.parseObject(response.getBody(),
+                    new TypeReference<ResultListPo<Setting>>() {
+                    });
+            if (!result.isSuccess()) {
+                ExceptionCode.ERROR_UNKNOWN.occur(result.getMessage());
+            } else {
+                DeviceSetting deviceSetting = new DeviceSetting();
+                if (null != result.getLstParameter()) {
+                    for (Setting setting : result.getLstParameter()) {
+                        switch (setting.getName()) {
+                            case "DeviceInfo.SoftwareVersion":
+                                deviceSetting.setSoftwareVersion((String) setting.getValue());
+                                break;
+                            case "DeviceInfo.SystemVersion":
+                                deviceSetting.setSystemVersion((String) setting.getValue());
+                                break;
+                            case "DeviceInfo.DeviceCode":
+                                deviceSetting.setDeviceCode((String) setting.getValue());
+                                break;
+                            case "DeviceInfo.IpAddress":
+                                deviceSetting.setIpAddress((String) setting.getValue());
+                                break;
+                            case "DeviceInfo.mask":
+                                deviceSetting.setMask((String) setting.getValue());
+                                break;
+                            case "DeviceInfo.Gateway":
+                                deviceSetting.setGateway((String) setting.getValue());
+                                break;
+                            case "DeviceInfo.DNS":
+                                deviceSetting.setDns((String) setting.getValue());
+                                break;
+                            case "DeviceInfo.AuthorizeDate":
+                                String date = (String) setting.getValue();
+                                if (!StringUtils.isEmpty(date)) {
+                                    deviceSetting.setAuthorizeDate(DateUtils.toLocal(DateUtils.parse(date)));
+                                }
+                                break;
+                            case "DeviceInfo.InformInterval":
+                                if (!ObjectUtils.isEmpty(setting.getValue())) {
+                                    deviceSetting.setInformInterval(Integer.parseInt(setting.getValue().toString()));
+                                }
+                                break;
+                            case "CompareOption.FaceLevel":
+                                deviceSetting.setFaceLevel((String) setting.getValue());
+                                break;
+                            case "CompareOption.LivingLevel":
+                                deviceSetting.setLivingLevel((String) setting.getValue());
+                                break;
+                            case "CompareOption.FaceSize":
+                                if (!ObjectUtils.isEmpty(setting.getValue())) {
+                                    deviceSetting.setFaceSize(Integer.parseInt(setting.getValue().toString()));
+                                }
+                                break;
+                            case "ServerInfo.DataServiceUrl":
+                                deviceSetting.setDataServiceUrl((String) setting.getValue());
+                                break;
+                            case "ServerInfo.DeviceServiceUrl":
+                                deviceSetting.setDeviceServiceUrl((String) setting.getValue());
+                                break;
+                            case "DeviceInfo.capacity":
+                                if (!ObjectUtils.isEmpty(setting.getValue())) {
+                                    deviceSetting.setCapacity(Integer.parseInt(setting.getValue().toString()));
+                                }
+                                break;
+                            default:
+                                break;
+                        }
+                    }
+                }
+                deviceSetting.ready();
+                return deviceSetting;
+            }
+        } else {
+            ExceptionCode.ERROR_UNKNOWN.occur("查询失败");
+        }
+        return null;
+    }
+
+    /**
+     * 保存参数设置
+     *
+     * @param deviceInfo
+     * @param deviceSetting
+     */
+    public void saveSetting(DeviceInfo deviceInfo, DeviceSetting deviceSetting) {
+        String url = String.format("http://%s:%s/DeviceInterface/SetParameterValues",
+                deviceInfo.getIp(), deviceInfo.getPort());
+        List<Setting> settings = new ArrayList<>();
+        deviceSetting.getModified().forEach((p, v) -> settings.add(new Setting(p, v.getValue())));
+        ResponseEntity<String> response = restTemplate.postForEntity(
+                url, new ReqListPo<>(settings), String.class);
+        if (response.getStatusCode() == HttpStatus.OK) {
+            ResultPo result = JSON.parseObject(response.getBody(), ResultPo.class);
+            if (!result.wasSuccess()) {
+                ExceptionCode.ERROR_UNKNOWN.occur(result.getMessage());
+            }
+        } else {
+            ExceptionCode.ERROR_UNKNOWN.occur("查询失败");
+        }
+    }
+
+    /**
+     * PING检测设备
+     *
+     * @param deviceInfo
+     * @return
+     */
+    public boolean ping(DeviceInfo deviceInfo) {
+        boolean isReachable = false;
+        try (Socket connect = new Socket()) {
+            InetSocketAddress endpointSocketAddr = new InetSocketAddress(deviceInfo.getIp(), deviceInfo.getPort());
+            connect.connect(endpointSocketAddr, 3000);
+            isReachable = connect.isConnected();
+        } catch (Exception e) {
+            logger.warn("connect error", e);
+        }
+        return isReachable;
+    }
 }
 }

+ 17 - 2
applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/DeviceManageController.java

@@ -1,9 +1,16 @@
 package com.usoftchina.smartschool.device.biometric;
 package com.usoftchina.smartschool.device.biometric;
 
 
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.TypeReference;
+import com.usoftchina.smartschool.device.context.SpringContextHolder;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.bind.annotation.RestController;
 
 
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.time.LocalDateTime;
+
 /**
 /**
  * @author yingp
  * @author yingp
  * @date 2019/11/14
  * @date 2019/11/14
@@ -17,10 +24,18 @@ public class DeviceManageController {
      *
      *
      * @param request
      * @param request
      * @return
      * @return
+     * @Deprecated 设备上传的数据有问题,不使用该接口
      */
      */
     @PostMapping("/ACSServer")
     @PostMapping("/ACSServer")
-    public ResultPo deviceHeart(RequestPo<HealthInfo> request) {
-        System.out.println(request);
+    @Deprecated
+    public ResultPo deviceHeart(HttpServletRequest request) throws IOException {
+        System.out.println(LocalDateTime.now() + " ACSServer " + request.getReader().readLine());
+        String body = request.getReader().readLine();
+        if (null != body && body.startsWith("{")) {
+            RequestPo<HealthInfo> req = JSON.parseObject(body, new TypeReference<RequestPo<HealthInfo>>() {
+            });
+            SpringContextHolder.getContext().publishEvent(new HeartbeatEvent(req.getResponseBody().getDeviceCode(), this));
+        }
         return ResultPo.success().setHoldRequests("0");
         return ResultPo.success().setHoldRequests("0");
     }
     }
 }
 }

+ 384 - 0
applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/DeviceSetting.java

@@ -0,0 +1,384 @@
+package com.usoftchina.smartschool.device.biometric;
+
+import com.usoftchina.smartschool.device.util.DirtyBean;
+import javafx.beans.property.*;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+
+import java.io.Serializable;
+import java.time.LocalDate;
+
+/**
+ * @author yingp
+ * @date 2019/11/19
+ */
+public class DeviceSetting extends DirtyBean implements Serializable {
+    /**
+     * 软件版本
+     */
+    private StringProperty softwareVersion;
+    /**
+     * 固件版本
+     */
+    private StringProperty systemVersion;
+    /**
+     * 设备编号
+     */
+    private StringProperty deviceCode;
+    private StringProperty ipAddress;
+    /**
+     * 子网掩码
+     */
+    private StringProperty mask;
+    /**
+     * 网关
+     */
+    private StringProperty gateway;
+    /**
+     * DNS
+     */
+    private StringProperty dns;
+    /**
+     * 授权截止日期
+     */
+    private ObjectProperty<LocalDate> authorizeDate;
+    /**
+     * 心跳间隔
+     */
+    private IntegerProperty informInterval;
+    /**
+     * 人脸阈值级别
+     */
+    private ObjectProperty<FaceLevel> faceLevel;
+    /**
+     * 活体阈值级别
+     */
+    private ObjectProperty<LivingLevel> livingLevel;
+    /**
+     * 人脸框大小(像素)
+     */
+    private IntegerProperty faceSize;
+    /**
+     * 数据上传地址
+     */
+    private StringProperty dataServiceUrl;
+    /**
+     * 心跳地址
+     */
+    private StringProperty deviceServiceUrl;
+    /**
+     * 已入库人像容量
+     */
+    private IntegerProperty capacity;
+
+    public String getSoftwareVersion() {
+        return softwareVersionProperty().get();
+    }
+
+    public StringProperty softwareVersionProperty() {
+        if (null == softwareVersion) {
+            softwareVersion = new SimpleStringProperty();
+        }
+        return softwareVersion;
+    }
+
+    public void setSoftwareVersion(String softwareVersion) {
+        this.softwareVersionProperty().set(softwareVersion);
+    }
+
+    public String getSystemVersion() {
+        return systemVersionProperty().get();
+    }
+
+    public StringProperty systemVersionProperty() {
+        if (null == systemVersion) {
+            systemVersion = new SimpleStringProperty();
+        }
+        return systemVersion;
+    }
+
+    public void setSystemVersion(String systemVersion) {
+        this.systemVersionProperty().set(systemVersion);
+    }
+
+    public String getDeviceCode() {
+        return deviceCodeProperty().get();
+    }
+
+    public StringProperty deviceCodeProperty() {
+        if (null == deviceCode) {
+            deviceCode = new SimpleStringProperty();
+        }
+        return deviceCode;
+    }
+
+    public void setDeviceCode(String deviceCode) {
+        this.deviceCodeProperty().set(deviceCode);
+    }
+
+    public String getIpAddress() {
+        return ipAddressProperty().get();
+    }
+
+    public StringProperty ipAddressProperty() {
+        if (null == ipAddress) {
+            ipAddress = new SimpleStringProperty();
+            ipAddress.addListener(new ChangeListener<String>() {
+                @Override
+                public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
+                    checkDirty("DeviceInfo.IpAddress", oldValue, newValue);
+                }
+            });
+        }
+        return ipAddress;
+    }
+
+    public void setIpAddress(String ipAddress) {
+        this.ipAddressProperty().set(ipAddress);
+    }
+
+    public String getMask() {
+        return maskProperty().get();
+    }
+
+    public StringProperty maskProperty() {
+        if (null == mask) {
+            mask = new SimpleStringProperty();
+            mask.addListener(new ChangeListener<String>() {
+                @Override
+                public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
+                    checkDirty("DeviceInfo.mask", oldValue, newValue);
+                }
+            });
+        }
+        return mask;
+    }
+
+    public void setMask(String mask) {
+        this.maskProperty().set(mask);
+    }
+
+    public String getGateway() {
+        return gatewayProperty().get();
+    }
+
+    public StringProperty gatewayProperty() {
+        if (null == gateway) {
+            gateway = new SimpleStringProperty();
+            gateway.addListener(new ChangeListener<String>() {
+                @Override
+                public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
+                    checkDirty("DeviceInfo.Gateway", oldValue, newValue);
+                }
+            });
+        }
+        return gateway;
+    }
+
+    public void setGateway(String gateway) {
+        this.gatewayProperty().set(gateway);
+    }
+
+    public String getDns() {
+        return dnsProperty().get();
+    }
+
+    public StringProperty dnsProperty() {
+        if (null == dns) {
+            dns = new SimpleStringProperty();
+            dns.addListener(new ChangeListener<String>() {
+                @Override
+                public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
+                    checkDirty("DeviceInfo.DNS", oldValue, newValue);
+                }
+            });
+        }
+        return dns;
+    }
+
+    public void setDns(String dns) {
+        this.dnsProperty().set(dns);
+    }
+
+    public LocalDate getAuthorizeDate() {
+        return authorizeDateProperty().get();
+    }
+
+    public ObjectProperty<LocalDate> authorizeDateProperty() {
+        if (null == authorizeDate) {
+            authorizeDate = new SimpleObjectProperty<>();
+            authorizeDate.addListener(new ChangeListener<LocalDate>() {
+                @Override
+                public void changed(ObservableValue<? extends LocalDate> observable, LocalDate oldValue, LocalDate newValue) {
+                    checkDirty("DeviceInfo.AuthorizeDate",
+                            (null == oldValue ? null : oldValue.toString()),
+                            (null == newValue ? null : newValue.toString()));
+                }
+            });
+        }
+        return authorizeDate;
+    }
+
+    public void setAuthorizeDate(LocalDate authorizeDate) {
+        this.authorizeDateProperty().set(authorizeDate);
+    }
+
+    public int getInformInterval() {
+        return informIntervalProperty().get();
+    }
+
+    public IntegerProperty informIntervalProperty() {
+        if (null == informInterval) {
+            informInterval = new SimpleIntegerProperty();
+            informInterval.addListener(new ChangeListener<Number>() {
+                @Override
+                public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
+                    checkDirty("DeviceInfo.InformInterval", oldValue, newValue);
+                }
+            });
+        }
+        return informInterval;
+    }
+
+    public void setInformInterval(int informInterval) {
+        this.informIntervalProperty().set(informInterval);
+    }
+
+    public String getFaceLevel() {
+        FaceLevel faceLevel = faceLevelProperty().get();
+        return null == faceLevel ? null : faceLevel.getValue();
+    }
+
+    public ObjectProperty<FaceLevel> faceLevelProperty() {
+        if (null == faceLevel) {
+            faceLevel = new SimpleObjectProperty<>();
+            faceLevel.addListener(new ChangeListener<FaceLevel>() {
+                @Override
+                public void changed(ObservableValue<? extends FaceLevel> observable, FaceLevel oldValue, FaceLevel newValue) {
+                    checkDirty("CompareOption.FaceLevel",
+                            (null == oldValue ? null : oldValue.getValue()),
+                            (null == newValue ? null : newValue.getValue()));
+                }
+            });
+        }
+        return faceLevel;
+    }
+
+    public void setFaceLevel(FaceLevel faceLevel) {
+        this.faceLevelProperty().set(faceLevel);
+    }
+
+    public void setFaceLevel(String faceLevel) {
+        if (null != faceLevel) {
+            faceLevelProperty().setValue(FaceLevel.of(faceLevel));
+        }
+    }
+
+    public String getLivingLevel() {
+        LivingLevel livingLevel = livingLevelProperty().get();
+        return null == livingLevel ? null : livingLevel.getValue();
+    }
+
+    public ObjectProperty<LivingLevel> livingLevelProperty() {
+        if (null == livingLevel) {
+            livingLevel = new SimpleObjectProperty<>();
+            livingLevel.addListener(new ChangeListener<LivingLevel>() {
+                @Override
+                public void changed(ObservableValue<? extends LivingLevel> observable, LivingLevel oldValue, LivingLevel newValue) {
+                    checkDirty("CompareOption.LivingLevel",
+                            (null == oldValue ? null : oldValue.getValue()),
+                            (null == newValue ? null : newValue.getValue()));
+                }
+            });
+        }
+        return livingLevel;
+    }
+
+    public void setLivingLevel(LivingLevel livingLevel) {
+        this.livingLevelProperty().set(livingLevel);
+    }
+
+    public void setLivingLevel(String livingLevel) {
+        if (null != livingLevel) {
+            this.livingLevelProperty().set(LivingLevel.of(livingLevel));
+        }
+    }
+
+    public int getFaceSize() {
+        return faceSizeProperty().get();
+    }
+
+    public IntegerProperty faceSizeProperty() {
+        if (null == faceSize) {
+            faceSize = new SimpleIntegerProperty();
+            faceSize.addListener(new ChangeListener<Number>() {
+                @Override
+                public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
+                    checkDirty("CompareOption.FaceSize", oldValue, newValue);
+                }
+            });
+        }
+        return faceSize;
+    }
+
+    public void setFaceSize(int faceSize) {
+        this.faceSizeProperty().set(faceSize);
+    }
+
+    public String getDataServiceUrl() {
+        return dataServiceUrlProperty().get();
+    }
+
+    public StringProperty dataServiceUrlProperty() {
+        if (null == dataServiceUrl) {
+            dataServiceUrl = new SimpleStringProperty();
+            dataServiceUrl.addListener(new ChangeListener<String>() {
+                @Override
+                public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
+                    checkDirty("ServerInfo.DataServiceUrl", oldValue, newValue);
+                }
+            });
+        }
+        return dataServiceUrl;
+    }
+
+    public void setDataServiceUrl(String dataServiceUrl) {
+        this.dataServiceUrlProperty().set(dataServiceUrl);
+    }
+
+    public String getDeviceServiceUrl() {
+        return deviceServiceUrlProperty().get();
+    }
+
+    public StringProperty deviceServiceUrlProperty() {
+        if (null == deviceServiceUrl) {
+            deviceServiceUrl = new SimpleStringProperty();
+            deviceServiceUrl.addListener(new ChangeListener<String>() {
+                @Override
+                public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
+                    checkDirty("ServerInfo.DeviceServiceUrl", oldValue, newValue);
+                }
+            });
+        }
+        return deviceServiceUrl;
+    }
+
+    public void setDeviceServiceUrl(String deviceServiceUrl) {
+        this.deviceServiceUrlProperty().set(deviceServiceUrl);
+    }
+
+    public int getCapacity() {
+        return capacityProperty().get();
+    }
+
+    public IntegerProperty capacityProperty() {
+        if (null == capacity) {
+            capacity = new SimpleIntegerProperty();
+        }
+        return capacity;
+    }
+
+    public void setCapacity(int capacity) {
+        this.capacityProperty().set(capacity);
+    }
+}

+ 44 - 0
applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/FaceLevel.java

@@ -0,0 +1,44 @@
+package com.usoftchina.smartschool.device.biometric;
+
+/**
+ * 人脸阈值级别
+ * 0 宽松 1 正常 2 严格
+ *
+ * @author yingp
+ * @date 2019/11/19
+ */
+public enum FaceLevel {
+    LEVEL_0("0", "宽松"),
+    LEVEL_1("1", "正常"),
+    LEVEL_2("2", "严格");
+
+    private final String value;
+    private final String text;
+
+    FaceLevel(String value, String text) {
+        this.value = value;
+        this.text = text;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public String getText() {
+        return text;
+    }
+
+    public static FaceLevel of(String value) {
+        for (FaceLevel faceLevel : values()) {
+            if (faceLevel.getValue().equals(value)) {
+                return faceLevel;
+            }
+        }
+        return LEVEL_0;
+    }
+
+    @Override
+    public String toString() {
+        return text;
+    }
+}

+ 2 - 0
applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/HealthInfo.java

@@ -1,6 +1,8 @@
 package com.usoftchina.smartschool.device.biometric;
 package com.usoftchina.smartschool.device.biometric;
 
 
 /**
 /**
+ * 设备心跳信息
+ *
  * @author yingp
  * @author yingp
  * @date 2019/11/14
  * @date 2019/11/14
  */
  */

+ 20 - 0
applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/HeartbeatEvent.java

@@ -0,0 +1,20 @@
+package com.usoftchina.smartschool.device.biometric;
+
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * @author yingp
+ * @date 2019/11/19
+ */
+public class HeartbeatEvent extends ApplicationEvent {
+    private final String deviceCode;
+
+    public HeartbeatEvent(String deviceCode, Object source) {
+        super(source);
+        this.deviceCode = deviceCode;
+    }
+
+    public String getDeviceCode() {
+        return deviceCode;
+    }
+}

+ 43 - 0
applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/LivingLevel.java

@@ -0,0 +1,43 @@
+package com.usoftchina.smartschool.device.biometric;
+
+/**
+ * 活体阈值级别
+ * 0 宽松 2 严格
+ *
+ * @author yingp
+ * @date 2019/11/19
+ */
+public enum LivingLevel {
+    LEVEL_0("0", "宽松"),
+    LEVEL_2("2", "严格");
+
+    private final String value;
+    private final String text;
+
+    LivingLevel(String value, String text) {
+        this.value = value;
+        this.text = text;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public String getText() {
+        return text;
+    }
+
+    public static LivingLevel of(String value) {
+        for (LivingLevel livingLevel : values()) {
+            if (livingLevel.getValue().equals(value)) {
+                return livingLevel;
+            }
+        }
+        return LEVEL_0;
+    }
+
+    @Override
+    public String toString() {
+        return text;
+    }
+}

+ 139 - 0
applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/PersonPo.java

@@ -0,0 +1,139 @@
+package com.usoftchina.smartschool.device.biometric;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 人员信息
+ *
+ * @author yingp
+ * @date 2019/11/18
+ */
+public class PersonPo implements Serializable {
+    private Integer id;
+    /**
+     * 数据类型
+     * -1:负样本
+     * 0:员工
+     * 1:访客
+     * 2:自定义人员类型 1
+     * 3:自定义人员类型 2
+     * 8:白名单
+     * 9:黑名单
+     */
+    private Integer dataType;
+    /**
+     * 操作标识
+     * 0:新增/修改 1:删除 (id 不存在视为新增,已存 在视为修改)
+     */
+    private Integer delFlag;
+    /**
+     * 照片
+     * JPG 图片 base64
+     */
+    private String picture;
+    /**
+     * 有效截止时间
+     */
+    private Date endTime;
+    /**
+     * 姓名
+     */
+    private String name;
+    /**
+     * 手机号
+     */
+    private String phone;
+    /**
+     * 性别编码
+     */
+    private String genderCode;
+    /**
+     * 民族编码
+     */
+    private String nationCode;
+    /**
+     * 门禁卡号
+     */
+    private String accessCardNum;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Integer getDataType() {
+        return dataType;
+    }
+
+    public void setDataType(Integer dataType) {
+        this.dataType = dataType;
+    }
+
+    public Integer getDelFlag() {
+        return delFlag;
+    }
+
+    public void setDelFlag(Integer delFlag) {
+        this.delFlag = delFlag;
+    }
+
+    public String getPicture() {
+        return picture;
+    }
+
+    public void setPicture(String picture) {
+        this.picture = picture;
+    }
+
+    public Date getEndTime() {
+        return endTime;
+    }
+
+    public void setEndTime(Date endTime) {
+        this.endTime = endTime;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+
+    public String getGenderCode() {
+        return genderCode;
+    }
+
+    public void setGenderCode(String genderCode) {
+        this.genderCode = genderCode;
+    }
+
+    public String getNationCode() {
+        return nationCode;
+    }
+
+    public void setNationCode(String nationCode) {
+        this.nationCode = nationCode;
+    }
+
+    public String getAccessCardNum() {
+        return accessCardNum;
+    }
+
+    public void setAccessCardNum(String accessCardNum) {
+        this.accessCardNum = accessCardNum;
+    }
+}

+ 23 - 0
applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/ReqListPo.java

@@ -0,0 +1,23 @@
+package com.usoftchina.smartschool.device.biometric;
+
+import java.util.List;
+
+/**
+ * @author yingp
+ * @date 2019/11/19
+ */
+public class ReqListPo<T> {
+    private List<T> lstParameter;
+
+    public ReqListPo(List<T> lstParameter) {
+        this.lstParameter = lstParameter;
+    }
+
+    public List<T> getLstParameter() {
+        return lstParameter;
+    }
+
+    public void setLstParameter(List<T> lstParameter) {
+        this.lstParameter = lstParameter;
+    }
+}

+ 51 - 0
applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/ResultListPo.java

@@ -0,0 +1,51 @@
+package com.usoftchina.smartschool.device.biometric;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @author yingp
+ * @date 2019/11/19
+ */
+public class ResultListPo<T> implements Serializable {
+    /**
+     * 返回状态
+     */
+    private String status;
+    /**
+     * 返回信息
+     */
+    private String message;
+
+    private List<T> lstParameter;
+
+    public String getStatus() {
+        return status;
+    }
+
+    public ResultListPo setStatus(String status) {
+        this.status = status;
+        return this;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public ResultListPo setMessage(String message) {
+        this.message = message;
+        return this;
+    }
+
+    public List<T> getLstParameter() {
+        return lstParameter;
+    }
+
+    public void setLstParameter(List<T> lstParameter) {
+        this.lstParameter = lstParameter;
+    }
+
+    public boolean isSuccess() {
+        return "0".equals(status);
+    }
+}

+ 7 - 1
applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/ResultPo.java

@@ -1,10 +1,12 @@
 package com.usoftchina.smartschool.device.biometric;
 package com.usoftchina.smartschool.device.biometric;
 
 
+import java.io.Serializable;
+
 /**
 /**
  * @author yingp
  * @author yingp
  * @date 2019/11/14
  * @date 2019/11/14
  */
  */
-public class ResultPo {
+public class ResultPo implements Serializable {
     /**
     /**
      * 返回状态
      * 返回状态
      */
      */
@@ -62,4 +64,8 @@ public class ResultPo {
         this.holdRequests = holdRequests;
         this.holdRequests = holdRequests;
         return this;
         return this;
     }
     }
+
+    public boolean wasSuccess() {
+        return "0".equals(status);
+    }
 }
 }

+ 34 - 0
applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/Setting.java

@@ -0,0 +1,34 @@
+package com.usoftchina.smartschool.device.biometric;
+
+/**
+ * @author yingp
+ * @date 2019/11/19
+ */
+public class Setting {
+    private String name;
+    private Object value;
+
+    public Setting() {
+    }
+
+    public Setting(String name, Object value) {
+        this.name = name;
+        this.value = value;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Object getValue() {
+        return value;
+    }
+
+    public void setValue(Object value) {
+        this.value = value;
+    }
+}

+ 6 - 2
applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/VerificationController.java

@@ -4,6 +4,10 @@ import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.bind.annotation.RestController;
 
 
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.time.LocalDateTime;
+
 /**
 /**
  * @author yingp
  * @author yingp
  * @date 2019/11/14
  * @date 2019/11/14
@@ -18,8 +22,8 @@ public class VerificationController {
      * @return
      * @return
      */
      */
     @PostMapping("/passlog/passFullLog")
     @PostMapping("/passlog/passFullLog")
-    public ResultPo passLog(AccessPo access) {
-        System.out.println(access);
+    public ResultPo passLog(HttpServletRequest request) throws IOException {
+        System.out.println(LocalDateTime.now() + " passFullLog " + request.getReader().readLine());
         return ResultPo.success();
         return ResultPo.success();
     }
     }
 }
 }

+ 6 - 2
applications/device/device-sdk/src/main/java/com/usoftchina/smartschool/device/api/DeviceApi.java

@@ -15,12 +15,16 @@ public interface DeviceApi {
      *
      *
      * @param info
      * @param info
      */
      */
-    void add(DeviceInfo info);
+    default void add(DeviceInfo info) {
+        throw new RuntimeException("Please override method add");
+    }
 
 
     /**
     /**
      * 删除设备
      * 删除设备
      *
      *
      * @param info
      * @param info
      */
      */
-    void remove(DeviceInfo info);
+    default void remove(DeviceInfo info) {
+        throw new RuntimeException("Please override method remove");
+    }
 }
 }