项目介绍
汽车维修服务管理平台是一个面向汽修店的综合业务管理系统。该平台帮助汽修店实现维修预约、工单管理、库存管理、客户管理等核心业务流程的数字化,提升运营效率。
技术架构
后端技术栈
- Spring Boot 2.x:应用开发框架
- Spring Security:认证和权限控制
- MyBatis Plus:ORM框架
- MySQL:关系型数据库
- Redis:缓存数据库
- JWT:用户会话管理
- Maven:项目构建工具
前端技术栈
- Vue.js:前端框架
- Element UI:组件库
- Axios:HTTP 请求
- Vuex:状态管理
- Vue Router:路由管理
核心功能模块
1. 维修预约管理
- 在线预约
- 预约时间段管理
- 预约状态跟踪
- 短信通知提醒
2. 工单管理
- 维修工单创建
- 维修进度跟踪
- 维修项目管理
- 维修费用计算
- 工单状态变更
3. 库存管理
- 配件入库登记
- 库存预警提醒
- 配件使用记录
- 库存盘点统计
4. 客户管理
- 客户信息管理
- 车辆信息管理
- 维修记录查询
- 会员积分管理
5. 员工管理
- 员工信息管理
- 工种分类管理
- 工作量统计
- 绩效考核
6. 财务管理
- 收支明细记录
- 营收报表统计
- 成本分析
- 利润核算
系统特点
- 全流程数字化
- 从预约到结算的完整业务闭环
- 减少人工操作环节
- 提高工作效率
- 数据可视化
- 直观的数据展示
- 多维度统计分析
- 辅助经营决策
- 用户体验
- 简洁清晰的界面设计
- 便捷的操作流程
- 完善的提示信息
- 安全可靠
- 严格的权限控制
- 敏感数据加密
- 操作日志记录
项目亮点
- 微服务架构
- 模块解耦,独立部署
- 服务高可用
- 便于扩展维护
- 性能优化
- Redis缓存机制
- SQL语句优化
- 前端资源压缩
- 工单智能派单
- 考虑技师专长
- 工作量均衡分配
- 提高维修效率
- 数据分析功能
- 客户消费分析
- 配件使用分析
- 维修项目分析
- 营收趋势分析
项目收获
- 掌握了完整的Java全栈开发技术栈
- 深入理解了微服务架构设计思想
- 提升了项目开发和管理能力
- 积累了丰富的业务领域经验
项目展望
- 引入人工智能技术,实现故障智能诊断
- 开发移动端应用,提供更便捷的服务
- 对接物联网设备,实现智能化管理
- 扩展更多增值服务功能
总结
本项目采用主流的Java全栈技术,实现了汽修行业核心业务的信息化管理。通过该项目的开发,不仅提升了技术能力,也深入理解了业务需求分析和系统架构设计的重要性。项目具有较强的实用价值,为汽修企业提供了完善的管理解决方案。
维修预约管理模块的核心代码实现
1. 数据库设计
sql
-- 预约表
CREATE TABLE appointment (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
customer_id BIGINT NOT NULL COMMENT '客户ID',
vehicle_id BIGINT NOT NULL COMMENT '车辆ID',
service_type VARCHAR(50) NOT NULL COMMENT '服务类型',
appointment_time DATETIME NOT NULL COMMENT '预约时间',
time_slot VARCHAR(20) NOT NULL COMMENT '预约时段',
status VARCHAR(20) NOT NULL COMMENT '预约状态',
description TEXT COMMENT '维修描述',
contact_name VARCHAR(50) NOT NULL COMMENT '联系人',
contact_phone VARCHAR(20) NOT NULL COMMENT '联系电话',
photos TEXT COMMENT '车辆照片URLs',
created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (customer_id) REFERENCES customer(id),
FOREIGN KEY (vehicle_id) REFERENCES vehicle(id)
);
-- 预约时段配置表
CREATE TABLE time_slot_config (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
slot_time VARCHAR(20) NOT NULL COMMENT '时间段',
max_capacity INT NOT NULL COMMENT '最大预约数',
current_capacity INT NOT NULL DEFAULT 0 COMMENT '当前预约数',
is_available BOOLEAN NOT NULL DEFAULT true COMMENT '是否可用',
created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- 预约状态变更记录表
CREATE TABLE appointment_status_log (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
appointment_id BIGINT NOT NULL,
old_status VARCHAR(20) COMMENT '原状态',
new_status VARCHAR(20) NOT NULL COMMENT '新状态',
operator_id BIGINT NOT NULL COMMENT '操作人',
remark TEXT COMMENT '备注',
created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (appointment_id) REFERENCES appointment(id)
);
2. 实体类设计
java
@Data
@Entity
@Table(name = "appointment")
public class Appointment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long customerId;
private Long vehicleId;
private String serviceType;
private LocalDateTime appointmentTime;
private String timeSlot;
@Enumerated(EnumType.STRING)
private AppointmentStatus status;
private String description;
private String contactName;
private String contactPhone;
private String photos;
private LocalDateTime createdTime;
private LocalDateTime updatedTime;
}
@Data
@Entity
@Table(name = "time_slot_config")
public class TimeSlotConfig {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String slotTime;
private Integer maxCapacity;
private Integer currentCapacity;
private Boolean isAvailable;
private LocalDateTime createdTime;
private LocalDateTime updatedTime;
}
3. 枚举定义
java
public enum AppointmentStatus {
PENDING("待确认"),
CONFIRMED("已确认"),
WAITING("待到店"),
ARRIVED("已到店"),
COMPLETED("已完成"),
CANCELLED("已取消");
private String description;
AppointmentStatus(String description) {
this.description = description;
}
}
public enum ServiceType {
REGULAR_MAINTENANCE("常规保养"),
REPAIR("故障维修"),
INSPECTION("年检服务"),
CUSTOM("其他服务");
private String description;
ServiceType(String description) {
this.description = description;
}
}
4. DTO对象
java
@Data
public class AppointmentDTO {
private Long customerId;
private Long vehicleId;
private String serviceType;
private String appointmentDate;
private String timeSlot;
private String description;
private String contactName;
private String contactPhone;
private List<String> photos;
}
@Data
public class TimeSlotDTO {
private String date;
private List<TimeSlotInfo> availableSlots;
@Data
public static class TimeSlotInfo {
private String slotTime;
private Integer availableCapacity;
private Boolean isAvailable;
}
}
5. Service层实现
java
@Service
@Transactional
public class AppointmentService {
@Autowired
private AppointmentRepository appointmentRepository;
@Autowired
private TimeSlotConfigRepository timeSlotConfigRepository;
@Autowired
private NotificationService notificationService;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public AppointmentResponse createAppointment(AppointmentDTO dto) {
// 1. 验证时间段是否可预约
validateTimeSlot(dto.getAppointmentDate(), dto.getTimeSlot());
// 2. 创建预约记录
Appointment appointment = new Appointment();
BeanUtils.copyProperties(dto, appointment);
appointment.setStatus(AppointmentStatus.PENDING);
// 3. 更新时间段容量
updateTimeSlotCapacity(dto.getAppointmentDate(), dto.getTimeSlot());
// 4. 保存预约信息
appointment = appointmentRepository.save(appointment);
// 5. 发送通知
notificationService.sendAppointmentNotification(appointment);
return buildResponse(appointment);
}
@Cacheable(value = "timeSlots", key = "#date")
public List<TimeSlotDTO> getAvailableTimeSlots(String date) {
List<TimeSlotConfig> configs = timeSlotConfigRepository.findByDateAndIsAvailable(date, true);
return convertToDTO(configs);
}
public void updateAppointmentStatus(Long appointmentId, AppointmentStatus newStatus) {
Appointment appointment = appointmentRepository.findById(appointmentId)
.orElseThrow(() -> new BusinessException("预约不存在"));
// 记录状态变更
logStatusChange(appointment, newStatus);
// 更新状态
appointment.setStatus(newStatus);
appointmentRepository.save(appointment);
// 发送状态变更通知
notificationService.sendStatusChangeNotification(appointment);
}
private void validateTimeSlot(String date, String timeSlot) {
String cacheKey = "timeSlot:" + date + ":" + timeSlot;
TimeSlotConfig config = (TimeSlotConfig) redisTemplate.opsForValue().get(cacheKey);
if (config == null) {
config = timeSlotConfigRepository.findByDateAndSlotTime(date, timeSlot)
.orElseThrow(() -> new BusinessException("无效的时间段"));
}
if (!config.getIsAvailable() || config.getCurrentCapacity() >= config.getMaxCapacity()) {
throw new BusinessException("该时间段已约满");
}
}
private void updateTimeSlotCapacity(String date, String timeSlot) {
timeSlotConfigRepository.incrementCurrentCapacity(date, timeSlot);
// 更新缓存
String cacheKey = "timeSlot:" + date + ":" + timeSlot;
redisTemplate.delete(cacheKey);
}
}
6. Controller层实现
java
@RestController
@RequestMapping("/api/appointments")
public class AppointmentController {
@Autowired
private AppointmentService appointmentService;
@PostMapping
public Result<AppointmentResponse> createAppointment(@RequestBody @Valid AppointmentDTO dto) {
try {
AppointmentResponse response = appointmentService.createAppointment(dto);
return Result.success(response);
} catch (BusinessException e) {
return Result.failure(e.getMessage());
}
}
@GetMapping("/time-slots")
public Result<List<TimeSlotDTO>> getAvailableTimeSlots(
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") String date) {
List<TimeSlotDTO> slots = appointmentService.getAvailableTimeSlots(date);
return Result.success(slots);
}
@PutMapping("/{id}/status")
public Result<Void> updateStatus(
@PathVariable Long id,
@RequestParam AppointmentStatus status) {
appointmentService.updateAppointmentStatus(id, status);
return Result.success();
}
@GetMapping("/{id}")
public Result<AppointmentResponse> getAppointment(@PathVariable Long id) {
AppointmentResponse appointment = appointmentService.getAppointmentById(id);
return Result.success(appointment);
}
}
7. 通知服务实现
java
@Service
public class NotificationService {
@Autowired
private SMSProvider smsProvider;
@Autowired
private NotificationTemplateService templateService;
@Async
public void sendAppointmentNotification(Appointment appointment) {
try {
String template = templateService.getTemplate("APPOINTMENT_CREATED");
String content = buildNotificationContent(template, appointment);
SMSRequest request = SMSRequest.builder()
.mobile(appointment.getContactPhone())
.content(content)
.build();
smsProvider.send(request);
} catch (Exception e) {
log.error("发送预约通知失败", e);
// 添加重试机制
retryNotification(appointment);
}
}
@Async
public void sendStatusChangeNotification(Appointment appointment) {
String template = templateService.getTemplate("STATUS_CHANGED");
String content = buildNotificationContent(template, appointment);
SMSRequest request = SMSRequest.builder()
.mobile(appointment.getContactPhone())
.content(content)
.build();
smsProvider.send(request);
}
private void retryNotification(Appointment appointment) {
// 使用重试策略(如:exponential backoff)
}
}
8. 缓存配置
java
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// 设置key的序列化方式
template.setKeySerializer(new StringRedisSerializer());
// 设置value的序列化方式
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
template.setValueSerializer(serializer);
return template;
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(1))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
return RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
}
}
9. 异常处理
java
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public Result<Void> handleBusinessException(BusinessException e) {
return Result.failure(e.getMessage());
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result<Void> handleValidationException(MethodArgumentNotValidException e) {
String message = e.getBindingResult().getFieldErrors().stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.joining(", "));
return Result.failure(message);
}
@ExceptionHandler(Exception.class)
public Result<Void> handleException(Exception e) {
log.error("系统异常", e);
return Result.failure("系统繁忙,请稍后重试");
}
}
这些代码实现了预约管理的核心功能,包括:
- 预约创建和管理
- 时间段容量控制
- 状态跟踪和变更
- 短信通知
- 缓存优化
- 异常处理
关键特点:
- 使用Redis缓存热点数据(时间段信息)
- 异步处理通知发送
- 完善的异常处理机制
- 状态变更日志记录
- 预约容量控制
工单管理模块的核心代码实现
1. 数据库设计
sql
-- 工单主表
CREATE TABLE work_order (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
order_no VARCHAR(32) UNIQUE NOT NULL COMMENT '工单编号',
appointment_id BIGINT COMMENT '关联预约ID',
customer_id BIGINT NOT NULL COMMENT '客户ID',
vehicle_id BIGINT NOT NULL COMMENT '车辆ID',
technician_id BIGINT COMMENT '主责技师ID',
status VARCHAR(20) NOT NULL COMMENT '工单状态',
estimated_completion_time DATETIME COMMENT '预计完工时间',
actual_completion_time DATETIME COMMENT '实际完工时间',
total_amount DECIMAL(10,2) DEFAULT 0 COMMENT '总金额',
labor_cost DECIMAL(10,2) DEFAULT 0 COMMENT '工时费',
parts_cost DECIMAL(10,2) DEFAULT 0 COMMENT '配件费',
description TEXT COMMENT '维修描述',
diagnosis_result TEXT COMMENT '检查结果',
created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- 工单项目明细表
CREATE TABLE work_order_item (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
work_order_id BIGINT NOT NULL COMMENT '工单ID',
service_item_id BIGINT NOT NULL COMMENT '服务项目ID',
technician_id BIGINT COMMENT '技师ID',
status VARCHAR(20) NOT NULL COMMENT '项目状态',
estimated_hours DECIMAL(4,1) COMMENT '预计工时',
actual_hours DECIMAL(4,1) COMMENT '实际工时',
amount DECIMAL(10,2) COMMENT '项目金额',
remark TEXT COMMENT '备注',
created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (work_order_id) REFERENCES work_order(id)
);
-- 工单配件使用记录表
CREATE TABLE work_order_part (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
work_order_id BIGINT NOT NULL COMMENT '工单ID',
part_id BIGINT NOT NULL COMMENT '配件ID',
quantity INT NOT NULL COMMENT '使用数量',
unit_price DECIMAL(10,2) NOT NULL COMMENT '单价',
amount DECIMAL(10,2) NOT NULL COMMENT '金额',
created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (work_order_id) REFERENCES work_order(id)
);
-- 工单进度记录表
CREATE TABLE work_order_progress (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
work_order_id BIGINT NOT NULL COMMENT '工单ID',
status VARCHAR(20) NOT NULL COMMENT '状态',
operator_id BIGINT NOT NULL COMMENT '操作人',
remark TEXT COMMENT '备注',
photos TEXT COMMENT '照片URLs',
created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (work_order_id) REFERENCES work_order(id)
);
2. 实体类设计
java
@Data
@Entity
@Table(name = "work_order")
public class WorkOrder {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String orderNo;
private Long appointmentId;
private Long customerId;
private Long vehicleId;
private Long technicianId;
@Enumerated(EnumType.STRING)
private WorkOrderStatus status;
private LocalDateTime estimatedCompletionTime;
private LocalDateTime actualCompletionTime;
private BigDecimal totalAmount;
private BigDecimal laborCost;
private BigDecimal partsCost;
private String description;
private String diagnosisResult;
@OneToMany(mappedBy = "workOrder", cascade = CascadeType.ALL)
private List<WorkOrderItem> items;
@OneToMany(mappedBy = "workOrder", cascade = CascadeType.ALL)
private List<WorkOrderPart> parts;
}
@Data
@Entity
@Table(name = "work_order_item")
public class WorkOrderItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "work_order_id")
private WorkOrder workOrder;
private Long serviceItemId;
private Long technicianId;
@Enumerated(EnumType.STRING)
private ItemStatus status;
private BigDecimal estimatedHours;
private BigDecimal actualHours;
private BigDecimal amount;
private String remark;
}
3. 枚举定义
java
public enum WorkOrderStatus {
CREATED("已创建"),
INSPECTING("检查中"),
REPAIRING("维修中"),
QUALITY_CHECK("质检中"),
COMPLETED("已完成"),
CANCELLED("已取消");
private String description;
WorkOrderStatus(String description) {
this.description = description;
}
}
public enum ItemStatus {
PENDING("待处理"),
IN_PROGRESS("进行中"),
COMPLETED("已完成"),
CANCELLED("已取消");
private String description;
}
4. Service层实现
java
@Service
@Transactional
public class WorkOrderService {
@Autowired
private WorkOrderRepository workOrderRepository;
@Autowired
private WorkOrderItemRepository itemRepository;
@Autowired
private PartInventoryService partInventoryService;
@Autowired
private NotificationService notificationService;
public WorkOrderResponse createWorkOrder(WorkOrderDTO dto) {
// 1. 生成工单编号
String orderNo = generateOrderNo();
// 2. 创建工单
WorkOrder workOrder = new WorkOrder();
BeanUtils.copyProperties(dto, workOrder);
workOrder.setOrderNo(orderNo);
workOrder.setStatus(WorkOrderStatus.CREATED);
// 3. 创建工单项目
List<WorkOrderItem> items = createWorkOrderItems(dto.getItems(), workOrder);
workOrder.setItems(items);
// 4. 计算预估金额
calculateEstimatedAmount(workOrder);
// 5. 保存工单
workOrder = workOrderRepository.save(workOrder);
// 6. 发送通知
notificationService.sendWorkOrderCreatedNotification(workOrder);
return buildResponse(workOrder);
}
public void updateWorkOrderStatus(Long id, WorkOrderStatus newStatus, String remark) {
WorkOrder workOrder = workOrderRepository.findById(id)
.orElseThrow(() -> new BusinessException("工单不存在"));
// 1. 验证状态变更是否合法
validateStatusTransition(workOrder.getStatus(), newStatus);
// 2. 更新状态
workOrder.setStatus(newStatus);
// 3. 记录进度
saveProgress(workOrder, newStatus, remark);
// 4. 特殊状态处理
handleSpecialStatus(workOrder, newStatus);
// 5. 保存更新
workOrderRepository.save(workOrder);
// 6. 发送通知
notificationService.sendStatusChangeNotification(workOrder);
}
public void addWorkOrderPart(Long workOrderId, WorkOrderPartDTO dto) {
WorkOrder workOrder = workOrderRepository.findById(workOrderId)
.orElseThrow(() -> new BusinessException("工单不存在"));
// 1. 检查库存
partInventoryService.checkInventory(dto.getPartId(), dto.getQuantity());
// 2. 创建配件使用记录
WorkOrderPart part = new WorkOrderPart();
BeanUtils.copyProperties(dto, part);
part.setWorkOrder(workOrder);
// 3. 计算金额
calculatePartAmount(part);
// 4. 更新工单金额
updateWorkOrderAmount(workOrder, part.getAmount());
// 5. 扣减库存
partInventoryService.deductInventory(dto.getPartId(), dto.getQuantity());
// 6. 保存记录
workOrderPartRepository.save(part);
}
public void updateWorkOrderProgress(Long id, ProgressUpdateDTO dto) {
WorkOrder workOrder = workOrderRepository.findById(id)
.orElseThrow(() -> new BusinessException("工单不存在"));
// 1. 更新项目进度
updateItemsProgress(workOrder, dto.getItemProgresses());
// 2. 更新工时
updateLaborHours(workOrder, dto.getItemProgresses());
// 3. 重新计算费用
recalculateAmount(workOrder);
// 4. 保存更新
workOrderRepository.save(workOrder);
// 5. 记录进度
saveProgress(workOrder, dto.getRemark(), dto.getPhotos());
}
private void calculateEstimatedAmount(WorkOrder workOrder) {
BigDecimal laborCost = calculateLaborCost(workOrder.getItems());
BigDecimal partsCost = calculatePartsCost(workOrder.getParts());
workOrder.setLaborCost(laborCost);
workOrder.setPartsCost(partsCost);
workOrder.setTotalAmount(laborCost.add(partsCost));
}
private void handleSpecialStatus(WorkOrder workOrder, WorkOrderStatus status) {
switch (status) {
case COMPLETED:
workOrder.setActualCompletionTime(LocalDateTime.now());
break;
case QUALITY_CHECK:
validateAllItemsCompleted(workOrder);
break;
}
}
}
5. Controller层实现
java
@RestController
@RequestMapping("/api/work-orders")
public class WorkOrderController {
@Autowired
private WorkOrderService workOrderService;
@PostMapping
public Result<WorkOrderResponse> createWorkOrder(@RequestBody @Valid WorkOrderDTO dto) {
WorkOrderResponse response = workOrderService.createWorkOrder(dto);
return Result.success(response);
}
@PutMapping("/{id}/status")
public Result<Void> updateStatus(
@PathVariable Long id,
@RequestParam WorkOrderStatus status,
@RequestParam(required = false) String remark) {
workOrderService.updateWorkOrderStatus(id, status, remark);
return Result.success();
}
@PostMapping("/{id}/parts")
public Result<Void> addPart(
@PathVariable Long id,
@RequestBody @Valid WorkOrderPartDTO dto) {
workOrderService.addWorkOrderPart(id, dto);
return Result.success();
}
@PutMapping("/{id}/progress")
public Result<Void> updateProgress(
@PathVariable Long id,
@RequestBody @Valid ProgressUpdateDTO dto) {
workOrderService.updateWorkOrderProgress(id, dto);
return Result.success();
}
@GetMapping("/{id}")
public Result<WorkOrderDetailResponse> getWorkOrder(@PathVariable Long id) {
WorkOrderDetailResponse detail = workOrderService.getWorkOrderDetail(id);
return Result.success(detail);
}
}
6. 费用计算服务
java
@Service
public class CostCalculationService {
@Autowired
private ServiceItemRepository serviceItemRepository;
@Autowired
private PartRepository partRepository;
public BigDecimal calculateLaborCost(List<WorkOrderItem> items) {
return items.stream()
.map(this::calculateItemLaborCost)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
public BigDecimal calculatePartsCost(List<WorkOrderPart> parts) {
return parts.stream()
.map(this::calculatePartCost)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
private BigDecimal calculateItemLaborCost(WorkOrderItem item) {
ServiceItem serviceItem = serviceItemRepository.findById(item.getServiceItemId())
.orElseThrow(() -> new BusinessException("服务项目不存在"));
return serviceItem.getHourlyRate()
.multiply(item.getActualHours() != null ?
item.getActualHours() : item.getEstimatedHours());
}
private BigDecimal calculatePartCost(WorkOrderPart part) {
return part.getUnitPrice().multiply(BigDecimal.valueOf(part.getQuantity()));
}
}
7. 状态机实现
java
@Component
public class WorkOrderStateMachine {
private static final Map<WorkOrderStatus, Set<WorkOrderStatus>> VALID_TRANSITIONS;
static {
VALID_TRANSITIONS = new HashMap<>();
VALID_TRANSITIONS.put(WorkOrderStatus.CREATED,
Set.of(WorkOrderStatus.INSPECTING, WorkOrderStatus.CANCELLED));
VALID_TRANSITIONS.put(WorkOrderStatus.INSPECTING,
Set.of(WorkOrderStatus.REPAIRING, WorkOrderStatus.CANCELLED));
VALID_TRANSITIONS.put(WorkOrderStatus.REPAIRING,
Set.of(WorkOrderStatus.QUALITY_CHECK, WorkOrderStatus.CANCELLED));
VALID_TRANSITIONS.put(WorkOrderStatus.QUALITY_CHECK,
Set.of(WorkOrderStatus.COMPLETED, WorkOrderStatus.REPAIRING));
}
public boolean canTransit(WorkOrderStatus current, WorkOrderStatus target) {
Set<WorkOrderStatus> validTargets = VALID_TRANSITIONS.get(current);
return validTargets != null && validTargets.contains(target);
}
public void validateTransition(WorkOrderStatus current, WorkOrderStatus target) {
if (!canTransit(current, target)) {
throw new BusinessException(
String.format("不允许从%s状态变更为%s状态",
current.getDescription(),
target.getDescription())
);
}
}
}
8. 进度跟踪实现
java
@Service
public class ProgressTrackingService {
@Autowired
private WorkOrderProgressRepository progressRepository;
@Autowired
private FileService fileService;
public void saveProgress(WorkOrder workOrder, String remark, List<MultipartFile> photos) {
// 1. 保存照片
List<String> photoUrls = new ArrayList<>();
if (photos != null && !photos.isEmpty()) {
photoUrls = photos.stream()
.map(photo -> fileService.uploadFile(photo))
.collect(Collectors.toList());
}
// 2. 创建进度记录
WorkOrderProgress progress = new WorkOrderProgress();
progress.setWorkOrderId(workOrder.getId());
progress.setStatus(workOrder.getStatus());
progress.setRemark(remark);
progress.setPhotos(String.join(",", photoUrls));
progress.setOperatorId(SecurityUtils.getCurrentUserId());
// 3. 保存记录
progressRepository.save(progress);
}
public List<ProgressResponse> getProgressHistory(Long workOrderId) {
List<WorkOrderProgress> progressList = progressRepository
.findByWorkOrderIdOrderByCreatedTimeDesc(workOrderId);
return progressList.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
}
private ProgressResponse convertToResponse(WorkOrderProgress progress) {
ProgressResponse response = new ProgressResponse();
BeanUtils.copyProperties(progress, response);
// 设置操作人信息
User operator = userService.getUser(progress.getOperatorId());
response.setOperatorName(operator.getName());
// 处理照片URL
if (StringUtils.hasText(progress.getPhotos())) {
response.setPhotoUrls(Arrays.asList(progress.getPhotos().split(",")));
}
return response;
}
}
9. 工单查询服务
java
@Service
public class WorkOrderQueryService {
@Autowired
private WorkOrderRepository workOrderRepository;
public Page<WorkOrderResponse> queryWorkOrders(WorkOrderQueryDTO query) {
// 1. 构建查询条件
Specification<WorkOrder> spec = buildSpecification(query);
// 2. 创建分页对象
PageRequest pageRequest = PageRequest.of(
query.getPageNum(),
query.getPageSize(),
Sort.by(Sort.Direction.DESC, "createdTime")
);
// 3. 执行查询
Page<WorkOrder> page = workOrderRepository.findAll(spec, pageRequest);
// 4. 转换响应
return page.map(this::convertToResponse);
}
private Specification<WorkOrder> buildSpecification(WorkOrderQueryDTO query) {
return (root, criteriaQuery, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
// 工单号
if (StringUtils.hasText(query.getOrderNo())) {
predicates.add(criteriaBuilder.like(
root.get("orderNo"),
"%" + query.getOrderNo() + "%"
));
}
// 状态
if (query.getStatus() != null) {
predicates.add(criteriaBuilder.equal(
root.get("status"),
query.getStatus()
));
}
// 时间范围
if (query.getStartTime() != null) {
predicates.add(criteriaBuilder.greaterThanOrEqualTo(
root.get("createdTime"),
query.getStartTime()
));
}
if (query.getEndTime() != null) {
predicates.add(criteriaBuilder.lessThanOrEqualTo(
root.get("createdTime"),
query.getEndTime()
));
}
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
};
}
}
这些代码实现了工单管理的核心功能,包括:
- 工单创建和管理
- 维修项目跟踪
- 配件使用管理
- 费用计算
- 状态流转控制
- 进度记录跟踪
关键特点:
- 完整的状态机实现
- 细粒度的费用计算
- 详细的进度跟踪
- 灵活的查询功能
- 完善的数据验证
库存管理模块的核心代码实现
1. 数据库设计
sql
-- 配件信息表
CREATE TABLE part (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
code VARCHAR(32) UNIQUE NOT NULL COMMENT '配件编号',
name VARCHAR(100) NOT NULL COMMENT '配件名称',
category_id BIGINT NOT NULL COMMENT '分类ID',
brand VARCHAR(50) COMMENT '品牌',
model VARCHAR(50) COMMENT '型号',
unit VARCHAR(20) NOT NULL COMMENT '单位',
price DECIMAL(10,2) NOT NULL COMMENT '单价',
min_stock INT NOT NULL COMMENT '最低库存',
max_stock INT NOT NULL COMMENT '最高库存',
shelf_life INT COMMENT '保质期(天)',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 0-停用 1-启用',
remark TEXT COMMENT '备注',
created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- 库存记录表
CREATE TABLE inventory (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
part_id BIGINT NOT NULL COMMENT '配件ID',
warehouse_id BIGINT NOT NULL COMMENT '仓库ID',
quantity INT NOT NULL DEFAULT 0 COMMENT '当前库存量',
locked_quantity INT NOT NULL DEFAULT 0 COMMENT '锁定数量',
created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY `uk_part_warehouse` (`part_id`, `warehouse_id`)
);
-- 入库记录表
CREATE TABLE stock_in (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
order_no VARCHAR(32) UNIQUE NOT NULL COMMENT '入库单号',
warehouse_id BIGINT NOT NULL COMMENT '仓库ID',
supplier_id BIGINT NOT NULL COMMENT '供应商ID',
type VARCHAR(20) NOT NULL COMMENT '入库类型',
status VARCHAR(20) NOT NULL COMMENT '状态',
total_amount DECIMAL(10,2) NOT NULL COMMENT '总金额',
operator_id BIGINT NOT NULL COMMENT '操作人',
remark TEXT COMMENT '备注',
created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- 入库明细表
CREATE TABLE stock_in_item (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
stock_in_id BIGINT NOT NULL COMMENT '入库单ID',
part_id BIGINT NOT NULL COMMENT '配件ID',
quantity INT NOT NULL COMMENT '数量',
unit_price DECIMAL(10,2) NOT NULL COMMENT '单价',
amount DECIMAL(10,2) NOT NULL COMMENT '金额',
batch_no VARCHAR(50) COMMENT '批次号',
production_date DATE COMMENT '生产日期',
expiry_date DATE COMMENT '过期日期',
created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (stock_in_id) REFERENCES stock_in(id)
);
-- 库存变动记录表
CREATE TABLE inventory_transaction (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
part_id BIGINT NOT NULL COMMENT '配件ID',
warehouse_id BIGINT NOT NULL COMMENT '仓库ID',
type VARCHAR(20) NOT NULL COMMENT '变动类型',
quantity INT NOT NULL COMMENT '变动数量',
before_quantity INT NOT NULL COMMENT '变动前数量',
after_quantity INT NOT NULL COMMENT '变动后数量',
reference_no VARCHAR(32) COMMENT '关联单号',
operator_id BIGINT NOT NULL COMMENT '操作人',
remark TEXT COMMENT '备注',
created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- 库存盘点表
CREATE TABLE inventory_check (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
check_no VARCHAR(32) UNIQUE NOT NULL COMMENT '盘点单号',
warehouse_id BIGINT NOT NULL COMMENT '仓库ID',
status VARCHAR(20) NOT NULL COMMENT '状态',
start_time DATETIME NOT NULL COMMENT '开始时间',
end_time DATETIME COMMENT '结束时间',
operator_id BIGINT NOT NULL COMMENT '操作人',
remark TEXT COMMENT '备注',
created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- 盘点明细表
CREATE TABLE inventory_check_item (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
check_id BIGINT NOT NULL COMMENT '盘点单ID',
part_id BIGINT NOT NULL COMMENT '配件ID',
system_quantity INT NOT NULL COMMENT '系统数量',
actual_quantity INT NOT NULL COMMENT '实际数量',
difference INT NOT NULL COMMENT '差异数量',
remark TEXT COMMENT '备注',
created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (check_id) REFERENCES inventory_check(id)
);
2. 实体类设计
java
@Data
@Entity
@Table(name = "part")
public class Part {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String code;
private String name;
private Long categoryId;
private String brand;
private String model;
private String unit;
private BigDecimal price;
private Integer minStock;
private Integer maxStock;
private Integer shelfLife;
private Integer status;
private String remark;
private LocalDateTime createdTime;
private LocalDateTime updatedTime;
}
@Data
@Entity
@Table(name = "inventory")
public class Inventory {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long partId;
private Long warehouseId;
private Integer quantity;
private Integer lockedQuantity;
private LocalDateTime createdTime;
private LocalDateTime updatedTime;
@Version
private Integer version; // 乐观锁版本号
}
@Data
@Entity
@Table(name = "stock_in")
public class StockIn {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String orderNo;
private Long warehouseId;
private Long supplierId;
@Enumerated(EnumType.STRING)
private StockInType type;
@Enumerated(EnumType.STRING)
private StockInStatus status;
private BigDecimal totalAmount;
private Long operatorId;
private String remark;
private LocalDateTime createdTime;
@OneToMany(mappedBy = "stockIn", cascade = CascadeType.ALL)
private List<StockInItem> items;
}
3. 入库服务实现
java
@Service
@Transactional
public class StockInService {
@Autowired
private StockInRepository stockInRepository;
@Autowired
private InventoryService inventoryService;
@Autowired
private SequenceGenerator sequenceGenerator;
public StockInResponse createStockIn(StockInDTO dto) {
// 1. 生成入库单号
String orderNo = sequenceGenerator.generateStockInNo();
// 2. 创建入库单
StockIn stockIn = new StockIn();
BeanUtils.copyProperties(dto, stockIn);
stockIn.setOrderNo(orderNo);
stockIn.setStatus(StockInStatus.PENDING);
// 3. 创建入库明细
List<StockInItem> items = createStockInItems(dto.getItems(), stockIn);
stockIn.setItems(items);
// 4. 计算总金额
BigDecimal totalAmount = calculateTotalAmount(items);
stockIn.setTotalAmount(totalAmount);
// 5. 保存入库单
stockIn = stockInRepository.save(stockIn);
return buildResponse(stockIn);
}
public void confirmStockIn(Long id) {
StockIn stockIn = stockInRepository.findById(id)
.orElseThrow(() -> new BusinessException("入库单不存在"));
// 1. 验证状态
if (stockIn.getStatus() != StockInStatus.PENDING) {
throw new BusinessException("入库单状态不正确");
}
// 2. 更新库存
for (StockInItem item : stockIn.getItems()) {
inventoryService.increaseStock(
item.getPartId(),
stockIn.getWarehouseId(),
item.getQuantity(),
stockIn.getOrderNo()
);
}
// 3. 更新入库单状态
stockIn.setStatus(StockInStatus.COMPLETED);
stockInRepository.save(stockIn);
}
}
4. 库存服务实现
java
@Service
@Transactional
public class InventoryService {
@Autowired
private InventoryRepository inventoryRepository;
@Autowired
private InventoryTransactionRepository transactionRepository;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public void increaseStock(Long partId, Long warehouseId, Integer quantity, String referenceNo) {
// 1. 获取或创建库存记录
Inventory inventory = getOrCreateInventory(partId, warehouseId);
// 2. 更新库存(使用乐观锁)
int retryCount = 0;
while (retryCount < 3) {
try {
inventory.setQuantity(inventory.getQuantity() + quantity);
inventoryRepository.save(inventory);
break;
} catch (OptimisticLockingFailureException e) {
if (++retryCount == 3) {
throw new BusinessException("更新库存失败,请重试");
}
inventory = inventoryRepository.findByPartIdAndWarehouseId(partId, warehouseId)
.orElseThrow();
}
}
// 3. 记录库存变动
saveInventoryTransaction(
inventory,
TransactionType.STOCK_IN,
quantity,
referenceNo
);
// 4. 检查库存预警
checkStockWarning(inventory);
}
public void decreaseStock(Long partId, Long warehouseId, Integer quantity, String referenceNo) {
// 1. 获取库存记录
Inventory inventory = inventoryRepository.findByPartIdAndWarehouseId(partId, warehouseId)
.orElseThrow(() -> new BusinessException("库存记录不存在"));
// 2. 检查库存是否足够
if (inventory.getQuantity() - inventory.getLockedQuantity() < quantity) {
throw new BusinessException("可用库存不足");
}
// 3. 更新库存(使用乐观锁)
int retryCount = 0;
while (retryCount < 3) {
try {
inventory.setQuantity(inventory.getQuantity() - quantity);
inventoryRepository.save(inventory);
break;
} catch (OptimisticLockingFailureException e) {
if (++retryCount == 3) {
throw new BusinessException("更新库存失败,请重试");
}
inventory = inventoryRepository.findByPartIdAndWarehouseId(partId, warehouseId)
.orElseThrow();
}
}
// 4. 记录库存变动
saveInventoryTransaction(
inventory,
TransactionType.STOCK_OUT,
-quantity,
referenceNo
);
// 5. 检查库存预警
checkStockWarning(inventory);
}
private void checkStockWarning(Inventory inventory) {
Part part = partRepository.findById(inventory.getPartId())
.orElseThrow();
// 检查是否低于最低库存
if (inventory.getQuantity() <= part.getMinStock()) {
// 发送库存预警
sendStockWarning(inventory, part);
// 缓存预警信息
cacheStockWarning(inventory, part);
}
}
}
5. 库存盘点服务实现
java
@Service
@Transactional
public class InventoryCheckService {
@Autowired
private InventoryCheckRepository checkRepository;
@Autowired
private InventoryService inventoryService;
public InventoryCheckResponse createCheck(InventoryCheckDTO dto) {
// 1. 生成盘点单号
String checkNo = sequenceGenerator.generateCheckNo();
// 2. 创建盘点单
InventoryCheck check = new InventoryCheck();
BeanUtils.copyProperties(dto, check);
check.setCheckNo(checkNo);
check.setStatus(CheckStatus.IN_PROGRESS);
check.setStartTime(LocalDateTime.now());
// 3. 获取系统库存数据
List<InventoryCheckItem> items = generateCheckItems(dto.getWarehouseId());
check.setItems(items);
// 4. 保存盘点单
check = checkRepository.save(check);
return buildResponse(check);
}
public void completeCheck(Long id, List<CheckItemDTO> items) {
InventoryCheck check = checkRepository.findById(id)
.orElseThrow(() -> new BusinessException("盘点单不存在"));
// 1. 验证状态
if (check.getStatus() != CheckStatus.IN_PROGRESS) {
throw new BusinessException("盘点单状态不正确");
}
// 2. 更新盘点结果
updateCheckItems(check, items);
// 3. 处理盘盈盘亏
handleInventoryDifference(check);
// 4. 完成盘点
check.setStatus(CheckStatus.COMPLETED);
check.setEndTime(LocalDateTime.now());
checkRepository.save(check);
}
private void handleInventoryDifference(InventoryCheck check) {
for (InventoryCheckItem item : check.getItems()) {
if (item.getDifference() != 0) {
// 记录库存调整
inventoryService.adjustStock(
item.getPartId(),
check.getWarehouseId(),
item.getDifference(),
check.getCheckNo()
);
}
}
}
}
6. 库存预警服务实现
java
@Service
public class StockWarningService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private NotificationService notificationService;
private static final String WARNING_KEY_PREFIX = "stock:warning:";
public void sendStockWarning(Inventory inventory, Part part) {
StockWarningMessage message = StockWarningMessage.builder()
.partCode(part.getCode())
.partName(part.getName())
.currentStock(inventory.getQuantity())
.minStock(part.getMinStock())
.warehouseId(inventory.getWarehouseId())
.build();
// 1. 发送消息通知
notificationService.sendStockWarning(message);
// 2. 缓存预警信息
cacheWarningInfo(inventory.getPartId(), message);
}
public List<StockWarningMessage> getWarningList() {
Set<String> keys = redisTemplate.keys(WARNING_KEY_PREFIX + "*");
if (keys == null || keys.isEmpty()) {
return Collections.emptyList();
}
return keys.stream()
.map(key -> (StockWarningMessage) redisTemplate.opsForValue().get(key))
.collect(Collectors.toList());
}
private void cacheWarningInfo(Long partId, StockWarningMessage message) {
String key = WARNING_KEY_PREFIX + partId;
redisTemplate.opsForValue().set(key, message, 24, TimeUnit.HOURS);
}
}
7. 库存统计服务实现
java
@Service
public class InventoryStatisticsService {
@Autowired
private InventoryRepository inventoryRepository;
@Autowired
private InventoryTransactionRepository transactionRepository;
public InventoryStatisticsResponse getStatistics(StatisticsQueryDTO query) {
// 1. 库存总量统计
InventoryTotalVO total = calculateInventoryTotal(query);
// 2. 库存金额统计
InventoryAmountVO amount = calculateInventoryAmount(query);
// 3. 库存周转率统计
TurnoverRateVO turnover = calculateTurnoverRate(query);
// 4. 库存预警统计
WarningStatisticsVO warning = calculateWarningStatistics(query);
return InventoryStatisticsResponse.builder()
.total(total)
.amount(amount)
.turnover(turnover)
.warning(warning)
.build();
}
public List<InventoryTransactionVO> getTransactionHistory(
Long partId, LocalDateTime startTime, LocalDateTime endTime) {
List<InventoryTransaction> transactions =
transactionRepository.findByPartIdAndCreatedTimeBetween(
partId, startTime, endTime);
return transactions.stream()
.map(this::convertToVO)
.collect(Collectors.toList());
}
private TurnoverRateVO calculateTurnoverRate(StatisticsQueryDTO query) {
// 1. 获取期初库存
BigDecimal beginningInventory = getBeginningInventory(query);
// 2. 获取期末库存
BigDecimal endingInventory = getEndingInventory(query);
// 3. 获取期间出库数量
BigDecimal outboundQuantity = getOutboundQuantity(query);
// 4. 计算平均库存
BigDecimal averageInventory = beginningInventory.add(endingInventory)
.divide(BigDecimal.valueOf(2), 2, RoundingMode.HALF_UP);
// 5. 计算周转率
BigDecimal turnoverRate = BigDecimal.ZERO;
if (averageInventory.compareTo(BigDecimal.ZERO) > 0) {
turnoverRate = outboundQuantity.divide(averageInventory, 2, RoundingMode.HALF_UP);
}
return new TurnoverRateVO(turnoverRate);
}
}
8. 库存查询接口实现
java
@RestController
@RequestMapping("/api/inventory")
public class InventoryController {
@Autowired
private InventoryService inventoryService;
@Autowired
private InventoryStatisticsService statisticsService;
@GetMapping("/stock")
public Result<Page<InventoryVO>> queryStock(InventoryQueryDTO query) {
Page<InventoryVO> page = inventoryService.queryStock(query);
return Result.success(page);
}
@GetMapping("/warning")
public Result<List<StockWarningMessage>> getWarningList() {
List<StockWarningMessage> warnings = inventoryService.getWarningList();
return Result.success(warnings);
}
@GetMapping("/statistics")
public Result<InventoryStatisticsResponse> getStatistics(StatisticsQueryDTO query) {
InventoryStatisticsResponse statistics = statisticsService.getStatistics(query);
return Result.success(statistics);
}
@GetMapping("/transaction-history")
public Result<List<InventoryTransactionVO>> getTransactionHistory(
@RequestParam Long partId,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime startTime,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime endTime) {
List<InventoryTransactionVO> history =
statisticsService.getTransactionHistory(partId, startTime, endTime);
return Result.success(history);
}
}
这些代码实现了库存管理的核心功能,包括:
- 配件入库管理
- 库存变动控制
- 库存预警机制
- 库存盘点处理
- 库存统计分析
关键特点:
- 使用乐观锁控制并发
- Redis缓存预警信息
- 完整的库存变动追踪
- 详细的统计分析功能
- 支持库存盘点处理
客户管理模块的核心代码实现
1. 数据库设计
sql
-- 客户信息表
CREATE TABLE customer (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
code VARCHAR(32) UNIQUE NOT NULL COMMENT '客户编号',
name VARCHAR(100) NOT NULL COMMENT '客户姓名',
phone VARCHAR(20) NOT NULL COMMENT '联系电话',
id_card VARCHAR(18) COMMENT '身份证号',
gender TINYINT COMMENT '性别: 0-女 1-男',
birthday DATE COMMENT '生日',
email VARCHAR(100) COMMENT '邮箱',
address TEXT COMMENT '地址',
source VARCHAR(50) COMMENT '客户来源',
level VARCHAR(20) NOT NULL DEFAULT 'NORMAL' COMMENT '客户等级',
points INT NOT NULL DEFAULT 0 COMMENT '积分',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 0-禁用 1-启用',
remark TEXT COMMENT '备注',
created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- 车辆信息表
CREATE TABLE vehicle (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
customer_id BIGINT NOT NULL COMMENT '客户ID',
plate_number VARCHAR(20) NOT NULL COMMENT '车牌号',
brand VARCHAR(50) NOT NULL COMMENT '品牌',
model VARCHAR(50) NOT NULL COMMENT '型号',
vin VARCHAR(50) UNIQUE COMMENT '车架号',
engine_number VARCHAR(50) COMMENT '发动机号',
color VARCHAR(20) COMMENT '颜色',
mileage INT COMMENT '行驶里程',
purchase_date DATE COMMENT '购买日期',
insurance_expiry DATE COMMENT '保险到期日',
last_service_date DATE COMMENT '最后保养日期',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态',
created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (customer_id) REFERENCES customer(id)
);
-- 维修记录表
CREATE TABLE service_record (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
work_order_id BIGINT NOT NULL COMMENT '工单ID',
customer_id BIGINT NOT NULL COMMENT '客户ID',
vehicle_id BIGINT NOT NULL COMMENT '车辆ID',
service_type VARCHAR(50) NOT NULL COMMENT '服务类型',
service_items TEXT COMMENT '服务项目',
total_amount DECIMAL(10,2) NOT NULL COMMENT '总金额',
service_date DATE NOT NULL COMMENT '服务日期',
mileage INT COMMENT '服务时里程',
technician_id BIGINT COMMENT '技师ID',
remark TEXT COMMENT '备注',
created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (customer_id) REFERENCES customer(id),
FOREIGN KEY (vehicle_id) REFERENCES vehicle(id)
);
-- 积分变动记录表
CREATE TABLE points_transaction (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
customer_id BIGINT NOT NULL COMMENT '客户ID',
type VARCHAR(20) NOT NULL COMMENT '变动类型',
points INT NOT NULL COMMENT '变动积分',
before_points INT NOT NULL COMMENT '变动前积分',
after_points INT NOT NULL COMMENT '变动后积分',
reference_no VARCHAR(32) COMMENT '关联单号',
remark TEXT COMMENT '备注',
created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (customer_id) REFERENCES customer(id)
);
2. 实体类设计
java
@Data
@Entity
@Table(name = "customer")
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String code;
private String name;
private String phone;
private String idCard;
private Integer gender;
private LocalDate birthday;
private String email;
private String address;
private String source;
@Enumerated(EnumType.STRING)
private CustomerLevel level;
private Integer points;
private Integer status;
private String remark;
private LocalDateTime createdTime;
private LocalDateTime updatedTime;
@OneToMany(mappedBy = "customer", cascade = CascadeType.ALL)
private List<Vehicle> vehicles;
}
@Data
@Entity
@Table(name = "vehicle")
public class Vehicle {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "customer_id")
private Customer customer;
private String plateNumber;
private String brand;
private String model;
private String vin;
private String engineNumber;
private String color;
private Integer mileage;
private LocalDate purchaseDate;
private LocalDate insuranceExpiry;
private LocalDate lastServiceDate;
private Integer status;
}
3. 客户服务实现
java
@Service
@Transactional
public class CustomerService {
@Autowired
private CustomerRepository customerRepository;
@Autowired
private SequenceGenerator sequenceGenerator;
@Autowired
private PointsService pointsService;
public CustomerResponse createCustomer(CustomerDTO dto) {
// 1. 验证手机号是否已存在
validatePhone(dto.getPhone());
// 2. 生成客户编号
String code = sequenceGenerator.generateCustomerCode();
// 3. 创建客户信息
Customer customer = new Customer();
BeanUtils.copyProperties(dto, customer);
customer.setCode(code);
customer.setLevel(CustomerLevel.NORMAL);
customer.setPoints(0);
customer.setStatus(1);
// 4. 保存客户信息
customer = customerRepository.save(customer);
// 5. 处理车辆信息
if (dto.getVehicles() != null && !dto.getVehicles().isEmpty()) {
List<Vehicle> vehicles = createVehicles(dto.getVehicles(), customer);
customer.setVehicles(vehicles);
}
return buildResponse(customer);
}
public void updateCustomer(Long id, CustomerUpdateDTO dto) {
Customer customer = customerRepository.findById(id)
.orElseThrow(() -> new BusinessException("客户不存在"));
// 1. 如果修改手机号,需要验证唯一性
if (!customer.getPhone().equals(dto.getPhone())) {
validatePhone(dto.getPhone());
}
// 2. 更新客户信息
BeanUtils.copyProperties(dto, customer);
customerRepository.save(customer);
}
public CustomerDetailResponse getCustomerDetail(Long id) {
Customer customer = customerRepository.findById(id)
.orElseThrow(() -> new BusinessException("客户不存在"));
// 1. 获取客户基本信息
CustomerDetailResponse response = buildDetailResponse(customer);
// 2. 获取车辆信息
List<VehicleVO> vehicles = getCustomerVehicles(id);
response.setVehicles(vehicles);
// 3. 获取最近维修记录
List<ServiceRecordVO> serviceRecords = getRecentServiceRecords(id);
response.setServiceRecords(serviceRecords);
// 4. 获取积分明细
List<PointsTransactionVO> pointsHistory = getPointsHistory(id);
response.setPointsHistory(pointsHistory);
return response;
}
@Cacheable(value = "customerLevel", key = "#customerId")
public CustomerLevel calculateCustomerLevel(Long customerId) {
// 1. 获取消费总额
BigDecimal totalAmount = serviceRecordRepository
.calculateTotalAmount(customerId);
// 2. 计算客户等级
return CustomerLevel.calculateLevel(totalAmount);
}
}
4. 车辆服务实现
java
@Service
@Transactional
public class VehicleService {
@Autowired
private VehicleRepository vehicleRepository;
@Autowired
private ServiceRecordRepository serviceRecordRepository;
public VehicleResponse addVehicle(VehicleDTO dto) {
// 1. 验证车牌号是否已存在
validatePlateNumber(dto.getPlateNumber());
// 2. 创建车辆信息
Vehicle vehicle = new Vehicle();
BeanUtils.copyProperties(dto, vehicle);
// 3. 保存车辆信息
vehicle = vehicleRepository.save(vehicle);
return buildResponse(vehicle);
}
public void updateVehicle(Long id, VehicleUpdateDTO dto) {
Vehicle vehicle = vehicleRepository.findById(id)
.orElseThrow(() -> new BusinessException("车辆不存在"));
// 1. 如果修改车牌号,需要验证唯一性
if (!vehicle.getPlateNumber().equals(dto.getPlateNumber())) {
validatePlateNumber(dto.getPlateNumber());
}
// 2. 更新车辆信息
BeanUtils.copyProperties(dto, vehicle);
vehicleRepository.save(vehicle);
}
public VehicleDetailResponse getVehicleDetail(Long id) {
Vehicle vehicle = vehicleRepository.findById(id)
.orElseThrow(() -> new BusinessException("车辆不存在"));
// 1. 获取车辆基本信息
VehicleDetailResponse response = buildDetailResponse(vehicle);
// 2. 获取维修记录
List<ServiceRecordVO> serviceRecords = getServiceRecords(id);
response.setServiceRecords(serviceRecords);
// 3. 获取保养提醒
List<MaintenanceReminderVO> reminders = calculateMaintenanceReminders(vehicle);
response.setMaintenanceReminders(reminders);
return response;
}
private List<MaintenanceReminderVO> calculateMaintenanceReminders(Vehicle vehicle) {
List<MaintenanceReminderVO> reminders = new ArrayList<>();
// 1. 常规保养提醒(按里程)
if (vehicle.getMileage() != null) {
int nextServiceMileage = calculateNextServiceMileage(vehicle.getMileage());
reminders.add(new MaintenanceReminderVO(
"常规保养",
"行驶里程达到" + nextServiceMileage + "公里时建议进行保养"
));
}
// 2. 保险到期提醒
if (vehicle.getInsuranceExpiry() != null) {
LocalDate today = LocalDate.now();
long daysUntilExpiry = ChronoUnit.DAYS.between(today, vehicle.getInsuranceExpiry());
if (daysUntilExpiry <= 30) {
reminders.add(new MaintenanceReminderVO(
"保险到期提醒",
"保险将在" + daysUntilExpiry + "天后到期"
));
}
}
return reminders;
}
}
5. 积分服务实现
java
@Service
@Transactional
public class PointsService {
@Autowired
private CustomerRepository customerRepository;
@Autowired
private PointsTransactionRepository transactionRepository;
public void addPoints(PointsAddDTO dto) {
Customer customer = customerRepository.findById(dto.getCustomerId())
.orElseThrow(() -> new BusinessException("客户不存在"));
// 1. 计算积分变动
int beforePoints = customer.getPoints();
int afterPoints = beforePoints + dto.getPoints();
// 2. 更新客户积分
customer.setPoints(afterPoints);
customerRepository.save(customer);
// 3. 记录积分变动
savePointsTransaction(
customer.getId(),
PointsTransactionType.EARN,
dto.getPoints(),
beforePoints,
afterPoints,
dto.getReferenceNo(),
dto.getRemark()
);
// 4. 检查等级变更
checkAndUpdateLevel(customer);
}
public void deductPoints(PointsDeductDTO dto) {
Customer customer = customerRepository.findById(dto.getCustomerId())
.orElseThrow(() -> new BusinessException("客户不存在"));
// 1. 验证积分是否足够
if (customer.getPoints() < dto.getPoints()) {
throw new BusinessException("积分不足");
}
// 2. 计算积分变动
int beforePoints = customer.getPoints();
int afterPoints = beforePoints - dto.getPoints();
// 3. 更新客户积分
customer.setPoints(afterPoints);
customerRepository.save(customer);
// 4. 记录积分变动
savePointsTransaction(
customer.getId(),
PointsTransactionType.CONSUME,
-dto.getPoints(),
beforePoints,
afterPoints,
dto.getReferenceNo(),
dto.getRemark()
);
}
public PointsStatisticsResponse getPointsStatistics(Long customerId) {
// 1. 获取当前积分
Customer customer = customerRepository.findById(customerId)
.orElseThrow(() -> new BusinessException("客户不存在"));
// 2. 获取积分汇总信息
PointsSummaryVO summary = calculatePointsSummary(customerId);
// 3. 获取近期积分变动
List<PointsTransactionVO> recentTransactions =
getRecentTransactions(customerId);
return PointsStatisticsResponse.builder()
.currentPoints(customer.getPoints())
.summary(summary)
.recentTransactions(recentTransactions)
.build();
}
private void checkAndUpdateLevel(Customer customer) {
CustomerLevel newLevel = customerService.calculateCustomerLevel(customer.getId());
if (newLevel != customer.getLevel()) {
// 更新客户等级
customer.setLevel(newLevel);
customerRepository.save(customer);
// 发送等级变更通知
notificationService.sendLevelChangeNotification(customer);
}
}
}
6. 维修记录查询服务
java
@Service
public class ServiceRecordQueryService {
@Autowired
private ServiceRecordRepository serviceRecordRepository;
public Page<ServiceRecordVO> queryServiceRecords(ServiceRecordQueryDTO query) {
// 1. 构建查询条件
Specification<ServiceRecord> spec = buildSpecification(query);
// 2. 创建分页对象
PageRequest pageRequest = PageRequest.of(
query.getPageNum(),
query.getPageSize(),
Sort.by(Sort.Direction.DESC, "serviceDate")
);
// 3. 执行查询
Page<ServiceRecord> page = serviceRecordRepository.findAll(spec, pageRequest);
// 4. 转换响应
return page.map(this::convertToVO);
}
public ServiceRecordDetailResponse getServiceRecordDetail(Long id) {
ServiceRecord record = serviceRecordRepository.findById(id)
.orElseThrow(() -> new BusinessException("维修记录不存在"));
// 1. 获取基本信息
ServiceRecordDetailResponse response = buildDetailResponse(record);
// 2. 获取服务项目明细
List<ServiceItemVO> items = getServiceItems(record);
response.setItems(items);
// 3. 获取配件使用记录
List<PartUsageVO> partUsages = getPartUsages(record);
response.setPartUsages(partUsages);
return response;
}
public List<ServiceStatisticsVO> getServiceStatistics(Long customerId) {
// 1. 获取维修频次统计
Map<String, Long> serviceFrequency = calculateServiceFrequency(customerId);
// 2. 获取维修金额统计
Map<String, BigDecimal> serviceAmount = calculateServiceAmount(customerId);
// 3. 获取常见维修项目
List<CommonServiceItemVO> commonItems = getCommonServiceItems(customerId);
return buildStatisticsResponse(serviceFrequency, serviceAmount, commonItems);
}
}
7. 客户查询接口实现
java
@RestController
@RequestMapping("/api/customers")
public class CustomerController {
@Autowired
private CustomerService customerService;
@Autowired
private VehicleService vehicleService;
@Autowired
private PointsService pointsService;
@PostMapping
public Result<CustomerResponse> createCustomer(@RequestBody @Valid CustomerDTO dto) {
CustomerResponse response = customerService.createCustomer(dto);
return Result.success(response);
}
@PutMapping("/{id}")
public Result<Void> updateCustomer(
@PathVariable Long id,
@RequestBody @Valid CustomerUpdateDTO dto) {
customerService.updateCustomer(id, dto);
return Result.success();
}
@GetMapping("/{id}")
public Result<CustomerDetailResponse> getCustomerDetail(@PathVariable Long id) {
CustomerDetailResponse detail = customerService.getCustomerDetail(id);
return Result.success(detail);
}
@PostMapping("/{id}/vehicles")
public Result<VehicleResponse> addVehicle(
@PathVariable Long id,
@RequestBody @Valid VehicleDTO dto) {
dto.setCustomerId(id);
VehicleResponse response = vehicleService.addVehicle(dto);
return Result.success(response);
}
@PostMapping("/{id}/points/add")
public Result<Void> addPoints(
@PathVariable Long id,
@RequestBody @Valid PointsAddDTO dto) {
dto.setCustomerId(id);
pointsService.addPoints(dto);
return Result.success();
}
@GetMapping("/{id}/points/statistics")
public Result<PointsStatisticsResponse> getPointsStatistics(@PathVariable Long id) {
PointsStatisticsResponse statistics = pointsService.getPointsStatistics(id);
return Result.success(statistics);
}
@GetMapping("/{id}/service-records")
public Result<Page<ServiceRecordVO>> getServiceRecords(
@PathVariable Long id,
ServiceRecordQueryDTO query) {
query.setCustomerId(id);
Page<ServiceRecordVO> page = serviceRecordQueryService.queryServiceRecords(query);
return Result.success(page);
}
}
这些代码实现了客户管理的核心功能,包括:
- 客户信息的CRUD操作
- 车辆信息管理
- 维修记录查询
- 积分管理和等级计算
- 统计分析功能
关键特点:
- 完整的客户生命周期管理
- 车辆信息关联
- 维修记录追踪
- 积分变动记录
- 客户等级自动计算
- 保养提醒功能
员工管理模块的核心代码实现
1. 数据库设计
sql
-- 员工信息表
CREATE TABLE employee (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
code VARCHAR(32) UNIQUE NOT NULL COMMENT '员工编号',
name VARCHAR(50) NOT NULL COMMENT '姓名',
gender TINYINT NOT NULL COMMENT '性别: 0-女 1-男',
id_card VARCHAR(18) UNIQUE NOT NULL COMMENT '身份证号',
phone VARCHAR(20) NOT NULL COMMENT '联系电话',
email VARCHAR(100) COMMENT '邮箱',
address TEXT COMMENT '住址',
job_type_id BIGINT NOT NULL COMMENT '工种ID',
department_id BIGINT NOT NULL COMMENT '部门ID',
entry_date DATE NOT NULL COMMENT '入职日期',
leave_date DATE COMMENT '离职日期',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 0-离职 1-在职',
created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- 工种分类表
CREATE TABLE job_type (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL COMMENT '工种名称',
description TEXT COMMENT '工种描述',
base_salary DECIMAL(10,2) NOT NULL COMMENT '基本工资',
commission_rate DECIMAL(5,2) COMMENT '提成比例',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 0-禁用 1-启用',
created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- 工作量记录表
CREATE TABLE work_record (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
employee_id BIGINT NOT NULL COMMENT '员工ID',
work_order_id BIGINT NOT NULL COMMENT '工单ID',
service_item_id BIGINT NOT NULL COMMENT '服务项目ID',
work_hours DECIMAL(5,2) NOT NULL COMMENT '工时',
amount DECIMAL(10,2) NOT NULL COMMENT '金额',
commission DECIMAL(10,2) NOT NULL COMMENT '提成金额',
work_date DATE NOT NULL COMMENT '工作日期',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态',
created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (employee_id) REFERENCES employee(id)
);
-- 绩效考核表
CREATE TABLE performance_evaluation (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
employee_id BIGINT NOT NULL COMMENT '员工ID',
evaluation_month VARCHAR(7) NOT NULL COMMENT '考核月份 YYYY-MM',
work_quality_score INT NOT NULL COMMENT '工作质量得分',
work_attitude_score INT NOT NULL COMMENT '工作态度得分',
customer_feedback_score INT NOT NULL COMMENT '客户反馈得分',
total_score INT NOT NULL COMMENT '总分',
evaluator_id BIGINT NOT NULL COMMENT '评估人ID',
evaluation_date DATE NOT NULL COMMENT '评估日期',
remark TEXT COMMENT '评语',
created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (employee_id) REFERENCES employee(id),
UNIQUE KEY `uk_employee_month` (`employee_id`, `evaluation_month`)
);
-- 考核指标表
CREATE TABLE evaluation_criteria (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL COMMENT '指标名称',
category VARCHAR(50) NOT NULL COMMENT '指标类别',
description TEXT COMMENT '指标描述',
weight INT NOT NULL COMMENT '权重',
max_score INT NOT NULL COMMENT '最高分',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态',
created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
2. 实体类设计
java
@Data
@Entity
@Table(name = "employee")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String code;
private String name;
private Integer gender;
private String idCard;
private String phone;
private String email;
private String address;
private Long jobTypeId;
private Long departmentId;
private LocalDate entryDate;
private LocalDate leaveDate;
private Integer status;
@ManyToOne
@JoinColumn(name = "job_type_id", insertable = false, updatable = false)
private JobType jobType;
}
@Data
@Entity
@Table(name = "job_type")
public class JobType {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
private BigDecimal baseSalary;
private BigDecimal commissionRate;
private Integer status;
}
@Data
@Entity
@Table(name = "performance_evaluation")
public class PerformanceEvaluation {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long employeeId;
private String evaluationMonth;
private Integer workQualityScore;
private Integer workAttitudeScore;
private Integer customerFeedbackScore;
private Integer totalScore;
private Long evaluatorId;
private LocalDate evaluationDate;
private String remark;
}
3. 员工服务实现
java
@Service
@Transactional
public class EmployeeService {
@Autowired
private EmployeeRepository employeeRepository;
@Autowired
private SequenceGenerator sequenceGenerator;
public EmployeeResponse createEmployee(EmployeeDTO dto) {
// 1. 验证身份证号是否已存在
validateIdCard(dto.getIdCard());
// 2. 生成员工编号
String code = sequenceGenerator.generateEmployeeCode();
// 3. 创建员工信息
Employee employee = new Employee();
BeanUtils.copyProperties(dto, employee);
employee.setCode(code);
employee.setStatus(1);
// 4. 保存员工信息
employee = employeeRepository.save(employee);
// 5. 创建系统账号
createUserAccount(employee);
return buildResponse(employee);
}
public void updateEmployee(Long id, EmployeeUpdateDTO dto) {
Employee employee = employeeRepository.findById(id)
.orElseThrow(() -> new BusinessException("员工不存在"));
// 1. 如果修改身份证号,需要验证唯一性
if (!employee.getIdCard().equals(dto.getIdCard())) {
validateIdCard(dto.getIdCard());
}
// 2. 更新员工信息
BeanUtils.copyProperties(dto, employee);
employeeRepository.save(employee);
}
public void handleEmployeeResignation(Long id, ResignationDTO dto) {
Employee employee = employeeRepository.findById(id)
.orElseThrow(() -> new BusinessException("员工不存在"));
// 1. 设置离职信息
employee.setLeaveDate(dto.getLeaveDate());
employee.setStatus(0);
// 2. 更新员工状态
employeeRepository.save(employee);
// 3. 处理相关系统账号
disableUserAccount(employee.getCode());
// 4. 记录离职信息
saveResignationRecord(employee, dto);
}
private void createUserAccount(Employee employee) {
UserCreateDTO userDto = UserCreateDTO.builder()
.username(employee.getCode())
.name(employee.getName())
.phone(employee.getPhone())
.email(employee.getEmail())
.roleIds(getRolesByJobType(employee.getJobTypeId()))
.build();
userService.createUser(userDto);
}
}
4. 工种管理服务实现
java
@Service
@Transactional
public class JobTypeService {
@Autowired
private JobTypeRepository jobTypeRepository;
public JobTypeResponse createJobType(JobTypeDTO dto) {
// 1. 验证工种名称是否已存在
validateJobTypeName(dto.getName());
// 2. 创建工种信息
JobType jobType = new JobType();
BeanUtils.copyProperties(dto, jobType);
jobType.setStatus(1);
// 3. 保存工种信息
jobType = jobTypeRepository.save(jobType);
return buildResponse(jobType);
}
public void updateJobType(Long id, JobTypeUpdateDTO dto) {
JobType jobType = jobTypeRepository.findById(id)
.orElseThrow(() -> new BusinessException("工种不存在"));
// 1. 如果修改名称,需要验证唯一性
if (!jobType.getName().equals(dto.getName())) {
validateJobTypeName(dto.getName());
}
// 2. 更新工种信息
BeanUtils.copyProperties(dto, jobType);
jobTypeRepository.save(jobType);
}
public void updateCommissionRate(Long id, BigDecimal newRate) {
JobType jobType = jobTypeRepository.findById(id)
.orElseThrow(() -> new BusinessException("工种不存在"));
// 1. 验证提成比例
if (newRate.compareTo(BigDecimal.ZERO) < 0 ||
newRate.compareTo(BigDecimal.valueOf(100)) > 0) {
throw new BusinessException("提成比例必须在0-100之间");
}
// 2. 更新提成比例
jobType.setCommissionRate(newRate);
jobTypeRepository.save(jobType);
}
}
5. 工作量统计服务实现
java
@Service
public class WorkloadStatisticsService {
@Autowired
private WorkRecordRepository workRecordRepository;
public WorkloadStatisticsResponse getEmployeeWorkload(
Long employeeId, LocalDate startDate, LocalDate endDate) {
// 1. 获取工作量汇总
WorkloadSummaryVO summary = calculateWorkloadSummary(employeeId, startDate, endDate);
// 2. 获取每日工作量
List<DailyWorkloadVO> dailyWorkloads =
calculateDailyWorkload(employeeId, startDate, endDate);
// 3. 获取项目分布
List<ServiceItemDistributionVO> itemDistribution =
calculateServiceItemDistribution(employeeId, startDate, endDate);
return WorkloadStatisticsResponse.builder()
.summary(summary)
.dailyWorkloads(dailyWorkloads)
.itemDistribution(itemDistribution)
.build();
}
public Page<WorkRecordVO> queryWorkRecords(WorkRecordQueryDTO query) {
// 1. 构建查询条件
Specification<WorkRecord> spec = buildSpecification(query);
// 2. 创建分页对象
PageRequest pageRequest = PageRequest.of(
query.getPageNum(),
query.getPageSize(),
Sort.by(Sort.Direction.DESC, "workDate")
);
// 3. 执行查询
Page<WorkRecord> page = workRecordRepository.findAll(spec, pageRequest);
// 4. 转换响应
return page.map(this::convertToVO);
}
private WorkloadSummaryVO calculateWorkloadSummary(
Long employeeId, LocalDate startDate, LocalDate endDate) {
// 1. 计算总工时
BigDecimal totalHours = workRecordRepository
.sumWorkHours(employeeId, startDate, endDate);
// 2. 计算总金额
BigDecimal totalAmount = workRecordRepository
.sumAmount(employeeId, startDate, endDate);
// 3. 计算总提成
BigDecimal totalCommission = workRecordRepository
.sumCommission(employeeId, startDate, endDate);
// 4. 计算完成工单数
Long orderCount = workRecordRepository
.countWorkOrders(employeeId, startDate, endDate);
return WorkloadSummaryVO.builder()
.totalHours(totalHours)
.totalAmount(totalAmount)
.totalCommission(totalCommission)
.orderCount(orderCount)
.build();
}
}
6. 绩效考核服务实现
java
@Service
@Transactional
public class PerformanceEvaluationService {
@Autowired
private PerformanceEvaluationRepository evaluationRepository;
@Autowired
private EvaluationCriteriaRepository criteriaRepository;
public void createEvaluation(PerformanceEvaluationDTO dto) {
// 1. 验证是否已存在当月考核
validateMonthlyEvaluation(dto.getEmployeeId(), dto.getEvaluationMonth());
// 2. 计算总分
int totalScore = calculateTotalScore(dto);
// 3. 创建考核记录
PerformanceEvaluation evaluation = new PerformanceEvaluation();
BeanUtils.copyProperties(dto, evaluation);
evaluation.setTotalScore(totalScore);
evaluation.setEvaluationDate(LocalDate.now());
evaluation.setEvaluatorId(SecurityUtils.getCurrentUserId());
// 4. 保存考核记录
evaluationRepository.save(evaluation);
// 5. 处理绩效奖金
handlePerformanceBonus(evaluation);
}
public PerformanceStatisticsResponse getPerformanceStatistics(
Long employeeId, String startMonth, String endMonth) {
// 1. 获取考核汇总
PerformanceSummaryVO summary =
calculatePerformanceSummary(employeeId, startMonth, endMonth);
// 2. 获取月度考核趋势
List<MonthlyPerformanceVO> monthlyTrend =
calculateMonthlyTrend(employeeId, startMonth, endMonth);
// 3. 获取各项得分分布
List<ScoreDistributionVO> scoreDistribution =
calculateScoreDistribution(employeeId, startMonth, endMonth);
return PerformanceStatisticsResponse.builder()
.summary(summary)
.monthlyTrend(monthlyTrend)
.scoreDistribution(scoreDistribution)
.build();
}
private void handlePerformanceBonus(PerformanceEvaluation evaluation) {
// 1. 获取员工信息
Employee employee = employeeRepository.findById(evaluation.getEmployeeId())
.orElseThrow();
// 2. 获取工种信息
JobType jobType = jobTypeRepository.findById(employee.getJobTypeId())
.orElseThrow();
// 3. 计算绩效奖金
BigDecimal bonus = calculatePerformanceBonus(
evaluation.getTotalScore(),
jobType.getBaseSalary()
);
// 4. 保存奖金记录
saveBonusRecord(evaluation, bonus);
}
private int calculateTotalScore(PerformanceEvaluationDTO dto) {
// 1. 获取考核指标权重
Map<String, Integer> weights = getEvaluationWeights();
// 2. 计算加权总分
return dto.getWorkQualityScore() * weights.get("QUALITY") / 100 +
dto.getWorkAttitudeScore() * weights.get("ATTITUDE") / 100 +
dto.getCustomerFeedbackScore() * weights.get("FEEDBACK") / 100;
}
}
7. 员工查询接口实现
java
@RestController
@RequestMapping("/api/employees")
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
@Autowired
private WorkloadStatisticsService workloadService;
@Autowired
private PerformanceEvaluationService performanceService;
@PostMapping
public Result<EmployeeResponse> createEmployee(@RequestBody @Valid EmployeeDTO dto) {
EmployeeResponse response = employeeService.createEmployee(dto);
return Result.success(response);
}
@PutMapping("/{id}")
public Result<Void> updateEmployee(
@PathVariable Long id,
@RequestBody @Valid EmployeeUpdateDTO dto) {
employeeService.updateEmployee(id, dto);
return Result.success();
}
@PostMapping("/{id}/resignation")
public Result<Void> handleResignation(
@PathVariable Long id,
@RequestBody @Valid ResignationDTO dto) {
employeeService.handleEmployeeResignation(id, dto);
return Result.success();
}
@GetMapping("/{id}/workload")
public Result<WorkloadStatisticsResponse> getWorkloadStatistics(
@PathVariable Long id,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) {
WorkloadStatisticsResponse statistics =
workloadService.getEmployeeWorkload(id, startDate, endDate);
return Result.success(statistics);
}
@PostMapping("/{id}/performance-evaluation")
public Result<Void> createPerformanceEvaluation(
@PathVariable Long id,
@RequestBody @Valid PerformanceEvaluationDTO dto) {
dto.setEmployeeId(id);
performanceService.createEvaluation(dto);
return Result.success();
}
@GetMapping("/{id}/performance-statistics")
public Result<PerformanceStatisticsResponse> getPerformanceStatistics(
@PathVariable Long id,
@RequestParam String startMonth,
@RequestParam String endMonth) {
PerformanceStatisticsResponse statistics =
performanceService.getPerformanceStatistics(id, startMonth, endMonth);
return Result.success(statistics);
}
}
这些代码实现了员工管理的核心功能,包括:
- 员工信息的CRUD操作
- 工种分类管理
- 工作量统计分析
- 绩效考核管理
关键特点:
- 完整的员工生命周期管理
- 灵活的工种配置
- 详细的工作量记录
- 多维度的绩效考核
- 自动化的提成计算
- 完善的统计分析功能
财务管理模块的核心代码实现
1. 数据库设计
sql
-- 财务收支记录表
CREATE TABLE financial_transaction (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
transaction_no VARCHAR(32) UNIQUE NOT NULL COMMENT '交易编号',
type VARCHAR(20) NOT NULL COMMENT '交易类型: INCOME/EXPENSE',
category_id BIGINT NOT NULL COMMENT '类别ID',
amount DECIMAL(12,2) NOT NULL COMMENT '金额',
payment_method VARCHAR(20) NOT NULL COMMENT '支付方式',
transaction_date DATE NOT NULL COMMENT '交易日期',
reference_no VARCHAR(32) COMMENT '关联单号',
reference_type VARCHAR(20) COMMENT '关联单据类型',
operator_id BIGINT NOT NULL COMMENT '操作人',
remark TEXT COMMENT '备注',
created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- 收支类别表
CREATE TABLE transaction_category (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL COMMENT '类别名称',
type VARCHAR(20) NOT NULL COMMENT '类型: INCOME/EXPENSE',
parent_id BIGINT COMMENT '父类别ID',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态',
created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- 成本记录表
CREATE TABLE cost_record (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
cost_type VARCHAR(20) NOT NULL COMMENT '成本类型',
category_id BIGINT NOT NULL COMMENT '类别ID',
amount DECIMAL(12,2) NOT NULL COMMENT '金额',
record_month VARCHAR(7) NOT NULL COMMENT '记录月份 YYYY-MM',
remark TEXT COMMENT '备注',
created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- 利润核算表
CREATE TABLE profit_statement (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
statement_month VARCHAR(7) NOT NULL COMMENT '核算月份 YYYY-MM',
total_revenue DECIMAL(12,2) NOT NULL COMMENT '总收入',
total_cost DECIMAL(12,2) NOT NULL COMMENT '总成本',
gross_profit DECIMAL(12,2) NOT NULL COMMENT '毛利润',
operating_expenses DECIMAL(12,2) NOT NULL COMMENT '运营费用',
net_profit DECIMAL(12,2) NOT NULL COMMENT '净利润',
status VARCHAR(20) NOT NULL COMMENT '状态',
created_by BIGINT NOT NULL COMMENT '创建人',
created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY `uk_month` (`statement_month`)
);
-- 收入明细表
CREATE TABLE revenue_detail (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
statement_id BIGINT NOT NULL COMMENT '利润表ID',
category_id BIGINT NOT NULL COMMENT '收入类别ID',
amount DECIMAL(12,2) NOT NULL COMMENT '金额',
percentage DECIMAL(5,2) NOT NULL COMMENT '占比',
created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (statement_id) REFERENCES profit_statement(id)
);
-- 成本明细表
CREATE TABLE cost_detail (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
statement_id BIGINT NOT NULL COMMENT '利润表ID',
category_id BIGINT NOT NULL COMMENT '成本类别ID',
amount DECIMAL(12,2) NOT NULL COMMENT '金额',
percentage DECIMAL(5,2) NOT NULL COMMENT '占比',
created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (statement_id) REFERENCES profit_statement(id)
);
2. 实体类设计
java
@Data
@Entity
@Table(name = "financial_transaction")
public class FinancialTransaction {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String transactionNo;
@Enumerated(EnumType.STRING)
private TransactionType type;
private Long categoryId;
private BigDecimal amount;
@Enumerated(EnumType.STRING)
private PaymentMethod paymentMethod;
private LocalDate transactionDate;
private String referenceNo;
private String referenceType;
private Long operatorId;
private String remark;
@ManyToOne
@JoinColumn(name = "category_id", insertable = false, updatable = false)
private TransactionCategory category;
}
@Data
@Entity
@Table(name = "profit_statement")
public class ProfitStatement {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String statementMonth;
private BigDecimal totalRevenue;
private BigDecimal totalCost;
private BigDecimal grossProfit;
private BigDecimal operatingExpenses;
private BigDecimal netProfit;
@Enumerated(EnumType.STRING)
private StatementStatus status;
private Long createdBy;
@OneToMany(mappedBy = "statement", cascade = CascadeType.ALL)
private List<RevenueDetail> revenueDetails;
@OneToMany(mappedBy = "statement", cascade = CascadeType.ALL)
private List<CostDetail> costDetails;
}
3. 财务交易服务实现
java
@Service
@Transactional
public class FinancialTransactionService {
@Autowired
private FinancialTransactionRepository transactionRepository;
@Autowired
private SequenceGenerator sequenceGenerator;
public TransactionResponse recordTransaction(TransactionDTO dto) {
// 1. 生成交易编号
String transactionNo = sequenceGenerator.generateTransactionNo();
// 2. 创建交易记录
FinancialTransaction transaction = new FinancialTransaction();
BeanUtils.copyProperties(dto, transaction);
transaction.setTransactionNo(transactionNo);
transaction.setOperatorId(SecurityUtils.getCurrentUserId());
// 3. 保存交易记录
transaction = transactionRepository.save(transaction);
// 4. 更新相关统计数据
updateStatistics(transaction);
return buildResponse(transaction);
}
public Page<TransactionVO> queryTransactions(TransactionQueryDTO query) {
// 1. 构建查询条件
Specification<FinancialTransaction> spec = buildSpecification(query);
// 2. 创建分页对象
PageRequest pageRequest = PageRequest.of(
query.getPageNum(),
query.getPageSize(),
Sort.by(Sort.Direction.DESC, "transactionDate")
);
// 3. 执行查询
Page<FinancialTransaction> page = transactionRepository.findAll(spec, pageRequest);
// 4. 转换响应
return page.map(this::convertToVO);
}
public TransactionStatisticsResponse getStatistics(
LocalDate startDate, LocalDate endDate) {
// 1. 获取收入统计
List<CategoryStatisticsVO> incomeStats =
calculateCategoryStatistics(TransactionType.INCOME, startDate, endDate);
// 2. 获取支出统计
List<CategoryStatisticsVO> expenseStats =
calculateCategoryStatistics(TransactionType.EXPENSE, startDate, endDate);
// 3. 获取支付方式统计
List<PaymentMethodStatisticsVO> paymentStats =
calculatePaymentMethodStatistics(startDate, endDate);
return TransactionStatisticsResponse.builder()
.incomeStatistics(incomeStats)
.expenseStatistics(expenseStats)
.paymentMethodStatistics(paymentStats)
.build();
}
private void updateStatistics(FinancialTransaction transaction) {
// 1. 更新日统计
updateDailyStatistics(transaction);
// 2. 更新月统计
updateMonthlyStatistics(transaction);
// 3. 更新类别统计
updateCategoryStatistics(transaction);
}
}
4. 营收报表服务实现
java
@Service
public class RevenueReportService {
@Autowired
private FinancialTransactionRepository transactionRepository;
public RevenueReportResponse generateMonthlyReport(String month) {
// 1. 获取收入汇总
RevenueSummaryVO summary = calculateRevenueSummary(month);
// 2. 获取收入趋势
List<DailyRevenueVO> dailyTrend = calculateDailyRevenue(month);
// 3. 获取收入构成
List<RevenueCategoryVO> categoryDistribution =
calculateCategoryDistribution(month);
// 4. 获取同比环比数据
ComparisonDataVO comparison = calculateComparison(month);
return RevenueReportResponse.builder()
.summary(summary)
.dailyTrend(dailyTrend)
.categoryDistribution(categoryDistribution)
.comparison(comparison)
.build();
}
private RevenueSummaryVO calculateRevenueSummary(String month) {
// 1. 计算总收入
BigDecimal totalRevenue = transactionRepository
.sumByTypeAndMonth(TransactionType.INCOME, month);
// 2. 计算服务收入
BigDecimal serviceRevenue = transactionRepository
.sumByCategoryAndMonth(Arrays.asList(1L, 2L), month);
// 3. 计算配件收入
BigDecimal partsRevenue = transactionRepository
.sumByCategoryAndMonth(Arrays.asList(3L), month);
// 4. 计算其他收入
BigDecimal otherRevenue = totalRevenue
.subtract(serviceRevenue)
.subtract(partsRevenue);
return RevenueSummaryVO.builder()
.totalRevenue(totalRevenue)
.serviceRevenue(serviceRevenue)
.partsRevenue(partsRevenue)
.otherRevenue(otherRevenue)
.build();
}
}
5. 成本分析服务实现
java
@Service
public class CostAnalysisService {
@Autowired
private CostRecordRepository costRecordRepository;
public CostAnalysisResponse analyzeMonthlyCost(String month) {
// 1. 获取成本汇总
CostSummaryVO summary = calculateCostSummary(month);
// 2. 获取成本构成
List<CostCategoryVO> categoryAnalysis = analyzeCostCategory(month);
// 3. 获取成本趋势
List<MonthlyCostTrendVO> costTrend = analyzeCostTrend(month);
// 4. 计算成本比率
List<CostRatioVO> costRatios = calculateCostRatios(month);
return CostAnalysisResponse.builder()
.summary(summary)
.categoryAnalysis(categoryAnalysis)
.costTrend(costTrend)
.costRatios(costRatios)
.build();
}
private List<CostRatioVO> calculateCostRatios(String month) {
List<CostRatioVO> ratios = new ArrayList<>();
// 1. 获取总收入
BigDecimal totalRevenue = revenueService.calculateMonthlyRevenue(month);
// 2. 获取各类成本
Map<String, BigDecimal> costMap = costRecordRepository
.getCostsByTypeAndMonth(month);
// 3. 计算各项成本比率
for (Map.Entry<String, BigDecimal> entry : costMap.entrySet()) {
BigDecimal ratio = BigDecimal.ZERO;
if (totalRevenue.compareTo(BigDecimal.ZERO) > 0) {
ratio = entry.getValue()
.divide(totalRevenue, 4, RoundingMode.HALF_UP)
.multiply(BigDecimal.valueOf(100));
}
ratios.add(new CostRatioVO(
entry.getKey(),
entry.getValue(),
ratio
));
}
return ratios;
}
}
6. 利润核算服务实现
java
@Service
@Transactional
public class ProfitStatementService {
@Autowired
private ProfitStatementRepository statementRepository;
@Autowired
private RevenueReportService revenueService;
@Autowired
private CostAnalysisService costService;
public void generateMonthlyStatement(String month) {
// 1. 验证月份是否已生成报表
validateMonth(month);
// 2. 创建利润表
ProfitStatement statement = new ProfitStatement();
statement.setStatementMonth(month);
statement.setStatus(StatementStatus.DRAFT);
statement.setCreatedBy(SecurityUtils.getCurrentUserId());
// 3. 计算收入数据
calculateRevenue(statement);
// 4. 计算成本数据
calculateCost(statement);
// 5. 计算利润数据
calculateProfit(statement);
// 6. 保存利润表
statement = statementRepository.save(statement);
// 7. 生成明细数据
generateDetails(statement);
}
public void approveStatement(Long id) {
ProfitStatement statement = statementRepository.findById(id)
.orElseThrow(() -> new BusinessException("利润表不存在"));
// 1. 验证状态
if (statement.getStatus() != StatementStatus.DRAFT) {
throw new BusinessException("只有草稿状态的利润表可以审批");
}
// 2. 更新状态
statement.setStatus(StatementStatus.APPROVED);
statementRepository.save(statement);
// 3. 生成财务凭证
generateVoucher(statement);
}
public ProfitAnalysisResponse analyzeProfitTrend(
String startMonth, String endMonth) {
// 1. 获取利润趋势
List<MonthlyProfitVO> profitTrend =
calculateProfitTrend(startMonth, endMonth);
// 2. 计算利润率
List<ProfitRatioVO> profitRatios =
calculateProfitRatios(startMonth, endMonth);
// 3. 计算同比数据
List<YearOverYearVO> yearOverYear =
calculateYearOverYear(startMonth, endMonth);
return ProfitAnalysisResponse.builder()
.profitTrend(profitTrend)
.profitRatios(profitRatios)
.yearOverYear(yearOverYear)
.build();
}
private void calculateProfit(ProfitStatement statement) {
// 1. 计算毛利润
BigDecimal grossProfit = statement.getTotalRevenue()
.subtract(statement.getTotalCost());
statement.setGrossProfit(grossProfit);
// 2. 计算净利润
BigDecimal netProfit = grossProfit
.subtract(statement.getOperatingExpenses());
statement.setNetProfit(netProfit);
}
}
7. 财务报表接口实现
java
@RestController
@RequestMapping("/api/finance")
public class FinanceController {
@Autowired
private FinancialTransactionService transactionService;
@Autowired
private RevenueReportService revenueService;
@Autowired
private CostAnalysisService costService;
@Autowired
private ProfitStatementService profitService;
@PostMapping("/transactions")
public Result<TransactionResponse> recordTransaction(
@RequestBody @Valid TransactionDTO dto) {
TransactionResponse response = transactionService.recordTransaction(dto);
return Result.success(response);
}
@GetMapping("/transactions/statistics")
public Result<TransactionStatisticsResponse> getTransactionStatistics(
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) {
TransactionStatisticsResponse statistics =
transactionService.getStatistics(startDate, endDate);
return Result.success(statistics);
}
@GetMapping("/revenue/monthly-report")
public Result<RevenueReportResponse> getMonthlyRevenueReport(
@RequestParam String month) {
RevenueReportResponse report = revenueService.generateMonthlyReport(month);
return Result.success(report);
}
@GetMapping("/cost/analysis")
public Result<CostAnalysisResponse> getCostAnalysis(
@RequestParam String month) {
CostAnalysisResponse analysis = costService.analyzeMonthlyCost(month);
return Result.success(analysis);
}
@PostMapping("/profit-statements")
public Result<Void> generateProfitStatement(@RequestParam String month) {
profitService.generateMonthlyStatement(month);
return Result.success();
}
@PostMapping("/profit-statements/{id}/approve")
public Result<Void> approveProfitStatement(@PathVariable Long id) {
profitService.approveStatement(id);
return Result.success();
}
@GetMapping("/profit/analysis")
public Result<ProfitAnalysisResponse> getProfitAnalysis(
@RequestParam String startMonth,
@RequestParam String endMonth) {
ProfitAnalysisResponse analysis =
profitService.analyzeProfitTrend(startMonth, endMonth);
return Result.success(analysis);
}
}
这些代码实现了财务管理的核心功能,包括:
- 收支明细记录
- 营收报表统计
- 成本分析
- 利润核算
关键特点:
- 完整的财务交易记录
- 多维度的营收分析
- 详细的成本核算
- 自动化的利润计算
- 完善的报表功能
- 支持同比环比分析