增加移动考勤机

This commit is contained in:
2025-10-14 16:02:32 +08:00
parent d6263c6430
commit 87e58ca4af
8 changed files with 947 additions and 1 deletions

View File

@ -18,6 +18,14 @@
<dependencies>
<!-- Java WebSocket 标准API -->
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
<scope>provided</scope>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.drewnoakes</groupId>-->
<!-- <artifactId>metadata-extractor</artifactId>-->

View File

@ -0,0 +1,163 @@
package org.dromara.mobileAttendanceMachine;
import lombok.extern.log4j.Log4j2;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
/**
* @Author 铁憨憨
* @Date 2025/10/14 15:48
* @Version 1.0
*
* 考勤设备消息发送工具类对应Golang的ws包工具方法
*/
@Service
@Log4j2
public class DeviceMessageSender {
/**
* 组装下发人员信息对应Golang的TheSenderInformationOfTheAssemblyPersonnel
* @param sn 设备编号
* @param userId 用户ID
* @param name 用户名
* @param face 人脸模板地址HTTP链接
* @return 异常信息无异常则返回null
*/
public static Exception sendPersonnelInformation(String sn, String userId, String name, String face) {
try {
// 生成UUID
String sUuid = DeviceWebSocketServer.generateUUIDWithSixRandomDigits();
// 构建人员信息消息体
KqjEntity.PeopleInformation people = new KqjEntity().new PeopleInformation();
people.setCmd("to_device");
people.setFrom(sUuid);
people.setTo(sn);
// 构建消息数据
KqjEntity.PeopleInData data = new KqjEntity().new PeopleInData();
data.setCmd("addUser");
data.setUserId(userId);
data.setName(name);
data.setFaceTemplate(face);
data.setIdValid(""); // 永久有效
people.setData(data);
// 发送请求并等待响应(忽略响应结果,只关注是否成功)
DeviceWebSocketServer.sendRequestAndWaitResponse(sn, sUuid, people);
return null;
} catch (Exception e) {
log.error("下发人员信息失败SN: {}, UserID: {}", sn, userId, e);
return e;
}
}
/**
* 获取打卡设备所有人员对应Golang的SelectUserAll
* @param sn 设备编号
* @return 响应结果(包含人员信息)
* @throws Exception 发送或接收过程中的异常
*/
public static KqjEntity.CommonResponse getAllUsers(String sn) throws Exception {
// 生成UUID
String sUuid = DeviceWebSocketServer.generateUUIDWithSixRandomDigits();
// 构建获取人员信息请求
KqjEntity.PersonnelInformationAcquisition request = new KqjEntity().new PersonnelInformationAcquisition();
request.setCmd("to_device");
request.setFrom(sUuid);
request.setTo(sn);
// 构建请求数据
KqjEntity.PersonnelInformationAcquisitionTwo data = new KqjEntity().new PersonnelInformationAcquisitionTwo();
data.setCmd("getUserInfo");
data.setValue(1); // 1表示获取所有人员
request.setData(data);
// 发送请求并返回响应
return DeviceWebSocketServer.sendRequestAndWaitResponse(sn, sUuid, request);
}
/**
* 删除指定人员对应Golang的DelByUserId
* @param sn 设备编号
* @param userId 要删除的用户ID
* @return 响应结果
* @throws Exception 发送或接收过程中的异常
*/
public static KqjEntity.CommonResponse deleteUser(String sn, String userId) throws Exception {
// 生成UUID
String sUuid = DeviceWebSocketServer.generateUUIDWithSixRandomDigits();
// 构建删除人员请求
KqjEntity.DeletionOfPersonnel request = new KqjEntity().new DeletionOfPersonnel();
request.setCmd("to_device");
request.setFrom(sUuid);
request.setTo(sn);
// 构建请求数据
KqjEntity.DeletionOfPersonnelData data = new KqjEntity().new DeletionOfPersonnelData();
data.setCmd("delUser");
data.setUserId(userId);
data.setUserType(0); // 0表示人脸接口下发的数据
request.setData(data);
// 发送请求并返回响应
return DeviceWebSocketServer.sendRequestAndWaitResponse(sn, sUuid, request);
}
/**
* 批量删除指定人员对应Golang的BatchDelete
* @param sn 设备编号
* @param userIds 要删除的用户ID列表
* @return 响应结果
* @throws Exception 发送或接收过程中的异常
*/
public static KqjEntity.CommonResponse batchDeleteUsers(String sn, String[] userIds) throws Exception {
// 生成UUID
String sUuid = DeviceWebSocketServer.generateUUIDWithSixRandomDigits();
// 构建批量删除请求
KqjEntity.BatchDeletion request = new KqjEntity().new BatchDeletion();
request.setCmd("to_device");
request.setFrom(sUuid);
request.setTo(sn);
// 构建请求数据
KqjEntity.BatchDeletionData data = new KqjEntity().new BatchDeletionData();
data.setCmd("delMultiUser");
data.setUserIds(userIds);
data.setUserType(0); // 0表示人脸接口下发的数据
request.setData(data);
// 发送请求并返回响应
return DeviceWebSocketServer.sendRequestAndWaitResponse(sn, sUuid, request);
}
/**
* 删除指定考勤机全部人员对应Golang的DelAll
* @param sn 设备编号
* @return 响应结果
* @throws Exception 发送或接收过程中的异常
*/
public static KqjEntity.CommonResponse deleteAllUsers(String sn) throws Exception {
// 生成UUID
String sUuid = DeviceWebSocketServer.generateUUIDWithSixRandomDigits();
// 构建删除全部人员请求
KqjEntity.DeletionALlOfPersonnel request = new KqjEntity().new DeletionALlOfPersonnel();
request.setCmd("to_device");
request.setFrom(sUuid);
request.setTo(sn);
// 构建请求数据
KqjEntity.DeletionALlOfPersonnelData data = new KqjEntity().new DeletionALlOfPersonnelData();
data.setCmd("delAllUser");
data.setUserType(0); // 0表示人脸接口下发的数据
request.setData(data);
// 发送请求并返回响应
return DeviceWebSocketServer.sendRequestAndWaitResponse(sn, sUuid, request);
}
}

View File

@ -0,0 +1,472 @@
package org.dromara.mobileAttendanceMachine;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.log4j.Log4j2;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.http.WebSocket;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
/**
* WebSocket服务端实现对应Golang的HandleWebSocket逻辑
* ServerEndpoint注解指定WebSocket连接路径
*/
@ServerEndpoint("/ws/device")
@Log4j2
public class DeviceWebSocketServer {
// ------------------------------ 常量定义对应Golang的const ------------------------------
public static class Constants {
public static final String DECLARE = "declare"; // 设备初上线
public static final String PING = "ping"; // 心跳
public static final String TO_CLIENT = "to_client"; // 服务器消息下发到客户端的响应
}
// JSON序列化/反序列化工具(单例)
private static final ObjectMapper objectMapper = new ObjectMapper();
// 1. 存储所有连接的设备信息key: 设备SNvalue: 设备信息)
private static final Map<String, KqjEntity.DeviceInfo> connectedDevices = new ConcurrentHashMap<>();
// 2. 存储UUID对应的响应通道key: UUIDvalue: 响应结果容器)
private static final Map<String, ResponseHolder<KqjEntity.CommonResponse>> responseChannels = new ConcurrentHashMap<>();
// 当前连接的WebSocket会话
private Session session;
// 当前连接的设备SN连接建立后从DECLARE消息中提取
private String currentDeviceSn;
/**
* 连接建立时触发对应Golang中upgrader.Upgrade后的初始化逻辑
*/
@OnOpen
public void onOpen(Session session) {
this.session = session;
log.info("新的WebSocket连接建立会话ID: {}", session.getId());
try {
// 读取设备第一条消息DECLARE消息完成设备注册
KqjEntity.DeclareMessage declareMsg = registerDevice();
this.currentDeviceSn = declareMsg.getSn();
log.info("设备注册成功SN: {}, 设备信息: {}", currentDeviceSn, declareMsg);
} catch (Exception e) {
log.error("设备注册失败,关闭连接", e);
try {
session.close(new CloseReason(CloseReason.CloseCodes.PROTOCOL_ERROR, "设备注册失败"));
} catch (IOException ex) {
log.error("关闭异常连接失败", ex);
}
}
}
/**
* 接收客户端消息时触发对应Golang的for循环读取消息逻辑
*/
@OnMessage
public void onMessage(String message, Session session) {
if (message == null || message.isEmpty()) {
log.warn("收到空消息,忽略处理");
return;
}
try {
// 先解析通用消息的CMD字段对应Golang的GenericMessage
KqjEntity.GenericMessage genericMsg = objectMapper.readValue(message, KqjEntity.GenericMessage.class);
String cmd = genericMsg.getCmd();
if (cmd == null) {
log.warn("收到无CMD字段的消息忽略: {}", message);
return;
}
// 根据CMD类型处理不同逻辑对应Golang的switch case
switch (cmd) {
case Constants.DECLARE:
log.info("设备在线心跳DECLARESN: {}", currentDeviceSn);
break;
case Constants.PING:
handlePing(message);
break;
case Constants.TO_CLIENT:
handleToClientResponse(message);
break;
default:
log.warn("收到未知CMD消息类型: {}, 内容: {}", cmd, message);
}
} catch (Exception e) {
log.error("处理消息失败,消息内容: {}", message, e);
}
}
/**
* 连接关闭时触发对应Golang的defer conn.Close()和资源清理逻辑)
*/
@OnClose
public void onClose(Session session, CloseReason closeReason) {
log.info("WebSocket连接关闭会话ID: {}, 原因: {}", session.getId(), closeReason);
// 1. 移除设备连接信息
if (currentDeviceSn != null) {
connectedDevices.remove(currentDeviceSn);
// 更新设备状态为离线对应Golang的service.BusAttendanceMachine().Change
updateDeviceStatus(currentDeviceSn, "0");
}
// 2. 清理当前设备对应的响应通道(避免内存泄漏)
responseChannels.entrySet().removeIf(entry -> {
if (entry.getValue().getSn().equals(currentDeviceSn)) {
entry.getValue().getResultFuture().completeExceptionally(
new Exception("设备连接已关闭,响应通道清理")
);
return true;
}
return false;
});
this.session = null;
this.currentDeviceSn = null;
}
/**
* 连接异常时触发
*/
@OnError
public void onError(Session session, Throwable throwable) {
log.error("WebSocket连接异常会话ID: {}", session.getId(), throwable);
// 异常时主动关闭连接
try {
if (session.isOpen()) {
session.close(new CloseReason(CloseReason.CloseCodes.UNEXPECTED_CONDITION, "连接异常"));
}
} catch (IOException e) {
log.error("关闭异常连接失败", e);
}
}
// ------------------------------ 核心业务方法 ------------------------------
/**
* 设备注册对应Golang的addDevice函数
* 读取DECLARE消息解析设备信息并存储
*/
private KqjEntity.DeclareMessage registerDevice() throws Exception {
// 阻塞读取第一条消息DECLARE消息
String firstMessage = readFirstMessage();
KqjEntity.DeclareMessage declareMsg = objectMapper.readValue(firstMessage, KqjEntity.DeclareMessage.class);
// 校验SN合法性
String sn = declareMsg.getSn();
if (sn == null || sn.isEmpty()) {
throw new IllegalArgumentException("设备SN为空注册失败");
}
// 解析客户端IP和端口对应Golang的parseRemoteAddr
InetSocketAddress remoteAddr = (InetSocketAddress) session.getUserProperties().get("javax.websocket.endpoint.remoteAddress");
String ip = remoteAddr.getAddress().getHostAddress();
String port = String.valueOf(remoteAddr.getPort());
// 存储设备信息
KqjEntity.DeviceInfo deviceInfo = new KqjEntity.DeviceInfo();
deviceInfo.setIp(ip);
deviceInfo.setPort(port);
// ======================== 添加类型检查 ========================
// 检查session是否能转换为WebSocket避免强转失败
if (!(session instanceof WebSocket)) {
throw new IllegalStateException("WebSocket容器不支持Session转WebSocket无法注册设备SN: " + sn);
}
// 强转并赋值
deviceInfo.setConn((WebSocket) session); // 用Session替换Golang的*websocket.Conn
// ============================================================
connectedDevices.put(sn, deviceInfo);
// 调用业务服务注册设备对应Golang的service.BusAttendanceMachine().Register
registerDeviceToService(sn);
return declareMsg;
}
/**
* 处理PING消息对应Golang的handlePing函数
*/
private void handlePing(String message) throws Exception {
KqjEntity.DeclareMessage pingMsg = objectMapper.readValue(message, KqjEntity.DeclareMessage.class);
String sn = pingMsg.getSn();
// 1. 回复PONG消息
KqjEntity.PongMessage pongMsg = new KqjEntity.PongMessage();
pongMsg.setCmd(Constants.PING);
pongMsg.setFrom("server");
pongMsg.setTo(sn);
KqjEntity.PongMessageData pongData = new KqjEntity.PongMessageData();
pongData.setCmd("pong");
pongMsg.setData(pongData);
// 发送PONG消息
session.getBasicRemote().sendText(objectMapper.writeValueAsString(pongMsg));
log.info("发送PONG消息给设备SN: {}", sn);
// 2. 更新设备连接状态对应Golang的connectedDevices[sn]重设)
if (connectedDevices.containsKey(sn)) {
KqjEntity.DeviceInfo deviceInfo = connectedDevices.get(sn);
deviceInfo.setConn((WebSocket) session);
connectedDevices.put(sn, deviceInfo);
}
// 3. 调用业务服务更新设备状态对应Golang的service.BusAttendanceMachine().Register
registerDeviceToService(sn);
}
/**
* 处理TO_CLIENT响应消息对应Golang的requestResponse函数
*/
private void handleToClientResponse(String message) throws Exception {
log.info("收到TO_CLIENT响应消息: {}", message);
KqjEntity.CommonResponse commonResp = objectMapper.readValue(message, KqjEntity.CommonResponse.class);
// 根据UUID查找响应通道传递响应结果
String uuid = commonResp.getTo();
ResponseHolder<KqjEntity.CommonResponse> holder = responseChannels.get(uuid);
if (holder != null) {
holder.getResultFuture().complete(commonResp);
responseChannels.remove(uuid); // 移除已完成的通道
log.info("响应已分发到UUID: {}, 响应内容: {}", uuid, commonResp);
} else {
log.warn("未找到UUID: {}对应的响应通道,响应丢弃", uuid);
}
}
// ------------------------------ 工具方法 ------------------------------
/**
* 读取设备第一条消息(阻塞直到收到消息)
*/
private String readFirstMessage() throws Exception {
// 使用CompletableFuture等待消息
final java.util.concurrent.CompletableFuture<String> firstMsgFuture = new java.util.concurrent.CompletableFuture<>();
// 使用AtomicReference包装临时处理器解决未初始化问题
final java.util.concurrent.atomic.AtomicReference<MessageHandler.Whole<String>> tempHandlerRef = new java.util.concurrent.atomic.AtomicReference<>();
// 定义临时消息处理器
MessageHandler.Whole<String> tempHandler = msg -> {
if (!firstMsgFuture.isDone()) {
firstMsgFuture.complete(msg);
// 从引用中获取处理器并移除
session.removeMessageHandler(tempHandlerRef.get());
}
};
// 将处理器存入引用
tempHandlerRef.set(tempHandler);
// 注册处理器
session.addMessageHandler(tempHandler);
// 等待消息超时10秒
return firstMsgFuture.get(10, TimeUnit.SECONDS);
// // 使用Java并发工具等待消息模拟Golang的阻塞读取
// final java.util.concurrent.CompletableFuture<String> firstMsgFuture = new java.util.concurrent.CompletableFuture<>();
//
// // 临时注册消息处理器,读取第一条消息后移除
// MessageHandler.Whole<String> tempHandler = msg -> {
// if (!firstMsgFuture.isDone()) {
// firstMsgFuture.complete(msg);
// // 移除临时处理器(避免重复处理)
// session.removeMessageHandler(tempHandler);
// }
// };
// session.addMessageHandler(tempHandler);
//
// // 等待消息超时10秒防止设备一直不发消息
// return firstMsgFuture.get(10, TimeUnit.SECONDS);
}
/**
* 发送消息给指定设备对应Golang的sendMessageToDevice函数
*/
public static boolean sendMessageToDevice(String sn, String uuid, Object message) {
// 1. 检查设备是否在线
KqjEntity.DeviceInfo deviceInfo = connectedDevices.get(sn);
if (deviceInfo == null) {
log.warn("设备不存在SN: {}", sn);
responseChannels.remove(uuid);
return false;
}
// 2. 将WebSocket转回Session因为状态由Session管理
WebSocket webSocket = deviceInfo.getConn();
if (!(webSocket instanceof Session)) {
log.warn("设备连接类型错误无法判断状态SN: {}", sn);
responseChannels.remove(uuid);
return false;
}
Session session = (Session) webSocket;
// 3. 检查连接是否打开
if (!session.isOpen()) {
log.warn("设备连接已关闭SN: {}", sn);
connectedDevices.remove(sn); // 移除已关闭的设备
responseChannels.remove(uuid);
return false;
}
try {
// 4. 序列化消息并发送
String msgJson = objectMapper.writeValueAsString(message);
session.getBasicRemote().sendText(msgJson); // 通过Session发送消息
log.info("发送消息给设备SN: {}, UUID: {}, 消息: {}", sn, uuid, msgJson);
return true;
} catch (Exception e) {
log.error("发送消息失败SN: {}, UUID: {}", sn, uuid, e);
// 发送失败时移除设备(可能连接已异常)
connectedDevices.remove(sn);
responseChannels.remove(uuid);
return false;
}
// // 1. 检查设备是否在线
// KqjEntity.DeviceInfo deviceInfo = connectedDevices.get(sn);
// if (deviceInfo == null || !deviceInfo.getSession().isOpen()) {
// log.warn("设备不在线SN: {}", sn);
// // 清理无效的响应通道
// responseChannels.remove(uuid);
// return false;
// }
//
// try {
// // 2. 序列化消息并发送
// String msgJson = objectMapper.writeValueAsString(message);
// deviceInfo.getSession().getBasicRemote().sendText(msgJson);
// log.info("发送消息给设备SN: {}, UUID: {}, 消息: {}", sn, uuid, msgJson);
// return true;
//
// } catch (Exception e) {
// log.error("发送消息失败SN: {}, UUID: {}", sn, uuid, e);
// // 清理异常的响应通道
// responseChannels.remove(uuid);
// return false;
// }
}
/**
* 发送请求并等待响应对应Golang的SendRequestAndWaitResponse函数
*/
public static KqjEntity.CommonResponse sendRequestAndWaitResponse(String sn, String uuid, Object payload) throws Exception {
// 1. 创建响应结果容器
ResponseHolder<KqjEntity.CommonResponse> responseHolder = new ResponseHolder<>(sn);
responseChannels.put(uuid, responseHolder);
try {
// 2. 发送请求
boolean sendSuccess = sendMessageToDevice(sn, uuid, payload);
if (!sendSuccess) {
throw new Exception("发送请求失败设备不在线或发送异常SN: " + sn);
}
// 3. 等待响应超时10秒
return responseHolder.getResultFuture().get(10, TimeUnit.SECONDS);
} catch (java.util.concurrent.TimeoutException e) {
log.error("等待响应超时SN: {}, UUID: {}", sn, uuid);
responseChannels.remove(uuid);
throw new Exception("等待响应超时10秒", e);
} finally {
// 清理响应通道(防止内存泄漏)
responseChannels.remove(uuid);
}
}
/**
* 生成带6位随机数的UUID对应Golang的GenerateUUIDWithSixRandomDigits函数
*/
public static String generateUUIDWithSixRandomDigits() {
// 生成标准UUID
String uuidStr = UUID.randomUUID().toString().replace("-", "");
// 生成6位随机数100000-999999
Random random = new Random();
int randomNum = random.nextInt(900000) + 100000;
// 拼接返回
return uuidStr + "-" + randomNum;
}
// ------------------------------ 业务服务调用模拟Golang的service层 ------------------------------
/**
* 注册设备到业务服务对应Golang的service.BusAttendanceMachine().Register
* 实际项目中替换为真实的Service调用
*/
private void registerDeviceToService(String sn) {
try {
// 模拟业务服务调用(如更新数据库设备状态为在线)
log.info("调用业务服务注册设备SN: {}", sn);
// TODO: 替换为真实的Service代码如Spring Bean调用
} catch (Exception e) {
log.error("业务服务注册设备失败SN: {}", sn, e);
}
}
/**
* 更新设备状态对应Golang的service.BusAttendanceMachine().Change
* 实际项目中替换为真实的Service调用
*/
private void updateDeviceStatus(String sn, String status) {
try {
log.info("调用业务服务更新设备状态SN: {}, 状态: {}", sn, status);
// TODO: 替换为真实的Service代码如Spring Bean调用
} catch (Exception e) {
log.error("业务服务更新设备状态失败SN: {}", sn, e);
}
}
// ------------------------------ 内部辅助类 ------------------------------
/**
* 响应结果容器对应Golang的chan CommonResponse
* 用CompletableFuture实现异步结果等待
*/
private static class ResponseHolder<T> {
private final String sn; // 关联的设备SN
private final java.util.concurrent.CompletableFuture<T> resultFuture;
public ResponseHolder(String sn) {
this.sn = sn;
this.resultFuture = new java.util.concurrent.CompletableFuture<>();
}
public String getSn() {
return sn;
}
public java.util.concurrent.CompletableFuture<T> getResultFuture() {
return resultFuture;
}
}
}

View File

@ -0,0 +1,299 @@
package org.dromara.mobileAttendanceMachine;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.net.http.WebSocket;
/**
* @Author 铁憨憨
* @Date 2025/10/14 14:47
* @Version 1.0
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class KqjEntity {
@Data
public static class DeviceInfo {
@JsonProperty("ip")
private String ip;
@JsonProperty("port")
private String port;
@JsonProperty("conn")
private WebSocket conn;
}
/**
* 通用消息结构,用于解析初始的 cmd 字段
*/
@Data
public class GenericMessage {
@JsonProperty("cmd")
private String cmd;
}
/**
* 公共响应结构
*/
@Data
public class CommonResponse {
@JsonProperty("cmd")
private String cmd;
@JsonProperty("from")
private String from;
@JsonProperty("to")
private String to;
@JsonProperty("data")
private CommonResponseData data;
}
@Data
public class CommonResponseData {
@JsonProperty("cmd")
private String cmd;
@JsonProperty("userIds")
private String[] userIds; // 用户IDS
@JsonProperty("user_id")
private String userId;
@JsonProperty("code")
private int code;
@JsonProperty("msg")
private String msg;
@JsonProperty("delFailed")
private DelMultiUserData[] delFailed;
}
@Data
public class DelMultiUserData {
@JsonProperty("user_id")
private String userId;
@JsonProperty("code")
private int code;
@JsonProperty("msg")
private String msg;
}
/**
* 设备上线消息
*/
@Data
public class DeclareMessage {
@JsonProperty("cmd")
private String cmd;
@JsonProperty("type")
private String type;
@JsonProperty("sn")
private String sn;
@JsonProperty("version_code")
private String versionCode;
@JsonProperty("version_name")
private String versionName;
@JsonProperty("token")
private String token;
@JsonProperty("ip")
private String ip;
@JsonProperty("timestamp")
private double timestamp;
}
@Data
public static class PongMessage {
@JsonProperty("cmd")
private String cmd;
@JsonProperty("from")
private String from;
@JsonProperty("to")
private String to;
@JsonProperty("data")
private PongMessageData data;
}
/**
* 心跳回复消息结构
*/
@Data
public static class PongMessageData {
@JsonProperty("cmd")
private String cmd;
}
/**
* 人员信息下发
*/
@Data
public class PeopleInformation {
@JsonProperty("cmd")
private String cmd; // 该接口固定为to_device
@JsonProperty("from")
private String from; // 可不填写,填写uuid来做为发送请求或响应的标识
@JsonProperty("to")
private String to; // 设备号(请查看公共设置中的设备号)
@JsonProperty("data")
private PeopleInData data;
}
@Data
public class PeopleInData {
@JsonProperty("cmd")
private String cmd;
@JsonProperty("user_id")
private String userId;
@JsonProperty("name")
private String name;
@JsonProperty("face_template")
private String faceTemplate; // http 链接图
@JsonProperty("id_valid")
private String idValid; // 人员有效期人员在这个时间点后无法通行格式yyyy-MM-dd 或者 yyyy-MM-dd HH:mm为 "" 则为永久
}
/**
* 删除人员
*/
@Data
public class DeletionOfPersonnel {
@JsonProperty("cmd")
private String cmd;
@JsonProperty("from")
private String from;
@JsonProperty("to")
private String to;
@JsonProperty("data")
private DeletionOfPersonnelData data;
}
@Data
public class DeletionOfPersonnelData {
@JsonProperty("cmd")
private String cmd;
@JsonProperty("user_id")
private String userId;
@JsonProperty("user_type")
private int userType; // 删除的用户类型0-人脸接口下发的数据 1-人证比对接口下发的数据
}
/**
* 批量删除
*/
@Data
public class BatchDeletion {
@JsonProperty("cmd")
private String cmd;
@JsonProperty("from")
private String from;
@JsonProperty("to")
private String to;
@JsonProperty("data")
private BatchDeletionData data;
}
@Data
public class BatchDeletionData {
@JsonProperty("cmd")
private String cmd;
@JsonProperty("user_ids")
private String[] userIds;
@JsonProperty("user_type")
private int userType; // 删除的用户类型0-人脸接口下发的数据 1-人证比对接口下发的数据
}
/**
* 删除全部人员
*/
@Data
public class DeletionALlOfPersonnel {
@JsonProperty("cmd")
private String cmd;
@JsonProperty("from")
private String from;
@JsonProperty("to")
private String to;
@JsonProperty("data")
private DeletionALlOfPersonnelData data;
}
@Data
public class DeletionALlOfPersonnelData {
@JsonProperty("cmd")
private String cmd;
@JsonProperty("user_type")
private int userType; // 删除的用户类型0-人脸接口下发的数据 1-人证比对接口下发的数据
}
/**
* 人员信息获取
*/
@Data
public class PersonnelInformationAcquisition {
@JsonProperty("cmd")
private String cmd; // 该接口固定为to_device
@JsonProperty("from")
private String from; // 可不填写,填写uuid来做为发送请求或响应的标识
@JsonProperty("to")
private String to; // 设备号(请查看公共设置中的设备号)
@JsonProperty("data")
private PersonnelInformationAcquisitionTwo data;
}
@Data
public class PersonnelInformationAcquisitionTwo {
@JsonProperty("cmd")
private String cmd;
@JsonProperty("value")
private int value;
}
}

View File

@ -383,6 +383,7 @@ public class SysUserController extends BaseController {
public TableDataInfo<SysUserVo> fbList(FbUserListDto dto, PageQuery pageQuery) {
return userService.selectPageFbUserList(dto, pageQuery);
}
/**
* 获取分包单位列表
*/

View File

@ -3,6 +3,7 @@ package org.dromara.transferData.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.xiaoymin.knife4j.annotations.Ignore;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.dromara.contractor.domain.SubConstructionUser;

View File

@ -1,5 +1,6 @@
package org.dromara.transferData.service;
import cn.dev33.satoken.secure.BCrypt;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
@ -566,6 +567,7 @@ public class TransferDataService {
sysUser.setNickName(user.getUserName());
sysUser.setPhonenumber(phone);
sysUser.setSex(user.getSex());
sysUser.setPassword(BCrypt.hashpw("123456"));
sysUser.setUserType(UserType.APP_USER.getUserType());
sysUserId = userService.save(sysUser);
} else {