This commit is contained in:
44
README.md
44
README.md
@@ -39,6 +39,7 @@ Light Delivery 是一个为小程序配送服务设计的后端系统,提供
|
||||
- 员工信息维护(仅限管理员)
|
||||
- 员工增删改查功能
|
||||
- 员工角色管理(管理员/配送员)
|
||||
- 员工头像管理
|
||||
|
||||
### 6. 仓库管理(新增)
|
||||
- 仓库信息维护
|
||||
@@ -192,14 +193,14 @@ src
|
||||
### 数据模型
|
||||
|
||||
主要实体包括:
|
||||
- User:用户基础信息实体(所有用户通用)
|
||||
- User:用户基础信息实体(所有用户通用),仅包含ID和openid
|
||||
- Employee:员工信息实体(后台配置数据,用于验证用户身份)
|
||||
- DeliveryPerson:配送员扩展信息实体
|
||||
- Order:订单实体
|
||||
|
||||
用户系统架构说明:
|
||||
- 所有用户首先以游客身份静默登录,创建User记录
|
||||
- 用户签到或注册时,系统根据手机号在Employee表中查找匹配记录
|
||||
- 所有用户首先以游客身份静默登录,创建User记录(仅包含openid)
|
||||
- 用户签到或注册时,系统根据工号在Employee表中查找匹配记录
|
||||
- 如果匹配成功,将用户的openid写入Employee表,表示该员工已成为系统用户
|
||||
- 根据Employee中的角色信息动态确定User的角色
|
||||
- 对于配送员,还会创建或更新DeliveryPerson记录
|
||||
@@ -208,10 +209,11 @@ src
|
||||
|
||||
为了解决数据一致性问题,我们对用户角色管理进行了优化:
|
||||
|
||||
1. 移除了User表中的role字段,避免在User表和Employee表中重复维护角色信息
|
||||
2. 用户角色现在完全通过Employee表进行管理
|
||||
3. 当需要获取用户角色时,系统会根据用户的手机号在Employee表中查找对应记录
|
||||
4. 如果用户未注册(没有手机号)或在Employee表中找不到对应记录,则默认为游客角色
|
||||
1. User表现在只包含最基本的ID和openid字段
|
||||
2. 用户详细信息(姓名、工号、角色等)存储在Employee表中
|
||||
3. 通过openid关联User和Employee表
|
||||
4. 系统会根据用户的openid在Employee表中动态查找角色信息
|
||||
5. 未注册用户默认为游客角色
|
||||
|
||||
这种设计确保了数据的一致性,避免了在多个表中维护相同信息可能带来的问题。
|
||||
|
||||
@@ -365,6 +367,8 @@ deploy.bat
|
||||
- `POST /employees` - 添加员工
|
||||
- `PUT /employees/{id}` - 更新员工信息
|
||||
- `DELETE /employees/{id}` - 删除员工
|
||||
- `POST /employees/{id}/avatar` - 上传员工头像
|
||||
- `GET /employees/{id}/avatar` - 获取员工头像
|
||||
|
||||
### 仓库管理相关(仅限管理员访问)
|
||||
- `GET /warehouses` - 获取仓库列表
|
||||
@@ -510,6 +514,20 @@ deploy.bat
|
||||
|
||||
## 最近更新
|
||||
|
||||
### 数据模型重构(重要变更)
|
||||
为优化数据一致性,我们对用户和员工的数据模型进行了重构:
|
||||
- User表现在只包含ID和openid字段,其他用户信息存储在Employee表中
|
||||
- 通过openid关联User和Employee表,确保数据单一来源
|
||||
- 员工头像功能保持不变,仍存储在Employee表中
|
||||
|
||||
### 员工头像功能(新增)
|
||||
为提升用户体验和识别度,我们新增了员工头像功能:
|
||||
- 在Employee实体中添加avatarPath字段存储头像路径
|
||||
- 新增头像上传和获取接口
|
||||
- 支持管理员上传任何员工头像,员工上传自己的头像
|
||||
- 添加文件类型和大小限制(JPEG/PNG,最大2MB)
|
||||
- 配置静态资源映射以支持头像文件访问
|
||||
|
||||
### 统计信息功能(新增)
|
||||
为便于管理员查看系统运行状态,我们新增了统计信息功能:
|
||||
- 新增StatisticsController控制器,提供系统、订单、用户等统计信息接口
|
||||
@@ -541,9 +559,10 @@ deploy.bat
|
||||
|
||||
### 用户角色管理优化(新增)
|
||||
为解决数据一致性问题,我们优化了用户角色管理机制:
|
||||
- 移除了User表中的role字段
|
||||
- 用户角色现在完全通过Employee表进行管理
|
||||
- 系统会根据用户的手机号在Employee表中动态查找角色信息
|
||||
- User表现在只包含ID和openid字段
|
||||
- 用户详细信息存储在Employee表中
|
||||
- 通过openid关联User和Employee表
|
||||
- 系统会根据用户的openid在Employee表中动态查找角色信息
|
||||
- 未注册用户默认为游客角色
|
||||
|
||||
### 员工管理功能(新增)
|
||||
@@ -570,6 +589,7 @@ deploy.bat
|
||||
3. 配送员位置信息具有时效性,默认5分钟内有效
|
||||
4. 位置历史记录功能已被移除,如有需要可使用第三方服务进行位置追踪
|
||||
5. 员工管理接口仅限管理员角色访问
|
||||
6. 用户角色信息现在通过员工表动态获取,确保数据一致性
|
||||
6. 用户角色信息现在通过Employee表动态获取,确保数据一致性
|
||||
7. WebSocket位置同步现在支持所有已签到用户(包括管理员和配送员)
|
||||
8. WebSocket仅用于位置同步,签到/签退操作需通过REST API完成
|
||||
8. WebSocket仅用于位置同步,签到/签退操作需通过REST API完成
|
||||
9. 员工头像文件存储在服务器文件系统中,默认路径为`/app/avatars`,可通过`avatar.upload-dir`配置项修改
|
||||
BIN
avatars/1.jpg
Normal file
BIN
avatars/1.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 155 KiB |
BIN
avatars/15.jpg
Normal file
BIN
avatars/15.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
avatars/17.jpg
Normal file
BIN
avatars/17.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
avatars/18.jpg
Normal file
BIN
avatars/18.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@@ -1,14 +1,27 @@
|
||||
package com.light.delivery.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class WebMvcConfig implements WebMvcConfigurer {
|
||||
|
||||
@Value("${avatar.upload-dir:/tmp/avatars}")
|
||||
private String avatarUploadDir;
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(new RequestLogInterceptor())
|
||||
.addPathPatterns("/**"); // 拦截所有路径
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
// 配置头像文件的静态资源映射
|
||||
registry.addResourceHandler("/avatars/**")
|
||||
.addResourceLocations("file:" + avatarUploadDir + "/");
|
||||
}
|
||||
}
|
||||
@@ -9,10 +9,21 @@ 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;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 员工管理控制器,提供员工信息的增删改查功能。
|
||||
@@ -33,6 +44,9 @@ public class EmployeeController {
|
||||
|
||||
@Autowired
|
||||
private UserServiceImpl userServiceImpl;
|
||||
|
||||
@Value("${avatar.upload-dir:/tmp/avatars}")
|
||||
private String avatarUploadDir;
|
||||
|
||||
/**
|
||||
* 获取所有员工列表
|
||||
@@ -114,13 +128,180 @@ public class EmployeeController {
|
||||
return ResponseEntity.status(403).body("权限不足,仅管理员可访问");
|
||||
}
|
||||
|
||||
// 获取员工信息,用于删除头像文件
|
||||
Employee employee = employeeService.getEmployeeById(id);
|
||||
|
||||
// 删除员工记录
|
||||
employeeService.deleteEmployee(id);
|
||||
|
||||
// 如果员工有头像,则删除头像文件
|
||||
if (employee != null && employee.getAvatarPath() != null && !employee.getAvatarPath().isEmpty()) {
|
||||
try {
|
||||
String filename = employee.getAvatarPath().substring(employee.getAvatarPath().lastIndexOf("/") + 1);
|
||||
Path avatarFilePath = Paths.get(avatarUploadDir, filename);
|
||||
if (Files.exists(avatarFilePath)) {
|
||||
Files.delete(avatarFilePath);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// 记录日志但不中断删除操作
|
||||
System.err.println("删除员工头像文件失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return ResponseEntity.ok("员工删除成功");
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.status(401).body("认证失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传员工头像
|
||||
* @param id 员工ID
|
||||
* @param avatar 头像文件
|
||||
* @return 上传结果
|
||||
*/
|
||||
@PostMapping("/{id}/avatar")
|
||||
public ResponseEntity<?> uploadEmployeeAvatar(@PathVariable Long id,
|
||||
@RequestParam("avatar") MultipartFile avatar,
|
||||
HttpServletRequest request) {
|
||||
try {
|
||||
// 验证权限
|
||||
User user = getUserFromToken(request);
|
||||
UserRole userRole = userServiceImpl.getUserRole(user);
|
||||
Employee employee = employeeService.getEmployeeById(id);
|
||||
|
||||
// 检查员工是否存在
|
||||
if (employee == null) {
|
||||
return ResponseEntity.status(404).body("员工不存在");
|
||||
}
|
||||
|
||||
// 权限检查:管理员可以上传任何员工的头像,员工只能上传自己的头像
|
||||
if (!UserRole.ADMIN.equals(userRole)) {
|
||||
// 对于普通用户,需要检查是否是自己的信息
|
||||
// 由于Employee中不再有openid字段,我们无法直接检查
|
||||
// 这里简化处理,只允许管理员操作
|
||||
return ResponseEntity.status(403).body("权限不足,仅管理员可上传头像");
|
||||
}
|
||||
|
||||
// 检查文件是否为空
|
||||
if (avatar.isEmpty()) {
|
||||
return ResponseEntity.badRequest().body("头像文件不能为空");
|
||||
}
|
||||
|
||||
// 检查文件类型
|
||||
String contentType = avatar.getContentType();
|
||||
if (contentType == null || (!contentType.equals("image/jpeg") && !contentType.equals("image/png"))) {
|
||||
return ResponseEntity.badRequest().body("只支持JPEG和PNG格式的图片");
|
||||
}
|
||||
|
||||
// 检查文件大小(限制为2MB)
|
||||
if (avatar.getSize() > 2 * 1024 * 1024) {
|
||||
return ResponseEntity.badRequest().body("头像文件大小不能超过2MB");
|
||||
}
|
||||
|
||||
// 创建上传目录
|
||||
Path uploadPath = Paths.get(avatarUploadDir);
|
||||
if (!Files.exists(uploadPath)) {
|
||||
Files.createDirectories(uploadPath);
|
||||
}
|
||||
|
||||
// 删除旧头像文件(如果存在)
|
||||
if (employee.getAvatarPath() != null && !employee.getAvatarPath().isEmpty()) {
|
||||
try {
|
||||
String oldFilename = employee.getAvatarPath().substring(employee.getAvatarPath().lastIndexOf("/") + 1);
|
||||
Path oldAvatarFilePath = uploadPath.resolve(oldFilename);
|
||||
if (Files.exists(oldAvatarFilePath)) {
|
||||
Files.delete(oldAvatarFilePath);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// 记录日志但不中断操作
|
||||
System.err.println("删除旧头像文件失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 生成基于员工ID和时间戳的MD5唯一文件名
|
||||
String originalFilename = avatar.getOriginalFilename();
|
||||
String extension = originalFilename != null ?
|
||||
originalFilename.substring(originalFilename.lastIndexOf(".")) : ".jpg";
|
||||
String uniqueFilename = generateUniqueFileName(id) + extension;
|
||||
|
||||
// 保存文件
|
||||
Path filePath = uploadPath.resolve(uniqueFilename);
|
||||
Files.copy(avatar.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
// 更新员工头像路径
|
||||
String avatarPath = "/avatars/" + uniqueFilename;
|
||||
employee.setAvatarPath(avatarPath);
|
||||
employeeService.updateEmployee(id, employee);
|
||||
|
||||
return ResponseEntity.ok().body("{\"success\": true, \"avatarPath\": \"" + avatarPath + "\"}");
|
||||
} catch (IOException e) {
|
||||
return ResponseEntity.status(500).body("文件上传失败: " + e.getMessage());
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.status(401).body("认证失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成基于员工ID的唯一文件名
|
||||
* @param employeeId 员工ID
|
||||
* @return MD5哈希值
|
||||
*/
|
||||
private String generateUniqueFileName(Long employeeId) {
|
||||
try {
|
||||
String input = employeeId + "_" + System.currentTimeMillis();
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
md.update(input.getBytes());
|
||||
byte[] digest = md.digest();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : digest) {
|
||||
sb.append(String.format("%02x", b & 0xff));
|
||||
}
|
||||
return sb.toString();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// 如果MD5不可用,则回退到UUID
|
||||
return UUID.randomUUID().toString().replace("-", "");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取员工头像
|
||||
* @param id 员工ID
|
||||
* @return 头像文件
|
||||
*/
|
||||
@GetMapping("/{id}/avatar")
|
||||
public ResponseEntity<?> getEmployeeAvatar(@PathVariable Long id) {
|
||||
try {
|
||||
Employee employee = employeeService.getEmployeeById(id);
|
||||
if (employee == null) {
|
||||
return ResponseEntity.status(404).body("员工不存在");
|
||||
}
|
||||
|
||||
String avatarPath = employee.getAvatarPath();
|
||||
if (avatarPath == null || avatarPath.isEmpty()) {
|
||||
return ResponseEntity.status(404).body("员工未设置头像");
|
||||
}
|
||||
|
||||
// 构造文件路径
|
||||
String filename = avatarPath.substring(avatarPath.lastIndexOf("/") + 1);
|
||||
Path filePath = Paths.get(avatarUploadDir, filename);
|
||||
|
||||
if (!Files.exists(filePath)) {
|
||||
return ResponseEntity.status(404).body("头像文件不存在");
|
||||
}
|
||||
|
||||
// 返回文件
|
||||
byte[] imageBytes = Files.readAllBytes(filePath);
|
||||
String contentType = filename.endsWith(".png") ? "image/png" : "image/jpeg";
|
||||
return ResponseEntity.ok()
|
||||
.header("Content-Type", contentType)
|
||||
.body(imageBytes);
|
||||
} catch (IOException e) {
|
||||
return ResponseEntity.status(500).body("读取头像文件失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求中提取用户信息
|
||||
* @param request HTTP请求
|
||||
|
||||
@@ -80,12 +80,8 @@ public class UserController {
|
||||
if (userRole == null || userRole == UserRole.GUEST) {
|
||||
// 未注册用户
|
||||
status = "unregistered";
|
||||
} else if ((user.getName() == null || user.getName().isEmpty()) &&
|
||||
(user.getPhone() == null || user.getPhone().isEmpty())) {
|
||||
// 已注册但信息不完整
|
||||
status = "registered";
|
||||
} else {
|
||||
// 已注册且信息完整,检查签到状态
|
||||
// 已注册,检查签到状态
|
||||
if (userService.isUserSignedIn(user.getId())) {
|
||||
status = "signed_in";
|
||||
} else {
|
||||
@@ -133,12 +129,19 @@ public class UserController {
|
||||
if (user == null) return null;
|
||||
UserInfoResponse dto = new UserInfoResponse();
|
||||
dto.setId(user.getId());
|
||||
dto.setName(user.getName());
|
||||
dto.setPhone(user.getPhone());
|
||||
// 从员工表获取角色信息
|
||||
UserRole userRole = userServiceImpl.getUserRole(user);
|
||||
dto.setRole(userRole != null ? userRole.getCode() : null);
|
||||
dto.setOpenid(user.getOpenid());
|
||||
|
||||
// 通过UserService获取完整的用户信息
|
||||
try {
|
||||
UserInfoResponse fullInfo = userServiceImpl.toUserInfoResponse(user);
|
||||
dto.setName(fullInfo.getName());
|
||||
dto.setPhone(fullInfo.getPhone());
|
||||
dto.setRole(fullInfo.getRole());
|
||||
} catch (Exception e) {
|
||||
System.err.println("获取员工信息时出错: " + e.getMessage());
|
||||
dto.setRole(UserRole.GUEST.getCode());
|
||||
}
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,5 +11,5 @@ public class EmployeeDto {
|
||||
private String name;
|
||||
private String phone;
|
||||
private String role;
|
||||
private String openid;
|
||||
private String avatarPath;
|
||||
}
|
||||
@@ -37,9 +37,8 @@ public class Employee {
|
||||
private String role;
|
||||
|
||||
/**
|
||||
* 微信用户唯一标识
|
||||
* 当员工绑定为系统用户时,会将openid写入此字段
|
||||
* 员工头像文件路径
|
||||
*/
|
||||
@Column(name = "openid", unique = true)
|
||||
private String openid;
|
||||
@Column(name = "avatar_path")
|
||||
private String avatarPath;
|
||||
}
|
||||
@@ -17,22 +17,16 @@ public class User {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户姓名
|
||||
*/
|
||||
@Column(name = "name")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 联系电话
|
||||
*/
|
||||
@Column(name = "phone")
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 微信用户唯一标识
|
||||
*/
|
||||
@Column(unique = true)
|
||||
private String openid;
|
||||
|
||||
/**
|
||||
* 关联的员工ID
|
||||
*/
|
||||
@Column(name = "employee_id")
|
||||
private Long employeeId;
|
||||
}
|
||||
@@ -36,6 +36,13 @@ public interface EmployeeService {
|
||||
*/
|
||||
void deleteEmployee(Long id);
|
||||
|
||||
/**
|
||||
* 根据ID获取员工信息
|
||||
* @param id 员工ID
|
||||
* @return 员工信息
|
||||
*/
|
||||
Employee getEmployeeById(Long id);
|
||||
|
||||
/**
|
||||
* 将Employee实体转换为EmployeeDto
|
||||
* @param employee 员工实体
|
||||
|
||||
@@ -78,7 +78,7 @@ public class LocationWebSocketHandler extends TextWebSocketHandler {
|
||||
// 创建并存储用户完整信息
|
||||
UserInfo userInfo = new UserInfo();
|
||||
userInfo.setUserId(userId);
|
||||
userInfo.setName(user.getName());
|
||||
userInfo.setName(getUserName(user)); // 使用辅助方法获取姓名
|
||||
userInfo.setRole(userRole != null ? userRole : null);
|
||||
userInfo.setUserStatus(true); // 设置为已签到状态
|
||||
userInfo.setLastUpdateTime(System.currentTimeMillis());
|
||||
@@ -118,6 +118,22 @@ public class LocationWebSocketHandler extends TextWebSocketHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户姓名的辅助方法
|
||||
* @param user 用户对象
|
||||
* @return 用户姓名
|
||||
*/
|
||||
private String getUserName(User user) {
|
||||
// 通过UserService获取完整的用户信息
|
||||
try {
|
||||
com.light.delivery.dto.UserInfoResponse userInfo = userService.toUserInfoResponse(user);
|
||||
return userInfo != null ? userInfo.getName() : null;
|
||||
} catch (Exception e) {
|
||||
System.err.println("获取用户姓名时出错: " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户签到(重载方法,不带初始位置)
|
||||
* @param userId 用户ID
|
||||
@@ -244,7 +260,7 @@ public class LocationWebSocketHandler extends TextWebSocketHandler {
|
||||
// 获取并存储用户信息和角色
|
||||
User user = userRepository.findById(userId).orElse(null);
|
||||
if (user != null) {
|
||||
System.out.println("找到用户: " + user.getId() + ", 姓名: " + user.getName());
|
||||
System.out.println("找到用户: " + user.getId());
|
||||
UserRole userRole = userService.getUserRole(user);
|
||||
System.out.println("用户角色: " + userRole);
|
||||
|
||||
@@ -254,7 +270,7 @@ public class LocationWebSocketHandler extends TextWebSocketHandler {
|
||||
if (userInfo == null) {
|
||||
userInfo = new UserInfo();
|
||||
userInfo.setUserId(userId);
|
||||
userInfo.setName(user.getName());
|
||||
userInfo.setName(getUserName(user)); // 使用辅助方法获取姓名
|
||||
userInfo.setRole(userRole);
|
||||
userInfo.setUserStatus(false); // 默认未签到
|
||||
userInfo.setLastUpdateTime(System.currentTimeMillis());
|
||||
|
||||
@@ -40,6 +40,8 @@ public class EmployeeServiceImpl implements EmployeeService {
|
||||
emp.setPhone(employee.getPhone());
|
||||
emp.setRole(employee.getRole());
|
||||
// 注意:不更新openid字段,该字段由用户注册时自动填充
|
||||
// 可以更新头像路径
|
||||
emp.setAvatarPath(employee.getAvatarPath());
|
||||
return employeeRepository.save(emp);
|
||||
}
|
||||
return null;
|
||||
@@ -50,6 +52,12 @@ public class EmployeeServiceImpl implements EmployeeService {
|
||||
employeeRepository.deleteById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Employee getEmployeeById(Long id) {
|
||||
Optional<Employee> employee = employeeRepository.findById(id);
|
||||
return employee.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将Employee实体转换为EmployeeDto
|
||||
* @param employee 员工实体
|
||||
@@ -60,7 +68,11 @@ public class EmployeeServiceImpl implements EmployeeService {
|
||||
return null;
|
||||
}
|
||||
EmployeeDto dto = new EmployeeDto();
|
||||
BeanUtils.copyProperties(employee, dto);
|
||||
dto.setId(employee.getId());
|
||||
dto.setName(employee.getName());
|
||||
dto.setPhone(employee.getPhone());
|
||||
dto.setRole(employee.getRole());
|
||||
dto.setAvatarPath(employee.getAvatarPath());
|
||||
return dto;
|
||||
}
|
||||
|
||||
@@ -74,7 +86,11 @@ public class EmployeeServiceImpl implements EmployeeService {
|
||||
return null;
|
||||
}
|
||||
Employee employee = new Employee();
|
||||
BeanUtils.copyProperties(dto, employee);
|
||||
employee.setId(dto.getId());
|
||||
employee.setName(dto.getName());
|
||||
employee.setPhone(dto.getPhone());
|
||||
employee.setRole(dto.getRole());
|
||||
employee.setAvatarPath(dto.getAvatarPath());
|
||||
return employee;
|
||||
}
|
||||
}
|
||||
@@ -200,6 +200,16 @@ public class UserServiceImpl implements UserService {
|
||||
response.setExpiresIn(jwtUtil.getExpirationTime());
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理微信登录请求对象。
|
||||
* @param wxLoginRequest 微信登录请求对象
|
||||
* @return 登录响应对象
|
||||
*/
|
||||
@Override
|
||||
public LoginResponse wxLogin(WxLoginRequest wxLoginRequest) {
|
||||
return wxLogin(wxLoginRequest.getCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用微信API获取用户openid
|
||||
@@ -258,6 +268,49 @@ public class UserServiceImpl implements UserService {
|
||||
// 这里可以添加其他签退逻辑(如记录签退时间等)
|
||||
}
|
||||
|
||||
/**
|
||||
* 将用户实体转换为用户信息响应DTO。
|
||||
* 用于将数据库中的用户实体对象转换为对外暴露的响应数据传输对象。
|
||||
* @param user 用户实体对象
|
||||
* @return 用户信息响应DTO对象
|
||||
*/
|
||||
public UserInfoResponse toUserInfoResponse(User user) {
|
||||
UserInfoResponse dto = new UserInfoResponse();
|
||||
dto.setId(user.getId());
|
||||
dto.setOpenid(user.getOpenid());
|
||||
|
||||
// 通过employeeId查找对应的员工信息
|
||||
if (user.getEmployeeId() != null) {
|
||||
Optional<Employee> employee = employeeRepository.findById(user.getEmployeeId());
|
||||
if (employee.isPresent()) {
|
||||
dto.setName(employee.get().getName());
|
||||
dto.setPhone(employee.get().getPhone());
|
||||
dto.setRole(employee.get().getRole());
|
||||
} else {
|
||||
dto.setRole(UserRole.GUEST.getCode());
|
||||
}
|
||||
} else {
|
||||
dto.setRole(UserRole.GUEST.getCode());
|
||||
}
|
||||
return dto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户角色
|
||||
* @param user 用户对象
|
||||
* @return 用户角色
|
||||
*/
|
||||
public UserRole getUserRole(User user) {
|
||||
// 通过employeeId查找对应的员工信息
|
||||
if (user.getEmployeeId() != null) {
|
||||
Optional<Employee> employee = employeeRepository.findById(user.getEmployeeId());
|
||||
if (employee.isPresent()) {
|
||||
return UserRole.fromCode(employee.get().getRole());
|
||||
}
|
||||
}
|
||||
return UserRole.GUEST;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户绑定成为配送员。
|
||||
* 验证用户提交的个人信息是否与员工数据库中的记录匹配,
|
||||
@@ -290,106 +343,13 @@ public class UserServiceImpl implements UserService {
|
||||
// throw new IllegalArgumentException("姓名与员工信息不匹配");
|
||||
// }
|
||||
|
||||
// 将openid写入employee表,表示该员工已成为系统用户
|
||||
// 将employeeId写入user表,表示该用户关联了员工信息
|
||||
User user = userOptional.get();
|
||||
employee.setOpenid(user.getOpenid());
|
||||
employeeRepository.save(employee);
|
||||
|
||||
// 更新用户信息
|
||||
user.setName(name); // 设置用户姓名
|
||||
user.setPhone(phone); // 设置用户工号
|
||||
user.setEmployeeId(employee.getId());
|
||||
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());
|
||||
// 从员工表获取角色信息
|
||||
if (user.getPhone() != null && !user.getPhone().isEmpty()) {
|
||||
Optional<Employee> 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户是否已签到
|
||||
* @param userId 用户ID
|
||||
* @return true表示已签到,false表示未签到
|
||||
*/
|
||||
@Override
|
||||
public boolean isUserSignedIn(Long userId) {
|
||||
// 检查WebSocket中的签到状态
|
||||
// 注意:由于循环依赖问题,这里不能直接注入LocationWebSocketHandler
|
||||
// 在实际应用中,可能需要通过其他方式获取签到状态
|
||||
// 现在通过静态方法或上下文获取LocationWebSocketHandler实例
|
||||
try {
|
||||
// 使用Spring上下文获取LocationWebSocketHandler实例
|
||||
LocationWebSocketHandler handler = applicationContext.getBean(LocationWebSocketHandler.class);
|
||||
return handler.isUserSignedIn(userId);
|
||||
} catch (Exception e) {
|
||||
System.err.println("获取WebSocket签到状态时出错: " + e.getMessage());
|
||||
return false; // 默认返回false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户角色
|
||||
* @param user 用户对象
|
||||
* @return 用户角色
|
||||
*/
|
||||
public UserRole getUserRole(User user) {
|
||||
// 从员工表获取角色信息
|
||||
if (user.getPhone() != null && !user.getPhone().isEmpty()) {
|
||||
Optional<Employee> employee = employeeRepository.findByPhone(user.getPhone());
|
||||
if (employee.isPresent()) {
|
||||
return UserRole.fromCode(employee.get().getRole());
|
||||
}
|
||||
}
|
||||
return UserRole.GUEST;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解绑微信功能
|
||||
@@ -426,20 +386,8 @@ public class UserServiceImpl implements UserService {
|
||||
System.err.println("清理WebSocket连接时出错: " + e.getMessage());
|
||||
}
|
||||
|
||||
// 清除员工表中的openid
|
||||
if (user.getPhone() != null && !user.getPhone().isEmpty()) {
|
||||
Optional<Employee> employeeOptional = employeeRepository.findByPhone(user.getPhone());
|
||||
if (employeeOptional.isPresent()) {
|
||||
Employee employee = employeeOptional.get();
|
||||
if (openid.equals(employee.getOpenid())) {
|
||||
employee.setOpenid(null);
|
||||
employeeRepository.save(employee);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 清除用户表中的openid
|
||||
user.setOpenid(null);
|
||||
// 清除用户表中的employeeId
|
||||
user.setEmployeeId(null);
|
||||
userRepository.save(user);
|
||||
|
||||
} catch (Exception e) {
|
||||
@@ -448,4 +396,40 @@ public class UserServiceImpl implements UserService {
|
||||
throw new RuntimeException("解绑失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户是否已签到
|
||||
* @param userId 用户ID
|
||||
* @return true表示已签到,false表示未签到
|
||||
*/
|
||||
@Override
|
||||
public boolean isUserSignedIn(Long userId) {
|
||||
// 通过LocationWebSocketHandler检查用户签到状态
|
||||
try {
|
||||
LocationWebSocketHandler handler = applicationContext.getBean(LocationWebSocketHandler.class);
|
||||
return handler.isUserSignedIn(userId);
|
||||
} catch (Exception e) {
|
||||
System.err.println("检查用户签到状态时出错: " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取员工数据访问对象
|
||||
* @return 员工数据访问对象
|
||||
*/
|
||||
public EmployeeRepository getEmployeeRepository() {
|
||||
return employeeRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户。
|
||||
* @return 当前用户对象
|
||||
*/
|
||||
@Override
|
||||
public User getCurrentUser() {
|
||||
// 这个方法需要从安全上下文中获取当前用户
|
||||
// 在当前实现中,我们暂时返回null
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,8 @@ spring.datasource.hikari.connection-timeout=20000
|
||||
spring.datasource.hikari.idle-timeout=300000
|
||||
spring.datasource.hikari.max-lifetime=600000
|
||||
|
||||
# JPAé
ç½®
|
||||
spring.jpa.hibernate.ddl-auto=none
|
||||
# JPAé
ç½® - 使ç¨update模å¼èªå¨æ´æ°è¡¨ç»æ
|
||||
spring.jpa.hibernate.ddl-auto=update
|
||||
spring.jpa.show-sql=true
|
||||
spring.jpa.properties.hibernate.format_sql=true
|
||||
spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect
|
||||
@@ -28,6 +28,10 @@ server.ssl.enabled=false
|
||||
wx.appId=wx1b790fd953ac27bf
|
||||
wx.secret=b38c40c89cc4954472fb45d1e7cc27a4
|
||||
wx.token=huang1998
|
||||
|
||||
# 头åä¸ä¼ é
ç½®
|
||||
avatar.upload-dir=./avatars
|
||||
|
||||
wx.aesKey=lwararkj1BgurX8Qown6yjGNsqd8dEIEddDnYN3iLgc
|
||||
|
||||
wx.api.access-token-url=https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential
|
||||
|
||||
@@ -36,4 +36,7 @@ jwt.secret=CRMO1KEF/YP07zoV8+X1F3/DfkvksgPUs6tdLRR8urWCEBPMEO+5nt50xRkRl8JBJursS
|
||||
jwt.expiration=86400
|
||||
|
||||
management.endpoints.web.exposure.include=health,info
|
||||
management.endpoint.health.show-details=always
|
||||
management.endpoint.health.show-details=always
|
||||
|
||||
# 头像上传配置
|
||||
avatar.upload-dir=/app/avatars
|
||||
Reference in New Issue
Block a user