徐锦洪 发表于 2024-12-28 11:19:48

Java全栈项目 - 汽车维修服务管理平台

项目介绍

汽车维修服务管理平台是一个面向汽修店的综合业务管理系统。该平台帮助汽修店实现维修预约、工单管理、库存管理、客户管理等核心业务流程的数字化,提拔运营服从。
技术架构

后端技术栈



[*]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. 数据库设计

-- 预约表
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. 实体类设计

@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. 枚举定义

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对象

@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层实现

@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层实现

@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. 关照服务实现

@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. 缓存配置

@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. 非常处理

@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. 数据库设计

-- 工单主表
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. 实体类设计

@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. 枚举定义

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层实现

@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层实现

@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. 费用盘算服务

@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. 状态机实现

@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. 进度跟踪实现

@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. 工单查询服务

@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));
      };
    }
}
这些代码实现了工单管理的核心功能,包罗:

[*]工单创建和管理
[*]维修项目跟踪
[*]配件使用管理
[*]费用盘算
[*]状态流转控制
[*]进度记录跟踪
关键特点:


[*]完整的状态机实现
[*]细粒度的费用盘算
[*]具体的进度跟踪
[*]灵活的查询功能
[*]完满的数据验证
库存管理模块的核心代码实现

1. 数据库设计

-- 配件信息表
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. 实体类设计

@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. 入库服务实现

@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. 库存服务实现

@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. 库存盘货服务实现

@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. 库存预警服务实现

@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. 库存统计服务实现

@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. 库存查询接口实现

@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. 数据库设计

-- 客户信息表
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. 实体类设计

@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. 客户服务实现

@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. 车辆服务实现

@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. 积分服务实现

@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. 维修记录查询服务

@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. 客户查询接口实现

@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. 数据库设计

-- 员工信息表
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. 实体类设计

@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. 员工服务实现

@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. 工种管理服务实现

@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. 工作量统计服务实现

@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. 绩效考核服务实现

@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. 员工查询接口实现

@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. 数据库设计

-- 财务收支记录表
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. 实体类设计

@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. 财务买卖业务服务实现

@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. 营收报表服务实现

@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. 成本分析服务实现

@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. 利润核算服务实现

@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. 财务报表接口实现

@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);
    }
}
这些代码实现了财务管理的核心功能,包罗:

[*]收支明细记录
[*]营收报表统计
[*]成本分析
[*]利润核算
关键特点:


[*]完整的财务买卖业务记录
[*]多维度的营收分析
[*]具体的成本核算
[*]自动化的利润盘算
[*]完满的报表功能
[*]支持同比环比分析

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Java全栈项目 - 汽车维修服务管理平台