初始提交:部署 Spring Boot 应用
This commit is contained in:
18
src/main/java/com/light/delivery/LightApplication.java
Normal file
18
src/main/java/com/light/delivery/LightApplication.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package com.light.delivery;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* Spring Boot 应用程序入口类。
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class LightApplication {
|
||||
/**
|
||||
* 主方法,启动 Spring Boot 应用。
|
||||
* @param args 命令行参数
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(LightApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.light.delivery.config;
|
||||
|
||||
import jakarta.servlet.*;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.util.ContentCachingRequestWrapper;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@Component
|
||||
public class CachingRequestFilter implements Filter {
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
||||
HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||
// 使用 ContentCachingRequestWrapper 包装原始请求
|
||||
ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request);
|
||||
// 继续执行过滤器链,传入包装后的请求
|
||||
filterChain.doFilter(wrappedRequest, servletResponse);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.light.delivery.config;
|
||||
|
||||
import org.apache.coyote.http11.Http11NioProtocol;
|
||||
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
|
||||
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
|
||||
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
|
||||
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.apache.catalina.Context;
|
||||
import org.apache.catalina.connector.Connector;
|
||||
|
||||
@Configuration
|
||||
public class HttpsRedirectConfig {
|
||||
|
||||
@Bean
|
||||
@Profile("prod")
|
||||
public ServletWebServerFactory servletContainer() {
|
||||
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {
|
||||
@Override
|
||||
protected void postProcessContext(Context context) {
|
||||
SecurityConstraint securityConstraint = new SecurityConstraint();
|
||||
securityConstraint.setUserConstraint("CONFIDENTIAL");
|
||||
SecurityCollection collection = new SecurityCollection();
|
||||
collection.addPattern("/*");
|
||||
securityConstraint.addCollection(collection);
|
||||
context.addConstraint(securityConstraint);
|
||||
}
|
||||
};
|
||||
tomcat.addAdditionalTomcatConnectors(createHttpConnector());
|
||||
return tomcat;
|
||||
}
|
||||
|
||||
private Connector createHttpConnector() {
|
||||
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
|
||||
connector.setScheme("http");
|
||||
connector.setPort(80); // HTTP端口
|
||||
connector.setSecure(false);
|
||||
connector.setRedirectPort(443); // 重定向到HTTPS端口
|
||||
return connector;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.light.delivery.config;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import org.springframework.web.util.ContentCachingRequestWrapper;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class RequestLogInterceptor implements HandlerInterceptor {
|
||||
private static final Logger logger = LoggerFactory.getLogger(RequestLogInterceptor.class);
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
// 关键:将请求转换为 ContentCachingRequestWrapper 类型
|
||||
ContentCachingRequestWrapper wrappedRequest = (ContentCachingRequestWrapper) request;
|
||||
|
||||
logger.debug("\n=== 收到请求 ===\nMethod: {}\nURI: {}\nParams: {}\nHeaders: {}\nBody: {}",
|
||||
request.getMethod(),
|
||||
request.getRequestURI(),
|
||||
request.getQueryString(),
|
||||
getHeadersAsString(request),
|
||||
getRequestBody(wrappedRequest)); // 传入包装后的请求
|
||||
return true;
|
||||
}
|
||||
|
||||
private String getHeadersAsString(HttpServletRequest request) {
|
||||
StringBuilder headers = new StringBuilder();
|
||||
request.getHeaderNames().asIterator().forEachRemaining(headerName ->
|
||||
headers.append(headerName).append(": ").append(request.getHeader(headerName)).append("\n"));
|
||||
return headers.toString();
|
||||
}
|
||||
|
||||
// 方法参数改为 ContentCachingRequestWrapper
|
||||
private String getRequestBody(ContentCachingRequestWrapper request) {
|
||||
try {
|
||||
// 从包装请求的缓存中获取字节数组
|
||||
byte[] contentAsByteArray = request.getContentAsByteArray();
|
||||
if (contentAsByteArray.length > 0) {
|
||||
return new String(contentAsByteArray, StandardCharsets.UTF_8);
|
||||
} else {
|
||||
return "[空请求体]";
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return "[无法读取请求体]";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.light.delivery.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
@Configuration
|
||||
@EnableScheduling
|
||||
public class SchedulingConfig {
|
||||
// 启用定时任务调度
|
||||
}
|
||||
14
src/main/java/com/light/delivery/config/WebMvcConfig.java
Normal file
14
src/main/java/com/light/delivery/config/WebMvcConfig.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package com.light.delivery.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class WebMvcConfig implements WebMvcConfigurer {
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(new RequestLogInterceptor())
|
||||
.addPathPatterns("/**"); // 拦截所有路径
|
||||
}
|
||||
}
|
||||
19
src/main/java/com/light/delivery/config/WebsocketConfig.java
Normal file
19
src/main/java/com/light/delivery/config/WebsocketConfig.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package com.light.delivery.config;
|
||||
|
||||
import com.light.delivery.service.LocationWebSocketHandler;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.socket.config.annotation.EnableWebSocket;
|
||||
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
|
||||
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSocket
|
||||
public class WebsocketConfig implements WebSocketConfigurer {
|
||||
|
||||
@Override
|
||||
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
|
||||
// 注册位置同步处理器
|
||||
registry.addHandler(new LocationWebSocketHandler(), "/ws/location")
|
||||
.setAllowedOrigins("*");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package com.light.delivery.controller;
|
||||
|
||||
import com.light.delivery.dto.DeliveryPersonResponse;
|
||||
import com.light.delivery.dto.OrderResponse;
|
||||
import com.light.delivery.model.DeliveryPerson;
|
||||
import com.light.delivery.service.DeliveryPersonService;
|
||||
import com.light.delivery.service.impl.LocationSyncServiceImpl;
|
||||
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.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 配送员相关接口控制器,提供配送员信息、位置、状态等管理功能。
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/delivery-persons")
|
||||
public class DeliveryPersonController {
|
||||
|
||||
/**
|
||||
* 配送员服务层依赖注入。
|
||||
*/
|
||||
@Autowired
|
||||
private DeliveryPersonService deliveryPersonService;
|
||||
|
||||
@Autowired
|
||||
private LocationSyncServiceImpl locationSyncService;
|
||||
|
||||
/**
|
||||
* 获取所有配送员信息。
|
||||
* @return 配送员列表
|
||||
*/
|
||||
@GetMapping
|
||||
public ResponseEntity<List<DeliveryPersonResponse>> getAllDeliveryPersons() {
|
||||
List<DeliveryPersonResponse> deliveryPersons = deliveryPersonService.getAllDeliveryPersons().stream().map(person -> {
|
||||
DeliveryPersonResponse response = new DeliveryPersonResponse();
|
||||
response.setId(person.getId());
|
||||
// 从服务器缓存中获取位置和状态信息,而不是从数据库
|
||||
response.setStatus(locationSyncService.getDeliveryPersonStatus(person.getId()));
|
||||
|
||||
DeliveryPersonResponse.Location location = new DeliveryPersonResponse.Location();
|
||||
location.setLongitude(locationSyncService.getDeliveryPersonLongitude(person.getId()));
|
||||
location.setLatitude(locationSyncService.getDeliveryPersonLatitude(person.getId()));
|
||||
response.setCurrentLocation(location);
|
||||
return response;
|
||||
}).collect(Collectors.toList());
|
||||
return ResponseEntity.ok(deliveryPersons);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取配送员详情。
|
||||
* @param id 配送员ID
|
||||
* @return 配送员对象
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<DeliveryPersonResponse> getDeliveryPersonById(@PathVariable Long id) {
|
||||
DeliveryPerson person = deliveryPersonService.getDeliveryPersonById(id);
|
||||
DeliveryPersonResponse response = new DeliveryPersonResponse();
|
||||
response.setId(person.getId());
|
||||
// 从服务器缓存中获取位置和状态信息,而不是从数据库
|
||||
response.setStatus(locationSyncService.getDeliveryPersonStatus(id));
|
||||
|
||||
DeliveryPersonResponse.Location location = new DeliveryPersonResponse.Location();
|
||||
location.setLongitude(locationSyncService.getDeliveryPersonLongitude(id));
|
||||
location.setLatitude(locationSyncService.getDeliveryPersonLatitude(id));
|
||||
response.setCurrentLocation(location);
|
||||
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("位置更新成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取货运人员的当前订单。
|
||||
* @param id 货运人员ID
|
||||
* @return 订单列表
|
||||
*/
|
||||
@GetMapping("/{id}/orders")
|
||||
public ResponseEntity<List<OrderResponse>> getCurrentOrders(@PathVariable Long id) {
|
||||
List<OrderResponse> orders = deliveryPersonService.getCurrentOrders(id).stream().map(order -> {
|
||||
OrderResponse response = new OrderResponse();
|
||||
response.setId(order.getId());
|
||||
response.setStatus(order.getStatus());
|
||||
response.setGoodsType(order.getGoodsType());
|
||||
response.setGoodsWeight(order.getGoodsWeight());
|
||||
response.setCreateTime(order.getCreateTime());
|
||||
response.setAssignTime(order.getAssignTime());
|
||||
response.setDeliveryTime(order.getDeliveryTime());
|
||||
OrderResponse.StartPoint startPoint = new OrderResponse.StartPoint();
|
||||
startPoint.setId(order.getWarehouseId());
|
||||
startPoint.setName(order.getWarehouseName());
|
||||
startPoint.setLongitude(order.getStartPointLongitude());
|
||||
startPoint.setLatitude(order.getStartPointLatitude());
|
||||
response.setStartPoint(startPoint);
|
||||
OrderResponse.EndPoint endPoint = new OrderResponse.EndPoint();
|
||||
endPoint.setName(order.getEndPointName());
|
||||
endPoint.setLongitude(order.getEndPointLongitude());
|
||||
endPoint.setLatitude(order.getEndPointLatitude());
|
||||
response.setEndPoint(endPoint);
|
||||
return response;
|
||||
}).collect(Collectors.toList());
|
||||
return ResponseEntity.ok(orders);
|
||||
}
|
||||
|
||||
/**
|
||||
* 位置请求体
|
||||
*/
|
||||
@Data
|
||||
public static class LocationRequest {
|
||||
private Double longitude;
|
||||
private Double latitude;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.light.delivery.controller;
|
||||
|
||||
import com.light.delivery.model.DeliveryPerson;
|
||||
import com.light.delivery.service.DeliveryPersonService;
|
||||
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.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 位置同步控制器,提供配送员位置信息查询接口。
|
||||
* 用于获取所有已签到配送员的实时位置信息。
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/location-sync")
|
||||
public class LocationSyncController {
|
||||
|
||||
/**
|
||||
* 配送员服务依赖注入
|
||||
*/
|
||||
@Autowired
|
||||
private DeliveryPersonService deliveryPersonService;
|
||||
|
||||
/**
|
||||
* 获取所有已签到配送员的实时位置。
|
||||
* 仅返回有位置信息的配送员(最近有更新位置的)。
|
||||
* @return 配送员位置列表
|
||||
*/
|
||||
@GetMapping("/delivery-persons/locations")
|
||||
public ResponseEntity<List<DeliveryPersonLocation>> getDeliveryPersonLocations() {
|
||||
List<DeliveryPerson> allDeliveryPersons = deliveryPersonService.getAllDeliveryPersons();
|
||||
|
||||
// 过滤出有位置信息的配送员(最近有更新位置的)
|
||||
List<DeliveryPersonLocation> locations = allDeliveryPersons.stream()
|
||||
.filter(person -> person.getLatitude() != null && person.getLongitude() != null)
|
||||
.map(person -> {
|
||||
DeliveryPersonLocation location = new DeliveryPersonLocation();
|
||||
location.setDeliveryPersonId(person.getId());
|
||||
location.setLatitude(person.getLatitude());
|
||||
location.setLongitude(person.getLongitude());
|
||||
location.setStatus(person.getStatus());
|
||||
return location;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return ResponseEntity.ok(locations);
|
||||
}
|
||||
|
||||
/**
|
||||
* 配送员位置信息DTO,用于向前端返回配送员位置数据。
|
||||
*/
|
||||
@Data
|
||||
public static class DeliveryPersonLocation {
|
||||
/**
|
||||
* 配送员ID
|
||||
*/
|
||||
private Long deliveryPersonId;
|
||||
|
||||
/**
|
||||
* 纬度
|
||||
*/
|
||||
private Double latitude;
|
||||
|
||||
/**
|
||||
* 经度
|
||||
*/
|
||||
private Double longitude;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private String status;
|
||||
}
|
||||
}
|
||||
154
src/main/java/com/light/delivery/controller/OrderController.java
Normal file
154
src/main/java/com/light/delivery/controller/OrderController.java
Normal file
@@ -0,0 +1,154 @@
|
||||
package com.light.delivery.controller;
|
||||
|
||||
import com.light.delivery.dto.OrderResponse;
|
||||
import com.light.delivery.model.Order;
|
||||
import com.light.delivery.service.OrderService;
|
||||
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.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 订单相关接口控制器,提供订单的查询、分配、状态更新等功能。
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/orders")
|
||||
public class OrderController {
|
||||
|
||||
/**
|
||||
* 订单服务层依赖注入。
|
||||
*/
|
||||
@Autowired
|
||||
private OrderService orderService;
|
||||
|
||||
/**
|
||||
* 获取所有待分配订单。
|
||||
* @return 待分配订单列表
|
||||
*/
|
||||
@GetMapping("/pending")
|
||||
public ResponseEntity<List<OrderResponse>> getPendingOrders() {
|
||||
List<OrderResponse> orders = orderService.getPendingOrders().stream().map(order -> {
|
||||
OrderResponse response = new OrderResponse();
|
||||
OrderResponse.StartPoint startPoint = new OrderResponse.StartPoint();
|
||||
startPoint.setId(order.getWarehouseId());
|
||||
startPoint.setName(order.getWarehouseName());
|
||||
startPoint.setLongitude(order.getStartPointLongitude());
|
||||
startPoint.setLatitude(order.getStartPointLatitude());
|
||||
response.setStartPoint(startPoint);
|
||||
|
||||
OrderResponse.EndPoint endPoint = new OrderResponse.EndPoint();
|
||||
endPoint.setName(order.getEndPointName());
|
||||
endPoint.setLongitude(order.getEndPointLongitude());
|
||||
endPoint.setLatitude(order.getEndPointLatitude());
|
||||
response.setEndPoint(endPoint);
|
||||
|
||||
response.setId(order.getId());
|
||||
response.setStatus(order.getStatus());
|
||||
response.setGoodsType(order.getGoodsType());
|
||||
response.setGoodsWeight(order.getGoodsWeight());
|
||||
response.setCreateTime(order.getCreateTime());
|
||||
response.setAssignTime(order.getAssignTime());
|
||||
response.setDeliveryTime(order.getDeliveryTime());
|
||||
return response;
|
||||
}).collect(Collectors.toList());
|
||||
return ResponseEntity.ok(orders);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指派订单给特定货运人员。
|
||||
* @param id 订单ID
|
||||
* @param assignRequest 包含 deliveryPersonId
|
||||
* @return 操作结果
|
||||
*/
|
||||
@PostMapping("/{id}/assign")
|
||||
public ResponseEntity<Void> assignOrder(@PathVariable Long id, @RequestBody AssignRequest assignRequest) {
|
||||
orderService.assignOrder(id, assignRequest.getDeliveryPersonId());
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取订单详情。
|
||||
* @param id 订单ID
|
||||
* @return 订单对象
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<OrderResponse> getOrderById(@PathVariable Long id) {
|
||||
Order order = orderService.getOrderById(id);
|
||||
OrderResponse response = new OrderResponse();
|
||||
OrderResponse.StartPoint startPoint = new OrderResponse.StartPoint();
|
||||
startPoint.setId(order.getWarehouseId());
|
||||
startPoint.setName(order.getWarehouseName());
|
||||
startPoint.setLongitude(order.getStartPointLongitude());
|
||||
startPoint.setLatitude(order.getStartPointLatitude());
|
||||
response.setStartPoint(startPoint);
|
||||
|
||||
OrderResponse.EndPoint endPoint = new OrderResponse.EndPoint();
|
||||
endPoint.setName(order.getEndPointName());
|
||||
endPoint.setLongitude(order.getEndPointLongitude());
|
||||
endPoint.setLatitude(order.getEndPointLatitude());
|
||||
response.setEndPoint(endPoint);
|
||||
|
||||
response.setId(order.getId());
|
||||
response.setStatus(order.getStatus());
|
||||
response.setGoodsType(order.getGoodsType());
|
||||
response.setGoodsWeight(order.getGoodsWeight());
|
||||
response.setCreateTime(order.getCreateTime());
|
||||
response.setAssignTime(order.getAssignTime());
|
||||
response.setDeliveryTime(order.getDeliveryTime());
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新订单状态。
|
||||
* @param id 订单ID
|
||||
* @param status 新状态
|
||||
*/
|
||||
@PutMapping("/{id}/status")
|
||||
public void updateOrderStatus(@PathVariable Long id, @RequestParam String status) {
|
||||
orderService.updateOrderStatus(id, status);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取货运人员的当前订单。
|
||||
* @param deliveryPersonId 货运人员ID
|
||||
* @return 订单列表
|
||||
*/
|
||||
@GetMapping("/delivery-persons/{deliveryPersonId}/orders")
|
||||
public ResponseEntity<List<OrderResponse>> getOrdersByDeliveryPerson(@PathVariable Long deliveryPersonId) {
|
||||
List<OrderResponse> orders = orderService.getOrdersByDeliveryPerson(deliveryPersonId).stream().map(order -> {
|
||||
OrderResponse response = new OrderResponse();
|
||||
// ...OrderResponse字段映射...
|
||||
response.setId(order.getId());
|
||||
response.setStatus(order.getStatus());
|
||||
response.setGoodsType(order.getGoodsType());
|
||||
response.setGoodsWeight(order.getGoodsWeight());
|
||||
response.setCreateTime(order.getCreateTime());
|
||||
response.setAssignTime(order.getAssignTime());
|
||||
response.setDeliveryTime(order.getDeliveryTime());
|
||||
OrderResponse.StartPoint startPoint = new OrderResponse.StartPoint();
|
||||
startPoint.setId(order.getWarehouseId());
|
||||
startPoint.setName(order.getWarehouseName());
|
||||
startPoint.setLongitude(order.getStartPointLongitude());
|
||||
startPoint.setLatitude(order.getStartPointLatitude());
|
||||
response.setStartPoint(startPoint);
|
||||
OrderResponse.EndPoint endPoint = new OrderResponse.EndPoint();
|
||||
endPoint.setName(order.getEndPointName());
|
||||
endPoint.setLongitude(order.getEndPointLongitude());
|
||||
endPoint.setLatitude(order.getEndPointLatitude());
|
||||
response.setEndPoint(endPoint);
|
||||
return response;
|
||||
}).collect(Collectors.toList());
|
||||
return ResponseEntity.ok(orders);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指派请求体
|
||||
*/
|
||||
@Data
|
||||
public static class AssignRequest {
|
||||
private Long deliveryPersonId;
|
||||
}
|
||||
}
|
||||
174
src/main/java/com/light/delivery/controller/UserController.java
Normal file
174
src/main/java/com/light/delivery/controller/UserController.java
Normal file
@@ -0,0 +1,174 @@
|
||||
package com.light.delivery.controller;
|
||||
|
||||
import com.light.delivery.dto.UserInfoResponse;
|
||||
import com.light.delivery.dto.UserResponse;
|
||||
import com.light.delivery.model.LoginResponse;
|
||||
import com.light.delivery.model.RegisterRequest;
|
||||
import com.light.delivery.model.User;
|
||||
import com.light.delivery.model.WxLoginRequest;
|
||||
import com.light.delivery.service.UserService;
|
||||
import com.light.delivery.util.JwtUtil;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 用户相关接口控制器,提供登录、获取用户信息、登出等功能。
|
||||
* 处理用户认证、授权和基本信息管理等相关HTTP请求。
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/user")
|
||||
public class UserController {
|
||||
|
||||
/**
|
||||
* 用户服务层依赖注入。
|
||||
*/
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
/**
|
||||
* Jwt工具类依赖注入。
|
||||
*/
|
||||
@Autowired
|
||||
private JwtUtil jwtUtil;
|
||||
|
||||
/**
|
||||
* 获取当前用户信息接口。
|
||||
* @param request HTTP请求对象,用于提取认证令牌
|
||||
* @return 当前用户信息
|
||||
*/
|
||||
@GetMapping("/info")
|
||||
public ResponseEntity<?> getUserInfo(HttpServletRequest request) {
|
||||
String token = extractToken(request);
|
||||
if (token == null) {
|
||||
return ResponseEntity.badRequest().body("Authorization token is missing");
|
||||
}
|
||||
|
||||
try {
|
||||
User user = userService.getUserInfo(token);
|
||||
UserInfoResponse response = toUserInfoResponse(user);
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.badRequest().body("Invalid token: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将User实体转换为UserInfoResponse DTO。
|
||||
* @param user 用户实体
|
||||
* @return 用户信息响应DTO
|
||||
*/
|
||||
private UserInfoResponse toUserInfoResponse(User user) {
|
||||
if (user == null) return null;
|
||||
UserInfoResponse dto = new UserInfoResponse();
|
||||
dto.setId(user.getId());
|
||||
dto.setName(user.getName());
|
||||
dto.setPhone(user.getPhone());
|
||||
dto.setRole(user.getRole() != null ? user.getRole().getCode() : null);
|
||||
dto.setOpenid(user.getOpenid());
|
||||
return dto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从HTTP请求头中提取JWT令牌。
|
||||
* @param request HTTP请求对象
|
||||
* @return JWT令牌字符串
|
||||
*/
|
||||
private String extractToken(HttpServletRequest request) {
|
||||
String bearerToken = request.getHeader("Authorization");
|
||||
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
|
||||
return bearerToken.substring(7);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登出接口。
|
||||
* @param request HTTP请求对象,用于提取认证令牌
|
||||
* @return 操作结果
|
||||
*/
|
||||
@PostMapping("/logout")
|
||||
public ResponseEntity<String> logout(HttpServletRequest request) {
|
||||
String token = extractToken(request);
|
||||
userService.logout(token);
|
||||
return ResponseEntity.ok("登出成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信小程序登录接口,接收code,返回openid和token。
|
||||
* @param wxLoginRequest 微信登录请求对象
|
||||
* @return 登录响应
|
||||
*/
|
||||
@PostMapping("/wxlogin")
|
||||
public ResponseEntity<LoginResponse> wxLogin(@RequestBody WxLoginRequest wxLoginRequest) {
|
||||
System.out.println("[wxLogin] 收到请求参数: code=" + wxLoginRequest.getCode());
|
||||
LoginResponse loginResponse = userService.wxLogin(wxLoginRequest.getCode());
|
||||
return ResponseEntity.ok(loginResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户签到接口。
|
||||
* @param request HTTP请求对象,用于提取认证令牌
|
||||
* @return 更新后的用户信息
|
||||
*/
|
||||
@PostMapping("/signin")
|
||||
public ResponseEntity<?> signIn(HttpServletRequest request) {
|
||||
String token = extractToken(request);
|
||||
if (token == null) {
|
||||
return ResponseEntity.badRequest().body("Authorization token is missing");
|
||||
}
|
||||
|
||||
try {
|
||||
String username = jwtUtil.getUsernameFromToken(token);
|
||||
User user = userService.getUserInfo(token);
|
||||
User updatedUser = userService.signIn(user.getId());
|
||||
UserInfoResponse response = toUserInfoResponse(updatedUser);
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.badRequest().body("Invalid token: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册为骑手接口。
|
||||
* @param request HTTP请求对象,用于提取认证令牌
|
||||
* @param registerRequest 注册请求对象,包含姓名和手机号
|
||||
* @return 更新后的用户信息
|
||||
*/
|
||||
@PostMapping("/register")
|
||||
public ResponseEntity<UserInfoResponse> registerAsDeliveryPerson(
|
||||
HttpServletRequest request,
|
||||
@RequestBody RegisterRequest registerRequest) {
|
||||
try {
|
||||
String token = extractToken(request);
|
||||
System.out.println("收到注册请求,Token: " + token);
|
||||
|
||||
if (token == null || token.isEmpty()) {
|
||||
System.err.println("缺少Authorization token");
|
||||
return ResponseEntity.badRequest().body(null);
|
||||
}
|
||||
|
||||
User user = userService.getUserInfo(token);
|
||||
System.out.println("获取到用户信息: " + user);
|
||||
|
||||
User updatedUser = userService.registerAsDeliveryPerson(
|
||||
user.getId(),
|
||||
registerRequest.getName(),
|
||||
registerRequest.getPhone());
|
||||
UserInfoResponse response = toUserInfoResponse(updatedUser);
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// 记录错误日志
|
||||
System.err.println("注册配送员时发生错误: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
return ResponseEntity.badRequest().build();
|
||||
} catch (Exception e) {
|
||||
// 记录未预期的错误
|
||||
System.err.println("注册配送员时发生未预期错误: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
return ResponseEntity.status(500).build();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.light.delivery.controller;
|
||||
|
||||
import com.light.delivery.dto.OrderResponse;
|
||||
import com.light.delivery.dto.WarehouseInfo;
|
||||
import com.light.delivery.model.Order;
|
||||
import com.light.delivery.model.Warehouse;
|
||||
import com.light.delivery.service.WarehouseService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 仓库相关接口控制器,提供仓库的增查及订单查询等功能。
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/warehouses")
|
||||
public class WarehouseController {
|
||||
|
||||
/**
|
||||
* 仓库服务层依赖注入。
|
||||
*/
|
||||
@Autowired
|
||||
private WarehouseService warehouseService;
|
||||
|
||||
/**
|
||||
* 获取所有仓库信息。
|
||||
* @return 仓库列表
|
||||
*/
|
||||
@GetMapping
|
||||
public ResponseEntity<List<WarehouseInfo>> getAllWarehouses() {
|
||||
List<WarehouseInfo> warehouses = warehouseService.getAllWarehouses().stream().map(warehouse -> {
|
||||
WarehouseInfo info = new WarehouseInfo();
|
||||
info.setId(warehouse.getId());
|
||||
info.setName(warehouse.getName());
|
||||
info.setAddress(warehouse.getAddress());
|
||||
info.setContact(warehouse.getContact());
|
||||
info.setPhone(warehouse.getPhone());
|
||||
info.setDescription(warehouse.getDescription());
|
||||
info.setStatus(warehouse.getStatus());
|
||||
info.setCapacity(warehouse.getCapacity());
|
||||
info.setLatitude(warehouse.getLatitude());
|
||||
info.setLongitude(warehouse.getLongitude());
|
||||
return info;
|
||||
}).collect(Collectors.toList());
|
||||
return ResponseEntity.ok(warehouses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取仓库详情。
|
||||
* @param id 仓库ID
|
||||
* @return 仓库对象
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<WarehouseInfo> getWarehouseById(@PathVariable Long id) {
|
||||
Warehouse warehouse = warehouseService.getWarehouseById(id);
|
||||
WarehouseInfo info = new WarehouseInfo();
|
||||
info.setId(warehouse.getId());
|
||||
info.setName(warehouse.getName());
|
||||
info.setAddress(warehouse.getAddress());
|
||||
info.setContact(warehouse.getContact());
|
||||
info.setPhone(warehouse.getPhone());
|
||||
info.setDescription(warehouse.getDescription());
|
||||
info.setStatus(warehouse.getStatus());
|
||||
info.setCapacity(warehouse.getCapacity());
|
||||
info.setLatitude(warehouse.getLatitude());
|
||||
info.setLongitude(warehouse.getLongitude());
|
||||
return ResponseEntity.ok(info);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定仓库的所有订单。
|
||||
* @param id 仓库ID
|
||||
* @return 订单列表
|
||||
*/
|
||||
@GetMapping("/{id}/orders")
|
||||
public ResponseEntity<List<OrderResponse>> getWarehouseOrders(@PathVariable Long id) {
|
||||
List<OrderResponse> orders = warehouseService.getWarehouseOrders(id).stream().map(order -> {
|
||||
OrderResponse response = new OrderResponse();
|
||||
response.setId(order.getId());
|
||||
response.setStatus(order.getStatus());
|
||||
response.setGoodsType(order.getGoodsType());
|
||||
response.setGoodsWeight(order.getGoodsWeight());
|
||||
response.setCreateTime(order.getCreateTime());
|
||||
response.setAssignTime(order.getAssignTime());
|
||||
response.setDeliveryTime(order.getDeliveryTime());
|
||||
|
||||
OrderResponse.StartPoint startPoint = new OrderResponse.StartPoint();
|
||||
startPoint.setId(order.getWarehouseId());
|
||||
startPoint.setName(order.getWarehouseName());
|
||||
startPoint.setLongitude(order.getStartPointLongitude());
|
||||
startPoint.setLatitude(order.getStartPointLatitude());
|
||||
response.setStartPoint(startPoint);
|
||||
|
||||
OrderResponse.EndPoint endPoint = new OrderResponse.EndPoint();
|
||||
endPoint.setName(order.getEndPointName());
|
||||
endPoint.setLongitude(order.getEndPointLongitude());
|
||||
endPoint.setLatitude(order.getEndPointLatitude());
|
||||
response.setEndPoint(endPoint);
|
||||
|
||||
return response;
|
||||
}).collect(Collectors.toList());
|
||||
return ResponseEntity.ok(orders);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新仓库。
|
||||
* @param warehouse 仓库对象
|
||||
* @return 创建后的仓库对象
|
||||
*/
|
||||
@PostMapping
|
||||
public Warehouse createWarehouse(@RequestBody Warehouse warehouse) {
|
||||
return warehouseService.createWarehouse(warehouse);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.light.delivery.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class DeliveryPersonResponse {
|
||||
private Long id;
|
||||
private String name;
|
||||
private String phone;
|
||||
private String status;
|
||||
private Location currentLocation;
|
||||
|
||||
@Data
|
||||
public static class Location {
|
||||
private Double longitude;
|
||||
private Double latitude;
|
||||
}
|
||||
}
|
||||
32
src/main/java/com/light/delivery/dto/OrderResponse.java
Normal file
32
src/main/java/com/light/delivery/dto/OrderResponse.java
Normal file
@@ -0,0 +1,32 @@
|
||||
package com.light.delivery.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class OrderResponse {
|
||||
private Long id;
|
||||
private StartPoint startPoint;
|
||||
private EndPoint endPoint;
|
||||
private String status;
|
||||
private String goodsType;
|
||||
private Double goodsWeight;
|
||||
private Long createTime;
|
||||
private Long assignTime;
|
||||
private Long deliveryTime;
|
||||
|
||||
@Data
|
||||
public static class StartPoint {
|
||||
private Long id;
|
||||
private String name;
|
||||
private Double longitude;
|
||||
private Double latitude;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class EndPoint {
|
||||
private Long id;
|
||||
private String name;
|
||||
private Double longitude;
|
||||
private Double latitude;
|
||||
}
|
||||
}
|
||||
35
src/main/java/com/light/delivery/dto/UserInfoResponse.java
Normal file
35
src/main/java/com/light/delivery/dto/UserInfoResponse.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package com.light.delivery.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 用户信息响应 DTO,专用于前后端数据交互,避免直接暴露实体。
|
||||
* 该类仅包含用户基本信息,不包含敏感或角色特定的信息。
|
||||
*/
|
||||
@Data
|
||||
public class UserInfoResponse {
|
||||
/**
|
||||
* 用户唯一标识符
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户姓名
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 用户联系电话
|
||||
*/
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 用户角色
|
||||
*/
|
||||
private String role;
|
||||
|
||||
/**
|
||||
* 微信用户唯一标识
|
||||
*/
|
||||
private String openid;
|
||||
}
|
||||
13
src/main/java/com/light/delivery/dto/UserResponse.java
Normal file
13
src/main/java/com/light/delivery/dto/UserResponse.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package com.light.delivery.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class UserResponse {
|
||||
private Long id;
|
||||
private String name;
|
||||
private String nickName;
|
||||
private String role;
|
||||
private String avatarUrl;
|
||||
private String phone;
|
||||
}
|
||||
17
src/main/java/com/light/delivery/dto/WarehouseInfo.java
Normal file
17
src/main/java/com/light/delivery/dto/WarehouseInfo.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package com.light.delivery.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class WarehouseInfo {
|
||||
private Long id;
|
||||
private String name;
|
||||
private String address;
|
||||
private String contact;
|
||||
private String phone;
|
||||
private String description;
|
||||
private String status;
|
||||
private Integer capacity;
|
||||
private Double latitude;
|
||||
private Double longitude;
|
||||
}
|
||||
76
src/main/java/com/light/delivery/model/DeliveryPerson.java
Normal file
76
src/main/java/com/light/delivery/model/DeliveryPerson.java
Normal file
@@ -0,0 +1,76 @@
|
||||
package com.light.delivery.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 配送员实体类,表示系统中配送员的扩展信息。
|
||||
* 配送员是用户的一种角色,除了基本的用户信息外,还有配送相关的特殊属性。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "delivery_person")
|
||||
@Data
|
||||
public class DeliveryPerson {
|
||||
/**
|
||||
* 配送员唯一标识符
|
||||
*/
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 关联的用户ID
|
||||
*/
|
||||
@Column(name = "user_id")
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 当前纬度位置(瞬态,不存数据库)
|
||||
*/
|
||||
@Transient
|
||||
private Double latitude;
|
||||
|
||||
/**
|
||||
* 当前经度位置(瞬态,不存数据库)
|
||||
*/
|
||||
@Transient
|
||||
private Double longitude;
|
||||
|
||||
/**
|
||||
* 配送员状态(如 AVAILABLE 可接单, DELIVERING 配送中)(瞬态,不存数据库)
|
||||
*/
|
||||
@Transient
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 当前配送订单ID
|
||||
*/
|
||||
@Column(name = "current_order_id")
|
||||
private Long currentOrderId;
|
||||
|
||||
/**
|
||||
* 获取配送员当前位置信息
|
||||
* @return 当前位置对象
|
||||
*/
|
||||
public CurrentLocation getCurrentLocation() {
|
||||
CurrentLocation loc = new CurrentLocation();
|
||||
// 注意:实际位置应从缓存服务获取,此处仅返回内存中的值
|
||||
loc.setLongitude(this.longitude);
|
||||
loc.setLatitude(this.latitude);
|
||||
return loc;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前位置信息内部类
|
||||
*/
|
||||
public static class CurrentLocation {
|
||||
private Double longitude;
|
||||
private Double latitude;
|
||||
|
||||
public Double getLongitude() { return longitude; }
|
||||
public void setLongitude(Double longitude) { this.longitude = longitude; }
|
||||
public Double getLatitude() { return latitude; }
|
||||
public void setLatitude(Double latitude) { this.latitude = latitude; }
|
||||
}
|
||||
}
|
||||
45
src/main/java/com/light/delivery/model/Employee.java
Normal file
45
src/main/java/com/light/delivery/model/Employee.java
Normal file
@@ -0,0 +1,45 @@
|
||||
package com.light.delivery.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 员工信息实体类,用于存储后台配置的员工数据。
|
||||
* 当用户注册为配送员或管理员时,系统会根据手机号在此表中查找匹配记录。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "employee")
|
||||
@Data
|
||||
public class Employee {
|
||||
/**
|
||||
* 员工唯一标识符
|
||||
*/
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 员工姓名
|
||||
*/
|
||||
@Column(name = "name")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 员工联系电话
|
||||
*/
|
||||
@Column(name = "phone", unique = true)
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 员工角色(如"DELIVERY_PERSON", "ADMIN")
|
||||
*/
|
||||
@Column(name = "role")
|
||||
private String role;
|
||||
|
||||
/**
|
||||
* 微信用户唯一标识
|
||||
* 当员工注册为系统用户时,会将openid写入此字段
|
||||
*/
|
||||
@Column(name = "openid", unique = true)
|
||||
private String openid;
|
||||
}
|
||||
36
src/main/java/com/light/delivery/model/LoginResponse.java
Normal file
36
src/main/java/com/light/delivery/model/LoginResponse.java
Normal file
@@ -0,0 +1,36 @@
|
||||
package com.light.delivery.model;
|
||||
|
||||
import com.light.delivery.dto.UserInfoResponse;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 登录响应类,封装用户登录成功后的返回信息。
|
||||
* 包含JWT令牌、用户基本信息和会话相关信息。
|
||||
*/
|
||||
@Data
|
||||
public class LoginResponse {
|
||||
/**
|
||||
* JWT访问令牌
|
||||
*/
|
||||
private String token;
|
||||
|
||||
/**
|
||||
* 用户基本信息
|
||||
*/
|
||||
private UserInfoResponse user;
|
||||
|
||||
/**
|
||||
* 微信用户唯一标识
|
||||
*/
|
||||
private String openid;
|
||||
|
||||
/**
|
||||
* 微信会话密钥
|
||||
*/
|
||||
private String sessionKey;
|
||||
|
||||
/**
|
||||
* 令牌过期时间(秒)
|
||||
*/
|
||||
private Long expiresIn;
|
||||
}
|
||||
62
src/main/java/com/light/delivery/model/Order.java
Normal file
62
src/main/java/com/light/delivery/model/Order.java
Normal file
@@ -0,0 +1,62 @@
|
||||
package com.light.delivery.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 订单实体类,表示订单的详细信息。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "orders")
|
||||
@Data
|
||||
public class Order {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
/** 订单编号 */
|
||||
private String orderNo;
|
||||
/** 仓库ID */
|
||||
private Long warehouseId;
|
||||
/** 仓库名称 */
|
||||
private String warehouseName;
|
||||
/** 配送员ID */
|
||||
private Long deliveryPersonId;
|
||||
/** 配送员名称 */
|
||||
private String deliveryPersonName;
|
||||
/** 订单状态(pending/assigned/in_transit/delivered) */
|
||||
private String status;
|
||||
/** 客户姓名 */
|
||||
private String customerName;
|
||||
/** 客户电话 */
|
||||
private String customerPhone;
|
||||
/** 配送地址 */
|
||||
private String deliveryAddress;
|
||||
/** 配送纬度 */
|
||||
private Double deliveryLatitude;
|
||||
/** 配送经度 */
|
||||
private Double deliveryLongitude;
|
||||
/** 订单金额 */
|
||||
private Double amount;
|
||||
/** 创建时间戳 */
|
||||
private Long createTime;
|
||||
/** 指派时间戳(可选) */
|
||||
private Long assignTime;
|
||||
/** 配送完成时间戳(可选) */
|
||||
private Long deliveryTime;
|
||||
/** 货物类型 */
|
||||
private String goodsType;
|
||||
/** 货物重量(公斤) */
|
||||
private Double goodsWeight;
|
||||
/** 起点名称(仓库) */
|
||||
private String startPointName;
|
||||
/** 起点经度 */
|
||||
private Double startPointLongitude;
|
||||
/** 起点纬度 */
|
||||
private Double startPointLatitude;
|
||||
/** 终点名称 */
|
||||
private String endPointName;
|
||||
/** 终点经度 */
|
||||
private Double endPointLongitude;
|
||||
/** 终点纬度 */
|
||||
private Double endPointLatitude;
|
||||
}
|
||||
20
src/main/java/com/light/delivery/model/RegisterRequest.java
Normal file
20
src/main/java/com/light/delivery/model/RegisterRequest.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package com.light.delivery.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 骑手注册请求数据模型,用于接收用户注册为配送员时提交的信息。
|
||||
* 包含姓名和手机号,用于与员工信息表进行匹配验证。
|
||||
*/
|
||||
@Data
|
||||
public class RegisterRequest {
|
||||
/**
|
||||
* 用户真实姓名
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 用户手机号
|
||||
*/
|
||||
private String phone;
|
||||
}
|
||||
44
src/main/java/com/light/delivery/model/User.java
Normal file
44
src/main/java/com/light/delivery/model/User.java
Normal file
@@ -0,0 +1,44 @@
|
||||
package com.light.delivery.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 用户实体类,表示系统中的用户信息。
|
||||
* 所有用户首先以游客身份登录系统,之后可以注册为配送员或管理员。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "user")
|
||||
@Data
|
||||
public class User {
|
||||
/**
|
||||
* 用户唯一标识符
|
||||
*/
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户姓名
|
||||
*/
|
||||
@Column(name = "name")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 联系电话
|
||||
*/
|
||||
@Column(name = "phone")
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 用户角色(如 ADMIN, DELIVERY_PERSON, GUEST)
|
||||
*/
|
||||
@Enumerated(EnumType.STRING)
|
||||
private UserRole role;
|
||||
|
||||
/**
|
||||
* 微信用户唯一标识
|
||||
*/
|
||||
@Column(unique = true)
|
||||
private String openid;
|
||||
}
|
||||
65
src/main/java/com/light/delivery/model/UserRole.java
Normal file
65
src/main/java/com/light/delivery/model/UserRole.java
Normal file
@@ -0,0 +1,65 @@
|
||||
package com.light.delivery.model;
|
||||
|
||||
/**
|
||||
* 用户角色枚举
|
||||
* 定义系统中所有可能的用户角色类型
|
||||
*/
|
||||
public enum UserRole {
|
||||
/**
|
||||
* 管理员角色,拥有最高权限
|
||||
*/
|
||||
ADMIN("ADMIN", "管理员"),
|
||||
|
||||
/**
|
||||
* 配送员角色,可以接单和更新位置
|
||||
*/
|
||||
DELIVERY_PERSON("DELIVERY_PERSON", "配送员"),
|
||||
|
||||
/**
|
||||
* 游客角色,默认角色,权限最低
|
||||
*/
|
||||
GUEST("GUEST", "游客");
|
||||
|
||||
private final String code;
|
||||
private final String description;
|
||||
|
||||
UserRole(String code, String description) {
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取角色代码
|
||||
* @return 角色代码
|
||||
*/
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取角色描述
|
||||
* @return 角色描述
|
||||
*/
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据代码获取UserRole枚举值
|
||||
* @param code 角色代码
|
||||
* @return 对应的UserRole枚举值,如果未找到则返回GUEST
|
||||
*/
|
||||
public static UserRole fromCode(String code) {
|
||||
for (UserRole role : UserRole.values()) {
|
||||
if (role.getCode().equals(code)) {
|
||||
return role;
|
||||
}
|
||||
}
|
||||
return GUEST; // 默认返回游客角色
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
45
src/main/java/com/light/delivery/model/Warehouse.java
Normal file
45
src/main/java/com/light/delivery/model/Warehouse.java
Normal file
@@ -0,0 +1,45 @@
|
||||
package com.light.delivery.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 仓库实体类,表示仓库的基本信息。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "warehouse")
|
||||
@Data
|
||||
public class Warehouse {
|
||||
/** 仓库ID */
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "id")
|
||||
private Long id;
|
||||
/** 仓库名称 */
|
||||
@Column(name = "name")
|
||||
private String name;
|
||||
/** 仓库纬度 */
|
||||
@Column(name = "latitude")
|
||||
private Double latitude;
|
||||
/** 仓库经度 */
|
||||
@Column(name = "longitude")
|
||||
private Double longitude;
|
||||
/** 仓库地址 */
|
||||
@Column(name = "address")
|
||||
private String address;
|
||||
/** 联系电话 */
|
||||
@Column(name = "phone")
|
||||
private String phone;
|
||||
/** 联系人 */
|
||||
@Column(name = "contact")
|
||||
private String contact;
|
||||
/** 描述 */
|
||||
@Column(name = "description")
|
||||
private String description;
|
||||
/** 状态 */
|
||||
@Column(name = "status")
|
||||
private String status;
|
||||
/** 容量 */
|
||||
@Column(name = "capacity")
|
||||
private Integer capacity;
|
||||
}
|
||||
20
src/main/java/com/light/delivery/model/WxLoginRequest.java
Normal file
20
src/main/java/com/light/delivery/model/WxLoginRequest.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package com.light.delivery.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 微信登录请求类,封装微信登录所需的参数。
|
||||
* 主要用于接收微信小程序登录时传递的code参数。
|
||||
*/
|
||||
@Data
|
||||
public class WxLoginRequest {
|
||||
/**
|
||||
* 微信登录凭证code
|
||||
*/
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 无参构造函数
|
||||
*/
|
||||
public WxLoginRequest() {}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.light.delivery.repository;
|
||||
|
||||
import com.light.delivery.model.DeliveryPerson;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface DeliveryPersonRepository extends JpaRepository<DeliveryPerson, Long> {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.light.delivery.repository;
|
||||
|
||||
import com.light.delivery.model.Employee;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 员工数据访问接口,提供对员工信息的数据库操作。
|
||||
* 继承JpaRepository,拥有基本的CRUD操作能力。
|
||||
*/
|
||||
@Repository
|
||||
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
|
||||
/**
|
||||
* 根据手机号查找员工信息
|
||||
* @param phone 员工手机号
|
||||
* @return 员工信息Optional对象
|
||||
*/
|
||||
Optional<Employee> findByPhone(String phone);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.light.delivery.repository;
|
||||
|
||||
import com.light.delivery.model.Order;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface OrderRepository extends JpaRepository<Order, Long> {
|
||||
List<Order> findByStatus(String status);
|
||||
List<Order> findByWarehouseId(Long warehouseId);
|
||||
List<Order> findByDeliveryPersonId(Long deliveryPersonId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.light.delivery.repository;
|
||||
|
||||
import com.light.delivery.model.User;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
/**
|
||||
* 用户数据访问接口,提供对用户信息的数据库操作。
|
||||
* 继承JpaRepository,拥有基本的CRUD操作能力。
|
||||
*/
|
||||
@Repository
|
||||
public interface UserRepository extends JpaRepository<User, Long> {
|
||||
/**
|
||||
* 根据微信openid查找用户
|
||||
* @param openid 微信openid
|
||||
* @return 用户信息
|
||||
*/
|
||||
User findByOpenid(String openid);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.light.delivery.repository;
|
||||
|
||||
import com.light.delivery.model.Warehouse;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface WarehouseRepository extends JpaRepository<Warehouse, Long> {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.light.delivery.service;
|
||||
|
||||
import com.light.delivery.model.DeliveryPerson;
|
||||
import com.light.delivery.model.Order;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 配送员服务接口,定义配送员相关的业务操作。
|
||||
*/
|
||||
public interface DeliveryPersonService {
|
||||
/**
|
||||
* 获取所有配送员信息。
|
||||
* @return 配送员列表
|
||||
*/
|
||||
List<DeliveryPerson> getAllDeliveryPersons();
|
||||
/**
|
||||
* 根据ID获取配送员详情。
|
||||
* @param id 配送员ID
|
||||
* @return 配送员对象
|
||||
*/
|
||||
DeliveryPerson getDeliveryPersonById(Long id);
|
||||
/**
|
||||
* 更新配送员当前位置。
|
||||
* @param id 配送员ID
|
||||
* @param longitude 经度
|
||||
* @param latitude 纬度
|
||||
*/
|
||||
void updateLocation(Long id, Double longitude, Double latitude);
|
||||
/**
|
||||
* 获取指定配送员的所有订单。
|
||||
* @param id 配送员ID
|
||||
* @return 订单列表
|
||||
*/
|
||||
List<Order> getCurrentOrders(Long id);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.light.delivery.service;
|
||||
|
||||
/**
|
||||
* 位置同步服务接口
|
||||
*/
|
||||
public interface LocationSyncService {
|
||||
|
||||
/**
|
||||
* 处理配送员位置更新
|
||||
* @param deliveryPersonId 配送员ID
|
||||
* @param longitude 经度
|
||||
* @param latitude 纬度
|
||||
*/
|
||||
void handleLocationUpdate(Long deliveryPersonId, Double longitude, Double latitude);
|
||||
|
||||
/**
|
||||
* 订阅位置更新
|
||||
* @param sessionId 会话ID
|
||||
* @param deliveryPersonId 配送员ID
|
||||
*/
|
||||
void subscribe(String sessionId, Long deliveryPersonId);
|
||||
|
||||
/**
|
||||
* 取消订阅位置更新
|
||||
* @param sessionId 会话ID
|
||||
*/
|
||||
void unsubscribe(String sessionId);
|
||||
|
||||
/**
|
||||
* 清理过期的位置信息
|
||||
*/
|
||||
void cleanupExpiredLocations();
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
package com.light.delivery.service;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.socket.CloseStatus;
|
||||
import org.springframework.web.socket.TextMessage;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
import org.springframework.web.socket.handler.TextWebSocketHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Component
|
||||
public class LocationWebSocketHandler extends TextWebSocketHandler {
|
||||
|
||||
// 存储所有连接的会话,key为配送员ID,value为会话
|
||||
private final Map<Long, WebSocketSession> sessions = new ConcurrentHashMap<>();
|
||||
|
||||
// 存储配送员最后更新位置的时间
|
||||
private final Map<Long, Long> lastUpdateTimes = new ConcurrentHashMap<>();
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
// 位置过期时间(毫秒),默认5分钟
|
||||
private static final long LOCATION_EXPIRE_TIME = 5 * 60 * 1000;
|
||||
|
||||
@Override
|
||||
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
|
||||
// 连接建立时,可以在这里进行身份验证
|
||||
System.out.println("WebSocket连接已建立: " + session.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
|
||||
try {
|
||||
// 解析消息
|
||||
String payload = message.getPayload();
|
||||
LocationMessage locationMessage = objectMapper.readValue(payload, LocationMessage.class);
|
||||
|
||||
if ("updateLocation".equals(locationMessage.getType())) {
|
||||
// 更新位置信息
|
||||
handleLocationUpdate(locationMessage);
|
||||
} else if ("subscribe".equals(locationMessage.getType())) {
|
||||
// 订阅位置更新
|
||||
handleSubscribe(session, locationMessage);
|
||||
} else if ("unsubscribe".equals(locationMessage.getType())) {
|
||||
// 取消订阅
|
||||
handleUnsubscribe(session, locationMessage);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("处理WebSocket消息时出错: " + e.getMessage());
|
||||
session.sendMessage(new TextMessage("{\"error\":\"消息处理失败\"}"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理位置更新消息
|
||||
*/
|
||||
private void handleLocationUpdate(LocationMessage locationMessage) {
|
||||
Long deliveryPersonId = locationMessage.getDeliveryPersonId();
|
||||
if (deliveryPersonId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新最后更新时间
|
||||
lastUpdateTimes.put(deliveryPersonId, System.currentTimeMillis());
|
||||
|
||||
try {
|
||||
// 广播位置更新给所有订阅者
|
||||
broadcastLocationUpdate(locationMessage);
|
||||
} catch (IOException e) {
|
||||
System.err.println("广播位置更新时出错: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理订阅消息
|
||||
*/
|
||||
private void handleSubscribe(WebSocketSession session, LocationMessage locationMessage) throws IOException {
|
||||
Long deliveryPersonId = locationMessage.getDeliveryPersonId();
|
||||
if (deliveryPersonId != null) {
|
||||
sessions.put(deliveryPersonId, session);
|
||||
session.sendMessage(new TextMessage("{\"type\":\"subscribed\",\"deliveryPersonId\":" + deliveryPersonId + "}"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理取消订阅消息
|
||||
*/
|
||||
private void handleUnsubscribe(WebSocketSession session, LocationMessage locationMessage) {
|
||||
Long deliveryPersonId = locationMessage.getDeliveryPersonId();
|
||||
if (deliveryPersonId != null) {
|
||||
sessions.remove(deliveryPersonId);
|
||||
lastUpdateTimes.remove(deliveryPersonId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 广播位置更新给所有连接的客户端
|
||||
*/
|
||||
private void broadcastLocationUpdate(LocationMessage locationMessage) throws IOException {
|
||||
String message = objectMapper.writeValueAsString(locationMessage);
|
||||
TextMessage textMessage = new TextMessage(message);
|
||||
|
||||
// 发送给所有连接的客户端
|
||||
for (WebSocketSession session : sessions.values()) {
|
||||
if (session.isOpen()) {
|
||||
session.sendMessage(textMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
|
||||
// 连接关闭时,移除会话
|
||||
sessions.values().remove(session);
|
||||
System.out.println("WebSocket连接已关闭: " + session.getId() + ", 状态: " + status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
|
||||
System.err.println("WebSocket传输错误: " + exception.getMessage());
|
||||
sessions.values().remove(session);
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时检查并清理过期的位置信息(每30秒执行一次)
|
||||
*/
|
||||
@Scheduled(fixedRate = 30000)
|
||||
public void cleanupExpiredLocations() {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
for (Map.Entry<Long, Long> entry : lastUpdateTimes.entrySet()) {
|
||||
Long deliveryPersonId = entry.getKey();
|
||||
Long lastUpdateTime = entry.getValue();
|
||||
|
||||
// 如果超过过期时间,则移除该配送员的信息
|
||||
if (currentTime - lastUpdateTime > LOCATION_EXPIRE_TIME) {
|
||||
sessions.remove(deliveryPersonId);
|
||||
lastUpdateTimes.remove(deliveryPersonId);
|
||||
System.out.println("已清理过期位置信息,配送员ID: " + deliveryPersonId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部消息类,用于解析WebSocket消息
|
||||
*/
|
||||
public static class LocationMessage {
|
||||
private String type; // 消息类型: updateLocation, subscribe, unsubscribe
|
||||
private Long deliveryPersonId; // 配送员ID
|
||||
private Double latitude; // 纬度
|
||||
private Double longitude; // 经度
|
||||
private Long timestamp; // 时间戳
|
||||
|
||||
// Getters and Setters
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Long getDeliveryPersonId() {
|
||||
return deliveryPersonId;
|
||||
}
|
||||
|
||||
public void setDeliveryPersonId(Long deliveryPersonId) {
|
||||
this.deliveryPersonId = deliveryPersonId;
|
||||
}
|
||||
|
||||
public Double getLatitude() {
|
||||
return latitude;
|
||||
}
|
||||
|
||||
public void setLatitude(Double latitude) {
|
||||
this.latitude = latitude;
|
||||
}
|
||||
|
||||
public Double getLongitude() {
|
||||
return longitude;
|
||||
}
|
||||
|
||||
public void setLongitude(Double longitude) {
|
||||
this.longitude = longitude;
|
||||
}
|
||||
|
||||
public Long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(Long timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
}
|
||||
}
|
||||
41
src/main/java/com/light/delivery/service/OrderService.java
Normal file
41
src/main/java/com/light/delivery/service/OrderService.java
Normal file
@@ -0,0 +1,41 @@
|
||||
package com.light.delivery.service;
|
||||
|
||||
import com.light.delivery.model.Order;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 订单服务接口,定义订单相关的业务操作。
|
||||
*/
|
||||
public interface OrderService {
|
||||
/**
|
||||
* 获取所有待分配订单。
|
||||
* @return 待分配订单列表
|
||||
*/
|
||||
List<Order> getPendingOrders();
|
||||
/**
|
||||
* 分配订单给指定配送员。
|
||||
* @param id 订单ID
|
||||
* @param deliveryPersonId 配送员ID
|
||||
*/
|
||||
void assignOrder(Long id, Long deliveryPersonId);
|
||||
/**
|
||||
* 根据ID获取订单详情。
|
||||
* @param id 订单ID
|
||||
* @return 订单对象
|
||||
*/
|
||||
Order getOrderById(Long id);
|
||||
/**
|
||||
* 更新订单状态。
|
||||
* @param id 订单ID
|
||||
* @param status 新状态
|
||||
*/
|
||||
void updateOrderStatus(Long id, String status);
|
||||
|
||||
/**
|
||||
* 根据配送员ID获取订单列表。
|
||||
* @param deliveryPersonId 配送员ID
|
||||
* @return 订单列表
|
||||
*/
|
||||
List<Order> getOrdersByDeliveryPerson(Long deliveryPersonId);
|
||||
}
|
||||
50
src/main/java/com/light/delivery/service/UserService.java
Normal file
50
src/main/java/com/light/delivery/service/UserService.java
Normal file
@@ -0,0 +1,50 @@
|
||||
package com.light.delivery.service;
|
||||
|
||||
import com.light.delivery.model.LoginResponse;
|
||||
import com.light.delivery.model.User;
|
||||
import com.light.delivery.model.WxLoginRequest;
|
||||
|
||||
/**
|
||||
* 用户服务接口,定义用户相关的业务操作。
|
||||
*/
|
||||
public interface UserService {
|
||||
/**
|
||||
* 获取用户信息。
|
||||
* @param token 用户 token
|
||||
* @return 用户信息
|
||||
*/
|
||||
User getUserInfo(String token);
|
||||
|
||||
/**
|
||||
* 用户登出。
|
||||
* @param token 用户 token
|
||||
*/
|
||||
void logout(String token);
|
||||
|
||||
/**
|
||||
* 微信小程序登录,接收code,返回登录响应。
|
||||
*/
|
||||
LoginResponse wxLogin(String code);
|
||||
|
||||
/**
|
||||
* 获取当前已登录用户(从上下文或模拟实现中)
|
||||
*/
|
||||
User getCurrentUser();
|
||||
|
||||
|
||||
/**
|
||||
* 用户签到功能
|
||||
* @param userId 用户ID
|
||||
* @return 签到结果
|
||||
*/
|
||||
User signIn(Long userId);
|
||||
|
||||
/**
|
||||
* 注册为骑手
|
||||
* @param userId 用户ID
|
||||
* @param name 姓名
|
||||
* @param phone 手机号
|
||||
* @return 注册结果
|
||||
*/
|
||||
User registerAsDeliveryPerson(Long userId, String name, String phone);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.light.delivery.service;
|
||||
|
||||
import com.light.delivery.model.Order;
|
||||
import com.light.delivery.model.Warehouse;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 仓库服务接口,定义仓库相关的业务操作。
|
||||
*/
|
||||
public interface WarehouseService {
|
||||
/**
|
||||
* 获取所有仓库信息。
|
||||
* @return 仓库列表
|
||||
*/
|
||||
List<Warehouse> getAllWarehouses();
|
||||
/**
|
||||
* 根据ID获取仓库详情。
|
||||
* @param id 仓库ID
|
||||
* @return 仓库对象
|
||||
*/
|
||||
Warehouse getWarehouseById(Long id);
|
||||
/**
|
||||
* 获取指定仓库的所有订单。
|
||||
* @param id 仓库ID
|
||||
* @return 订单列表
|
||||
*/
|
||||
List<Order> getWarehouseOrders(Long id);
|
||||
/**
|
||||
* 创建新仓库。
|
||||
* @param warehouse 仓库对象
|
||||
* @return 创建后的仓库对象
|
||||
*/
|
||||
Warehouse createWarehouse(Warehouse warehouse);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.light.delivery.service.impl;
|
||||
|
||||
import com.light.delivery.model.DeliveryPerson;
|
||||
import com.light.delivery.model.Order;
|
||||
import com.light.delivery.repository.DeliveryPersonRepository;
|
||||
import com.light.delivery.repository.OrderRepository;
|
||||
import com.light.delivery.service.DeliveryPersonService;
|
||||
import com.light.delivery.service.LocationSyncService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class DeliveryPersonServiceImpl implements DeliveryPersonService {
|
||||
@Autowired
|
||||
private DeliveryPersonRepository deliveryPersonRepository;
|
||||
|
||||
@Autowired
|
||||
private OrderRepository orderRepository;
|
||||
|
||||
@Autowired
|
||||
private LocationSyncService locationSyncService;
|
||||
|
||||
@Override
|
||||
public List<DeliveryPerson> getAllDeliveryPersons() {
|
||||
return deliveryPersonRepository.findAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeliveryPerson getDeliveryPersonById(Long id) {
|
||||
Optional<DeliveryPerson> person = deliveryPersonRepository.findById(id);
|
||||
return person.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLocation(Long id, Double longitude, Double latitude) {
|
||||
// 使用 LocationSyncService 更新位置信息到服务器缓存,而不是直接更新数据库
|
||||
locationSyncService.handleLocationUpdate(id, longitude, latitude);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Order> getCurrentOrders(Long id) {
|
||||
return orderRepository.findByDeliveryPersonId(id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package com.light.delivery.service.impl;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.light.delivery.model.DeliveryPerson;
|
||||
import com.light.delivery.repository.DeliveryPersonRepository;
|
||||
import com.light.delivery.service.LocationSyncService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Service
|
||||
public class LocationSyncServiceImpl implements LocationSyncService {
|
||||
|
||||
@Autowired
|
||||
private DeliveryPersonRepository deliveryPersonRepository;
|
||||
|
||||
// 存储会话ID与配送员ID的映射关系
|
||||
private final ConcurrentHashMap<String, Long> sessionDeliveryPersonMap = new ConcurrentHashMap<>();
|
||||
|
||||
// 存储配送员位置信息(经度、纬度)
|
||||
private final ConcurrentHashMap<Long, Double> deliveryPersonLongitudeMap = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<Long, Double> deliveryPersonLatitudeMap = new ConcurrentHashMap<>();
|
||||
|
||||
// 存储配送员状态信息
|
||||
private final ConcurrentHashMap<Long, String> deliveryPersonStatusMap = new ConcurrentHashMap<>();
|
||||
|
||||
// 存储配送员最后更新位置的时间
|
||||
private final ConcurrentHashMap<Long, LocalDateTime> lastUpdateTimes = new ConcurrentHashMap<>();
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
// 位置过期时间(分钟)
|
||||
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
|
||||
public void subscribe(String sessionId, Long deliveryPersonId) {
|
||||
sessionDeliveryPersonMap.put(sessionId, deliveryPersonId);
|
||||
lastUpdateTimes.put(deliveryPersonId, LocalDateTime.now());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribe(String sessionId) {
|
||||
Long deliveryPersonId = sessionDeliveryPersonMap.remove(sessionId);
|
||||
if (deliveryPersonId != null) {
|
||||
// 从缓存中移除配送员信息
|
||||
deliveryPersonLongitudeMap.remove(deliveryPersonId);
|
||||
deliveryPersonLatitudeMap.remove(deliveryPersonId);
|
||||
deliveryPersonStatusMap.remove(deliveryPersonId);
|
||||
lastUpdateTimes.remove(deliveryPersonId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanupExpiredLocations() {
|
||||
LocalDateTime currentTime = LocalDateTime.now();
|
||||
lastUpdateTimes.entrySet().removeIf(entry -> {
|
||||
LocalDateTime lastUpdateTime = entry.getValue();
|
||||
long minutesBetween = java.time.Duration.between(lastUpdateTime, currentTime).toMinutes();
|
||||
if (minutesBetween > LOCATION_EXPIRE_MINUTES) {
|
||||
// 从缓存中清除过期的位置信息
|
||||
Long deliveryPersonId = entry.getKey();
|
||||
deliveryPersonLongitudeMap.remove(deliveryPersonId);
|
||||
deliveryPersonLatitudeMap.remove(deliveryPersonId);
|
||||
deliveryPersonStatusMap.remove(deliveryPersonId);
|
||||
return true; // 移除这个过期的条目
|
||||
}
|
||||
return false; // 保留未过期的条目
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配送员当前经度
|
||||
* @param deliveryPersonId 配送员ID
|
||||
* @return 经度
|
||||
*/
|
||||
public Double getDeliveryPersonLongitude(Long deliveryPersonId) {
|
||||
return deliveryPersonLongitudeMap.get(deliveryPersonId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配送员当前纬度
|
||||
* @param deliveryPersonId 配送员ID
|
||||
* @return 纬度
|
||||
*/
|
||||
public Double getDeliveryPersonLatitude(Long deliveryPersonId) {
|
||||
return deliveryPersonLatitudeMap.get(deliveryPersonId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置配送员状态
|
||||
* @param deliveryPersonId 配送员ID
|
||||
* @param status 状态
|
||||
*/
|
||||
public void setDeliveryPersonStatus(Long deliveryPersonId, String status) {
|
||||
deliveryPersonStatusMap.put(deliveryPersonId, status);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配送员状态
|
||||
* @param deliveryPersonId 配送员ID
|
||||
* @return 状态
|
||||
*/
|
||||
public String getDeliveryPersonStatus(Long deliveryPersonId) {
|
||||
return deliveryPersonStatusMap.get(deliveryPersonId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.light.delivery.service.impl;
|
||||
|
||||
import com.light.delivery.model.Order;
|
||||
import com.light.delivery.repository.OrderRepository;
|
||||
import com.light.delivery.service.OrderService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 订单服务实现类,提供订单相关的业务逻辑实现。
|
||||
*/
|
||||
@Service
|
||||
public class OrderServiceImpl implements OrderService {
|
||||
@Autowired
|
||||
private OrderRepository orderRepository;
|
||||
|
||||
/**
|
||||
* 获取所有待分配订单。
|
||||
* @return 待分配订单列表
|
||||
*/
|
||||
@Override
|
||||
public List<Order> getPendingOrders() {
|
||||
return orderRepository.findByStatus("PENDING");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assignOrder(Long id, Long deliveryPersonId) {
|
||||
Optional<Order> optional = orderRepository.findById(id);
|
||||
if (optional.isPresent()) {
|
||||
Order order = optional.get();
|
||||
order.setDeliveryPersonId(deliveryPersonId);
|
||||
order.setStatus("ASSIGNED");
|
||||
orderRepository.save(order);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Order getOrderById(Long id) {
|
||||
return orderRepository.findById(id).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateOrderStatus(Long id, String status) {
|
||||
Optional<Order> optional = orderRepository.findById(id);
|
||||
if (optional.isPresent()) {
|
||||
Order order = optional.get();
|
||||
order.setStatus(status);
|
||||
orderRepository.save(order);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Order> getOrdersByDeliveryPerson(Long deliveryPersonId) {
|
||||
return orderRepository.findByDeliveryPersonId(deliveryPersonId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
package com.light.delivery.service.impl;
|
||||
|
||||
import com.light.delivery.dto.UserInfoResponse;
|
||||
import com.light.delivery.model.Employee;
|
||||
import com.light.delivery.model.LoginResponse;
|
||||
import com.light.delivery.model.User;
|
||||
import com.light.delivery.model.UserRole;
|
||||
import com.light.delivery.model.WxLoginRequest;
|
||||
import com.light.delivery.repository.EmployeeRepository;
|
||||
import com.light.delivery.repository.UserRepository;
|
||||
import com.light.delivery.service.UserService;
|
||||
import com.light.delivery.util.JwtUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 用户服务实现类,处理用户相关的业务逻辑。
|
||||
* 包括用户登录、注册、信息获取、签到等核心功能。
|
||||
*/
|
||||
@Service
|
||||
public class UserServiceImpl implements UserService {
|
||||
|
||||
/**
|
||||
* 用户数据访问对象,用于操作用户相关数据库表
|
||||
*/
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
/**
|
||||
* 员工数据访问对象,用于操作员工相关数据库表
|
||||
*/
|
||||
@Autowired
|
||||
private EmployeeRepository employeeRepository;
|
||||
|
||||
/**
|
||||
* JWT工具类,用于生成和解析JWT令牌
|
||||
*/
|
||||
@Autowired
|
||||
private JwtUtil jwtUtil;
|
||||
|
||||
/**
|
||||
* 根据JWT令牌获取用户信息。
|
||||
* 从token中解析出用户的openid,然后查询数据库获取完整的用户信息。
|
||||
* @param token 用户的JWT令牌
|
||||
* @return 用户实体对象
|
||||
* @throws IllegalArgumentException 当令牌无效或用户不存在时抛出异常
|
||||
*/
|
||||
@Override
|
||||
public User getUserInfo(String token) {
|
||||
if (token == null || token.isEmpty()) {
|
||||
throw new IllegalArgumentException("Authorization token is missing");
|
||||
}
|
||||
|
||||
try {
|
||||
// 解析token获取用户openid
|
||||
String openid = jwtUtil.extractUsername(token);
|
||||
if (openid == null || openid.isEmpty()) {
|
||||
throw new IllegalArgumentException("Invalid token");
|
||||
}
|
||||
|
||||
User user = userRepository.findByOpenid(openid);
|
||||
if (user == null) {
|
||||
throw new IllegalArgumentException("用户不存在");
|
||||
}
|
||||
return user;
|
||||
} catch (Exception e) {
|
||||
// 捕获JWT解析异常等
|
||||
throw new IllegalArgumentException("Invalid token: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登出逻辑。
|
||||
* 由于使用无状态JWT认证,服务器端无需特殊处理。
|
||||
* 客户端应负责删除本地存储的token。
|
||||
* @param token 用户的JWT令牌(当前实现中未使用)
|
||||
*/
|
||||
@Override
|
||||
public void logout(String token) {
|
||||
// 无状态JWT,登出可为空实现,或清理本地缓存等
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理微信登录逻辑。
|
||||
* 通过微信登录凭证(code)获取用户身份信息,创建或更新用户记录,并生成JWT令牌。
|
||||
* 实际项目中应调用微信API来获取真实的openid。
|
||||
* @param code 微信授权登录返回的临时凭证
|
||||
* @return 登录响应对象,包含JWT令牌、用户信息等
|
||||
* @throws IllegalArgumentException 当登录凭证为空时抛出异常
|
||||
*/
|
||||
@Override
|
||||
public LoginResponse wxLogin(String code) {
|
||||
if (code == null || code.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("登录code不能为空");
|
||||
}
|
||||
|
||||
// 模拟微信登录,实际应该调用微信API获取 openid
|
||||
String openid = "openid_" + code; // 模拟 openid
|
||||
|
||||
User user = userRepository.findByOpenid(openid);
|
||||
if (user == null) {
|
||||
// 首次登录,创建新用户,默认为游客角色
|
||||
user = new User();
|
||||
user.setRole(UserRole.GUEST); // 默认为游客角色
|
||||
user.setOpenid(openid);
|
||||
userRepository.save(user);
|
||||
} else {
|
||||
// 检查并修复用户角色
|
||||
if (user.getRole() == null) {
|
||||
// 如果用户角色为空,设置为游客角色
|
||||
user.setRole(UserRole.GUEST);
|
||||
userRepository.save(user);
|
||||
}
|
||||
}
|
||||
|
||||
// 确保用户角色不为空,防止在生成token时出现空指针异常
|
||||
if (user.getRole() == null) {
|
||||
user.setRole(UserRole.GUEST);
|
||||
}
|
||||
|
||||
String token = jwtUtil.generateToken(user.getOpenid(), user.getRole().getCode());
|
||||
LoginResponse response = new LoginResponse();
|
||||
response.setToken(token);
|
||||
response.setUser(toUserInfoResponse(user));
|
||||
response.setOpenid(user.getOpenid());
|
||||
response.setSessionKey("");
|
||||
response.setExpiresIn(jwtUtil.getExpirationTime());
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户签到功能。
|
||||
* 更新指定用户的信息,模拟签到过程。
|
||||
* 注意:当前实现中使用随机UUID作为openid,实际应用中应使用真实用户标识。
|
||||
* @param userId 用户唯一标识
|
||||
* @return 更新后的用户对象
|
||||
* @throws IllegalArgumentException 当指定ID的用户不存在时抛出异常
|
||||
*/
|
||||
@Override
|
||||
public User signIn(Long userId) {
|
||||
Optional<User> userOptional = userRepository.findById(userId);
|
||||
if (!userOptional.isPresent()) {
|
||||
throw new IllegalArgumentException("用户不存在");
|
||||
}
|
||||
|
||||
User user = userOptional.get();
|
||||
// 不再更改用户的openid,保持原有的openid不变
|
||||
userRepository.save(user);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户注册成为配送员。
|
||||
* 验证用户提交的个人信息是否与员工数据库中的记录匹配,
|
||||
* 如果验证成功,则更新用户角色为配送员,并建立用户与员工的关联。
|
||||
* @param userId 用户系统内的唯一标识
|
||||
* @param name 用户真实姓名
|
||||
* @param phone 用户手机号码
|
||||
* @return 更新后的用户对象
|
||||
* @throws IllegalArgumentException 当用户不存在、手机号未找到或姓名不匹配时抛出异常
|
||||
*/
|
||||
@Override
|
||||
public User registerAsDeliveryPerson(Long userId, String name, String phone) {
|
||||
System.out.println("尝试注册配送员,用户ID: " + userId);
|
||||
|
||||
Optional<User> userOptional = userRepository.findById(userId);
|
||||
if (!userOptional.isPresent()) {
|
||||
System.err.println("未找到ID为 " + userId + " 的用户");
|
||||
throw new IllegalArgumentException("用户不存在");
|
||||
}
|
||||
|
||||
// 查找员工信息表中是否有该手机号
|
||||
Optional<Employee> employeeOptional = employeeRepository.findByPhone(phone);
|
||||
if (!employeeOptional.isPresent()) {
|
||||
throw new IllegalArgumentException("该手机号不在员工信息表中,请确认手机号是否正确");
|
||||
}
|
||||
|
||||
Employee employee = employeeOptional.get();
|
||||
if (!employee.getName().equals(name)) {
|
||||
throw new IllegalArgumentException("姓名与员工信息不匹配");
|
||||
}
|
||||
|
||||
// 将openid写入employee表,表示该员工已成为系统用户
|
||||
employee.setOpenid(userOptional.get().getOpenid());
|
||||
employeeRepository.save(employee);
|
||||
|
||||
// 更新用户信息
|
||||
User user = userOptional.get();
|
||||
user.setName(name); // 设置用户姓名
|
||||
user.setPhone(phone); // 设置用户手机号
|
||||
user.setRole(UserRole.fromCode(employee.getRole())); // 设置为员工对应的权限角色
|
||||
userRepository.save(user);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理微信登录请求对象。
|
||||
* 从WxLoginRequest对象中提取code参数,调用字符串参数版本的wxLogin方法。
|
||||
* @param wxLoginRequest 微信登录请求对象,包含登录所需参数
|
||||
* @return 登录响应对象
|
||||
*/
|
||||
public LoginResponse wxLogin(WxLoginRequest wxLoginRequest) {
|
||||
return wxLogin(wxLoginRequest.getCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户。
|
||||
* 在实际应用中,应从Spring Security上下文中获取当前认证用户。
|
||||
* 当前为简化实现,返回数据库中的第一个用户。
|
||||
* @return 当前用户对象
|
||||
* @throws IllegalArgumentException 当数据库中没有用户时抛出异常
|
||||
*/
|
||||
@Override
|
||||
public User getCurrentUser() {
|
||||
// 这里应从安全上下文获取当前用户,简化为取第一个用户
|
||||
List<User> users = userRepository.findAll();
|
||||
if (users.isEmpty()) throw new IllegalArgumentException("无用户");
|
||||
return users.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将用户实体转换为用户信息响应DTO。
|
||||
* 用于将数据库中的用户实体对象转换为对外暴露的响应数据传输对象。
|
||||
* @param user 用户实体对象
|
||||
* @return 用户信息响应DTO对象
|
||||
*/
|
||||
private UserInfoResponse toUserInfoResponse(User user) {
|
||||
UserInfoResponse dto = new UserInfoResponse();
|
||||
dto.setId(user.getId());
|
||||
dto.setName(user.getName());
|
||||
dto.setPhone(user.getPhone());
|
||||
dto.setRole(user.getRole() != null ? user.getRole().getCode() : null);
|
||||
dto.setOpenid(user.getOpenid());
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.light.delivery.service.impl;
|
||||
|
||||
import com.light.delivery.model.Order;
|
||||
import com.light.delivery.model.Warehouse;
|
||||
import com.light.delivery.repository.OrderRepository;
|
||||
import com.light.delivery.repository.WarehouseRepository;
|
||||
import com.light.delivery.service.WarehouseService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 仓库服务实现类,提供仓库相关的业务逻辑实现。
|
||||
*/
|
||||
@Service
|
||||
public class WarehouseServiceImpl implements WarehouseService {
|
||||
@Autowired
|
||||
private WarehouseRepository warehouseRepository;
|
||||
@Autowired
|
||||
private OrderRepository orderRepository;
|
||||
|
||||
/**
|
||||
* 获取所有仓库信息。
|
||||
* @return 仓库列表
|
||||
*/
|
||||
@Override
|
||||
public List<Warehouse> getAllWarehouses() {
|
||||
return warehouseRepository.findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取仓库详情。
|
||||
* @param id 仓库ID
|
||||
* @return 仓库对象
|
||||
*/
|
||||
@Override
|
||||
public Warehouse getWarehouseById(Long id) {
|
||||
Optional<Warehouse> warehouse = warehouseRepository.findById(id);
|
||||
return warehouse.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定仓库的所有订单。
|
||||
* @param id 仓库ID
|
||||
* @return 订单列表
|
||||
*/
|
||||
@Override
|
||||
public List<Order> getWarehouseOrders(Long id) {
|
||||
return orderRepository.findByWarehouseId(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新仓库。
|
||||
* @param warehouse 仓库对象
|
||||
* @return 创建的仓库对象
|
||||
*/
|
||||
@Override
|
||||
public Warehouse createWarehouse(Warehouse warehouse) {
|
||||
return warehouseRepository.save(warehouse);
|
||||
}
|
||||
}
|
||||
179
src/main/java/com/light/delivery/util/JwtUtil.java
Normal file
179
src/main/java/com/light/delivery/util/JwtUtil.java
Normal file
@@ -0,0 +1,179 @@
|
||||
package com.light.delivery.util;
|
||||
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import java.util.Base64;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
|
||||
/**
|
||||
* JWT 工具类,负责生成、解析和校验 JWT Token。
|
||||
* 提供Token的创建、解析、验证等核心功能。
|
||||
*/
|
||||
@Component
|
||||
public class JwtUtil {
|
||||
|
||||
/**
|
||||
* JWT 密钥,从配置文件注入。
|
||||
*/
|
||||
@Value("${jwt.secret}")
|
||||
private String secret;
|
||||
|
||||
/**
|
||||
* JWT 过期时间(秒),从配置文件注入。
|
||||
*/
|
||||
@Value("${jwt.expiration}")
|
||||
private Long expiration;
|
||||
|
||||
|
||||
/**
|
||||
* 生成一个安全的随机密钥,并打印其Base64编码字符串(用于配置到配置文件中)
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
// 生成一个专门用于 HS512 算法的安全随机密钥
|
||||
SecretKey secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS512);
|
||||
|
||||
// 将密钥转换为Base64字符串,方便存储在配置文件中
|
||||
String base64Key = Base64.getEncoder().encodeToString(secretKey.getEncoded());
|
||||
|
||||
System.out.println("Generated Base64 Secret Key: ");
|
||||
System.out.println(base64Key);
|
||||
// 复制控制台输出的这一长串字符,放到你的配置里
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成包含用户名和角色的 JWT Token。
|
||||
* @param username 用户名
|
||||
* @param role 角色
|
||||
* @return 生成的 Token 字符串
|
||||
*/
|
||||
public String generateToken(String username, String role) {
|
||||
Map<String, Object> claims = new HashMap<>();
|
||||
claims.put("role", role);
|
||||
return createToken(claims, username);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 JWT Token。
|
||||
* @param claims 载荷信息
|
||||
* @param subject 用户名
|
||||
* @return Token 字符串
|
||||
*/
|
||||
private String createToken(Map<String, Object> claims, String subject) {
|
||||
return Jwts.builder()
|
||||
.setClaims(claims)
|
||||
.setSubject(subject)
|
||||
.setIssuedAt(new Date(System.currentTimeMillis()))
|
||||
.setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
|
||||
.signWith(SignatureAlgorithm.HS512, secret)
|
||||
.compact();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 从Token中提取用户名。
|
||||
* @param token Token字符串
|
||||
* @return 用户名
|
||||
*/
|
||||
public String getUsernameFromToken(String token) {
|
||||
if (token == null) {
|
||||
throw new IllegalArgumentException("Token cannot be null");
|
||||
}
|
||||
|
||||
Claims claims = Jwts.parser()
|
||||
.setSigningKey(secret) // secret 为你的 JWT 密钥
|
||||
.parseClaimsJws(token.replace("Bearer ", ""))
|
||||
.getBody();
|
||||
return claims.getSubject(); // subject 通常存储用户名
|
||||
}
|
||||
|
||||
/**
|
||||
* 从Token中提取用户名。
|
||||
* @param token Token字符串
|
||||
* @return 用户名
|
||||
*/
|
||||
public String extractUsername(String token) {
|
||||
return getUsernameFromToken(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验 Token 是否有效。
|
||||
* @param token Token 字符串
|
||||
* @param username 用户名
|
||||
* @return 是否有效
|
||||
*/
|
||||
public Boolean validateToken(String token, String username) {
|
||||
if (token == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final String extractedUsername = getUsernameFromToken(token);
|
||||
return (extractedUsername.equals(username) && !isTokenExpired(token));
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 Token 中提取角色。
|
||||
* @param token Token 字符串
|
||||
* @return 角色
|
||||
*/
|
||||
public String extractRole(String token) {
|
||||
final Claims claims = extractAllClaims(token);
|
||||
return (String) claims.get("role");
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 Token 中提取过期时间。
|
||||
* @param token Token 字符串
|
||||
* @return 过期时间
|
||||
*/
|
||||
public Date extractExpiration(String token) {
|
||||
return extractClaim(token, Claims::getExpiration);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 Token 中提取自定义信息。
|
||||
* @param token Token 字符串
|
||||
* @param claimsResolver 提取函数
|
||||
* @param <T> 返回类型
|
||||
* @return 提取的自定义信息
|
||||
*/
|
||||
public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
|
||||
final Claims claims = extractAllClaims(token);
|
||||
return claimsResolver.apply(claims);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 Token 中的所有声明。
|
||||
* @param token Token 字符串
|
||||
* @return 声明信息
|
||||
*/
|
||||
private Claims extractAllClaims(String token) {
|
||||
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断 Token 是否过期。
|
||||
* @param token Token 字符串
|
||||
* @return 是否过期
|
||||
*/
|
||||
private Boolean isTokenExpired(String token) {
|
||||
return extractExpiration(token).before(new Date());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 JWT 过期时间。
|
||||
* @return 过期时间(秒)
|
||||
*/
|
||||
public Long getExpirationTime() {
|
||||
return expiration;
|
||||
}
|
||||
}
|
||||
16
src/main/resources/application-dev.yml
Normal file
16
src/main/resources/application-dev.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
# 开发环境配置
|
||||
server:
|
||||
port: 8080
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: light-delivery-backend-dev
|
||||
|
||||
logging:
|
||||
level:
|
||||
com.light.delivery: DEBUG
|
||||
|
||||
# JWT配置
|
||||
jwt:
|
||||
secret: lightDeliveryDevSecretKey
|
||||
expiration: 86400
|
||||
20
src/main/resources/application-prod.yml
Normal file
20
src/main/resources/application-prod.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
# 生产环境配置
|
||||
server:
|
||||
port: 443
|
||||
ssl:
|
||||
key-store-type: PKCS12
|
||||
key-store: /etc/ssl/certs/www.doubleyin.cn.pfx
|
||||
key-store-password: ${KEY_STORE_PASSWORD}
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: light-delivery-backend-prod
|
||||
|
||||
logging:
|
||||
level:
|
||||
com.light.delivery: INFO
|
||||
|
||||
# JWT配置
|
||||
jwt:
|
||||
secret: lightDeliveryProdSecretKey
|
||||
expiration: 86400
|
||||
38
src/main/resources/application.properties
Normal file
38
src/main/resources/application.properties
Normal file
@@ -0,0 +1,38 @@
|
||||
spring.application.name=Light
|
||||
|
||||
# --- MySQL Database Configuration (Remote Server) ---
|
||||
spring.datasource.url=jdbc:mysql://115.190.121.151:3306/light_delivery?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||
spring.datasource.username=double
|
||||
spring.datasource.password=Hu@ng1998!
|
||||
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||
|
||||
# JPA / Hibernate Settings
|
||||
spring.jpa.hibernate.ddl-auto=update
|
||||
spring.jpa.show-sql=true
|
||||
spring.jpa.properties.hibernate.format_sql=true
|
||||
# For MySQL
|
||||
spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect
|
||||
|
||||
# HikariCP Connection Pool Settings
|
||||
spring.datasource.hikari.maximum-pool-size=10
|
||||
spring.datasource.hikari.minimum-idle=2
|
||||
spring.datasource.hikari.connection-timeout=30000
|
||||
spring.datasource.hikari.idle-timeout=600000
|
||||
spring.datasource.hikari.max-lifetime=1800000
|
||||
|
||||
# ------ WeChat Configuration ------
|
||||
wx.appId=wx1b790fd953ac27bf
|
||||
wx.secret=b38c40c89cc4954472fb45d1e7cc27a4
|
||||
wx.token=huang1998
|
||||
wx.aesKey=lwararkj1BgurX8Qown6yjGNsqd8dEIEddDnYN3iLgc
|
||||
|
||||
# WeChat API URLs
|
||||
wx.api.access-token-url=https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential
|
||||
wx.api.code2session-url=https://api.weixin.qq.com/sns/jscode2session
|
||||
|
||||
# Logging Configuration
|
||||
logging.level.com.light.delivery.config=DEBUG
|
||||
|
||||
# JWT Configuration
|
||||
jwt.secret=R/GuND+mK487AS+bUW5uqGmt5dyCkPf3g+uRdxHBY1eYEhxxG1cNgnfrk0l+vv+Xi8werl4P2lDAs0gaiqAqRA==
|
||||
jwt.expiration=86400
|
||||
16
src/main/resources/application.yml
Normal file
16
src/main/resources/application.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
# 默认配置
|
||||
spring:
|
||||
profiles:
|
||||
active: dev
|
||||
|
||||
server:
|
||||
port: 8080
|
||||
|
||||
logging:
|
||||
level:
|
||||
com.light.delivery: DEBUG
|
||||
|
||||
# JWT配置
|
||||
jwt:
|
||||
secret: lightDeliverySecretKey
|
||||
expiration: 86400
|
||||
Reference in New Issue
Block a user