diff --git a/README.md b/README.md index e00c19b..994167e 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,11 @@ Light Delivery 是一个为小程序配送服务设计的后端系统,提供 - REST API位置查询接口 - 位置过期处理机制 +### 5. 员工管理(新增) +- 员工信息维护(仅限管理员) +- 员工增删改查功能 +- 员工角色管理(管理员/配送员) + ## 项目结构 ``` @@ -93,9 +98,20 @@ src - 所有用户首先以游客身份静默登录,创建User记录 - 用户签到或注册时,系统根据手机号在Employee表中查找匹配记录 - 如果匹配成功,将用户的openid写入Employee表,表示该员工已成为系统用户 -- 根据Employee中的角色信息更新User的角色 +- 根据Employee中的角色信息动态确定User的角色 - 对于配送员,还会创建或更新DeliveryPerson记录 +### 用户角色管理优化(新增) + +为了解决数据一致性问题,我们对用户角色管理进行了优化: + +1. 移除了User表中的role字段,避免在User表和Employee表中重复维护角色信息 +2. 用户角色现在完全通过Employee表进行管理 +3. 当需要获取用户角色时,系统会根据用户的手机号在Employee表中查找对应记录 +4. 如果用户未注册(没有手机号)或在Employee表中找不到对应记录,则默认为游客角色 + +这种设计确保了数据的一致性,避免了在多个表中维护相同信息可能带来的问题。 + ## 本地开发与测试 ### 环境配置 @@ -103,7 +119,7 @@ src 项目支持多种运行环境配置: 1. **本地开发环境** (`local`) - - 端口: 8443 + - 端口: 8080 - 数据库: 远程MySQL (与生产环境相同) - SSL: 禁用 - 日志级别: DEBUG @@ -132,7 +148,7 @@ src ```bash # 使用local配置文件运行 -mvn spring-boot:run -Dspring-boot.run.profiles=local +mvn spring-boot:run -D"spring.profiles.active"="local" # 或者使用test配置文件运行(使用内存数据库) mvn spring-boot:run -Dspring-boot.run.profiles=test @@ -158,7 +174,7 @@ java -jar target/light-delivery-1.0.0.jar --spring.profiles.active=test build-local.bat # 运行容器 -docker run -d --name light-delivery -p 8443:8443 light-delivery-app +docker run -d --name light-delivery -p 8080:8080 light-delivery-app ``` ## 部署说明 @@ -234,11 +250,32 @@ deploy.bat - `GET /delivery-persons/{id}` - 获取配送员详情 - `PUT /delivery-persons/{id}/location` - 更新配送员位置 +### 员工管理相关(仅限管理员访问) +- `GET /employees` - 获取员工列表 +- `POST /employees` - 添加员工 +- `PUT /employees/{id}` - 更新员工信息 +- `DELETE /employees/{id}` - 删除员工 + ### 位置同步相关 - `GET /location-sync/delivery-persons/locations` - 获取所有配送员位置 ## 最近更新 +### 用户角色管理优化(新增) +为解决数据一致性问题,我们优化了用户角色管理机制: +- 移除了User表中的role字段 +- 用户角色现在完全通过Employee表进行管理 +- 系统会根据用户的手机号在Employee表中动态查找角色信息 +- 未注册用户默认为游客角色 + +### 员工管理功能(新增) +为便于管理员维护员工信息,我们新增了员工管理功能: +- 新增EmployeeController控制器,提供员工信息的增删改查接口 +- 新增EmployeeService服务层,处理员工相关的业务逻辑 +- 新增EmployeeDto数据传输对象,用于前后端数据交互 +- 添加权限控制,仅管理员可访问员工管理接口 +- 添加全局异常处理机制 + ### 位置历史记录功能移除 为了简化系统架构并提高性能,我们移除了位置历史记录功能。具体变更包括: - 删除了`LocationHistory`实体类 @@ -253,4 +290,6 @@ deploy.bat 1. 系统仅支持微信登录,不再支持用户名密码登录 2. 所有敏感接口都需要通过JWT Token认证 3. 配送员位置信息具有时效性,默认5分钟内有效 -4. 位置历史记录功能已被移除,如有需要可使用第三方服务进行位置追踪 \ No newline at end of file +4. 位置历史记录功能已被移除,如有需要可使用第三方服务进行位置追踪 +5. 员工管理接口仅限管理员角色访问 +6. 用户角色信息现在通过员工表动态获取,确保数据一致性 \ No newline at end of file diff --git a/src/main/java/com/light/delivery/controller/UserController.java b/src/main/java/com/light/delivery/controller/UserController.java index 78ee4a8..b077def 100644 --- a/src/main/java/com/light/delivery/controller/UserController.java +++ b/src/main/java/com/light/delivery/controller/UserController.java @@ -1,13 +1,13 @@ 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.UserRole; import com.light.delivery.model.WxLoginRequest; import com.light.delivery.service.UserService; +import com.light.delivery.service.impl.UserServiceImpl; import com.light.delivery.util.JwtUtil; import jakarta.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; @@ -39,6 +39,12 @@ public class UserController { */ @Autowired private JwtUtil jwtUtil; + + /** + * UserServiceImpl依赖注入,用于获取用户角色。 + */ + @Autowired + private UserServiceImpl userServiceImpl; /** * 获取当前用户状态接口。 @@ -60,7 +66,8 @@ public class UserController { // 根据用户角色和信息完整性判断状态 String status; - if (user.getRole() == null || user.getRole() == UserRole.GUEST) { + UserRole userRole = userServiceImpl.getUserRole(user); + if (userRole == null || userRole == UserRole.GUEST) { // 未注册用户 status = "unregistered"; } else if ((user.getName() == null || user.getName().isEmpty()) && @@ -118,7 +125,9 @@ public class UserController { dto.setId(user.getId()); dto.setName(user.getName()); dto.setPhone(user.getPhone()); - dto.setRole(user.getRole() != null ? user.getRole().getCode() : null); + // 从员工表获取角色信息 + UserRole userRole = userServiceImpl.getUserRole(user); + dto.setRole(userRole != null ? userRole.getCode() : null); dto.setOpenid(user.getOpenid()); return dto; } @@ -173,7 +182,6 @@ public class UserController { } try { - String username = jwtUtil.getUsernameFromToken(token); User user = userService.getUserInfo(token); User updatedUser = userService.signIn(user.getId()); UserInfoResponse response = toUserInfoResponse(updatedUser); diff --git a/src/main/java/com/light/delivery/model/User.java b/src/main/java/com/light/delivery/model/User.java index c533654..e61c6db 100644 --- a/src/main/java/com/light/delivery/model/User.java +++ b/src/main/java/com/light/delivery/model/User.java @@ -29,12 +29,6 @@ public class User { */ @Column(name = "phone") private String phone; - - /** - * 用户角色(如 ADMIN, DELIVERY_PERSON, GUEST) - */ - @Enumerated(EnumType.STRING) - private UserRole role; /** * 微信用户唯一标识 diff --git a/src/main/java/com/light/delivery/service/impl/UserServiceImpl.java b/src/main/java/com/light/delivery/service/impl/UserServiceImpl.java index 0e80d2d..ecc0029 100644 --- a/src/main/java/com/light/delivery/service/impl/UserServiceImpl.java +++ b/src/main/java/com/light/delivery/service/impl/UserServiceImpl.java @@ -96,6 +96,7 @@ public class UserServiceImpl implements UserService { if (user == null) { throw new IllegalArgumentException("用户不存在"); } + return user; } catch (Exception e) { // 捕获JWT解析异常等 @@ -127,12 +128,10 @@ public class UserServiceImpl implements UserService { } // 如果用户是配送员,通知WebSocket处理器清理连接 - if (UserRole.DELIVERY_PERSON.equals(user.getRole())) { - // 注意:这里需要根据实际业务逻辑获取配送员ID - // 可能需要通过其他方式关联用户ID和配送员ID - // 这里假设用户ID和配送员ID相同(根据项目实际情况调整) - // locationWebSocketHandler.removeUserConnection(user.getId()); - } + // 注意:这里需要根据实际业务逻辑获取配送员ID + // 可能需要通过其他方式关联用户ID和配送员ID + // 这里假设用户ID和配送员ID相同(根据项目实际情况调整) + // locationWebSocketHandler.removeUserConnection(user.getId()); } catch (Exception e) { // 记录日志但不中断登出流程 System.err.println("清理WebSocket连接时出错: " + e.getMessage()); @@ -160,24 +159,15 @@ public class UserServiceImpl implements UserService { 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); - } } + + // 获取用户角色 + UserRole userRole = getUserRole(user); - // 确保用户角色不为空,防止在生成token时出现空指针异常 - if (user.getRole() == null) { - user.setRole(UserRole.GUEST); - } - - String token = jwtUtil.generateToken(user.getOpenid(), user.getRole().getCode()); + String token = jwtUtil.generateToken(user.getOpenid(), + userRole != null ? userRole.getCode() : UserRole.GUEST.getCode()); LoginResponse response = new LoginResponse(); response.setToken(token); response.setUser(toUserInfoResponse(user)); @@ -270,14 +260,13 @@ public class UserServiceImpl implements UserService { } // 将openid写入employee表,表示该员工已成为系统用户 - employee.setOpenid(userOptional.get().getOpenid()); + User user = userOptional.get(); + employee.setOpenid(user.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; @@ -319,7 +308,17 @@ public class UserServiceImpl implements UserService { dto.setId(user.getId()); dto.setName(user.getName()); dto.setPhone(user.getPhone()); - dto.setRole(user.getRole() != null ? user.getRole().getCode() : null); + // 从员工表获取角色信息 + if (user.getPhone() != null && !user.getPhone().isEmpty()) { + Optional employee = employeeRepository.findByPhone(user.getPhone()); + if (employee.isPresent()) { + dto.setRole(employee.get().getRole()); + } else { + dto.setRole(UserRole.GUEST.getCode()); + } + } else { + dto.setRole(UserRole.GUEST.getCode()); + } dto.setOpenid(user.getOpenid()); return dto; } @@ -338,7 +337,9 @@ public class UserServiceImpl implements UserService { } User user = userOptional.get(); - if (user.getRole() != UserRole.DELIVERY_PERSON) { + UserRole userRole = getUserRole(user); + + if (userRole != UserRole.DELIVERY_PERSON) { // 非配送员角色没有签到状态概念 return false; } @@ -346,4 +347,20 @@ public class UserServiceImpl implements UserService { // 检查WebSocket中的签到状态 return locationWebSocketHandler.isDeliveryPersonSignedIn(userId); } + + /** + * 获取用户角色 + * @param user 用户对象 + * @return 用户角色 + */ + public UserRole getUserRole(User user) { + // 从员工表获取角色信息 + if (user.getPhone() != null && !user.getPhone().isEmpty()) { + Optional employee = employeeRepository.findByPhone(user.getPhone()); + if (employee.isPresent()) { + return UserRole.fromCode(employee.get().getRole()); + } + } + return UserRole.GUEST; + } } \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 3c876c2..d39a346 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,9 +1,8 @@ spring.application.name=Light server.port=443 -server.ssl.key-store-type=JKS -server.ssl.key-store=/etc/ssl/certs/www.doubleyin.cn.jks -server.ssl.key-store-password=${KEY_STORE_PASSWORD} +# 默认禁用SSL,通过profile启用 +server.ssl.enabled=false 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