编辑 | blame | 历史 | 原始文档

任务车辆关联关系设计

关系概述

根据业务需求,任务管理系统需要建立以下关联关系:

  1. 任务 ↔ 车辆:一个任务可以关联多辆车,一辆车可以执行多个任务
  2. 车辆 ↔ 机构:每辆车归属于一个机构
  3. 任务 ↔ 执行人:每个任务分配给一个执行人
  4. 执行人 ↔ 机构:每个执行人归属于一个机构

数据库关系设计

1. 核心关联关系

erDiagram
    sys_task ||--o{ sys_task_vehicle : "一对多"
    tb_vehicle_info ||--o{ sys_task_vehicle : "一对多"
    sys_dept ||--o{ tb_vehicle_info : "一对多"
    sys_dept ||--o{ sys_task : "一对多"
    sys_user ||--o{ sys_task : "一对多"
    sys_dept ||--o{ sys_user : "一对多"
    
    sys_task {
        bigint task_id PK
        varchar task_code UK
        varchar task_type
        varchar task_status
        bigint creator_id FK
        bigint assignee_id FK
        bigint dept_id FK
        bigint vehicle_id FK
    }
    
    tb_vehicle_info {
        bigint vehicle_id PK
        varchar platform_code
        varchar vehicle_no UK
        varchar vehicle_type
        varchar vehicle_brand
        varchar vehicle_model
        varchar vehicle_color
        char vehicle_status
        varchar device_id
        bigint dept_id FK
    }
    
    sys_task_vehicle {
        bigint id PK
        bigint task_id FK
        bigint vehicle_id FK
        datetime assign_time
        varchar assign_by
        varchar status
        varchar remark
    }
    
    sys_dept {
        bigint dept_id PK
        varchar dept_name
        bigint parent_id
    }
    
    sys_user {
        bigint user_id PK
        varchar user_name
        bigint dept_id FK
    }

2. 关联关系说明

2.1 任务与车辆关联

  • 关系类型:多对多(通过中间表 sys_task_vehicle
  • 业务规则
  • 一个任务可以分配多辆车(如:主车+备用车、多车协同作业)
  • 一辆车可以执行多个任务(按时间顺序,避免冲突)
  • 任务车辆关联有独立状态管理(已分配、执行中、已完成、已取消)
  • 支持批量分配和取消分配
  • 车辆分配时验证车辆状态和可用性

2.2 车辆与机构关联

  • 关系类型:多对一
  • 业务规则
  • 每辆车必须归属于一个机构
  • 机构删除时,车辆归属设置为NULL(软删除)
  • 支持按机构查询车辆

2.3 任务与执行人关联

  • 关系类型:多对一
  • 业务规则
  • 每个任务分配给一个执行人
  • 执行人必须与任务在同一机构或上级机构
  • 支持任务重新分配

2.4 执行人与机构关联

  • 关系类型:多对一
  • 业务规则
  • 每个执行人归属于一个机构
  • 支持机构层级权限控制

数据权限控制

1. 机构数据隔离

-- 查询任务时按机构过滤
SELECT t.* FROM sys_task t 
WHERE t.dept_id IN (
    SELECT dept_id FROM sys_dept 
    WHERE FIND_IN_SET(dept_id, @user_dept_ids)
);

-- 查询车辆时按机构过滤
SELECT v.* FROM tb_vehicle_info v 
WHERE v.dept_id IN (
    SELECT dept_id FROM sys_dept 
    WHERE FIND_IN_SET(dept_id, @user_dept_ids)
);

2. 权限验证规则

  • 任务创建:只能创建本机构及下级机构的任务
  • 车辆分配:只能分配本机构及下级机构的车辆
  • 执行人分配:只能分配给本机构及下级机构的用户
  • 数据查询:只能查看本机构及下级机构的数据

业务场景示例

1. 创建维修任务并分配多辆车

-- 1. 创建任务
INSERT INTO sys_task (task_code, task_type, task_status, creator_id, dept_id, ...)
VALUES ('TASK202401150001', 'MAINTENANCE', 'PENDING', 100, 200, ...);

-- 2. 分配多辆车(主车+备用车)
INSERT INTO sys_task_vehicle (task_id, vehicle_id, assign_by, status, remark)
VALUES 
(1, 10, 'admin', 'ASSIGNED', '分配主维修车'),
(1, 11, 'admin', 'ASSIGNED', '分配备用维修车'),
(1, 12, 'admin', 'ASSIGNED', '分配工具车');

-- 3. 分配执行人
UPDATE sys_task SET assignee_id = 150 WHERE task_id = 1;

2. 查询机构下的所有任务和车辆

-- 查询机构任务
SELECT t.task_code, t.task_type, t.task_status, 
       u.user_name as assignee_name,
       d.dept_name
FROM sys_task t
LEFT JOIN sys_user u ON t.assignee_id = u.user_id
LEFT JOIN sys_dept d ON t.dept_id = d.dept_id
WHERE t.dept_id = 200;

-- 查询机构车辆
SELECT v.vehicle_no, v.vehicle_type, v.vehicle_status,
       d.dept_name
FROM tb_vehicle_info v
LEFT JOIN sys_dept d ON v.dept_id = d.dept_id
WHERE v.dept_id = 200;

3. 查询任务的车辆分配情况

SELECT t.task_code, t.task_type,
       v.vehicle_no, v.vehicle_type,
       tv.assign_time, tv.status, tv.remark
FROM sys_task t
LEFT JOIN sys_task_vehicle tv ON t.task_id = tv.task_id
LEFT JOIN tb_vehicle_info v ON tv.vehicle_id = v.vehicle_id
WHERE t.task_id = 1;

接口设计扩展

1. 车辆管理接口

// 查询机构车辆列表
export function listVehicleByDept(deptId, query) {
  return request({
    url: '/api/vehicle/list-by-dept/' + deptId,
    method: 'get',
    params: query
  })
}

// 查询可用车辆(未分配任务的车辆)
export function listAvailableVehicles(deptId, taskType) {
  return request({
    url: '/api/vehicle/available',
    method: 'get',
    params: { deptId, taskType }
  })
}

2. 任务车辆分配接口

// 批量分配车辆
export function assignVehiclesToTask(taskId, vehicleIds, remark) {
  return request({
    url: '/api/task/' + taskId + '/assign-vehicles',
    method: 'post',
    data: { vehicleIds, remark }
  })
}

// 查询任务车辆使用情况
export function getTaskVehicleUsage(taskId) {
  return request({
    url: '/api/task/' + taskId + '/vehicle-usage',
    method: 'get'
  })
}

数据一致性保证

1. 外键约束

  • 任务表引用车辆表:ON DELETE SET NULL
  • 任务车辆关联表:ON DELETE CASCADE
  • 车辆表引用机构表:ON DELETE SET NULL

2. 业务规则验证

  • 车辆分配时验证车辆状态(必须为正常状态)
  • 任务分配时验证执行人权限(同机构或上级机构)
  • 机构删除时处理关联数据(软删除或转移)

3. 事务控制

@Transactional
public void assignMultipleVehiclesToTask(Long taskId, List<Long> vehicleIds, String remark) {
    // 1. 验证任务状态
    Task task = taskMapper.selectById(taskId);
    if (task.getStatus() == TaskStatus.COMPLETED) {
        throw new BusinessException("已完成的任务不能分配车辆");
    }
    
    List<TaskVehicle> assignedVehicles = new ArrayList<>();
    
    for (Long vehicleId : vehicleIds) {
        // 2. 验证车辆状态和可用性
        Vehicle vehicle = vehicleMapper.selectById(vehicleId);
        if (vehicle.getStatus() != VehicleStatus.ACTIVE) {
            throw new BusinessException("车辆ID " + vehicleId + " 状态异常,不能分配");
        }
        
        // 3. 检查车辆是否已被其他任务占用
        if (isVehicleAssignedToOtherTask(vehicleId, taskId)) {
            throw new BusinessException("车辆ID " + vehicleId + " 已被其他任务占用");
        }
        
        // 4. 创建关联记录
        TaskVehicle taskVehicle = new TaskVehicle();
        taskVehicle.setTaskId(taskId);
        taskVehicle.setVehicleId(vehicleId);
        taskVehicle.setStatus(TaskVehicleStatus.ASSIGNED);
        taskVehicle.setRemark(remark);
        taskVehicleMapper.insert(taskVehicle);
        assignedVehicles.add(taskVehicle);
    }
    
    // 5. 记录操作日志
    logTaskOperation(taskId, "ASSIGN_MULTIPLE_VEHICLES", 
        "分配了 " + vehicleIds.size() + " 辆车: " + vehicleIds.toString());
}

@Transactional
public void unassignVehicleFromTask(Long taskId, Long vehicleId) {
    // 1. 验证任务车辆关联是否存在
    TaskVehicle taskVehicle = taskVehicleMapper.selectByTaskAndVehicle(taskId, vehicleId);
    if (taskVehicle == null) {
        throw new BusinessException("任务车辆关联不存在");
    }
    
    // 2. 验证是否可以取消分配
    if (taskVehicle.getStatus() == TaskVehicleStatus.COMPLETED) {
        throw new BusinessException("已完成的任务车辆关联不能取消");
    }
    
    // 3. 删除关联记录
    taskVehicleMapper.deleteById(taskVehicle.getId());
    
    // 4. 记录操作日志
    logTaskOperation(taskId, "UNASSIGN_VEHICLE", "取消分配车辆ID: " + vehicleId);
}

总结

通过以上设计,实现了任务、车辆、机构和执行人之间的完整关联关系:

  1. 数据完整性:通过外键约束保证数据一致性
  2. 权限控制:基于机构的数据隔离和权限验证
  3. 业务灵活性:支持多车辆分配和状态管理
  4. 扩展性:预留了设备ID等字段,支持GPS定位等功能扩展

这个设计既满足了当前的业务需求,又为未来的功能扩展留下了空间。