修改签到和更新逻辑
All checks were successful
构建并部署 Spring Boot 应用 / build-and-deploy (push) Successful in 8m53s
All checks were successful
构建并部署 Spring Boot 应用 / build-and-deploy (push) Successful in 8m53s
This commit is contained in:
77
README.md
77
README.md
@@ -91,7 +91,7 @@ src
|
|||||||
系统通过WebSocket实现实时位置同步,端点为:`/ws/location`。
|
系统通过WebSocket实现实时位置同步,端点为:`/ws/location`。
|
||||||
|
|
||||||
##### 连接建立
|
##### 连接建立
|
||||||
客户端通过WebSocket连接到`/ws/location`端点建立连接。
|
客户端通过WebSocket连接到`/ws/location?userId={userId}`端点建立连接,需要在查询参数中提供用户ID。
|
||||||
|
|
||||||
##### 消息格式
|
##### 消息格式
|
||||||
所有消息都使用JSON格式。
|
所有消息都使用JSON格式。
|
||||||
@@ -127,33 +127,40 @@ src
|
|||||||
{
|
{
|
||||||
"userId": 123,
|
"userId": 123,
|
||||||
"name": "张三",
|
"name": "张三",
|
||||||
"role": "DELIVERY_PERSON"
|
"role": "DELIVERY_PERSON",
|
||||||
},
|
"userStatus": true,
|
||||||
{
|
"lastUpdateTime": 1634567890000,
|
||||||
"userId": 456,
|
"locationData": {
|
||||||
"name": "李四",
|
"latitude": 25.0342,
|
||||||
"role": "ADMIN"
|
"longitude": 102.7057,
|
||||||
|
"timestamp": 1634567890000
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **updateLocation(位置更新)** - 服务器向所有已签到用户广播位置更新
|
3. **userLocationList(用户位置列表)** - 服务器每30秒发送所有在线用户的位置列表
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "updateLocation",
|
"type": "userLocationList",
|
||||||
"userId": 123,
|
"users": [
|
||||||
"userRole": "DELIVERY_PERSON",
|
{
|
||||||
"latitude": 25.0342,
|
"userId": 123,
|
||||||
"longitude": 102.7057,
|
"locationData": {
|
||||||
"timestamp": 1634567890000
|
"latitude": 25.0342,
|
||||||
|
"longitude": 102.7057,
|
||||||
|
"timestamp": 1634567890000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
##### 交互流程
|
##### 交互流程
|
||||||
|
|
||||||
```
|
```
|
||||||
用户签到] --> B{签到成功?}
|
[用户签到] --> B{签到成功?}
|
||||||
B -->|是| C[建立WebSocket连接]
|
B -->|是| C[建立WebSocket连接]
|
||||||
B -->|否| D[签到失败]
|
B -->|否| D[签到失败]
|
||||||
C --> E[自动订阅位置更新]
|
C --> E[自动订阅位置更新]
|
||||||
@@ -161,8 +168,7 @@ src
|
|||||||
F --> G[接收在线用户列表]
|
F --> G[接收在线用户列表]
|
||||||
G --> H[开始位置更新循环]
|
G --> H[开始位置更新循环]
|
||||||
H --> I[发送位置更新]
|
H --> I[发送位置更新]
|
||||||
I --> J[接收位置广播]
|
I --> J[每30秒接收一次位置列表]
|
||||||
J --> H
|
|
||||||
K[用户签退] --> L{签退成功?}
|
K[用户签退] --> L{签退成功?}
|
||||||
L -->|是| M[自动取消订阅并关闭WebSocket连接]
|
L -->|是| M[自动取消订阅并关闭WebSocket连接]
|
||||||
L -->|否| N[签退失败]
|
L -->|否| N[签退失败]
|
||||||
@@ -331,6 +337,7 @@ deploy.bat
|
|||||||
- `GET /user/info` - 获取用户信息
|
- `GET /user/info` - 获取用户信息
|
||||||
- `POST /user/logout` - 用户登出
|
- `POST /user/logout` - 用户登出
|
||||||
- `POST /user/signin` - 用户签到
|
- `POST /user/signin` - 用户签到
|
||||||
|
- `POST /user/signout` - 用户签退
|
||||||
- `POST /user/register` - 注册为配送员
|
- `POST /user/register` - 注册为配送员
|
||||||
|
|
||||||
### 订单相关
|
### 订单相关
|
||||||
@@ -341,7 +348,7 @@ deploy.bat
|
|||||||
### 配送员相关
|
### 配送员相关
|
||||||
- `GET /delivery-persons` - 获取配送员列表
|
- `GET /delivery-persons` - 获取配送员列表
|
||||||
- `GET /delivery-persons/{id}` - 获取配送员详情
|
- `GET /delivery-persons/{id}` - 获取配送员详情
|
||||||
- `PUT /delivery-persons/{id}/location` - 更新配送员位置
|
- `GET /delivery-persons/{id}/orders` - 获取配送员订单
|
||||||
|
|
||||||
### 员工管理相关(仅限管理员访问)
|
### 员工管理相关(仅限管理员访问)
|
||||||
- `GET /employees` - 获取员工列表
|
- `GET /employees` - 获取员工列表
|
||||||
@@ -388,26 +395,33 @@ deploy.bat
|
|||||||
{
|
{
|
||||||
"userId": 123,
|
"userId": 123,
|
||||||
"name": "张三",
|
"name": "张三",
|
||||||
"role": "DELIVERY_PERSON"
|
"role": "DELIVERY_PERSON",
|
||||||
},
|
"userStatus": true,
|
||||||
{
|
"lastUpdateTime": 1634567890000,
|
||||||
"userId": 456,
|
"locationData": {
|
||||||
"name": "李四",
|
"latitude": 25.0342,
|
||||||
"role": "ADMIN"
|
"longitude": 102.7057,
|
||||||
|
"timestamp": 1634567890000
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **updateLocation(位置更新)** - 服务器向所有已签到用户广播位置更新
|
3. **userLocationList(用户位置列表)** - 服务器每30秒发送所有在线用户的位置列表
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "updateLocation",
|
"type": "userLocationList",
|
||||||
"userId": 123,
|
"users": [
|
||||||
"userRole": "DELIVERY_PERSON",
|
{
|
||||||
"latitude": 25.0342,
|
"userId": 123,
|
||||||
"longitude": 102.7057,
|
"locationData": {
|
||||||
"timestamp": 1634567890000
|
"latitude": 25.0342,
|
||||||
|
"longitude": 102.7057,
|
||||||
|
"timestamp": 1634567890000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -420,6 +434,7 @@ deploy.bat
|
|||||||
- 用户需要先通过REST API签到,然后才能建立WebSocket连接并接收位置更新
|
- 用户需要先通过REST API签到,然后才能建立WebSocket连接并接收位置更新
|
||||||
- 用户签退时,服务器会自动取消订阅并关闭WebSocket连接
|
- 用户签退时,服务器会自动取消订阅并关闭WebSocket连接
|
||||||
- 客户端不再需要发送subscribe/unsubscribe消息,这些操作由服务器自动处理
|
- 客户端不再需要发送subscribe/unsubscribe消息,这些操作由服务器自动处理
|
||||||
|
- 服务器现在每30秒批量发送一次所有在线用户的位置信息,而不是实时发送单个用户位置更新
|
||||||
|
|
||||||
### WebSocket位置同步增强(新增)
|
### WebSocket位置同步增强(新增)
|
||||||
为了支持管理员和配送员都能接收位置更新信息,我们对WebSocket位置同步功能进行了增强:
|
为了支持管理员和配送员都能接收位置更新信息,我们对WebSocket位置同步功能进行了增强:
|
||||||
|
@@ -68,18 +68,6 @@ public class DeliveryPersonController {
|
|||||||
response.setCurrentLocation(location);
|
response.setCurrentLocation(location);
|
||||||
return ResponseEntity.ok(response);
|
return ResponseEntity.ok(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新指定货运人员位置。
|
|
||||||
* @param id 货运人员ID
|
|
||||||
* @param locationRequest 包含经纬度
|
|
||||||
* @return 操作结果
|
|
||||||
*/
|
|
||||||
@PutMapping("/{id}/location")
|
|
||||||
public ResponseEntity<String> updateLocation(@PathVariable Long id, @RequestBody LocationRequest locationRequest) {
|
|
||||||
deliveryPersonService.updateLocation(id, locationRequest.getLongitude(), locationRequest.getLatitude());
|
|
||||||
return ResponseEntity.ok("位置更新成功");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取货运人员的当前订单。
|
* 获取货运人员的当前订单。
|
||||||
@@ -112,13 +100,4 @@ public class DeliveryPersonController {
|
|||||||
}).collect(Collectors.toList());
|
}).collect(Collectors.toList());
|
||||||
return ResponseEntity.ok(orders);
|
return ResponseEntity.ok(orders);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 位置请求体
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public static class LocationRequest {
|
|
||||||
private Double longitude;
|
|
||||||
private Double latitude;
|
|
||||||
}
|
|
||||||
}
|
}
|
@@ -1,91 +0,0 @@
|
|||||||
package com.light.delivery.controller;
|
|
||||||
|
|
||||||
import com.light.delivery.service.LocationWebSocketHandler;
|
|
||||||
import lombok.Data;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 位置同步控制器,提供已签到员工位置信息查询接口。
|
|
||||||
* 用于获取所有已签到员工的实时位置信息。
|
|
||||||
*/
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/location-sync")
|
|
||||||
public class LocationSyncController {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* WebSocket位置处理器依赖注入
|
|
||||||
*/
|
|
||||||
@Autowired
|
|
||||||
private LocationWebSocketHandler locationWebSocketHandler;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取所有已签到员工的实时位置。
|
|
||||||
* 仅返回有位置信息的员工(最近有更新位置的)。
|
|
||||||
* @return 员工位置列表
|
|
||||||
*/
|
|
||||||
@GetMapping("/employees/locations")
|
|
||||||
public ResponseEntity<List<EmployeeLocation>> getEmployeeLocations() {
|
|
||||||
// 获取所有已签到的员工位置信息
|
|
||||||
Map<Long, LocationWebSocketHandler.UserInfo> userInfos = locationWebSocketHandler.getAllSignedInUsers();
|
|
||||||
|
|
||||||
List<EmployeeLocation> locations = userInfos.entrySet().stream()
|
|
||||||
.map(entry -> {
|
|
||||||
Long userId = entry.getKey();
|
|
||||||
LocationWebSocketHandler.UserInfo userInfo = entry.getValue();
|
|
||||||
|
|
||||||
EmployeeLocation location = new EmployeeLocation();
|
|
||||||
location.setEmployeeId(userId);
|
|
||||||
location.setLatitude(locationWebSocketHandler.getUserLatitude(userId));
|
|
||||||
location.setLongitude(locationWebSocketHandler.getUserLongitude(userId));
|
|
||||||
location.setStatus(locationWebSocketHandler.getUserStatus(userId));
|
|
||||||
location.setRole(userInfo.getRole() != null ? userInfo.getRole() : "");
|
|
||||||
location.setName(userInfo.getName() != null ? userInfo.getName() : "");
|
|
||||||
return location;
|
|
||||||
})
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
return ResponseEntity.ok(locations);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 在线员工位置信息DTO,用于向前端返回员工位置数据。
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public static class EmployeeLocation {
|
|
||||||
/**
|
|
||||||
* 员工ID
|
|
||||||
*/
|
|
||||||
private Long employeeId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 姓名
|
|
||||||
*/
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 纬度
|
|
||||||
*/
|
|
||||||
private Double latitude;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 经度
|
|
||||||
*/
|
|
||||||
private Double longitude;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 状态
|
|
||||||
*/
|
|
||||||
private String status;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 角色
|
|
||||||
*/
|
|
||||||
private String role;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -180,15 +180,26 @@ public class UserController {
|
|||||||
/**
|
/**
|
||||||
* 用户签到接口。
|
* 用户签到接口。
|
||||||
* @param request HTTP请求对象,用于提取认证令牌
|
* @param request HTTP请求对象,用于提取认证令牌
|
||||||
|
* @param initialLocation 初始位置信息
|
||||||
* @return 更新后的用户信息
|
* @return 更新后的用户信息
|
||||||
*/
|
*/
|
||||||
@PostMapping("/signin")
|
@PostMapping("/signin")
|
||||||
public ResponseEntity<?> signIn(HttpServletRequest request) {
|
public ResponseEntity<?> signIn(HttpServletRequest request, @RequestBody LocationWebSocketHandler.LocationData initialLocation) {
|
||||||
String token = extractToken(request);
|
String token = extractToken(request);
|
||||||
if (token == null) {
|
if (token == null) {
|
||||||
return ResponseEntity.badRequest().body("Authorization token is missing");
|
return ResponseEntity.badRequest().body("Authorization token is missing");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查位置信息是否为空
|
||||||
|
if (initialLocation == null) {
|
||||||
|
return ResponseEntity.badRequest().body("Initial location information is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查位置信息的必要字段是否为空
|
||||||
|
if (initialLocation.getLatitude() == null || initialLocation.getLongitude() == null) {
|
||||||
|
return ResponseEntity.badRequest().body("Latitude and longitude are required in location information");
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
User user = userService.getUserInfo(token);
|
User user = userService.getUserInfo(token);
|
||||||
User updatedUser = userService.signIn(user.getId());
|
User updatedUser = userService.signIn(user.getId());
|
||||||
@@ -197,7 +208,7 @@ public class UserController {
|
|||||||
// 通知WebSocket处理器用户已签到并自动订阅
|
// 通知WebSocket处理器用户已签到并自动订阅
|
||||||
try {
|
try {
|
||||||
LocationWebSocketHandler handler = applicationContext.getBean(LocationWebSocketHandler.class);
|
LocationWebSocketHandler handler = applicationContext.getBean(LocationWebSocketHandler.class);
|
||||||
handler.userSignedIn(user.getId());
|
handler.userSignedIn(user.getId(), initialLocation);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.err.println("通知WebSocket签到状态时出错: " + e.getMessage());
|
System.err.println("通知WebSocket签到状态时出错: " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
@@ -20,13 +20,6 @@ public interface DeliveryPersonService {
|
|||||||
* @return 配送员对象
|
* @return 配送员对象
|
||||||
*/
|
*/
|
||||||
DeliveryPerson getDeliveryPersonById(Long id);
|
DeliveryPerson getDeliveryPersonById(Long id);
|
||||||
/**
|
|
||||||
* 更新配送员当前位置。
|
|
||||||
* @param id 配送员ID
|
|
||||||
* @param longitude 经度
|
|
||||||
* @param latitude 纬度
|
|
||||||
*/
|
|
||||||
void updateLocation(Long id, Double longitude, Double latitude);
|
|
||||||
/**
|
/**
|
||||||
* 获取指定配送员的所有订单。
|
* 获取指定配送员的所有订单。
|
||||||
* @param id 配送员ID
|
* @param id 配送员ID
|
||||||
|
@@ -5,14 +5,6 @@ package com.light.delivery.service;
|
|||||||
*/
|
*/
|
||||||
public interface LocationSyncService {
|
public interface LocationSyncService {
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理配送员位置更新
|
|
||||||
* @param deliveryPersonId 配送员ID
|
|
||||||
* @param longitude 经度
|
|
||||||
* @param latitude 纬度
|
|
||||||
*/
|
|
||||||
void handleLocationUpdate(Long deliveryPersonId, Double longitude, Double latitude);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 订阅位置更新
|
* 订阅位置更新
|
||||||
* @param sessionId 会话ID
|
* @param sessionId 会话ID
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -34,12 +34,6 @@ public class DeliveryPersonServiceImpl implements DeliveryPersonService {
|
|||||||
return person.orElse(null);
|
return person.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateLocation(Long id, Double longitude, Double latitude) {
|
|
||||||
// 使用 LocationSyncService 更新位置信息到服务器缓存,而不是直接更新数据库
|
|
||||||
locationSyncService.handleLocationUpdate(id, longitude, latitude);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Order> getCurrentOrders(Long id) {
|
public List<Order> getCurrentOrders(Long id) {
|
||||||
return orderRepository.findByDeliveryPersonId(id);
|
return orderRepository.findByDeliveryPersonId(id);
|
||||||
|
@@ -34,19 +34,7 @@ public class LocationSyncServiceImpl implements LocationSyncService {
|
|||||||
// 位置过期时间(分钟)
|
// 位置过期时间(分钟)
|
||||||
private static final int LOCATION_EXPIRE_MINUTES = 5;
|
private static final int LOCATION_EXPIRE_MINUTES = 5;
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleLocationUpdate(Long deliveryPersonId, Double longitude, Double latitude) {
|
|
||||||
try {
|
|
||||||
// 更新内存缓存中的配送员位置,而不是数据库
|
|
||||||
deliveryPersonLongitudeMap.put(deliveryPersonId, longitude);
|
|
||||||
deliveryPersonLatitudeMap.put(deliveryPersonId, latitude);
|
|
||||||
|
|
||||||
// 更新最后更新时间
|
|
||||||
lastUpdateTimes.put(deliveryPersonId, LocalDateTime.now());
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println("更新配送员位置时出错: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void subscribe(String sessionId, Long deliveryPersonId) {
|
public void subscribe(String sessionId, Long deliveryPersonId) {
|
||||||
|
Reference in New Issue
Block a user