Browse Source

客户端开发

yingp 5 years ago
parent
commit
d6dca764cf
69 changed files with 2401 additions and 363 deletions
  1. 1 1
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/DeviceClient.java
  2. 15 5
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/config/DeviceServerProperties.java
  3. 40 3
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/control/IconFont.java
  4. 32 40
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/control/IntegerField.java
  5. 107 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/control/NumberField.java
  6. 76 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/CloudSettingController.java
  7. 41 5
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/GateController.java
  8. 2 2
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/GateIssueController.java
  9. 2 5
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/GateIssueStatusController.java
  10. 12 2
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/GateSettingController.java
  11. 104 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/IcCardController.java
  12. 207 7
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/PersonController.java
  13. 32 4
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/PersonEditController.java
  14. 146 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/PersonIssueStatusController.java
  15. 84 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/SchoolController.java
  16. 73 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/jdbc/ReturnKeyStatementCallback.java
  17. 26 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/jdbc/ReturnKeyStatementCreator.java
  18. 53 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/listener/AccessControlListener.java
  19. 51 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/po/CloudSetting.java
  20. 4 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/po/FaceGate.java
  21. 6 18
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/po/GatePerson.java
  22. 15 24
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/po/GatePersonView.java
  23. 115 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/po/IcCard.java
  24. 12 12
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/po/Person.java
  25. 7 16
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/po/PersonGateView.java
  26. 39 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/po/PersonGroup.java
  27. 52 7
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/po/School.java
  28. 28 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/repository/BaseRepository.java
  29. 39 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/repository/CloudSettingRepository.java
  30. 32 11
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/repository/FaceGateRepository.java
  31. 16 20
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/repository/GatePersonRepository.java
  32. 44 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/repository/IcCardRepository.java
  33. 65 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/repository/PersonGroupRepository.java
  34. 24 14
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/repository/PersonRepository.java
  35. 8 7
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/repository/SchoolRepository.java
  36. 54 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/service/CloudService.java
  37. 45 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/service/CloudSettingService.java
  38. 3 35
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/service/FaceGateService.java
  39. 10 12
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/service/GatePersonService.java
  40. 38 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/service/IcCardService.java
  41. 53 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/service/PersonGroupService.java
  42. 5 6
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/service/PersonService.java
  43. 21 1
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/support/SplashScreen.java
  44. 4 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/task/GateScheduler.java
  45. 108 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/task/IcCardScheduler.java
  46. 12 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/util/AlertUtils.java
  47. 3 4
      applications/device/device-client-biometric/src/main/resources/application.yml
  48. 17 6
      applications/device/device-client-biometric/src/main/resources/schema.sql
  49. 29 0
      applications/device/device-client-biometric/src/main/resources/style/main.css
  50. 34 0
      applications/device/device-client-biometric/src/main/resources/view/cloud.fxml
  51. 1 3
      applications/device/device-client-biometric/src/main/resources/view/gate.fxml
  52. 5 9
      applications/device/device-client-biometric/src/main/resources/view/gate_issue_status.fxml
  53. 2 2
      applications/device/device-client-biometric/src/main/resources/view/gate_setting.fxml
  54. 53 0
      applications/device/device-client-biometric/src/main/resources/view/ic.fxml
  55. 6 6
      applications/device/device-client-biometric/src/main/resources/view/main.fxml
  56. 22 6
      applications/device/device-client-biometric/src/main/resources/view/person.fxml
  57. 2 2
      applications/device/device-client-biometric/src/main/resources/view/person_edit.fxml
  58. 0 14
      applications/device/device-client-biometric/src/main/resources/view/person_issue.fxml
  59. 31 0
      applications/device/device-client-biometric/src/main/resources/view/person_issue_status.fxml
  60. 40 0
      applications/device/device-client-biometric/src/main/resources/view/school.fxml
  61. 3 1
      applications/device/device-core/src/main/java/com/usoftchina/smartschool/device/exception/ExceptionCode.java
  62. 7 7
      applications/device/device-core/src/main/java/com/usoftchina/smartschool/device/property/DirtyBean.java
  63. 22 0
      applications/device/device-core/src/main/java/com/usoftchina/smartschool/device/property/DirtyObjectProperty.java
  64. 27 0
      applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/AccessControlEvent.java
  65. 113 33
      applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/AccessLog.java
  66. 0 1
      applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/BiometricDeviceService.java
  67. 4 1
      applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/DeviceManageController.java
  68. 10 10
      applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/DeviceSetting.java
  69. 7 1
      applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/VerificationController.java

+ 1 - 1
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/DeviceClient.java

@@ -22,7 +22,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
 public class DeviceClient extends AbstractJavaFxApplicationSupport {
 
     public static void main(String[] args) {
-        launch(DeviceClient.class, new SplashScreen(), args);
+        launch(DeviceClient.class, new SplashScreen("设备监听程序启动中"), args);
     }
 
     public static Image[] defaultIcons = new Image[]{new Image(ClientConstant.icon.toExternalForm())};

+ 15 - 5
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/config/DeviceServerProperties.java

@@ -13,13 +13,23 @@ public class DeviceServerProperties {
     /**
      * 门禁事件的服务端接口
      */
-    private String accessControlEvent;
+    private String accessRecordUrl;
 
-    public String getAccessControlEvent() {
-        return accessControlEvent;
+    private String icRecordUrl;
+
+    public String getAccessRecordUrl() {
+        return accessRecordUrl;
+    }
+
+    public void setAccessRecordUrl(String accessRecordUrl) {
+        this.accessRecordUrl = accessRecordUrl;
+    }
+
+    public String getIcRecordUrl() {
+        return icRecordUrl;
     }
 
-    public void setAccessControlEvent(String accessControlEvent) {
-        this.accessControlEvent = accessControlEvent;
+    public void setIcRecordUrl(String icRecordUrl) {
+        this.icRecordUrl = icRecordUrl;
     }
 }

+ 40 - 3
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/control/IconFont.java

@@ -1,8 +1,15 @@
 package com.usoftchina.smartschool.device.client.biometric.control;
 
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
 import javafx.scene.control.Label;
 import javafx.scene.text.Font;
 
+import java.util.HashMap;
+import java.util.Map;
+
 /**
  * @author yingp
  * @date 2019/11/20
@@ -10,14 +17,17 @@ import javafx.scene.text.Font;
 public class IconFont extends Label {
     private static Font FONT;
     private static final int defaultFontSize = 16;
+    private static Map<String, Character> unicodeMap;
 
     static {
         FONT = Font.loadFont(IconFont.class.getResourceAsStream("/style/iconfont.ttf"), defaultFontSize);
+        unicodeMap = new HashMap<>(3);
+        unicodeMap.put("face", '\ue6b6');
+        unicodeMap.put("person", '\ue8ba');
+        unicodeMap.put("setting", '\ue610');
     }
 
-    public static final IconFont FACE = new IconFont('\ue6b6');
-    public static final IconFont PERSON = new IconFont('\ue8ba');
-    public static final IconFont SETTING = new IconFont('\ue610');
+    private StringProperty value;
 
     public IconFont() {
         this.getStyleClass().add("icon-font");
@@ -28,4 +38,31 @@ public class IconFont extends Label {
         this();
         setText(String.valueOf(unicode));
     }
+
+    public IconFont(String value) {
+        this(unicodeMap.get(value));
+    }
+
+    public String getValue() {
+        return valueProperty().get();
+    }
+
+    public StringProperty valueProperty() {
+        if (null == value) {
+            value = new SimpleStringProperty(this, "value");
+            value.addListener(new ChangeListener<String>() {
+                @Override
+                public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
+                    if (null != newValue && unicodeMap.containsKey(newValue)) {
+                        setText(String.valueOf(unicodeMap.get(newValue)));
+                    }
+                }
+            });
+        }
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.valueProperty().set(value);
+    }
 }

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

@@ -1,50 +1,47 @@
 package com.usoftchina.smartschool.device.client.biometric.control;
 
-import javafx.beans.property.IntegerProperty;
-import javafx.beans.property.SimpleIntegerProperty;
+import javafx.beans.property.Property;
+import javafx.beans.property.SimpleObjectProperty;
 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;
+    private Property<Integer> value;
+    private Property<Integer> maxValue;
 
-    public final int getValue() {
-        return valueProperty().get();
+    public final Integer getValue() {
+        return valueProperty().getValue();
     }
 
-    public final void setValue(int value) {
-        valueProperty().set(value);
+    public final void setValue(Integer value) {
+        valueProperty().setValue(value);
     }
 
-    public IntegerProperty valueProperty() {
+    public Property<Integer> valueProperty() {
         if (null == value) {
-            value = new SimpleIntegerProperty(this, "value");
+            value = new SimpleObjectProperty<>(this, "value");
         }
         return value;
     }
 
-    public final int getMaxValue() {
-        return maxValueProperty().get();
+    public final Integer getMaxValue() {
+        return maxValueProperty().getValue();
     }
 
-    public final void setMaxValue(int maxVal) {
-        this.maxValueProperty().set(maxVal);
+    public final void setMaxValue(Integer maxVal) {
+        this.maxValueProperty().setValue(maxVal);
     }
 
-    public final IntegerProperty maxValueProperty() {
+    public final Property<Integer> maxValueProperty() {
         if (null == maxValue) {
-            maxValue = new SimpleIntegerProperty(this, "maxValue", Integer.MAX_VALUE);
+            maxValue = new SimpleObjectProperty(this, "maxValue", Double.MAX_VALUE);
         }
         return maxValue;
     }
@@ -53,14 +50,13 @@ public class IntegerField extends TextField {
         this(0);
     }
 
-    public IntegerField(int value) {
+    public IntegerField(Integer value) {
         this(value, NumberFormat.getInstance());
     }
 
-    public IntegerField(int value, NumberFormat nf) {
+    public IntegerField(Integer value, NumberFormat nf) {
         super();
         getStyleClass().add("integer-field");
-        this.nf = nf;
         initHandlers();
         setValue(value);
     }
@@ -80,31 +76,27 @@ public class IntegerField extends TextField {
             }
         });
 
+        textProperty().addListener(new ChangeListener<String>() {
+            @Override
+            public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
+                parseAndFormatInput();
+            }
+        });
+
         // Set text in field if BigDecimal property is changed from outside.
-        valueProperty().addListener(new ChangeListener<Number>() {
+        valueProperty().addListener(new ChangeListener<Integer>() {
             @Override
-            public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
-                setText(nf.format(newValue));
+            public void changed(ObservableValue<? extends Integer> observable, Integer oldValue, Integer newValue) {
+                setText(null == newValue ? null : String.valueOf(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()));
+        String input = getText();
+        if (input == null || input.length() == 0) {
+            return;
         }
+        setValue(Integer.parseInt(input));
     }
 }

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

@@ -0,0 +1,107 @@
+package com.usoftchina.smartschool.device.client.biometric.control;
+
+import javafx.beans.property.*;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.scene.control.TextField;
+
+import java.text.NumberFormat;
+import java.text.ParseException;
+
+/**
+ * @author yingp
+ * @date 2019/11/19
+ */
+public class NumberField extends TextField {
+    private final NumberFormat nf;
+    private Property<Number> value;
+    private Property<Number> maxValue;
+
+    public final Number getValue() {
+        return valueProperty().getValue();
+    }
+
+    public final void setValue(Number value) {
+        valueProperty().setValue(value);
+    }
+
+    public Property<Number> valueProperty() {
+        if (null == value) {
+            value = new SimpleObjectProperty<>(this, "value");
+        }
+        return value;
+    }
+
+    public final Number getMaxValue() {
+        return maxValueProperty().getValue();
+    }
+
+    public final void setMaxValue(Number maxVal) {
+        this.maxValueProperty().setValue(maxVal);
+    }
+
+    public final Property<Number> maxValueProperty() {
+        if (null == maxValue) {
+            maxValue = new SimpleObjectProperty(this, "maxValue", Double.MAX_VALUE);
+        }
+        return maxValue;
+    }
+
+    public NumberField() {
+        this(0);
+    }
+
+    public NumberField(Number value) {
+        this(value, NumberFormat.getInstance());
+    }
+
+    public NumberField(Number value, NumberFormat nf) {
+        super();
+        getStyleClass().add("number-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);
+            setValue(parsedNumber);
+            selectAll();
+        } catch (ParseException ex) {
+            setText(nf.format(getValue()));
+        }
+    }
+}

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

@@ -0,0 +1,76 @@
+package com.usoftchina.smartschool.device.client.biometric.controller;
+
+import com.usoftchina.smartschool.device.client.biometric.po.CloudSetting;
+import com.usoftchina.smartschool.device.client.biometric.service.CloudSettingService;
+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.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.fxml.Initializable;
+import javafx.scene.control.Button;
+import javafx.scene.control.TextArea;
+import org.springframework.util.StringUtils;
+
+import java.net.URL;
+import java.util.ResourceBundle;
+
+/**
+ * @author yingp
+ * @date 2019/11/21
+ */
+public class CloudSettingController implements Initializable {
+    @FXML
+    public TextArea accessRecordUrlField;
+    @FXML
+    public TextArea icRecordUrlField;
+    @FXML
+    public Button saveButton;
+    private CloudSettingService cloudSettingService;
+    private CloudSetting cloudSetting;
+
+    @Override
+    public void initialize(URL location, ResourceBundle resources) {
+        cloudSettingService = SpringContextHolder.getContext().getBean(CloudSettingService.class);
+        Platform.runLater(() -> {
+            cloudSetting = cloudSettingService.find();
+            if (null == cloudSetting) {
+                cloudSetting = new CloudSetting();
+            }
+            cloudSetting.ready();
+            initFields();
+        });
+    }
+
+    private void initFields() {
+        accessRecordUrlField.textProperty().bindBidirectional(cloudSetting.accessRecordUrlProperty());
+        icRecordUrlField.textProperty().bindBidirectional(cloudSetting.icRecordUrlProperty());
+        saveButton.setDisable(true);
+        cloudSetting.dirtyProperty().addListener(new ChangeListener<Boolean>() {
+            @Override
+            public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
+                saveButton.setDisable(!newValue);
+            }
+        });
+    }
+
+    @FXML
+    public void handleSave(ActionEvent event) {
+        if (null == cloudSetting || !cloudSetting.isDirty()) {
+            AlertUtils.warn("没有修改");
+            return;
+        }
+        if (StringUtils.isEmpty(cloudSetting.getAccessRecordUrl())) {
+            AlertUtils.warn("请填写闸机出入记录接口");
+            return;
+        }
+        Platform.runLater(() -> {
+            cloudSettingService.save(cloudSetting);
+            cloudSetting.ready();
+            NotifyUtils.info("保存成功");
+        });
+    }
+}

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

@@ -17,7 +17,6 @@ import javafx.collections.ObservableList;
 import javafx.event.ActionEvent;
 import javafx.fxml.FXML;
 import javafx.fxml.Initializable;
-import javafx.geometry.Pos;
 import javafx.scene.Scene;
 import javafx.scene.control.*;
 import javafx.scene.control.cell.PropertyValueFactory;
@@ -59,6 +58,8 @@ public class GateController implements Initializable {
     public Button settingButton;
     @FXML
     public TableColumn codeCol;
+    @FXML
+    public Button issueStatusButton;
     private FaceGateService faceGateService;
     private ObservableList<FaceGate> faceGateObservableList = FXCollections.observableArrayList();
 
@@ -121,10 +122,12 @@ public class GateController implements Initializable {
                     editButton.setDisable(true);
                     delButton.setDisable(true);
                     settingButton.setDisable(true);
+                    issueStatusButton.setDisable(true);
                 } else {
                     editButton.setDisable(false);
                     delButton.setDisable(false);
                     settingButton.setDisable(false);
+                    issueStatusButton.setDisable(false);
                 }
             }
         });
@@ -171,6 +174,11 @@ public class GateController implements Initializable {
         loadData();
     }
 
+    /**
+     * 修改设备基础信息
+     *
+     * @param event
+     */
     @FXML
     public void handleEdit(ActionEvent event) {
         FaceGate faceGate = tableView.getSelectionModel().getSelectedItem();
@@ -181,6 +189,11 @@ public class GateController implements Initializable {
         createGateEditPane(faceGate);
     }
 
+    /**
+     * 删除设备
+     *
+     * @param event
+     */
     @FXML
     public void handleDelete(ActionEvent event) {
         FaceGate faceGate = tableView.getSelectionModel().getSelectedItem();
@@ -200,14 +213,37 @@ public class GateController implements Initializable {
                 });
     }
 
-    @FXML
-    public void handleIssue(ActionEvent event) {
-    }
-
+    /**
+     * 查看下发到该设备的人员信息
+     *
+     * @param event
+     */
     @FXML
     public void handleIssueStatus(ActionEvent event) {
+        FaceGate faceGate = tableView.getSelectionModel().getSelectedItem();
+        if (null == faceGate) {
+            AlertUtils.warn("请先选择一行数据");
+            return;
+        }
+        try {
+            Scene scene = ViewUtils.load("/view/person_issue_status.fxml", faceGate.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);
+        }
     }
 
+    /**
+     * 设备参数设置
+     *
+     * @param event
+     */
     @FXML
     public void handleSetting(ActionEvent event) {
         FaceGate faceGate = tableView.getSelectionModel().getSelectedItem();

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

@@ -76,10 +76,10 @@ public class GateIssueController implements Initializable {
             Object params = scene.getParams();
             try {
                 if (params instanceof List) {
-                    gatePersonService.issue(((List<String>) params).toArray(new String[]{}), gateIds.toArray(new String[]{}),
+                    gatePersonService.issue(((List<Integer>) params).toArray(new Integer[]{}), gateIds.toArray(new String[]{}),
                             Date.from(endTime.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()));
                 } else {
-                    gatePersonService.issue((String) params, gateIds.toArray(new String[]{}),
+                    gatePersonService.issue((Integer) params, gateIds.toArray(new String[]{}),
                             Date.from(endTime.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()));
                 }
                 scene.getWindow().hide();

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

@@ -41,8 +41,6 @@ public class GateIssueStatusController implements Initializable {
     @FXML
     public TableColumn endTimeCol;
     @FXML
-    public TableColumn selectedCol;
-    @FXML
     public TableView<PersonGateView> tableView;
     @FXML
     public Button cancelIssueButton;
@@ -78,20 +76,19 @@ public class GateIssueStatusController implements Initializable {
         Platform.runLater(() -> {
             Object params = ((ParameterizedScene) tableView.getScene()).getParams();
             if (null != params) {
-                observableList.setAll(gatePersonRepository.findByPerson((String) params));
+                observableList.setAll(gatePersonRepository.findByPerson((Integer) 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.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
         tableView.setItems(observableList);
 
         tableView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<PersonGateView>() {

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

@@ -21,7 +21,7 @@ import javafx.fxml.FXML;
 import javafx.fxml.Initializable;
 import javafx.scene.Scene;
 import javafx.scene.control.*;
-import javafx.util.converter.NumberStringConverter;
+import javafx.util.converter.IntegerStringConverter;
 
 import java.net.URL;
 import java.util.ResourceBundle;
@@ -85,6 +85,7 @@ public class GateSettingController implements Initializable {
             public void changed(ObservableValue<? extends Scene> observable, Scene oldValue, Scene newValue) {
                 if (null != newValue) {
                     deviceSetting = new DeviceSetting();
+                    deviceSetting.ready();
                     if (newValue instanceof ParameterizedScene) {
                         // 监听传过来需要编辑的FaceGate对象
                         ((ParameterizedScene) newValue).paramsProperty().addListener(new ChangeListener() {
@@ -95,6 +96,7 @@ public class GateSettingController implements Initializable {
                                     Platform.runLater(() -> {
                                         deviceSetting = deviceService.getSetting(faceGate);
                                         if (null != deviceSetting) {
+                                            deviceSetting.ready();
                                             initFields();
                                         }
                                     });
@@ -131,7 +133,14 @@ public class GateSettingController implements Initializable {
         dataServiceUrlField.textProperty().bindBidirectional(deviceSetting.dataServiceUrlProperty());
         deviceServiceUrlField.textProperty().bindBidirectional(deviceSetting.deviceServiceUrlProperty());
         informIntervalField.valueProperty().bindBidirectional(deviceSetting.informIntervalProperty());
-        capacityLabel.textProperty().bindBidirectional(deviceSetting.capacityProperty(), new NumberStringConverter());
+        capacityLabel.textProperty().bindBidirectional(deviceSetting.capacityProperty(), new IntegerStringConverter());
+        saveButton.setDisable(true);
+        deviceSetting.dirtyProperty().addListener(new ChangeListener<Boolean>() {
+            @Override
+            public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
+                saveButton.setDisable(!newValue);
+            }
+        });
     }
 
     @FXML
@@ -140,6 +149,7 @@ public class GateSettingController implements Initializable {
             Platform.runLater(() -> {
                 try {
                     deviceService.saveSetting(faceGate, deviceSetting);
+                    deviceSetting.ready();
                     boolean ipChanged = !faceGate.getIp().equals(deviceSetting.getIpAddress())
                             && !"-".equals(deviceSetting.getIpAddress());
                     if (ipChanged) {

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

@@ -0,0 +1,104 @@
+package com.usoftchina.smartschool.device.client.biometric.controller;
+
+import com.usoftchina.smartschool.device.client.biometric.control.IntegerField;
+import com.usoftchina.smartschool.device.client.biometric.po.IcCard;
+import com.usoftchina.smartschool.device.client.biometric.service.IcCardService;
+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.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.fxml.Initializable;
+import javafx.scene.control.Button;
+import javafx.scene.control.TextField;
+import org.springframework.util.StringUtils;
+
+import java.net.URL;
+import java.util.ResourceBundle;
+
+/**
+ * @author yingp
+ * @date 2019/11/21
+ */
+public class IcCardController implements Initializable {
+    @FXML
+    public TextField ipField;
+    @FXML
+    public IntegerField portField;
+    @FXML
+    public TextField usernameField;
+    @FXML
+    public TextField passwordField;
+    @FXML
+    public TextField databaseNameField;
+    @FXML
+    public Button saveButton;
+
+    private IcCardService icCardService;
+
+    private IcCard icCard;
+
+    @Override
+    public void initialize(URL location, ResourceBundle resources) {
+        icCardService = SpringContextHolder.getContext().getBean(IcCardService.class);
+        Platform.runLater(() -> {
+            icCard = icCardService.find();
+            if (null == icCard) {
+                icCard = new IcCard();
+            }
+            icCard.ready();
+            initFields();
+        });
+    }
+
+    private void initFields() {
+        ipField.textProperty().bindBidirectional(icCard.ipProperty());
+        portField.valueProperty().bindBidirectional(icCard.portProperty());
+        usernameField.textProperty().bindBidirectional(icCard.usernameProperty());
+        passwordField.textProperty().bindBidirectional(icCard.passwordProperty());
+        databaseNameField.textProperty().bindBidirectional(icCard.databaseNameProperty());
+        saveButton.setDisable(true);
+        icCard.dirtyProperty().addListener(new ChangeListener<Boolean>() {
+            @Override
+            public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
+                saveButton.setDisable(!newValue);
+            }
+        });
+    }
+
+    @FXML
+    public void handleSave(ActionEvent event) {
+        if (null == icCard || !icCard.isDirty()) {
+            AlertUtils.warn("没有修改");
+            return;
+        }
+        if (StringUtils.isEmpty(icCard.getIp())) {
+            AlertUtils.warn("请填写IP地址");
+            return;
+        }
+        if (StringUtils.isEmpty(icCard.getPort())) {
+            AlertUtils.warn("请填写端口");
+            return;
+        }
+        if (StringUtils.isEmpty(icCard.getUsername())) {
+            AlertUtils.warn("请填写用户名");
+            return;
+        }
+        if (StringUtils.isEmpty(icCard.getPassword())) {
+            AlertUtils.warn("请填写密码");
+            return;
+        }
+        if (StringUtils.isEmpty(icCard.getDatabaseName())) {
+            AlertUtils.warn("请填写数据库名");
+            return;
+        }
+        Platform.runLater(() -> {
+            icCardService.save(icCard);
+            icCard.ready();
+            NotifyUtils.info("保存成功");
+        });
+    }
+}

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

@@ -3,7 +3,9 @@ package com.usoftchina.smartschool.device.client.biometric.controller;
 import com.usoftchina.smartschool.device.client.biometric.DeviceClient;
 import com.usoftchina.smartschool.device.client.biometric.po.FaceGate;
 import com.usoftchina.smartschool.device.client.biometric.po.Person;
+import com.usoftchina.smartschool.device.client.biometric.po.PersonGroup;
 import com.usoftchina.smartschool.device.client.biometric.po.SexType;
+import com.usoftchina.smartschool.device.client.biometric.service.PersonGroupService;
 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.NotifyUtils;
@@ -23,9 +25,10 @@ import javafx.scene.control.cell.PropertyValueFactory;
 import javafx.stage.Stage;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.util.StringUtils;
 
 import java.net.URL;
-import java.util.ResourceBundle;
+import java.util.*;
 
 /**
  * @author yingp
@@ -36,7 +39,7 @@ public class PersonController implements Initializable {
     @FXML
     public TableView<Person> tableView;
     @FXML
-    public TableColumn classCol;
+    public TableColumn groupCol;
     @FXML
     public TableColumn nameCol;
     @FXML
@@ -53,20 +56,93 @@ public class PersonController implements Initializable {
     public Button issueButton;
     @FXML
     public Button issueStatusButton;
+    @FXML
+    public ListView<PersonGroup> listView;
+    @FXML
+    public Button editGroupButton;
+    @FXML
+    public Button delGroupButton;
 
     private PersonService personService;
+    private PersonGroupService personGroupService;
 
+    private ObservableList<PersonGroup> personGroupObservableList = FXCollections.observableArrayList();
     private ObservableList<Person> personObservableList = FXCollections.observableArrayList();
+    private Map<Integer, String> personGroupMap = new HashMap<>(0);
 
     @Override
     public void initialize(URL location, ResourceBundle resources) {
         personService = SpringContextHolder.getContext().getBean(PersonService.class);
-        initTable();
+        personGroupService = SpringContextHolder.getContext().getBean(PersonGroupService.class);
+        initGroupList();
+        initPersonTable();
+    }
+
+    /**
+     * 用户组初始化
+     */
+    private void initGroupList() {
+        listView.setCellFactory((col) -> {
+            ListCell<PersonGroup> cell = new ListCell<PersonGroup>() {
+                @Override
+                protected void updateItem(PersonGroup item, boolean empty) {
+                    super.updateItem(item, empty);
+                    if (null != item) {
+                        this.setText(item.getName());
+                        this.getStyleClass().add("list-cell-text");
+                        if (null == item.getId()) {
+                            this.getStyleClass().add("list-cell-primary");
+                        } else {
+                            this.getStyleClass().remove("list-cell-primary");
+                        }
+                    } else {
+                        this.getStyleClass().remove("list-cell-primary");
+                        this.getStyleClass().remove("list-cell-text");
+                        this.setText(null);
+                    }
+                }
+            };
+            return cell;
+        });
+        listView.setItems(personGroupObservableList);
+        listView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<PersonGroup>() {
+            @Override
+            public void changed(ObservableValue<? extends PersonGroup> observable, PersonGroup oldValue, PersonGroup newValue) {
+                loadData();
+                if (null != newValue && null != newValue.getId()) {
+                    editGroupButton.setDisable(false);
+                    delGroupButton.setDisable(false);
+                } else {
+                    editGroupButton.setDisable(true);
+                    delGroupButton.setDisable(true);
+                }
+            }
+        });
+        loadGroups();
     }
 
-    private void initTable() {
+    /**
+     * 用户列表初始化
+     */
+    private void initPersonTable() {
         nameCol.setCellValueFactory(new PropertyValueFactory<>("name"));
-        classCol.setCellValueFactory(new PropertyValueFactory<>("clazz"));
+        groupCol.setCellValueFactory(new PropertyValueFactory<>("groupId"));
+        groupCol.setCellFactory((col) -> {
+                    TableCell<FaceGate, Integer> cell = new TableCell<FaceGate, Integer>() {
+
+                        @Override
+                        public void updateItem(Integer item, boolean empty) {
+                            super.updateItem(item, empty);
+                            if (null != item) {
+                                this.setText(personGroupMap.get(item));
+                            } else {
+                                this.setText(null);
+                            }
+                        }
+                    };
+                    return cell;
+                }
+        );
         sexCol.setCellValueFactory(new PropertyValueFactory<>("sex"));
         sexCol.setCellFactory((col) -> {
                     TableCell<FaceGate, SexType> cell = new TableCell<FaceGate, SexType>() {
@@ -89,7 +165,6 @@ public class PersonController implements Initializable {
 
         tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
         tableView.setItems(personObservableList);
-        loadData();
 
         tableView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Person>() {
             @Override
@@ -109,17 +184,43 @@ public class PersonController implements Initializable {
         });
     }
 
+    private void loadGroups() {
+        Platform.runLater(() -> {
+            List<PersonGroup> groupList = personGroupService.findAll();
+            if (null == groupList) {
+                groupList = new ArrayList<>(1);
+            }
+            groupList.forEach(g -> personGroupMap.put(g.getId(), g.getName()));
+            groupList.add(0, new PersonGroup(null, "全部分组"));
+            personGroupObservableList.setAll(FXCollections.observableList(groupList));
+            listView.getSelectionModel().selectFirst();
+        });
+    }
+
     private void loadData() {
         Platform.runLater(() -> {
-            personObservableList.setAll(FXCollections.observableList(personService.findAll()));
+            PersonGroup personGroup = listView.getSelectionModel().getSelectedItem();
+            Integer groupId = null == personGroup ? null : personGroup.getId();
+            List<Person> personList = null == groupId ? personService.findAll() : personService.findByGroupId(groupId);
+            personObservableList.setAll(FXCollections.observableList(personList));
         });
     }
 
+    /**
+     * 新增人员
+     *
+     * @param event
+     */
     @FXML
     public void handleAdd(ActionEvent event) {
         createGateEditPane(null);
     }
 
+    /**
+     * 修改人员
+     *
+     * @param event
+     */
     @FXML
     public void handleEdit(ActionEvent event) {
         Person person = tableView.getSelectionModel().getSelectedItem();
@@ -130,6 +231,11 @@ public class PersonController implements Initializable {
         createGateEditPane(person);
     }
 
+    /**
+     * 删除人员
+     *
+     * @param event
+     */
     @FXML
     public void handleDelete(ActionEvent event) {
         Person person = tableView.getSelectionModel().getSelectedItem();
@@ -150,6 +256,11 @@ public class PersonController implements Initializable {
 
     }
 
+    /**
+     * 刷新
+     *
+     * @param event
+     */
     @FXML
     public void handleRefresh(ActionEvent event) {
         loadData();
@@ -180,10 +291,20 @@ public class PersonController implements Initializable {
         return null;
     }
 
+    /**
+     * 导入
+     *
+     * @param event
+     */
     @FXML
     public void handleImport(ActionEvent event) {
     }
 
+    /**
+     * 批量下发
+     *
+     * @param event
+     */
     @FXML
     public void handleIssue(ActionEvent event) {
         Person person = tableView.getSelectionModel().getSelectedItem();
@@ -205,6 +326,11 @@ public class PersonController implements Initializable {
         }
     }
 
+    /**
+     * 查看下发状态
+     *
+     * @param event
+     */
     @FXML
     public void handleIssueStatus(ActionEvent event) {
         Person person = tableView.getSelectionModel().getSelectedItem();
@@ -225,4 +351,78 @@ public class PersonController implements Initializable {
             logger.error("Issue Status Error", e);
         }
     }
+
+    /**
+     * 添加分组
+     *
+     * @param event
+     */
+    @FXML
+    public void handleAddGroup(ActionEvent event) {
+        AlertUtils.input("输入分组名称").ifPresent(name -> {
+            if (!StringUtils.isEmpty(name)) {
+                Platform.runLater(() -> {
+                    try {
+                        PersonGroup personGroup = personGroupService.add(name);
+                        personGroupObservableList.add(personGroup);
+                        personGroupMap.put(personGroup.getId(), name);
+                    } catch (Exception e) {
+                        AlertUtils.error(e.getMessage());
+                    }
+                });
+            }
+        });
+    }
+
+    /**
+     * 修改分组
+     *
+     * @param event
+     */
+    @FXML
+    public void handleEditGroup(ActionEvent event) {
+        PersonGroup personGroup = listView.getSelectionModel().getSelectedItem();
+        if (null == personGroup || null == personGroup.getId()) {
+            AlertUtils.warn("请先选择分组");
+            return;
+        }
+        AlertUtils.input("输入分组名称", personGroup.getName()).ifPresent(name -> {
+            if (!StringUtils.isEmpty(name) && !name.equals(personGroup.getName())) {
+                Platform.runLater(() -> {
+                    try {
+                        personGroup.setName(name);
+                        personGroupService.update(personGroup);
+                        personGroupMap.put(personGroup.getId(), name);
+                        listView.refresh();
+                        tableView.refresh();
+                    } catch (Exception e) {
+                        AlertUtils.error(e.getMessage());
+                    }
+                });
+            }
+        });
+    }
+
+    /**
+     * 删除分组
+     *
+     * @param event
+     */
+    @FXML
+    public void handleDeleteGroup(ActionEvent event) {
+        PersonGroup personGroup = listView.getSelectionModel().getSelectedItem();
+        if (null == personGroup || null == personGroup.getId()) {
+            AlertUtils.warn("请先选择分组");
+            return;
+        }
+        Platform.runLater(() -> {
+            try {
+                personGroupService.delete(personGroup);
+                personGroupObservableList.remove(personGroup);
+                personGroupMap.remove(personGroup.getId());
+            } catch (Exception e) {
+                AlertUtils.error(e.getMessage());
+            }
+        });
+    }
 }

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

@@ -3,14 +3,15 @@ package com.usoftchina.smartschool.device.client.biometric.controller;
 import com.usoftchina.smartschool.device.client.biometric.DeviceClient;
 import com.usoftchina.smartschool.device.client.biometric.control.ParameterizedScene;
 import com.usoftchina.smartschool.device.client.biometric.po.Person;
+import com.usoftchina.smartschool.device.client.biometric.po.PersonGroup;
 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.PersonGroupService;
 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.NotifyUtils;
 import com.usoftchina.smartschool.device.client.biometric.util.ViewUtils;
 import com.usoftchina.smartschool.device.context.SpringContextHolder;
-import javafx.application.Platform;
 import javafx.beans.property.ObjectProperty;
 import javafx.beans.property.SimpleObjectProperty;
 import javafx.beans.value.ChangeListener;
@@ -30,11 +31,13 @@ import javafx.stage.FileChooser;
 import javafx.stage.Stage;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.util.CollectionUtils;
 import org.springframework.util.StringUtils;
 
 import java.io.File;
 import java.io.IOException;
 import java.net.URL;
+import java.util.List;
 import java.util.ResourceBundle;
 
 /**
@@ -50,7 +53,7 @@ public class PersonEditController implements Initializable {
     @FXML
     public TextField nameField;
     @FXML
-    public TextField classField;
+    public ComboBox<PersonGroup> groupBox;
     @FXML
     public Button goonButton;
     @FXML
@@ -73,8 +76,13 @@ public class PersonEditController implements Initializable {
     private ObservableList<SexType> sexOptions =
             FXCollections.observableArrayList(SexType.values());
 
+    private ObservableList<PersonGroup> personGroupOptions = FXCollections.observableArrayList();
+    private ObjectProperty<PersonGroup> personGroupProperty = new SimpleObjectProperty<>();
+    private PersonGroupService personGroupService;
+
     @Override
     public void initialize(URL location, ResourceBundle resources) {
+        personGroupService = SpringContextHolder.getContext().getBean(PersonGroupService.class);
         personService = SpringContextHolder.getContext().getBean(PersonService.class);
         fileService = SpringContextHolder.getContext().getBean(FileService.class);
 
@@ -103,8 +111,23 @@ public class PersonEditController implements Initializable {
     }
 
     private void initFields() {
+        if (personGroupOptions.isEmpty()) {
+            personGroupOptions.setAll(personGroupService.findAll());
+        }
+        if (0 != person.getGroupId()) {
+            boolean changed = null == personGroupProperty.getValue() || person.getGroupId() != personGroupProperty.getValue().getId();
+            if (changed) {
+                personGroupOptions.stream().filter(g -> g.getId().equals(person.getGroupId()))
+                        .findFirst()
+                        .ifPresent(g -> personGroupProperty.setValue(g));
+            }
+        }
         nameField.textProperty().bindBidirectional(person.nameProperty());
-        classField.textProperty().bindBidirectional(person.clazzProperty());
+        groupBox.valueProperty().bindBidirectional(personGroupProperty);
+        groupBox.setItems(personGroupOptions);
+        if (null == personGroupProperty.getValue()) {
+            groupBox.getSelectionModel().selectFirst();
+        }
         cardNoField.textProperty().bindBidirectional(person.cardNoProperty());
         sexBox.valueProperty().bindBidirectional(person.sexProperty());
         sexBox.setItems(sexOptions);
@@ -153,9 +176,12 @@ public class PersonEditController implements Initializable {
             person.setFaceImage(null);
         }
 
+        if (null != personGroupProperty.getValue()) {
+            person.setGroupId(personGroupProperty.getValue().getId());
+        }
+
         try {
             personService.save(person);
-            NotifyUtils.info("保存成功");
             onComplete.run();
         } catch (Exception e) {
             AlertUtils.error(e.getMessage());
@@ -167,12 +193,14 @@ public class PersonEditController implements Initializable {
         save(() -> {
             Stage stage = (Stage) nameField.getScene().getWindow();
             stage.close();
+            NotifyUtils.info("保存成功");
         });
     }
 
     @FXML
     public void handleSaveAndGoon(ActionEvent event) {
         save(() -> {
+            NotifyUtils.info("保存成功");
             // 继续新增
             person = new Person();
             initFields();

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

@@ -0,0 +1,146 @@
+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.po.GatePersonView;
+import com.usoftchina.smartschool.device.client.biometric.po.PersonGateView;
+import com.usoftchina.smartschool.device.client.biometric.po.SexType;
+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.TableCell;
+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 PersonIssueStatusController implements Initializable {
+    @FXML
+    public TableView<GatePersonView> tableView;
+    @FXML
+    public Button cancelIssueButton;
+    @FXML
+    public TableColumn personGroupCol;
+    @FXML
+    public TableColumn personNameCol;
+    @FXML
+    public TableColumn personSexCol;
+    @FXML
+    public TableColumn personCardNoCol;
+    @FXML
+    public TableColumn issueTimeCol;
+    @FXML
+    public TableColumn endTimeCol;
+
+    private GatePersonService gatePersonService;
+    private GatePersonRepository gatePersonRepository;
+
+    private ObservableList<GatePersonView> 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.findByGate((String) params));
+            }
+        });
+    }
+
+    private void initTable() {
+        personGroupCol.setCellValueFactory(new PropertyValueFactory<>("personGroup"));
+        personNameCol.setCellValueFactory(new PropertyValueFactory<>("personName"));
+        personSexCol.setCellValueFactory(new PropertyValueFactory<>("personSex"));
+        personSexCol.setCellFactory((col) -> {
+                    TableCell<FaceGate, SexType> cell = new TableCell<FaceGate, SexType>() {
+
+                        @Override
+                        public void updateItem(SexType item, boolean empty) {
+                            super.updateItem(item, empty);
+                            if (null != item) {
+                                this.setText(item.getText());
+                            } else {
+                                this.setText(null);
+                            }
+                        }
+                    };
+                    return cell;
+                }
+        );
+        personCardNoCol.setCellValueFactory(new PropertyValueFactory<>("personCardNo"));
+        issueTimeCol.setCellValueFactory(new PropertyValueFactory<>("issueTime"));
+        endTimeCol.setCellValueFactory(new PropertyValueFactory<>("endTime"));
+
+        tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
+        tableView.setItems(observableList);
+
+        tableView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<GatePersonView>() {
+            @Override
+            public void changed(ObservableValue<? extends GatePersonView> observable, GatePersonView oldValue, GatePersonView newValue) {
+                if (null == newValue) {
+                    cancelIssueButton.setDisable(true);
+                } else {
+                    cancelIssueButton.setDisable(false);
+                }
+            }
+        });
+    }
+
+    @FXML
+    public void handleCancelIssue(ActionEvent event) {
+        GatePersonView gatePersonView = tableView.getSelectionModel().getSelectedItem();
+        if (null == gatePersonView) {
+            AlertUtils.warn("请先选择一行数据");
+            return;
+        }
+        Platform.runLater(() -> {
+            try {
+                gatePersonService.cancelIssue(gatePersonView.getId());
+                NotifyUtils.info("取消成功");
+                loadData();
+            } catch (Exception e) {
+                AlertUtils.warn(e.getMessage());
+            }
+        });
+    }
+}

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

@@ -0,0 +1,84 @@
+package com.usoftchina.smartschool.device.client.biometric.controller;
+
+import com.usoftchina.smartschool.device.client.biometric.po.School;
+import com.usoftchina.smartschool.device.client.biometric.service.SchoolService;
+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.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.fxml.Initializable;
+import javafx.scene.control.Button;
+import javafx.scene.control.TextArea;
+import javafx.scene.control.TextField;
+import org.springframework.util.StringUtils;
+
+import java.net.URL;
+import java.util.ResourceBundle;
+
+/**
+ * @author yingp
+ * @date 2019/11/21
+ */
+public class SchoolController implements Initializable {
+    @FXML
+    public TextField idField;
+    @FXML
+    public TextArea nameField;
+    @FXML
+    public TextArea secretField;
+    @FXML
+    public Button saveButton;
+    private SchoolService schoolService;
+    private School school;
+
+    @Override
+    public void initialize(URL location, ResourceBundle resources) {
+        schoolService = SpringContextHolder.getContext().getBean(SchoolService.class);
+        Platform.runLater(() -> {
+            school = schoolService.find();
+            if (null == school) {
+                school = new School();
+            }
+            school.ready();
+            initFields();
+        });
+    }
+
+    private void initFields() {
+        idField.textProperty().bindBidirectional(school.idProperty());
+        nameField.textProperty().bindBidirectional(school.nameProperty());
+        secretField.textProperty().bindBidirectional(school.secretProperty());
+        saveButton.setDisable(true);
+        school.dirtyProperty().addListener(new ChangeListener<Boolean>() {
+            @Override
+            public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
+                saveButton.setDisable(!newValue);
+            }
+        });
+    }
+
+    @FXML
+    public void handleSave(ActionEvent event) {
+        if (null == school || !school.isDirty()) {
+            AlertUtils.warn("没有修改");
+            return;
+        }
+        if (StringUtils.isEmpty(school.getId())) {
+            AlertUtils.warn("请填写学校ID");
+            return;
+        }
+        if (StringUtils.isEmpty(school.getName())) {
+            AlertUtils.warn("请填写学校名称");
+            return;
+        }
+        Platform.runLater(() -> {
+            schoolService.save(school);
+            school.ready();
+            NotifyUtils.info("保存成功");
+        });
+    }
+}

+ 73 - 0
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/jdbc/ReturnKeyStatementCallback.java

@@ -0,0 +1,73 @@
+package com.usoftchina.smartschool.device.client.biometric.jdbc;
+
+import org.springframework.dao.DataAccessException;
+import org.springframework.jdbc.core.*;
+import org.springframework.jdbc.support.JdbcUtils;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+/**
+ * @author yingp
+ * @date 2019/5/8
+ */
+public class ReturnKeyStatementCallback implements PreparedStatementCallback<Number>, ParameterDisposer {
+
+    private final Object[] args;
+
+    public ReturnKeyStatementCallback(Object[] args) {
+        this.args = args;
+    }
+
+    @Override
+    public Number doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {
+        ResultSet rs = null;
+        try {
+            setValues(ps);
+            ps.executeUpdate();
+            rs = ps.getGeneratedKeys();
+            if (rs.next()) {
+                return rs.getLong(1);
+            }
+            return null;
+        } finally {
+            JdbcUtils.closeResultSet(rs);
+            JdbcUtils.closeStatement(ps);
+            cleanupParameters();
+        }
+    }
+
+    @Override
+    public void cleanupParameters() {
+        StatementCreatorUtils.cleanupParameters(this.args);
+    }
+
+    public void setValues(PreparedStatement ps) throws SQLException {
+        if (this.args != null) {
+            int i = 0;
+            for (; i < this.args.length; i++) {
+                Object arg = this.args[i];
+                doSetValue(ps, i + 1, arg);
+            }
+        }
+    }
+
+    /**
+     * Set the value for prepared statements specified parameter index using the passed in value.
+     * This method can be overridden by sub-classes if needed.
+     *
+     * @param ps                the PreparedStatement
+     * @param parameterPosition index of the parameter position
+     * @param argValue          the value to set
+     * @throws SQLException if thrown by PreparedStatement methods
+     */
+    protected void doSetValue(PreparedStatement ps, int parameterPosition, Object argValue) throws SQLException {
+        if (argValue instanceof SqlParameterValue) {
+            SqlParameterValue paramValue = (SqlParameterValue) argValue;
+            StatementCreatorUtils.setParameterValue(ps, parameterPosition, paramValue, paramValue.getValue());
+        } else {
+            StatementCreatorUtils.setParameterValue(ps, parameterPosition, SqlTypeValue.TYPE_UNKNOWN, argValue);
+        }
+    }
+}

+ 26 - 0
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/jdbc/ReturnKeyStatementCreator.java

@@ -0,0 +1,26 @@
+package com.usoftchina.smartschool.device.client.biometric.jdbc;
+
+import org.springframework.jdbc.core.PreparedStatementCreator;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+/**
+ * @author yingp
+ * @date 2019/5/8
+ */
+public class ReturnKeyStatementCreator implements PreparedStatementCreator {
+    private final String sql;
+    private final String keyColumn;
+
+    public ReturnKeyStatementCreator(String sql, String keyColumn) {
+        this.sql = sql;
+        this.keyColumn = keyColumn;
+    }
+
+    @Override
+    public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
+        return con.prepareStatement(sql, new String[]{keyColumn});
+    }
+}

+ 53 - 0
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/listener/AccessControlListener.java

@@ -0,0 +1,53 @@
+package com.usoftchina.smartschool.device.client.biometric.listener;
+
+import com.usoftchina.smartschool.device.biometric.AccessControlEvent;
+import com.usoftchina.smartschool.device.client.biometric.po.FaceGate;
+import com.usoftchina.smartschool.device.client.biometric.service.CloudService;
+import com.usoftchina.smartschool.device.client.biometric.service.FaceGateService;
+import com.usoftchina.smartschool.device.dto.AccessControlInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.event.EventListener;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author yingp
+ * @date 2019/3/11
+ */
+@Component
+public class AccessControlListener {
+    private Logger logger = LoggerFactory.getLogger(AccessControlListener.class);
+
+    @Autowired
+    private FaceGateService faceGateService;
+    @Autowired
+    private CloudService cloudService;
+
+    /**
+     * 门禁事件
+     *
+     * @param event
+     */
+    @Async
+    @EventListener(AccessControlEvent.class)
+    public void onAccessControlEvent(AccessControlEvent event) {
+        AccessControlInfo info = event.getAccessControlInfo();
+        FaceGate faceGate = faceGateService.findByCode(event.getDeviceCode());
+        // 通过门禁设备绑定的访问类型,来设置本次事件的访问类型
+        if (faceGate.isEntryType()) {
+            info.setEventType(AccessControlInfo.EventType.ENTRY);
+        } else {
+            info.setEventType(AccessControlInfo.EventType.EXIT);
+        }
+        if (logger.isDebugEnabled()) {
+            logger.debug("Device[{}] Port[{}] AccessControlEvent[{}]", faceGate.getIp(), faceGate.getPort(), info.toString());
+        }
+        try {
+            cloudService.saveAccessRecord(info);
+        } catch (Exception e) {
+            logger.error("Save AccessControlRecord Error: " + e.getMessage());
+        }
+    }
+}

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

@@ -0,0 +1,51 @@
+package com.usoftchina.smartschool.device.client.biometric.po;
+
+import com.usoftchina.smartschool.device.property.DirtyBean;
+import com.usoftchina.smartschool.device.property.DirtyObjectProperty;
+
+import java.io.Serializable;
+
+/**
+ * @author yingp
+ * @date 2019/11/21
+ */
+public class CloudSetting extends DirtyBean implements Serializable {
+    /**
+     * 闸机过人记录上传接口
+     */
+    private DirtyObjectProperty<String> accessRecordUrl;
+    /**
+     * IC卡消费记录上传接口
+     */
+    private DirtyObjectProperty<String> icRecordUrl;
+
+    public String getAccessRecordUrl() {
+        return accessRecordUrlProperty().get();
+    }
+
+    public DirtyObjectProperty<String> accessRecordUrlProperty() {
+        if (null == accessRecordUrl) {
+            accessRecordUrl = new DirtyObjectProperty<>(this, "accessRecordUrl");
+        }
+        return accessRecordUrl;
+    }
+
+    public void setAccessRecordUrl(String accessRecordUrl) {
+        this.accessRecordUrlProperty().set(accessRecordUrl);
+    }
+
+    public String getIcRecordUrl() {
+        return icRecordUrlProperty().get();
+    }
+
+    public DirtyObjectProperty<String> icRecordUrlProperty() {
+        if (null == icRecordUrl) {
+            icRecordUrl = new DirtyObjectProperty<>(this, "icRecordUrl");
+        }
+        return icRecordUrl;
+    }
+
+    public void setIcRecordUrl(String icRecordUrl) {
+        this.icRecordUrlProperty().set(icRecordUrl);
+    }
+}

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

@@ -234,4 +234,8 @@ public class FaceGate implements DeviceInfo {
             this.statusProperty().set(HealthStatus.of(status));
         }
     }
+
+    public boolean isEntryType() {
+        return AccessType.IN.getValue().equals(getAccessType());
+    }
 }

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

@@ -9,13 +9,9 @@ import java.util.Date;
  * @date 2019/11/18
  */
 public class GatePerson {
-    private String id;
-    private String personId;
+    private Integer id;
+    private Integer personId;
     private String gateId;
-    /**
-     * 下发后,返回的ID
-     */
-    private Integer devicePersonId;
     /**
      * 有效截止时间
      */
@@ -25,19 +21,19 @@ public class GatePerson {
      */
     private Date issueTime;
 
-    public String getId() {
+    public Integer getId() {
         return id;
     }
 
-    public void setId(String id) {
+    public void setId(Integer id) {
         this.id = id;
     }
 
-    public String getPersonId() {
+    public Integer getPersonId() {
         return personId;
     }
 
-    public void setPersonId(String personId) {
+    public void setPersonId(Integer personId) {
         this.personId = personId;
     }
 
@@ -49,14 +45,6 @@ public class GatePerson {
         this.gateId = gateId;
     }
 
-    public Integer getDevicePersonId() {
-        return devicePersonId;
-    }
-
-    public void setDevicePersonId(Integer devicePersonId) {
-        this.devicePersonId = devicePersonId;
-    }
-
     public Date getEndTime() {
         return endTime;
     }

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

@@ -12,30 +12,29 @@ import java.util.Date;
  * @date 2019/11/18
  */
 public class GatePersonView extends Selectable{
-    private String id;
-    private String personId;
+    private Integer id;
+    private Integer personId;
     private String gateId;
-    private Integer devicePersonId;
-    private StringProperty personClass;
+    private StringProperty personGroup;
     private StringProperty personName;
     private ObjectProperty<SexType> personSex;
     private StringProperty personCardNo;
     private ObjectProperty<Date> issueTime;
     private ObjectProperty<Date> endTime;
 
-    public String getId() {
+    public Integer getId() {
         return id;
     }
 
-    public void setId(String id) {
+    public void setId(Integer id) {
         this.id = id;
     }
 
-    public String getPersonId() {
+    public Integer getPersonId() {
         return personId;
     }
 
-    public void setPersonId(String personId) {
+    public void setPersonId(Integer personId) {
         this.personId = personId;
     }
 
@@ -47,27 +46,19 @@ public class GatePersonView extends Selectable{
         this.gateId = gateId;
     }
 
-    public Integer getDevicePersonId() {
-        return devicePersonId;
+    public String getPersonGroup() {
+        return personGroupProperty().get();
     }
 
-    public void setDevicePersonId(Integer devicePersonId) {
-        this.devicePersonId = devicePersonId;
-    }
-
-    public String getPersonClass() {
-        return personClassProperty().get();
-    }
-
-    public StringProperty personClassProperty() {
-        if (null == personClass) {
-            personClass = new SimpleStringProperty();
+    public StringProperty personGroupProperty() {
+        if (null == personGroup) {
+            personGroup = new SimpleStringProperty();
         }
-        return personClass;
+        return personGroup;
     }
 
-    public void setPersonClass(String clazz) {
-        this.personClassProperty().set(clazz);
+    public void setPersonGroup(String clazz) {
+        this.personGroupProperty().set(clazz);
     }
 
     public String getPersonName() {

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

@@ -0,0 +1,115 @@
+package com.usoftchina.smartschool.device.client.biometric.po;
+
+import com.usoftchina.smartschool.device.client.biometric.jdbc.Connectable;
+import com.usoftchina.smartschool.device.property.DirtyBean;
+import com.usoftchina.smartschool.device.property.DirtyObjectProperty;
+
+import java.io.Serializable;
+
+/**
+ * @author yingp
+ * @date 2019/11/21
+ */
+public class IcCard extends DirtyBean implements Serializable, Connectable {
+    private DirtyObjectProperty<String> ip;
+    private DirtyObjectProperty<Integer> port;
+    private DirtyObjectProperty<String> username;
+    private DirtyObjectProperty<String> password;
+    private DirtyObjectProperty<String> databaseName;
+
+    public String getIp() {
+        return ipProperty().get();
+    }
+
+    public DirtyObjectProperty<String> ipProperty() {
+        if (null == ip) {
+            ip = new DirtyObjectProperty<>(this, "ip");
+        }
+        return ip;
+    }
+
+    public void setIp(String ip) {
+        this.ipProperty().set(ip);
+    }
+
+    public Integer getPort() {
+        return portProperty().get();
+    }
+
+    public DirtyObjectProperty<Integer> portProperty() {
+        if (null == port) {
+            port = new DirtyObjectProperty<>(this, "port");
+        }
+        return port;
+    }
+
+    public void setPort(Integer port) {
+        this.portProperty().set(port);
+    }
+
+    public String getUsername() {
+        return usernameProperty().get();
+    }
+
+    public DirtyObjectProperty<String> usernameProperty() {
+        if (null == username) {
+            username = new DirtyObjectProperty<>(this, "username");
+        }
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.usernameProperty().set(username);
+    }
+
+    public String getPassword() {
+        return passwordProperty().get();
+    }
+
+    public DirtyObjectProperty<String> passwordProperty() {
+        if (null == password) {
+            password = new DirtyObjectProperty<>(this, "password");
+        }
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.passwordProperty().set(password);
+    }
+
+    public String getDatabaseName() {
+        return databaseNameProperty().get();
+    }
+
+    public DirtyObjectProperty<String> databaseNameProperty() {
+        if (null == databaseName) {
+            databaseName = new DirtyObjectProperty<>(this, "databaseName");
+        }
+        return databaseName;
+    }
+
+    public void setDatabaseName(String databaseName) {
+        this.databaseNameProperty().set(databaseName);
+    }
+
+    @Override
+    public String qualifier() {
+        return getDatabaseName();
+    }
+
+    @Override
+    public String url() {
+        return String.format("jdbc:sqlserver://%s:%s;DatabaseName=%s",
+                getIp(), getPort(), getDatabaseName());
+    }
+
+    @Override
+    public String username() {
+        return getUsername();
+    }
+
+    @Override
+    public String password() {
+        return getPassword();
+    }
+}

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

@@ -9,35 +9,35 @@ import java.util.Date;
  * @date 2019/11/15
  */
 public class Person {
-    private String id;
-    private StringProperty clazz;
+    private Integer id;
+    private IntegerProperty groupId;
     private StringProperty name;
     private ObjectProperty<SexType> sex;
     private StringProperty cardNo;
     private ObjectProperty<Date> createTime;
     private String faceImage;
 
-    public String getId() {
+    public Integer getId() {
         return id;
     }
 
-    public void setId(String id) {
+    public void setId(Integer id) {
         this.id = id;
     }
 
-    public String getClazz() {
-        return clazzProperty().get();
+    public int getGroupId() {
+        return groupIdProperty().get();
     }
 
-    public StringProperty clazzProperty() {
-        if (null == clazz) {
-            clazz = new SimpleStringProperty();
+    public IntegerProperty groupIdProperty() {
+        if (null == groupId) {
+            groupId = new SimpleIntegerProperty();
         }
-        return clazz;
+        return groupId;
     }
 
-    public void setClazz(String clazz) {
-        this.clazzProperty().set(clazz);
+    public void setGroupId(Integer groupId) {
+        this.groupIdProperty().set(groupId);
     }
 
     public String getName() {

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

@@ -11,11 +11,10 @@ import java.util.Date;
  * @author yingp
  * @date 2019/11/18
  */
-public class PersonGateView extends Selectable{
-    private String id;
-    private String personId;
+public class PersonGateView{
+    private Integer id;
+    private Integer personId;
     private String gateId;
-    private Integer devicePersonId;
     private StringProperty gateName;
     /**
      * 位置
@@ -25,19 +24,19 @@ public class PersonGateView extends Selectable{
     private ObjectProperty<Date> issueTime;
     private ObjectProperty<Date> endTime;
 
-    public String getId() {
+    public Integer getId() {
         return id;
     }
 
-    public void setId(String id) {
+    public void setId(Integer id) {
         this.id = id;
     }
 
-    public String getPersonId() {
+    public Integer getPersonId() {
         return personId;
     }
 
-    public void setPersonId(String personId) {
+    public void setPersonId(Integer personId) {
         this.personId = personId;
     }
 
@@ -49,14 +48,6 @@ public class PersonGateView extends Selectable{
         this.gateId = gateId;
     }
 
-    public Integer getDevicePersonId() {
-        return devicePersonId;
-    }
-
-    public void setDevicePersonId(Integer devicePersonId) {
-        this.devicePersonId = devicePersonId;
-    }
-
     public String getGateName() {
         return gateNameProperty().get();
     }

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

@@ -0,0 +1,39 @@
+package com.usoftchina.smartschool.device.client.biometric.po;
+
+/**
+ * @author yingp
+ * @date 2019/11/20
+ */
+public class PersonGroup {
+    private Integer id;
+    private String name;
+
+    public PersonGroup() {
+    }
+
+    public PersonGroup(Integer id, String name) {
+        this.id = id;
+        this.name = name;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String toString() {
+        return name;
+    }
+}

+ 52 - 7
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/po/School.java

@@ -1,46 +1,91 @@
 package com.usoftchina.smartschool.device.client.biometric.po;
 
+import com.usoftchina.smartschool.device.property.DirtyBean;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+
 import java.io.Serializable;
 
 /**
  * @author yingp
  * @date 2019/3/11
  */
-public class School implements Serializable {
+public class School extends DirtyBean implements Serializable {
     /**
      * 学校名称
      */
-    private String name;
+    private StringProperty name;
     /**
      * ID
      */
-    private String id;
+    private StringProperty id;
     /**
      * 私钥
      */
-    private String secret;
+    private StringProperty secret;
 
     public String getName() {
+        return nameProperty().get();
+    }
+
+    public StringProperty nameProperty() {
+        if (null == name) {
+            name = new SimpleStringProperty();
+            name.addListener(new ChangeListener<String>() {
+                @Override
+                public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
+                    checkDirty("name", oldValue, newValue);
+                }
+            });
+        }
         return name;
     }
 
     public void setName(String name) {
-        this.name = name;
+        this.nameProperty().set(name);
     }
 
     public String getId() {
+        return idProperty().get();
+    }
+
+    public StringProperty idProperty() {
+        if (null == id) {
+            id = new SimpleStringProperty();
+            id.addListener(new ChangeListener<String>() {
+                @Override
+                public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
+                    checkDirty("id", oldValue, newValue);
+                }
+            });
+        }
         return id;
     }
 
     public void setId(String id) {
-        this.id = id;
+        this.idProperty().set(id);
     }
 
     public String getSecret() {
+        return secretProperty().get();
+    }
+
+    public StringProperty secretProperty() {
+        if (null == secret) {
+            secret = new SimpleStringProperty();
+            secret.addListener(new ChangeListener<String>() {
+                @Override
+                public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
+                    checkDirty("secret", oldValue, newValue);
+                }
+            });
+        }
         return secret;
     }
 
     public void setSecret(String secret) {
-        this.secret = secret;
+        this.secretProperty().set(secret);
     }
 }

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

@@ -0,0 +1,28 @@
+package com.usoftchina.smartschool.device.client.biometric.repository;
+
+import com.usoftchina.smartschool.device.client.biometric.jdbc.ReturnKeyStatementCallback;
+import com.usoftchina.smartschool.device.client.biometric.jdbc.ReturnKeyStatementCreator;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+/**
+ * @author yingp
+ * @date 2019/11/21
+ */
+public abstract class BaseRepository {
+    @Autowired
+    protected JdbcTemplate jdbcTemplate;
+
+    /**
+     * 新增,并返回指定字段的值
+     *
+     * @param insertString
+     * @param keyColumn
+     * @param values
+     * @return
+     */
+    public Number insertAndReturnKey(String insertString, String keyColumn, Object... values) {
+        return jdbcTemplate.execute(new ReturnKeyStatementCreator(insertString, keyColumn),
+                new ReturnKeyStatementCallback(values));
+    }
+}

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

@@ -0,0 +1,39 @@
+package com.usoftchina.smartschool.device.client.biometric.repository;
+
+import com.usoftchina.smartschool.device.client.biometric.po.CloudSetting;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.stereotype.Repository;
+
+/**
+ * @author yingp
+ * @date 2019/11/21
+ */
+@Repository
+public class CloudSettingRepository extends BaseRepository {
+
+    @Cacheable("cloudSetting")
+    public CloudSetting find() {
+        try {
+            return jdbcTemplate.queryForObject("select * from cloud_setting",
+                    new BeanPropertyRowMapper<>(CloudSetting.class));
+        } catch (EmptyResultDataAccessException e) {
+            return null;
+        }
+    }
+
+    @Cacheable("cloudSetting")
+    public CloudSetting save(CloudSetting setting) {
+        jdbcTemplate.update("insert into cloud_setting (accessRecordUrl,icRecordUrl) values (?,?)",
+                setting.getAccessRecordUrl(), setting.getIcRecordUrl());
+        return setting;
+    }
+
+    @CacheEvict("cloudSetting")
+    public void update(CloudSetting setting) {
+        jdbcTemplate.update("update cloud_setting set accessRecordUrl=?,icRecordUrl=?",
+                setting.getAccessRecordUrl(), setting.getIcRecordUrl());
+    }
+}

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

@@ -1,12 +1,11 @@
 package com.usoftchina.smartschool.device.client.biometric.repository;
 
 import com.usoftchina.smartschool.device.client.biometric.po.FaceGate;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.cache.annotation.CacheEvict;
 import org.springframework.cache.annotation.Cacheable;
+import org.springframework.cache.annotation.Caching;
 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;
@@ -16,10 +15,7 @@ import java.util.List;
  * @date 2019/11/14
  */
 @Repository
-public class FaceGateRepository {
-
-    @Autowired
-    private JdbcTemplate jdbcTemplate;
+public class FaceGateRepository extends BaseRepository{
 
     public List<FaceGate> findAll() {
         try {
@@ -49,7 +45,22 @@ public class FaceGateRepository {
         }
     }
 
-    @CacheEvict(value = "faceGate", key = "#faceGate.id")
+    @Cacheable(value = "faceGate", key = "#code")
+    public FaceGate findByCode(String code) {
+        try {
+            return jdbcTemplate.queryForObject("select * from face_gate where code=?",
+                    new BeanPropertyRowMapper<>(FaceGate.class), code);
+        } catch (EmptyResultDataAccessException e) {
+            return null;
+        }
+    }
+
+    @Caching(
+            evict = {
+                    @CacheEvict(value = "faceGate", key = "#faceGate.id"),
+                    @CacheEvict(value = "faceGate", key = "#faceGate.code")
+            }
+    )
     public boolean save(FaceGate faceGate) {
         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(),
@@ -57,7 +68,12 @@ public class FaceGateRepository {
         return ret > 0;
     }
 
-    @CacheEvict(value = "faceGate", key = "#faceGate.id")
+    @Caching(
+            evict = {
+                    @CacheEvict(value = "faceGate", key = "#faceGate.id"),
+                    @CacheEvict(value = "faceGate", key = "#faceGate.code")
+            }
+    )
     public boolean update(FaceGate faceGate) {
         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(),
@@ -65,8 +81,13 @@ public class FaceGateRepository {
         return ret > 0;
     }
 
-    @CacheEvict(value = "faceGate", key = "#id")
-    public boolean delete(String id) {
-        return jdbcTemplate.update("delete from face_gate where id=?", id) > 0;
+    @Caching(
+            evict = {
+                    @CacheEvict(value = "faceGate", key = "#faceGate.id"),
+                    @CacheEvict(value = "faceGate", key = "#faceGate.code")
+            }
+    )
+    public boolean delete(FaceGate faceGate) {
+        return jdbcTemplate.update("delete from face_gate where id=?", faceGate.getId()) > 0;
     }
 }

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

@@ -1,10 +1,8 @@
 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;
@@ -14,12 +12,9 @@ import java.util.List;
  * @date 2019/11/14
  */
 @Repository
-public class GatePersonRepository {
+public class GatePersonRepository extends BaseRepository{
 
-    @Autowired
-    private JdbcTemplate jdbcTemplate;
-
-    public GatePerson findById(String id) {
+    public GatePerson findById(Integer id) {
         try {
             return jdbcTemplate.queryForObject("select * from gate_person where id=?",
                     new BeanPropertyRowMapper<>(GatePerson.class), id);
@@ -29,7 +24,7 @@ public class GatePersonRepository {
     }
 
 
-    public GatePerson findByGateAndPerson(String gateId, String personId) {
+    public GatePerson findByGateAndPerson(String gateId, Integer personId) {
         try {
             return jdbcTemplate.queryForObject("select * from gate_person where gateId=? and personId=?",
                     new BeanPropertyRowMapper<>(GatePerson.class), gateId, personId);
@@ -40,20 +35,20 @@ public class GatePersonRepository {
 
     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",
+            return jdbcTemplate.query("select gate_person.id,gate_person.personId,gate_person.gateId,gate_person.issueTime," +
+                            "person_group.name personGroup,person.name personName,person.cardNo personCardNo,person.sex personSex," +
+                            "gate_person.endTime from face_gate,person,gate_person,person_group " +
+                            "where face_gate.id=gate_person.gateId and person.id=gate_person.personId and person.groupId=person_group.id " +
+                            "and face_gate.id=? order by person.createTime",
                     new BeanPropertyRowMapper<>(GatePersonView.class), gateId);
         } catch (EmptyResultDataAccessException e) {
             return null;
         }
     }
 
-    public List<PersonGateView> findByPerson(String personId) {
+    public List<PersonGateView> findByPerson(Integer personId) {
         try {
-            return jdbcTemplate.query("select gate_person.id,gate_person.personId,gate_person.gateId,gate_person.devicePersonId," +
+            return jdbcTemplate.query("select gate_person.id,gate_person.personId,gate_person.gateId," +
                             "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=? " +
@@ -65,10 +60,11 @@ public class GatePersonRepository {
     }
 
     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(),
+        Number id = insertAndReturnKey("insert into gate_person(personId,gateId,issueTime,endTime) values " +
+                "(?,?,?,?)", "id", gatePerson.getPersonId(), gatePerson.getGateId(),
                 gatePerson.getIssueTime(), gatePerson.getEndTime());
-        return ret > 0;
+        gatePerson.setId(id.intValue());
+        return true;
     }
 
     public boolean update(GatePerson gatePerson) {
@@ -77,11 +73,11 @@ public class GatePersonRepository {
         return ret > 0;
     }
 
-    public boolean delete(String id) {
+    public boolean delete(Integer id) {
         return jdbcTemplate.update("delete from gate_person where id=?", id) > 0;
     }
 
-    public boolean deleteByPerson(String personId) {
+    public boolean deleteByPerson(Integer personId) {
         return jdbcTemplate.update("delete from gate_person where personId=?", personId) > 0;
     }
 

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

@@ -0,0 +1,44 @@
+package com.usoftchina.smartschool.device.client.biometric.repository;
+
+import com.usoftchina.smartschool.device.client.biometric.po.IcCard;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Repository;
+
+/**
+ * @author yingp
+ * @date 2019/3/11
+ */
+@Repository
+public class IcCardRepository {
+
+    @Autowired
+    private JdbcTemplate jdbcTemplate;
+
+    @Cacheable("icCard")
+    public IcCard find() {
+        try {
+            return jdbcTemplate.queryForObject("select * from iccard",
+                    new BeanPropertyRowMapper<>(IcCard.class));
+        } catch (EmptyResultDataAccessException e) {
+            return null;
+        }
+    }
+
+    @Cacheable("icCard")
+    public IcCard save(IcCard card) {
+        jdbcTemplate.update("insert into iccard (databaseName,ip,port,username,password) values (?,?,?,?,?)",
+                card.getDatabaseName(), card.getIp(), card.getPort(), card.getUsername(), card.getPassword());
+        return card;
+    }
+
+    @CacheEvict("icCard")
+    public void update(IcCard card) {
+        jdbcTemplate.update("update iccard set databaseName=?,ip=?,port=?,username=?,password=?",
+                card.getDatabaseName(), card.getIp(), card.getPort(), card.getUsername(), card.getPassword());
+    }
+}

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

@@ -0,0 +1,65 @@
+package com.usoftchina.smartschool.device.client.biometric.repository;
+
+import com.usoftchina.smartschool.device.client.biometric.po.PersonGroup;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+/**
+ * @author yingp
+ * @date 2019/11/14
+ */
+@Repository
+public class PersonGroupRepository extends BaseRepository{
+
+    public List<PersonGroup> findAll() {
+        try {
+            return jdbcTemplate.query("select * from person_group order by name",
+                    new BeanPropertyRowMapper<>(PersonGroup.class));
+        } catch (EmptyResultDataAccessException e) {
+            return null;
+        }
+    }
+
+    @Cacheable(value = "personGroup", key = "#id")
+    public PersonGroup findById(Integer id) {
+        try {
+            return jdbcTemplate.queryForObject("select * from person_group where id=?",
+                    new BeanPropertyRowMapper<>(PersonGroup.class), id);
+        } catch (EmptyResultDataAccessException e) {
+            return null;
+        }
+    }
+
+    public PersonGroup findByName(String name) {
+        try {
+            return jdbcTemplate.queryForObject("select * from person_group where name=?",
+                    new BeanPropertyRowMapper<>(PersonGroup.class), name);
+        } catch (EmptyResultDataAccessException e) {
+            return null;
+        }
+    }
+
+    @CacheEvict(value = "personGroup", key = "#personGroup.id")
+    public boolean save(PersonGroup personGroup) {
+        Number id = insertAndReturnKey("insert into person_group(name) values (?)",
+                "id", personGroup.getName());
+        personGroup.setId(id.intValue());
+        return true;
+    }
+
+    @CacheEvict(value = "personGroup", key = "#personGroup.id")
+    public boolean update(PersonGroup personGroup) {
+        int ret = jdbcTemplate.update("update person_group set name=? where id=?", personGroup.getName(), personGroup.getId());
+        return ret > 0;
+    }
+
+    @CacheEvict(value = "personGroup", key = "#id")
+    public boolean delete(Integer id) {
+        return jdbcTemplate.update("delete from person_group where id=?", id) > 0;
+    }
+}

+ 24 - 14
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/repository/PersonRepository.java

@@ -1,12 +1,10 @@
 package com.usoftchina.smartschool.device.client.biometric.repository;
 
 import com.usoftchina.smartschool.device.client.biometric.po.Person;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.cache.annotation.CacheEvict;
 import org.springframework.cache.annotation.Cacheable;
 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;
@@ -16,10 +14,7 @@ import java.util.List;
  * @date 2019/11/14
  */
 @Repository
-public class PersonRepository {
-
-    @Autowired
-    private JdbcTemplate jdbcTemplate;
+public class PersonRepository extends BaseRepository{
 
     public List<Person> findAll() {
         try {
@@ -40,7 +35,7 @@ public class PersonRepository {
     }
 
     @Cacheable(value = "person", key = "#id")
-    public Person findById(String id) {
+    public Person findById(Integer id) {
         try {
             return jdbcTemplate.queryForObject("select * from person where id=?",
                     new BeanPropertyRowMapper<>(Person.class), id);
@@ -51,21 +46,36 @@ public class PersonRepository {
 
     @CacheEvict(value = "person", key = "#person.id")
     public boolean save(Person person) {
-        int ret = jdbcTemplate.update("insert into person(id,name,cardNo,sex,clazz,createTime,faceImage) values " +
-                        "(?,?,?,?,?,?,?)", person.getId(), person.getName(), person.getCardNo(), person.getSex(),
-                person.getClazz(), person.getCreateTime(), person.getFaceImage());
-        return ret > 0;
+        Number id = insertAndReturnKey("insert into person(name,cardNo,sex,groupId,createTime,faceImage) values " +
+                        "(?,?,?,?,?,?)", "id", person.getName(), person.getCardNo(), person.getSex(),
+                person.getGroupId(), person.getCreateTime(), person.getFaceImage());
+        person.setId(id.intValue());
+        return true;
     }
 
     @CacheEvict(value = "person", key = "#person.id")
     public boolean update(Person person) {
-        int ret = jdbcTemplate.update("update person set name=?,cardNo=?,sex=?,clazz=?,faceImage=? where id=?",
-                person.getName(), person.getCardNo(), person.getSex(), person.getClazz(), person.getFaceImage(), person.getId());
+        int ret = jdbcTemplate.update("update person set name=?,cardNo=?,sex=?,groupId=?,faceImage=? where id=?",
+                person.getName(), person.getCardNo(), person.getSex(), person.getGroupId(), person.getFaceImage(), person.getId());
         return ret > 0;
     }
 
     @CacheEvict(value = "person", key = "#id")
-    public boolean delete(String id) {
+    public boolean delete(Integer id) {
         return jdbcTemplate.update("delete from person where id=?", id) > 0;
     }
+
+    public List<Person> findByGroupId(Integer groupId) {
+        try {
+            return jdbcTemplate.query("select * from person where groupId=? order by createTime desc",
+                    new BeanPropertyRowMapper<>(Person.class), groupId);
+        } catch (EmptyResultDataAccessException e) {
+            return null;
+        }
+    }
+
+    public int countByGroupId(Integer groupId) {
+        return jdbcTemplate.queryForObject("select count(1) from person where groupId=?",
+                Integer.class, groupId);
+    }
 }

+ 8 - 7
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/repository/SchoolRepository.java

@@ -1,10 +1,10 @@
 package com.usoftchina.smartschool.device.client.biometric.repository;
 
 import com.usoftchina.smartschool.device.client.biometric.po.School;
-import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.Cacheable;
 import org.springframework.dao.EmptyResultDataAccessException;
 import org.springframework.jdbc.core.BeanPropertyRowMapper;
-import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.stereotype.Repository;
 
 /**
@@ -12,11 +12,9 @@ import org.springframework.stereotype.Repository;
  * @date 2019/3/11
  */
 @Repository
-public class SchoolRepository {
-
-    @Autowired
-    private JdbcTemplate jdbcTemplate;
+public class SchoolRepository extends BaseRepository{
 
+    @Cacheable("school")
     public School find() {
         try {
             return jdbcTemplate.queryForObject("select * from school",
@@ -26,11 +24,14 @@ public class SchoolRepository {
         }
     }
 
-    public void save(School school) {
+    @Cacheable("school")
+    public School save(School school) {
         jdbcTemplate.update("insert into school (name,id,secret) values (?,?,?)", school.getName(),
                 school.getId(), school.getSecret());
+        return school;
     }
 
+    @CacheEvict("school")
     public void update(School school) {
         jdbcTemplate.update("update school set name=?,id=?,secret=?",
                 school.getName(), school.getId(), school.getSecret());

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

@@ -0,0 +1,54 @@
+package com.usoftchina.smartschool.device.client.biometric.service;
+
+import com.usoftchina.smartschool.device.base.Result;
+import com.usoftchina.smartschool.device.client.biometric.po.CloudSetting;
+import com.usoftchina.smartschool.device.client.biometric.po.School;
+import com.usoftchina.smartschool.device.dto.AccessControlInfo;
+import com.usoftchina.smartschool.device.exception.ExceptionCode;
+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.util.StringUtils;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * @author yingp
+ * @date 2019/11/21
+ */
+@Service
+public class CloudService {
+    @Autowired
+    private SchoolService schoolService;
+    @Autowired
+    private RestTemplate restTemplate;
+    @Autowired
+    private CloudSettingService cloudSettingService;
+    /**
+     * 远程保存门禁出入记录
+     *
+     * @param info
+     */
+    public void saveAccessRecord(AccessControlInfo info) {
+        //去除cardNo末尾中的\u0000...\u0000
+        info.setCardNo(info.getCardNo().trim());
+        School school = schoolService.find();
+        if (null == school) {
+            ExceptionCode.ERROR_UNKNOWN.occur("未设置学校参数");
+        }
+        CloudSetting setting = cloudSettingService.find();
+        if (null == setting || StringUtils.isEmpty(setting.getAccessRecordUrl())) {
+            ExceptionCode.ERROR_UNKNOWN.occur("未设置远端接口");
+        }
+        info.setSchoolId(school.getId());
+        ResponseEntity<Result> response = restTemplate.postForEntity(setting.getAccessRecordUrl(), info, Result.class);
+        if (response.getStatusCode() == HttpStatus.OK) {
+            Result result = response.getBody();
+            if (!result.isSuccess()) {
+                ExceptionCode.ERROR_UNKNOWN.occur(result.getMessage());
+            }
+        } else {
+            ExceptionCode.ERROR_UNKNOWN.occur("保存门禁出入记录失败");
+        }
+    }
+}

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

@@ -0,0 +1,45 @@
+package com.usoftchina.smartschool.device.client.biometric.service;
+
+import com.usoftchina.smartschool.device.client.biometric.config.DeviceServerProperties;
+import com.usoftchina.smartschool.device.client.biometric.po.CloudSetting;
+import com.usoftchina.smartschool.device.client.biometric.repository.CloudSettingRepository;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author yingp
+ * @date 2019/11/21
+ */
+@Service
+public class CloudSettingService implements InitializingBean {
+    @Autowired
+    private CloudSettingRepository cloudSettingRepository;
+
+    @Autowired
+    private DeviceServerProperties deviceServerProperties;
+
+    public CloudSetting find() {
+        return cloudSettingRepository.find();
+    }
+
+    public void save(CloudSetting setting) {
+        if (null != find()) {
+            cloudSettingRepository.update(setting);
+        } else {
+            cloudSettingRepository.save(setting);
+        }
+    }
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        CloudSetting setting = find();
+        if (null == setting) {
+            setting = new CloudSetting();
+            // 默认接口设置
+            setting.setAccessRecordUrl(deviceServerProperties.getAccessRecordUrl());
+            setting.setIcRecordUrl(deviceServerProperties.getIcRecordUrl());
+            save(setting);
+        }
+    }
+}

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

@@ -1,26 +1,20 @@
 package com.usoftchina.smartschool.device.client.biometric.service;
 
-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.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.GatePersonRepository;
 import com.usoftchina.smartschool.device.client.biometric.util.RandomUtils;
-import com.usoftchina.smartschool.device.dto.AccessControlInfo;
 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.Value;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.StringUtils;
-import org.springframework.web.client.RestTemplate;
 
 import java.net.InetAddress;
 import java.util.Date;
@@ -39,15 +33,6 @@ public class FaceGateService {
     @Autowired
     private GatePersonRepository gatePersonRepository;
 
-    @Autowired
-    private RestTemplate restTemplate;
-
-    @Autowired
-    private DeviceServerProperties deviceServerProperties;
-
-    @Autowired
-    private SchoolService schoolService;
-
     @Autowired
     private BiometricDeviceService deviceService;
 
@@ -149,7 +134,7 @@ public class FaceGateService {
             } catch (Exception e) {
                 logger.warn("unbind device error", e);
             }
-            faceGateRepository.delete(id);
+            faceGateRepository.delete(oldOne);
             gatePersonRepository.deleteByGate(id);
         }
     }
@@ -158,24 +143,7 @@ public class FaceGateService {
         return faceGateRepository.findById(id);
     }
 
-    /**
-     * 远程保存门禁出入记录
-     *
-     * @param info
-     */
-    public void saveAccessRecord(AccessControlInfo info) {
-        //去除cardNo末尾中的\u0000...\u0000
-        info.setCardNo(info.getCardNo().trim());
-        info.setSchoolId(schoolService.find().getId());
-        ResponseEntity<Result> response = restTemplate.postForEntity(
-                deviceServerProperties.getAccessControlEvent(), info, Result.class);
-        if (response.getStatusCode() == HttpStatus.OK) {
-            Result result = response.getBody();
-            if (!result.isSuccess()) {
-                ExceptionCode.ERROR_UNKNOWN.occur(result.getMessage());
-            }
-        } else {
-            ExceptionCode.ERROR_UNKNOWN.occur("保存门禁出入记录失败");
-        }
+    public FaceGate findByCode(String code) {
+        return faceGateRepository.findByCode(code);
     }
 }

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

@@ -53,13 +53,11 @@ public class GatePersonService {
      * @param gateId
      */
     @Transactional(rollbackFor = Exception.class)
-    public void issue(String personId, String gateId, Date endTime) {
+    public void issue(Integer 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);
@@ -76,7 +74,7 @@ public class GatePersonService {
         personPo.setEndTime(endTime);
         personPo.setName(person.getName());
         personPo.setGenderCode(String.valueOf(person.getSex()));
-        personPo.setId(gatePerson.getDevicePersonId());
+        personPo.setId(personId);
         try {
             File imageFile = fileService.getById(person.getFaceImage());
             String imageBase64 = Base64.getEncoder().encodeToString(Files.readAllBytes(imageFile.toPath()));
@@ -99,7 +97,7 @@ public class GatePersonService {
      * @param gateIds
      */
     @Transactional(rollbackFor = Exception.class)
-    public void issue(String personId, String[] gateIds, Date endTime) {
+    public void issue(Integer personId, String[] gateIds, Date endTime) {
         for (String gateId : gateIds) {
             issue(personId, gateId, endTime);
         }
@@ -112,8 +110,8 @@ public class GatePersonService {
      * @param gateIds
      */
     @Transactional(rollbackFor = Exception.class)
-    public void issue(String[] personIds, String[] gateIds, Date endTime) {
-        for (String personId : personIds) {
+    public void issue(Integer[] personIds, String[] gateIds, Date endTime) {
+        for (Integer personId : personIds) {
             issue(personId, gateIds, endTime);
         }
     }
@@ -124,7 +122,7 @@ public class GatePersonService {
      * @param personId
      */
     @Transactional(rollbackFor = Exception.class)
-    public void issue(String personId) {
+    public void issue(Integer personId) {
         List<FaceGate> gates = faceGateRepository.findAll();
         if (!CollectionUtils.isEmpty(gates)) {
             // 默认100年
@@ -139,11 +137,11 @@ public class GatePersonService {
      * @param id
      */
     @Transactional(rollbackFor = Exception.class)
-    public void cancelIssue(String id) {
+    public void cancelIssue(Integer id) {
         // 先删除设备上的记录
         GatePerson gatePerson = gatePersonRepository.findById(id);
         FaceGate faceGate = faceGateRepository.findById(gatePerson.getGateId());
-        deviceService.cancelIssue(faceGate, gatePerson.getDevicePersonId());
+        deviceService.cancelIssue(faceGate, gatePerson.getPersonId());
 
         gatePersonRepository.delete(id);
     }
@@ -154,13 +152,13 @@ public class GatePersonService {
      * @param personId
      */
     @Transactional(rollbackFor = Exception.class)
-    public void cancelIssueByPerson(String personId) {
+    public void cancelIssueByPerson(Integer 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());
+                deviceService.cancelIssue(faceGate, personId);
             }
 
             gatePersonRepository.deleteByPerson(personId);

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

@@ -0,0 +1,38 @@
+package com.usoftchina.smartschool.device.client.biometric.service;
+
+import com.usoftchina.smartschool.device.client.biometric.jdbc.DynamicDataSourceRegister;
+import com.usoftchina.smartschool.device.client.biometric.po.IcCard;
+import com.usoftchina.smartschool.device.client.biometric.repository.IcCardRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * @author yingp
+ * @date 2019/3/11
+ */
+@Service
+public class IcCardService {
+
+    @Autowired
+    private IcCardRepository icCardRepository;
+
+    @Autowired
+    private DynamicDataSourceRegister dynamicDataSourceRegister;
+
+    public IcCard find() {
+        return icCardRepository.find();
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    public void save(IcCard card) {
+        IcCard oldOne = find();
+        if (null != oldOne) {
+            icCardRepository.update(card);
+            dynamicDataSourceRegister.unregister(oldOne);
+        } else {
+            icCardRepository.save(card);
+        }
+        dynamicDataSourceRegister.createDataSource(card);
+    }
+}

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

@@ -0,0 +1,53 @@
+package com.usoftchina.smartschool.device.client.biometric.service;
+
+import com.usoftchina.smartschool.device.client.biometric.po.PersonGroup;
+import com.usoftchina.smartschool.device.client.biometric.repository.PersonGroupRepository;
+import com.usoftchina.smartschool.device.client.biometric.repository.PersonRepository;
+import com.usoftchina.smartschool.device.exception.ExceptionCode;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * @author yingp
+ * @date 2019/11/20
+ */
+@Service
+public class PersonGroupService {
+    @Autowired
+    private PersonGroupRepository personGroupRepository;
+    @Autowired
+    private PersonRepository personRepository;
+
+    public List<PersonGroup> findAll() {
+        return personGroupRepository.findAll();
+    }
+
+    public PersonGroup add(String groupName) {
+        PersonGroup personGroup = personGroupRepository.findByName(groupName);
+        if (null != personGroup) {
+            ExceptionCode.ERROR_GROUP_EXIST.occur("分组已存在");
+        }
+        personGroup = new PersonGroup();
+        personGroup.setName(groupName);
+        personGroupRepository.save(personGroup);
+        return personGroup;
+    }
+
+    public void update(PersonGroup personGroup) {
+        PersonGroup nameExistOne = personGroupRepository.findByName(personGroup.getName());
+        if (null != nameExistOne) {
+            ExceptionCode.ERROR_GROUP_EXIST.occur("分组已存在");
+        }
+        personGroupRepository.update(personGroup);
+    }
+
+    public void delete(PersonGroup personGroup) {
+        int usedCount = personRepository.countByGroupId(personGroup.getId());
+        if (usedCount > 0) {
+            ExceptionCode.ERROR_ILLEGAL.occur("分组已被使用,无法删除");
+        }
+        personGroupRepository.delete(personGroup.getId());
+    }
+}

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

@@ -2,7 +2,6 @@ package com.usoftchina.smartschool.device.client.biometric.service;
 
 import com.usoftchina.smartschool.device.client.biometric.po.Person;
 import com.usoftchina.smartschool.device.client.biometric.repository.PersonRepository;
-import com.usoftchina.smartschool.device.client.biometric.util.RandomUtils;
 import com.usoftchina.smartschool.device.exception.ExceptionCode;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -29,6 +28,10 @@ public class PersonService {
         return personRepository.findAll();
     }
 
+    public  List<Person> findByGroupId(Integer groupId) {
+        return personRepository.findByGroupId(groupId);
+    }
+
     /**
      * 保存人员信息
      *
@@ -45,10 +48,6 @@ public class PersonService {
             } else if (!person.getId().equals(oldOne.getId())) {
                 ExceptionCode.ERROR_PERSON_EXIST.occur();
             }
-        } else {
-            if (isNew) {
-                person.setId(RandomUtils.randomString());
-            }
         }
 
         if (isNew) {
@@ -60,7 +59,7 @@ public class PersonService {
     }
 
     @Transactional(rollbackFor = Exception.class)
-    public void delete(String id) {
+    public void delete(Integer id) {
         gatePersonService.cancelIssueByPerson(id);
         personRepository.delete(id);
     }

+ 21 - 1
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/support/SplashScreen.java

@@ -1,11 +1,17 @@
 package com.usoftchina.smartschool.device.client.biometric.support;
 
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
 import javafx.geometry.Insets;
 import javafx.scene.Node;
 import javafx.scene.Parent;
+import javafx.scene.control.Label;
 import javafx.scene.control.ProgressBar;
 import javafx.scene.image.ImageView;
 import javafx.scene.layout.VBox;
+import javafx.scene.paint.Color;
+import javafx.scene.text.Font;
+import javafx.scene.text.FontWeight;
 
 import java.util.concurrent.CompletableFuture;
 
@@ -16,17 +22,27 @@ import java.util.concurrent.CompletableFuture;
 public class SplashScreen {
     private static String DEFAULT_IMAGE = "/image/ydy.png";
     private final CompletableFuture<Runnable> showing = new CompletableFuture();
+    private StringProperty processText = new SimpleStringProperty();
 
     public SplashScreen() {
     }
 
+    public SplashScreen(String text) {
+        setText(text);
+    }
+
     public Parent getParent() {
         ImageView imageView = new ImageView(getClass().getResource(getImagePath()).toExternalForm());
         ProgressBar splashProgressBar = new ProgressBar();
         splashProgressBar.setPrefWidth(imageView.getImage().getWidth());
+        Label processLabel = new Label();
+        processLabel.textProperty().bind(processText);
+        processLabel.setTextFill(Color.valueOf("949495"));
+        processLabel.setFont(Font.font("Microsoft YaHei", FontWeight.BOLD, 14));
+        processLabel.setPadding(new Insets(5, 0, 0, 0));
         VBox vbox = new VBox();
         vbox.setPadding(new Insets(10));
-        vbox.getChildren().addAll(new Node[]{imageView, splashProgressBar});
+        vbox.getChildren().addAll(new Node[]{imageView, splashProgressBar, processLabel});
         return vbox;
     }
 
@@ -37,4 +53,8 @@ public class SplashScreen {
     public CompletableFuture<Runnable> getShowing() {
         return showing;
     }
+
+    public void setText(String text) {
+        processText.setValue(text);
+    }
 }

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

@@ -26,6 +26,9 @@ public class GateScheduler {
     private BiometricDeviceService deviceService;
     private final Logger logger = LoggerFactory.getLogger(GateScheduler.class);
 
+    /**
+     * ping设备检查健康状况
+     */
     @Scheduled(fixedDelay = 120000)
     public void pingDevice() {
         List<FaceGate> faceGates = faceGateService.findAll();
@@ -37,6 +40,7 @@ public class GateScheduler {
                     faceGate.setStatus(HealthStatus.HEALTHY.getValue());
                 } else {
                     faceGate.setStatus(HealthStatus.UN_HEALTHY.getValue());
+                    logger.warn(faceGate.getIp() + " maybe has a problem");
                 }
                 faceGateService.save(faceGate);
             });

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

@@ -0,0 +1,108 @@
+package com.usoftchina.smartschool.device.client.biometric.task;
+
+import com.alibaba.fastjson.JSON;
+import com.usoftchina.smartschool.device.base.Result;
+import com.usoftchina.smartschool.device.client.biometric.jdbc.DynamicDataSourceContextHolder;
+import com.usoftchina.smartschool.device.client.biometric.jdbc.DynamicDataSourceRegister;
+import com.usoftchina.smartschool.device.client.biometric.po.CloudSetting;
+import com.usoftchina.smartschool.device.client.biometric.po.IcCard;
+import com.usoftchina.smartschool.device.client.biometric.po.School;
+import com.usoftchina.smartschool.device.client.biometric.service.CloudSettingService;
+import com.usoftchina.smartschool.device.client.biometric.service.IcCardService;
+import com.usoftchina.smartschool.device.client.biometric.service.SchoolService;
+import com.usoftchina.smartschool.device.dto.AccTransDetail;
+import com.usoftchina.smartschool.device.exception.ExceptionCode;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.*;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.util.StringUtils;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.List;
+
+/**
+ * @author yingp
+ * @date 2019/11/21
+ */
+@Component
+public class IcCardScheduler {
+
+    @Autowired
+    private IcCardService icCardService;
+    @Autowired
+    private SchoolService schoolService;
+    @Autowired
+    private DynamicDataSourceRegister dynamicDataSourceRegister;
+    @Autowired
+    private CloudSettingService cloudSettingService;
+    @Autowired
+    private JdbcTemplate jdbcTemplate;
+    @Autowired
+    private RestTemplate restTemplate;
+
+    /**
+     * 轮询上传IC卡记录
+     */
+    @Scheduled(fixedRate = 1000 * 20, initialDelay = 5000)
+    public void postRecordTask() {
+        IcCard card = icCardService.find();
+        School school = schoolService.find();
+        CloudSetting cloudSetting = cloudSettingService.find();
+        String serverUrl = null == cloudSetting ? null : cloudSetting.getIcRecordUrl();
+        if (null != card && dynamicDataSourceRegister.contains(card) && null != school && !StringUtils.isEmpty(serverUrl)) {
+            DynamicDataSourceContextHolder.set(card);
+            try {
+                doTask(school, serverUrl);
+            } finally {
+                DynamicDataSourceContextHolder.clear();
+            }
+        }
+    }
+
+    //SQL语句
+    private static final String insertSql = "INSERT INTO XF_AccTransDetail_push(GUID,AccNo,AccTransDay,AccTransType,IMoneyValue,OMoneyValue,CardUseNum,CardMoneyValue,DevID,DevOwner,OprtNo,Remark,XFDataSysID,XFPosMoneyByCredit,SendStatus,CreateTime) "
+            + " SELECT SOURCE.GUID,SOURCE.AccNo,SOURCE.AccTransDay,SOURCE.AccTransType,SOURCE.IMoneyValue,SOURCE.OMoneyValue,SOURCE.CardUseNum,SOURCE.CardMoneyValue,SOURCE.DevID,SOURCE.DevOwner,SOURCE.OprtNo,SOURCE.Remark,SOURCE.XFDataSysID,SOURCE.XFPosMoneyByCredit,'待上传',GETDATE() "
+            + " FROM XF_AccTransDetail SOURCE WHERE NOT EXISTS (SELECT 1 FROM XF_AccTransDetail_push TARGET WHERE SOURCE.GUID = TARGET.GUID)";
+
+    private static final String getDataSql = "select top 100 XF_AccTransDetail_push.GUID,RS_EMP.EmpSysID,RS_EMP.EmpNo,RS_EMP.EmpName,XF_AccHead.AccNo, XF_AccTransDetail_push.AccTransType,XF_AccTransDetail_push.AccTransDay,XF_AccTransDetail_push.IMoneyValue,XF_AccTransDetail_push.OMoneyValue,XF_AccTransDetail_push.CardMoneyValue "
+            + "from XF_AccTransDetail_push left join XF_AccHead on XF_AccTransDetail_push.AccNo=XF_AccHead.AccNo left join Tx_EmpCard on Tx_EmpCard.CardID=XF_AccHead.CardID "
+            + "left join RS_EMP ON RS_EMP.EmpSysID=XF_AccHead.EmpSysID where XF_AccTransDetail_push.SendStatus=? order by AccTransDay desc";
+
+    private static final String updateSql = "update XF_AccTransDetail_push set SendStatus = ? where GUID in (";
+
+    private void doTask(School school, String serverUrl){
+        //1.准备本次需要传输的数据->转移至中间表
+        jdbcTemplate.execute(insertSql);
+        //2.获取本次传输的数据
+        List<AccTransDetail> resultList = jdbcTemplate.query(getDataSql, new BeanPropertyRowMapper<>(AccTransDetail.class), "待上传");
+        if (resultList.size() > 0) {
+            //3.传输
+            HttpHeaders headers = new HttpHeaders();
+            headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+            MultiValueMap<String, String> requestEntity = new LinkedMultiValueMap<>();
+            requestEntity.add("data", JSON.toJSONString(resultList));
+            requestEntity.add("school", school.getId());
+            HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(requestEntity, headers);
+            ResponseEntity<Result> response = restTemplate.postForEntity(serverUrl, httpEntity, Result.class);
+            if (response.getStatusCode() == HttpStatus.OK) {
+                Result<String> result = response.getBody();
+                if (!result.isSuccess()) {
+                    ExceptionCode.ERROR_UNKNOWN.occur(result.getMessage());
+                } else {
+                    //更新发送的数据
+                    String ids = result.getData();
+                    if (!StringUtils.isEmpty(ids)) {
+                        jdbcTemplate.update(updateSql + ids + ")", "已上传");
+                    }
+                }
+            } else {
+                ExceptionCode.ERROR_UNKNOWN.occur("IC卡消费记录发送失败");
+            }
+        }
+    }
+}

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

@@ -2,6 +2,7 @@ package com.usoftchina.smartschool.device.client.biometric.util;
 
 import javafx.scene.control.Alert;
 import javafx.scene.control.ButtonType;
+import javafx.scene.control.TextInputDialog;
 
 import java.util.Optional;
 
@@ -32,4 +33,15 @@ public class AlertUtils {
         alert.setContentText(message);
         return alert.showAndWait();
     }
+
+    public static Optional<String> input(String message, String defaultValue) {
+        TextInputDialog dialog = new TextInputDialog(defaultValue);
+        dialog.setHeaderText(null);
+        dialog.setContentText(message);
+        return dialog.showAndWait();
+    }
+
+    public static Optional<String> input(String message) {
+        return input(message, null);
+    }
 }

+ 3 - 4
applications/device/device-client-biometric/src/main/resources/application.yml

@@ -1,10 +1,9 @@
 device:
   server:
     # 门禁事件的服务端接口
-    accessControlEvent: https://school-api.ydyhz.com/api/device/accesscontrol/event
-  # IC卡传输的服务端接口
-  icCard:
-    icCardUrl: https://school-api.ydyhz.com/api/device/iccard/consume/record
+    accessRecordUrl: https://school-api.ydyhz.com/api/device/accesscontrol/event
+    # IC卡传输的服务端接口
+    icRecordUrl: https://school-api.ydyhz.com/api/device/iccard/consume/record
 server:
   tomcat:
     uri-encoding: UTF-8

+ 17 - 6
applications/device/device-client-biometric/src/main/resources/schema.sql

@@ -16,7 +16,7 @@ create table if not exists school
 (
 name varchar(500) not null,
 id varchar(32) not null,
-secret varchar(100) not null
+secret varchar(100)
 );
 
 create table if not exists iccard
@@ -28,11 +28,17 @@ password varchar(50) not null,
 databaseName varchar(30) not null
 );
 
+create table if not exists person_group
+(
+id int primary key auto_increment not null,
+name varchar(100) not null
+);
+
 create table if not exists person
 (
-id varchar(50) primary key not null,
+id int primary key auto_increment not null,
 name varchar(100) not null,
-clazz varchar(100),
+groupId int,
 cardNo varchar(20) not null,
 sex int,
 createTime datetime,
@@ -41,10 +47,15 @@ faceImage varchar(100)
 
 create table if not exists gate_person
 (
-id varchar(50) primary key not null,
+id int primary key auto_increment not null,
 gateId varchar(50) not null,
-personId varchar(50) not null,
-devicePersonId int,
+personId int not null,
 issueTime datetime,
 endTime datetime
+);
+
+create table if not exists cloud_setting
+(
+accessRecordUrl varchar(255),
+icRecordUrl varchar(255)
 );

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

@@ -197,3 +197,32 @@ AnchorPane, BorderPane, VBox {
     -fx-effect: dropshadow(three-pass-box, rgba(0, 0, 0, 0.8), 10, 0, 0, 0);
     -fx-border-color: #bfcfda;
 }
+
+/*list-view*/
+.person-group-list {
+    -fx-background-color: #f8f8fb;
+    -fx-background-insets: 0;
+    -fx-border-color: #bfcfda;
+    -fx-padding: 0;
+}
+
+.person-group-list .list-cell {
+    -fx-background-color: #f8f8fb;
+    -fx-padding: 6px 20px;
+    -fx-text-fill: -fx-dark-color;
+}
+
+.person-group-list .list-cell-text:focused {
+    -fx-background-color: -fx-light-color;
+    -fx-text-fill: -fx-primary-color;
+    -fx-border-width: 0;
+}
+
+.person-group-list .list-cell-text:hover {
+    -fx-background-color: -fx-light-color;
+}
+
+.person-group-list .list-cell.list-cell-primary {
+    -fx-font-weight: bold;
+}
+

+ 34 - 0
applications/device/device-client-biometric/src/main/resources/view/cloud.fxml

@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?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.CloudSettingController"
+            prefHeight="400.0" prefWidth="600.0">
+    <top>
+        <ToolBar BorderPane.alignment="CENTER">
+            <items>
+                <Button fx:id="saveButton" text="保存" onAction="#handleSave"/>
+            </items>
+        </ToolBar>
+    </top>
+    <center>
+        <AnchorPane BorderPane.alignment="CENTER">
+            <HBox layoutX="10" layoutY="30" styleClass="field-control">
+                <children>
+                    <Label alignment="CENTER_RIGHT" styleClass="field-label" prefHeight="40" prefWidth="120" style="-fx-padding: 0 5px 0 0" text="闸机通行记录接口" />
+                    <TextArea fx:id="accessRecordUrlField" prefHeight="80" prefWidth="300" wrapText="true"/>
+                </children>
+            </HBox>
+            <HBox layoutX="10" layoutY="120" styleClass="field-control">
+                <children>
+                    <Label alignment="CENTER_RIGHT" styleClass="field-label" prefHeight="40" prefWidth="120" style="-fx-padding: 0 5px 0 0" text="IC卡记录接口" />
+                    <TextArea fx:id="icRecordUrlField" prefHeight="80" prefWidth="300" wrapText="true"/>
+                </children>
+            </HBox>
+        </AnchorPane>
+    </center>
+</BorderPane>

+ 1 - 3
applications/device/device-client-biometric/src/main/resources/view/gate.fxml

@@ -22,9 +22,7 @@
                 <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"/>
+                <Button fx:id="issueStatusButton" text="下发状态" onAction="#handleIssueStatus" disable="true"/>
                 <Separator/>
                 <Button text="刷新" onAction="#handleRefresh"/>
             </items>

+ 5 - 9
applications/device/device-client-biometric/src/main/resources/view/gate_issue_status.fxml

@@ -1,8 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
-<?import java.lang.*?>
-<?import java.util.*?>
-<?import javafx.scene.*?>
 <?import javafx.scene.control.*?>
 <?import javafx.scene.layout.*?>
 
@@ -21,12 +18,11 @@
     <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="截止时间"/>
+                <TableColumn fx:id="gateNameCol" prefWidth="120.0" minWidth="120" text="设备名称" style="-fx-alignment: CENTER"/>
+                <TableColumn fx:id="gatePlaceCol" prefWidth="120.0" minWidth="120" text="设备位置" style="-fx-alignment: CENTER"/>
+                <TableColumn fx:id="gateIpCol" prefWidth="120.0" minWidth="120" text="设备IP" style="-fx-alignment: CENTER"/>
+                <TableColumn fx:id="issueTimeCol" prefWidth="160.0" minWidth="160" text="下发时间" style="-fx-alignment: CENTER"/>
+                <TableColumn fx:id="endTimeCol" prefWidth="160.0" minWidth="160" text="截止时间" style="-fx-alignment: CENTER"/>
             </columns>
         </TableView>
     </center>

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

@@ -85,13 +85,13 @@
         <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"/>
+                <TextArea fx:id="dataServiceUrlField" wrapText="true" prefWidth="300" prefHeight="90"/>
             </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"/>
+                <TextArea fx:id="deviceServiceUrlField" wrapText="true" prefWidth="300" prefHeight="90"/>
             </children>
         </HBox>
         <HBox styleClass="field-control" layoutX="440" layoutY="380">

+ 53 - 0
applications/device/device-client-biometric/src/main/resources/view/ic.fxml

@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.scene.control.*?>
+<?import javafx.scene.layout.*?>
+
+<?import com.usoftchina.smartschool.device.client.biometric.control.IntegerField?>
+<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.IcCardController"
+            prefHeight="400.0" prefWidth="600.0">
+    <top>
+        <ToolBar BorderPane.alignment="CENTER">
+            <items>
+                <Button fx:id="saveButton" text="保存" onAction="#handleSave"/>
+            </items>
+        </ToolBar>
+    </top>
+    <center>
+        <AnchorPane BorderPane.alignment="CENTER">
+            <HBox layoutX="10" layoutY="30" styleClass="field-control">
+                <children>
+                    <Label alignment="CENTER_RIGHT" styleClass="field-label" prefHeight="40" prefWidth="100" style="-fx-padding: 0 5px 0 0" text="IP地址" />
+                    <TextField fx:id="ipField" prefHeight="40" prefWidth="200" promptText="例如192.168.1.100"/>
+                </children>
+            </HBox>
+            <HBox layoutX="10" layoutY="80" styleClass="field-control">
+                <children>
+                    <Label alignment="CENTER_RIGHT" styleClass="field-label" prefHeight="40" prefWidth="100" style="-fx-padding: 0 5px 0 0" text="端口" />
+                    <IntegerField fx:id="portField" prefHeight="40" prefWidth="200" promptText="tcp端口,例如1433"/>
+                </children>
+            </HBox>
+            <HBox layoutX="10" layoutY="130" styleClass="field-control">
+                <children>
+                    <Label alignment="CENTER_RIGHT" styleClass="field-label" prefHeight="40" prefWidth="100" style="-fx-padding: 0 5px 0 0" text="用户名" />
+                    <TextField fx:id="usernameField" prefHeight="40" prefWidth="200" promptText="数据库账号,例如sa"/>
+                </children>
+            </HBox>
+            <HBox layoutX="10" layoutY="180" styleClass="field-control">
+                <children>
+                    <Label alignment="CENTER_RIGHT" styleClass="field-label" prefHeight="40" prefWidth="100" style="-fx-padding: 0 5px 0 0" text="密码" />
+                    <TextField fx:id="passwordField" prefHeight="40" prefWidth="200" />
+                </children>
+            </HBox>
+            <HBox layoutX="10" layoutY="230" styleClass="field-control">
+                <children>
+                    <Label alignment="CENTER_RIGHT" styleClass="field-label" prefHeight="40" prefWidth="100" style="-fx-padding: 0 5px 0 0" text="数据库" />
+                    <TextField fx:id="databaseNameField" prefHeight="40" prefWidth="200" />
+                </children>
+            </HBox>
+        </AnchorPane>
+    </center>
+</BorderPane>

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

@@ -18,7 +18,7 @@
                     <children>
                         <TreeItem expanded="true" value="设备管理">
                             <graphic>
-                                <IconFont fx:constant="FACE"/>
+                                <IconFont value="face"/>
                             </graphic>
                             <children>
                                 <NavItem value="人脸闸机" viewPath="/view/gate.fxml"/>
@@ -26,7 +26,7 @@
                         </TreeItem>
                         <TreeItem expanded="true" value="人员管理">
                             <graphic>
-                                <IconFont fx:constant="PERSON"/>
+                                <IconFont value="person"/>
                             </graphic>
                             <children>
                                 <NavItem value="人员信息" viewPath="/view/person.fxml"/>
@@ -34,12 +34,12 @@
                         </TreeItem>
                         <TreeItem expanded="true" value="基础设置">
                             <graphic>
-                                <IconFont fx:constant="SETTING"/>
+                                <IconFont value="setting"/>
                             </graphic>
                             <children>
-                                <NavItem value="学校信息" viewPath="/view/gate.fxml"/>
-                                <NavItem value="IC卡" viewPath="/view/gate.fxml"/>
-                                <NavItem value="云平台" viewPath="/view/gate.fxml"/>
+                                <NavItem value="学校信息" viewPath="/view/school.fxml"/>
+                                <NavItem value="IC卡" viewPath="/view/ic.fxml"/>
+                                <NavItem value="云平台" viewPath="/view/cloud.fxml"/>
                             </children>
                         </TreeItem>
                     </children>

+ 22 - 6
applications/device/device-client-biometric/src/main/resources/view/person.fxml

@@ -7,6 +7,10 @@
 <?import javafx.scene.control.ToolBar?>
 <?import javafx.scene.control.Button?>
 <?import javafx.scene.control.Separator?>
+<?import javafx.scene.control.TreeView?>
+<?import javafx.scene.control.TreeItem?>
+<?import com.usoftchina.smartschool.device.client.biometric.control.IconFont?>
+<?import javafx.scene.control.ListView?>
 <BorderPane xmlns="http://javafx.com/javafx/8.0.172-ea"
             stylesheets="/style/main.css"
             fx:controller="com.usoftchina.smartschool.device.client.biometric.controller.PersonController"
@@ -14,11 +18,17 @@
     <top>
         <ToolBar BorderPane.alignment="CENTER">
             <items>
-                <Button text="新增" onAction="#handleAdd"/>
+                <Button text="新增分组" onAction="#handleAddGroup"/>
                 <Separator/>
-                <Button fx:id="editButton" text="修改" onAction="#handleEdit" disable="true"/>
+                <Button fx:id="editGroupButton" text="修改分组" onAction="#handleEditGroup" disable="true"/>
                 <Separator/>
-                <Button fx:id="delButton" text="删除" onAction="#handleDelete" disable="true"/>
+                <Button fx:id="delGroupButton" text="删除分组" onAction="#handleDeleteGroup" disable="true"/>
+                <Separator/>
+                <Button text="新增人员" onAction="#handleAdd"/>
+                <Separator/>
+                <Button fx:id="editButton" text="修改人员" onAction="#handleEdit" disable="true"/>
+                <Separator/>
+                <Button fx:id="delButton" text="删除人员" onAction="#handleDelete" disable="true"/>
                 <Separator/>
                 <Button text="导入" onAction="#handleImport"/>
                 <Separator/>
@@ -33,12 +43,18 @@
     <center>
         <TableView fx:id="tableView" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
             <columns>
-                <TableColumn fx:id="classCol" prefWidth="120.0" minWidth="120" text="班级" style="-fx-alignment: CENTER"/>
+                <TableColumn fx:id="groupCol" prefWidth="120.0" minWidth="120" text="分组" style="-fx-alignment: CENTER"/>
                 <TableColumn fx:id="nameCol" prefWidth="100.0" minWidth="100" text="姓名" style="-fx-alignment: CENTER"/>
                 <TableColumn fx:id="sexCol" prefWidth="100.0" text="性别" style="-fx-alignment: CENTER"/>
-                <TableColumn fx:id="cardNoCol" prefWidth="120.0" minWidth="120" text="卡号" style="-fx-alignment: CENTER"/>
-                <TableColumn fx:id="createTimeCol" prefWidth="160.0" minWidth="160" text="创建时间" style="-fx-alignment: CENTER"/>
+                <TableColumn fx:id="cardNoCol" prefWidth="120.0" minWidth="120" text="卡号"
+                             style="-fx-alignment: CENTER"/>
+                <TableColumn fx:id="createTimeCol" prefWidth="160.0" minWidth="160" text="创建时间"
+                             style="-fx-alignment: CENTER"/>
             </columns>
         </TableView>
     </center>
+    <left>
+        <ListView fx:id="listView" styleClass="person-group-list" prefHeight="200.0" prefWidth="110.0" style="-fx-border-width: 0 1px 0 0">
+        </ListView>
+    </left>
 </BorderPane>

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

@@ -12,8 +12,8 @@
     <children>
         <HBox layoutX="10" layoutY="30" styleClass="field-control">
             <children>
-                <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" />
+                <Label alignment="CENTER_RIGHT" styleClass="field-label" prefHeight="40" prefWidth="100" style="-fx-padding: 0 5px 0 0" text="分组" />
+                <ComboBox fx:id="groupBox" prefHeight="40" prefWidth="200" />
             </children>
         </HBox>
         <HBox layoutX="10" layoutY="80" styleClass="field-control">

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

@@ -1,14 +0,0 @@
-<?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>

+ 31 - 0
applications/device/device-client-biometric/src/main/resources/view/person_issue_status.fxml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?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.PersonIssueStatusController"
+            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="personGroupCol" prefWidth="100.0" minWidth="100" text="分组" style="-fx-alignment: CENTER"/>
+                <TableColumn fx:id="personNameCol" prefWidth="100.0" minWidth="100" text="姓名" style="-fx-alignment: CENTER"/>
+                <TableColumn fx:id="personSexCol" prefWidth="60.0" minWidth="60" text="性别" style="-fx-alignment: CENTER"/>
+                <TableColumn fx:id="personCardNoCol" prefWidth="120.0" minWidth="120" text="卡号" style="-fx-alignment: CENTER"/>
+                <TableColumn fx:id="issueTimeCol" prefWidth="160.0" minWidth="160" text="下发时间" style="-fx-alignment: CENTER"/>
+                <TableColumn fx:id="endTimeCol" prefWidth="160.0" minWidth="160" text="截止时间" style="-fx-alignment: CENTER"/>
+            </columns>
+        </TableView>
+    </center>
+</BorderPane>
+

+ 40 - 0
applications/device/device-client-biometric/src/main/resources/view/school.fxml

@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?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.SchoolController"
+            prefHeight="400.0" prefWidth="600.0">
+    <top>
+        <ToolBar BorderPane.alignment="CENTER">
+            <items>
+                <Button fx:id="saveButton" text="保存" onAction="#handleSave"/>
+            </items>
+        </ToolBar>
+    </top>
+    <center>
+        <AnchorPane BorderPane.alignment="CENTER">
+            <HBox layoutX="10" layoutY="30" styleClass="field-control">
+                <children>
+                    <Label alignment="CENTER_RIGHT" styleClass="field-label" prefHeight="40" prefWidth="100" style="-fx-padding: 0 5px 0 0" text="学校ID" />
+                    <TextField fx:id="idField" prefHeight="40" prefWidth="200" />
+                </children>
+            </HBox>
+            <HBox layoutX="10" layoutY="80" styleClass="field-control">
+                <children>
+                    <Label alignment="CENTER_RIGHT" styleClass="field-label" prefHeight="40" prefWidth="100" style="-fx-padding: 0 5px 0 0" text="学校名称" />
+                    <TextArea fx:id="nameField" prefHeight="80" prefWidth="200" wrapText="true"/>
+                </children>
+            </HBox>
+            <HBox layoutX="10" layoutY="170" styleClass="field-control">
+                <children>
+                    <Label alignment="CENTER_RIGHT" styleClass="field-label" prefHeight="40" prefWidth="100" style="-fx-padding: 0 5px 0 0" text="私钥" />
+                    <TextArea fx:id="secretField" prefHeight="80" prefWidth="200" promptText="学校数据传输私钥" wrapText="true"/>
+                </children>
+            </HBox>
+        </AnchorPane>
+    </center>
+</BorderPane>

+ 3 - 1
applications/device/device-core/src/main/java/com/usoftchina/smartschool/device/exception/ExceptionCode.java

@@ -9,9 +9,11 @@ import org.springframework.util.StringUtils;
  */
 public enum ExceptionCode implements BaseExceptionCode {
 
+    ERROR_ILLEGAL(400),
     ERROR_UNKNOWN(500),
     ERROR_IP_PORT_EXIST(501),
-    ERROR_PERSON_EXIST(502);
+    ERROR_PERSON_EXIST(502),
+    ERROR_GROUP_EXIST(503);
 
     private int code;
 

+ 7 - 7
applications/device/device-core/src/main/java/com/usoftchina/smartschool/device/util/DirtyBean.java → applications/device/device-core/src/main/java/com/usoftchina/smartschool/device/property/DirtyBean.java

@@ -1,4 +1,4 @@
-package com.usoftchina.smartschool.device.util;
+package com.usoftchina.smartschool.device.property;
 
 import javafx.beans.property.BooleanProperty;
 import javafx.beans.property.SimpleBooleanProperty;
@@ -14,7 +14,7 @@ import java.util.Objects;
 public abstract class DirtyBean {
 
     private boolean initialized = false;
-    private Map<String, DirtyProperty> modified;
+    private Map<String, DirtyValue> modified;
     private BooleanProperty dirty;
 
     protected void checkDirty(String property, Object oldValue, Object newValue) {
@@ -26,9 +26,9 @@ public abstract class DirtyBean {
             modified = new HashMap<>(1);
         }
         if (!modified.containsKey(property)) {
-            modified.put(property, new DirtyProperty(oldValue, newValue));
+            modified.put(property, new DirtyValue(oldValue, newValue));
         } else {
-            DirtyProperty dirtyProperty = modified.get(property);
+            DirtyValue dirtyProperty = modified.get(property);
             if (Objects.equals(dirtyProperty.getOriginalValue(), newValue)) {
                 modified.remove(property);
             } else {
@@ -49,7 +49,7 @@ public abstract class DirtyBean {
         return dirty;
     }
 
-    public Map<String, DirtyProperty> getModified() {
+    public Map<String, DirtyValue> getModified() {
         return modified;
     }
 
@@ -59,11 +59,11 @@ public abstract class DirtyBean {
         dirtyProperty().setValue(false);
     }
 
-    public class DirtyProperty {
+    public class DirtyValue {
         private Object originalValue;
         private Object value;
 
-        public DirtyProperty(Object originalValue, Object value) {
+        public DirtyValue(Object originalValue, Object value) {
             this.originalValue = originalValue;
             this.value = value;
         }

+ 22 - 0
applications/device/device-core/src/main/java/com/usoftchina/smartschool/device/property/DirtyObjectProperty.java

@@ -0,0 +1,22 @@
+package com.usoftchina.smartschool.device.property;
+
+import javafx.beans.property.Property;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+
+/**
+ * @author yingp
+ * @date 2019/11/21
+ */
+public class DirtyObjectProperty<T> extends SimpleObjectProperty<T> implements Property<T> {
+    public DirtyObjectProperty(DirtyBean bean, String name) {
+        super(bean, name);
+        addListener(new ChangeListener<T>() {
+            @Override
+            public void changed(ObservableValue<? extends T> observable, T oldValue, T newValue) {
+                bean.checkDirty(name, oldValue, newValue);
+            }
+        });
+    }
+}

+ 27 - 0
applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/AccessControlEvent.java

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

+ 113 - 33
applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/AccessLog.java

@@ -1,5 +1,8 @@
 package com.usoftchina.smartschool.device.biometric;
 
+import com.usoftchina.smartschool.device.dto.AccessControlInfo;
+
+import java.util.Base64;
 import java.util.Date;
 
 /**
@@ -18,6 +21,14 @@ public class AccessLog {
      * 1:比对通过 2:比对不通过 3:未比对
      */
     private Integer verifyResult;
+    /**
+     * 比对类型
+     * 1:卡核验(人证)
+     * 2:员工核验(人像)
+     * 3:访客核验
+     * 4:人证+人像
+     */
+    private Integer compareType;
     /**
      * 通过时间
      */
@@ -25,7 +36,7 @@ public class AccessLog {
     /**
      * 人员 ID
      */
-    private String personId;
+    private Integer personId;
     private String name;
     /**
      * 性别代码
@@ -51,11 +62,37 @@ public class AccessLog {
     private String dataType;
     private Date endTime;
     private String accessCardNum;
-    private String cardImgPath;
-    private String sceneImgPath;
-    private String faceImgPath;
+    /**
+     * 底库照片
+     */
+    private String cardImg;
+    /**
+     * 场景照片
+     */
+    private String sceneImg;
+    /**
+     * 人脸采集图像
+     */
+    private String faceImg;
+    /**
+     * 红外照片
+     */
+    private String infraredImg;
+    /**
+     * 质量分数
+     */
     private Float faceQualityScore;
+    /**
+     * 人脸比分
+     */
     private Float faceCompareScore;
+    /**
+     * 比对结果
+     * 1 比对通过
+     * 2 比对不通过
+     * 3 未比对
+     */
+    private Integer faceCompareResult;
 
     public String getDeviceCode() {
         return deviceCode;
@@ -81,12 +118,12 @@ public class AccessLog {
         this.passTime = passTime;
     }
 
-    public String getPersonId() {
-        return personId;
+    public Integer getCompareType() {
+        return compareType;
     }
 
-    public void setPersonId(String personId) {
-        this.personId = personId;
+    public void setCompareType(Integer compareType) {
+        this.compareType = compareType;
     }
 
     public String getName() {
@@ -145,53 +182,77 @@ public class AccessLog {
         this.accessCardNum = accessCardNum;
     }
 
-    public String getCardImgPath() {
-        return cardImgPath;
+    public Float getFaceQualityScore() {
+        return faceQualityScore;
+    }
+
+    public void setFaceQualityScore(Float faceQualityScore) {
+        this.faceQualityScore = faceQualityScore;
+    }
+
+    public Float getFaceCompareScore() {
+        return faceCompareScore;
     }
 
-    public void setCardImgPath(String cardImgPath) {
-        this.cardImgPath = cardImgPath;
+    public void setFaceCompareScore(Float faceCompareScore) {
+        this.faceCompareScore = faceCompareScore;
     }
 
-    public String getSceneImgPath() {
-        return sceneImgPath;
+    public Integer getPersonId() {
+        return personId;
     }
 
-    public void setSceneImgPath(String sceneImgPath) {
-        this.sceneImgPath = sceneImgPath;
+    public void setPersonId(Integer personId) {
+        this.personId = personId;
     }
 
-    public String getFaceImgPath() {
-        return faceImgPath;
+    public String getCardImg() {
+        return cardImg;
     }
 
-    public void setFaceImgPath(String faceImgPath) {
-        this.faceImgPath = faceImgPath;
+    public void setCardImg(String cardImg) {
+        this.cardImg = cardImg;
     }
 
-    public Float getFaceQualityScore() {
-        return faceQualityScore;
+    public String getSceneImg() {
+        return sceneImg;
     }
 
-    public void setFaceQualityScore(Float faceQualityScore) {
-        this.faceQualityScore = faceQualityScore;
+    public void setSceneImg(String sceneImg) {
+        this.sceneImg = sceneImg;
     }
 
-    public Float getFaceCompareScore() {
-        return faceCompareScore;
+    public String getFaceImg() {
+        return faceImg;
     }
 
-    public void setFaceCompareScore(Float faceCompareScore) {
-        this.faceCompareScore = faceCompareScore;
+    public void setFaceImg(String faceImg) {
+        this.faceImg = faceImg;
+    }
+
+    public String getInfraredImg() {
+        return infraredImg;
+    }
+
+    public void setInfraredImg(String infraredImg) {
+        this.infraredImg = infraredImg;
+    }
+
+    public Integer getFaceCompareResult() {
+        return faceCompareResult;
+    }
+
+    public void setFaceCompareResult(Integer faceCompareResult) {
+        this.faceCompareResult = faceCompareResult;
     }
 
     @Override
     public String toString() {
-        return "AccessPo{" +
+        return "AccessLog{" +
                 "deviceCode='" + deviceCode + '\'' +
                 ", verifyResult=" + verifyResult +
                 ", passTime=" + passTime +
-                ", personId='" + personId + '\'' +
+                ", personId=" + personId +
                 ", name='" + name + '\'' +
                 ", genderCode='" + genderCode + '\'' +
                 ", nationCode='" + nationCode + '\'' +
@@ -199,11 +260,30 @@ public class AccessLog {
                 ", dataType='" + dataType + '\'' +
                 ", endTime=" + endTime +
                 ", accessCardNum='" + accessCardNum + '\'' +
-                ", cardImgPath='" + cardImgPath + '\'' +
-                ", sceneImgPath='" + sceneImgPath + '\'' +
-                ", faceImgPath='" + faceImgPath + '\'' +
                 ", faceQualityScore=" + faceQualityScore +
                 ", faceCompareScore=" + faceCompareScore +
+                ", faceCompareResult=" + faceCompareResult +
                 '}';
     }
+
+    public AccessControlInfo toAccessControlInfo() {
+        AccessControlInfo accessControlInfo = new AccessControlInfo();
+        accessControlInfo.setCardNo(getAccessCardNum());
+        accessControlInfo.setEventTime(getPassTime());
+        switch (getCompareType()) {
+            case 1:
+                accessControlInfo.setOpenMethod(AccessControlInfo.OpenMethod.CARD);
+                break;
+            case 2:
+                accessControlInfo.setOpenMethod(AccessControlInfo.OpenMethod.FACE_RECOGNITION);
+                break;
+            case 4:
+                accessControlInfo.setOpenMethod(AccessControlInfo.OpenMethod.CARD_AND_FACE);
+                break;
+            default:
+                accessControlInfo.setOpenMethod(AccessControlInfo.OpenMethod.UNKNOWN);
+        }
+        accessControlInfo.setImageData(Base64.getDecoder().decode(getSceneImg()));
+        return accessControlInfo;
+    }
 }

+ 0 - 1
applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/BiometricDeviceService.java

@@ -194,7 +194,6 @@ public class BiometricDeviceService implements DeviceApi {
                         }
                     }
                 }
-                deviceSetting.ready();
                 return deviceSetting;
             }
         } else {

+ 4 - 1
applications/device/device-sdk-biometric/src/main/java/com/usoftchina/smartschool/device/biometric/DeviceManageController.java

@@ -5,9 +5,12 @@ import com.alibaba.fastjson.TypeReference;
 import com.usoftchina.smartschool.device.context.SpringContextHolder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.RestTemplate;
 
 import javax.servlet.http.HttpServletRequest;
 import java.io.IOException;
@@ -33,10 +36,10 @@ public class DeviceManageController {
     @Deprecated
     public ResultPo deviceHeartbeat(HttpServletRequest request) throws IOException {
         String body = request.getReader().readLine();
-        logger.debug(body);
         if (null != body && body.startsWith("{")) {
             RequestPo<HealthInfo> req = JSON.parseObject(body, new TypeReference<RequestPo<HealthInfo>>() {
             });
+            logger.debug(req.getResponseBody().toString());
             SpringContextHolder.getContext().publishEvent(new HeartbeatEvent(req.getResponseBody().getDeviceCode(), this));
         }
         return ResultPo.success();

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

@@ -1,6 +1,6 @@
 package com.usoftchina.smartschool.device.biometric;
 
-import com.usoftchina.smartschool.device.util.DirtyBean;
+import com.usoftchina.smartschool.device.property.DirtyBean;
 import javafx.beans.property.*;
 import javafx.beans.value.ChangeListener;
 import javafx.beans.value.ObservableValue;
@@ -45,7 +45,7 @@ public class DeviceSetting extends DirtyBean implements Serializable {
     /**
      * 心跳间隔
      */
-    private IntegerProperty informInterval;
+    private ObjectProperty<Integer> informInterval;
     /**
      * 人脸阈值级别
      */
@@ -57,7 +57,7 @@ public class DeviceSetting extends DirtyBean implements Serializable {
     /**
      * 人脸框大小(像素)
      */
-    private IntegerProperty faceSize;
+    private ObjectProperty<Integer> faceSize;
     /**
      * 数据上传地址
      */
@@ -69,7 +69,7 @@ public class DeviceSetting extends DirtyBean implements Serializable {
     /**
      * 已入库人像容量
      */
-    private IntegerProperty capacity;
+    private ObjectProperty<Integer> capacity;
 
     public String getSoftwareVersion() {
         return softwareVersionProperty().get();
@@ -227,9 +227,9 @@ public class DeviceSetting extends DirtyBean implements Serializable {
         return informIntervalProperty().get();
     }
 
-    public IntegerProperty informIntervalProperty() {
+    public ObjectProperty<Integer> informIntervalProperty() {
         if (null == informInterval) {
-            informInterval = new SimpleIntegerProperty();
+            informInterval = new SimpleObjectProperty<>();
             informInterval.addListener(new ChangeListener<Number>() {
                 @Override
                 public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
@@ -308,9 +308,9 @@ public class DeviceSetting extends DirtyBean implements Serializable {
         return faceSizeProperty().get();
     }
 
-    public IntegerProperty faceSizeProperty() {
+    public ObjectProperty<Integer> faceSizeProperty() {
         if (null == faceSize) {
-            faceSize = new SimpleIntegerProperty();
+            faceSize = new SimpleObjectProperty<>();
             faceSize.addListener(new ChangeListener<Number>() {
                 @Override
                 public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
@@ -371,9 +371,9 @@ public class DeviceSetting extends DirtyBean implements Serializable {
         return capacityProperty().get();
     }
 
-    public IntegerProperty capacityProperty() {
+    public ObjectProperty<Integer> capacityProperty() {
         if (null == capacity) {
-            capacity = new SimpleIntegerProperty();
+            capacity = new SimpleObjectProperty<>();
         }
         return capacity;
     }

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

@@ -1,5 +1,7 @@
 package com.usoftchina.smartschool.device.biometric;
 
+import com.alibaba.fastjson.JSON;
+import com.usoftchina.smartschool.device.context.SpringContextHolder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.web.bind.annotation.PostMapping;
@@ -27,7 +29,11 @@ public class VerificationController {
     @PostMapping("/passlog/passFullLog")
     public ResultPo passLog(HttpServletRequest request) throws IOException {
         String body = request.getReader().readLine();
-        logger.debug(body);
+        if (null != body && body.startsWith("{")) {
+            AccessLog accessLog = JSON.parseObject(body, AccessLog.class);
+            logger.debug(accessLog.toString());
+            SpringContextHolder.getContext().publishEvent(new AccessControlEvent(this, accessLog.getDeviceCode(), accessLog.toAccessControlInfo()));
+        }
         return ResultPo.success();
     }
 }