wlzboy
2025-11-22 fd047fa7234dc11643dab8ecbf38e8d7a8ba0854
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
package com.ruoyi.system.service.impl;
 
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.system.domain.VehicleInfo;
import com.ruoyi.system.domain.VehicleDept;
import com.ruoyi.system.domain.VehicleSyncDTO;
import com.ruoyi.system.mapper.SysDeptMapper;
import com.ruoyi.system.mapper.VehicleInfoMapper;
import com.ruoyi.system.service.IVehicleInfoService;
import com.ruoyi.system.service.IVehicleSyncDataService;
import com.ruoyi.system.service.IVehicleSyncService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
 
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
/**
 * 车辆同步服务实现
 * 
 * 职责:将从 SQL Server 查询到的车辆数据同步到 MySQL
 * 
 * 数据流向:接收 DTO → 同步到 MySQL
 * 
 * @author ruoyi
 * @date 2025-10-20
 */
@Service
public class VehicleSyncServiceImpl implements IVehicleSyncService
{
    private static final Logger log = LoggerFactory.getLogger(VehicleSyncServiceImpl.class);
 
    @Autowired
    private IVehicleInfoService vehicleInfoService;
 
    @Autowired
    private SysDeptMapper sysDeptMapper;
    
    @Autowired
    private VehicleInfoMapper vehicleInfoMapper;
 
 
    /**
     * 同步车辆数据到MySQL
     * 
     * @param vehicles 从SQL Server查询的车辆列表
     * @return 同步结果
     */
    @Override
    @Transactional
    public AjaxResult syncVehicles(List<VehicleSyncDTO> vehicles)
    {
        if (vehicles == null || vehicles.isEmpty())
        {
            return AjaxResult.error("车辆数据为空,无法同步");
        }
 
        try
        {
            log.info("开始同步 {} 条车辆数据到 MySQL...", vehicles.size());
 
            int insertCount = 0;
            int updateCount = 0;
            int skipCount = 0;
            Map<String, String> errorMessages = new HashMap<>();
 
            for (VehicleSyncDTO vehicleDTO : vehicles)
            {
                try
                {
                    // 提取车牌号(去除括号中的内容)
                    String plateNumber = extractPlateNumber(vehicleDTO.getCarLicense());
                    
                    if (StringUtils.isBlank(plateNumber))
                    {
                        log.warn("车辆 CarID={} 车牌号为空,跳过同步", vehicleDTO.getCarId());
                        skipCount++;
                        continue;
                    }
 
                    // 查询车辆是否存在
                    VehicleInfo existingVehicle = findVehicleByPlateNumber(plateNumber);
 
                    // 解析所有分公司ID(CarOrdClass可能包含多个编码,如:HB,TI)
                    List<VehicleDept> vehicleDepts = parseVehicleDepts(vehicleDTO.getCarOrdClass());
                    
                    // 设置默认的主部门ID(第一个分公司)
                    Long primaryDeptId = null;
                    if (!vehicleDepts.isEmpty()) {
                        primaryDeptId = vehicleDepts.get(0).getDeptId();
                    }
 
                    if (existingVehicle != null)
                    {
                        // 更新车辆信息
                        existingVehicle.setCarId(vehicleDTO.getCarId());
                        existingVehicle.setDeptId(primaryDeptId); // 设置主部门
                        vehicleInfoService.updateVehicleInfo(existingVehicle);
                        
                        // 更新车辆-分公司关联
                        syncVehicleDepts(existingVehicle.getVehicleId(), vehicleDepts);
                        
                        updateCount++;
                        log.debug("更新车辆: {} (CarID={}), 关联分公司数: {}", 
                                plateNumber, vehicleDTO.getCarId(), vehicleDepts.size());
                    }
                    else
                    {
                        // 新增车辆信息
                        VehicleInfo newVehicle = new VehicleInfo();
                        newVehicle.setVehicleNo(plateNumber);
                        newVehicle.setCarId(vehicleDTO.getCarId());
                        newVehicle.setDeptId(primaryDeptId); // 设置主部门
                        newVehicle.setStatus("0");
                        newVehicle.setPlatformCode("LEGACY"); // 标记为旧系统同步
                        newVehicle.setRemark("从旧系统同步,CarID: " + vehicleDTO.getCarId());
                        vehicleInfoService.insertVehicleInfo(newVehicle);
                        
                        // 新增车辆-分公司关联
                        syncVehicleDepts(newVehicle.getVehicleId(), vehicleDepts);
                        
                        insertCount++;
                        log.debug("新增车辆: {} (CarID={}), 关联分公司数: {}", 
                                plateNumber, vehicleDTO.getCarId(), vehicleDepts.size());
                    }
                }
                catch (Exception e)
                {
                    String errorMsg = String.format("同步车辆 CarID=%d 失败: %s", 
                            vehicleDTO.getCarId(), e.getMessage());
                    log.error(errorMsg, e);
                    errorMessages.put("CarID_" + vehicleDTO.getCarId(), errorMsg);
                }
            }
 
            String resultMsg = String.format(
                    "车辆同步完成 - 新增: %d, 更新: %d, 跳过: %d, 失败: %d",
                    insertCount, updateCount, skipCount, errorMessages.size());
 
            log.info(resultMsg);
 
            Map<String, Object> result = new HashMap<>();
            result.put("insertCount", insertCount);
            result.put("updateCount", updateCount);
            result.put("skipCount", skipCount);
            result.put("errorCount", errorMessages.size());
            result.put("errors", errorMessages);
 
            if (errorMessages.isEmpty())
            {
                return AjaxResult.success(resultMsg, result);
            }
            else
            {
                return AjaxResult.warn(resultMsg, result);
            }
        }
        catch (Exception e)
        {
            log.error("同步车辆数据到 MySQL 失败", e);
            return AjaxResult.error("同步失败: " + e.getMessage());
        }
    }
 
 
    /**
     * 提取车牌号(去除括号中的内容)
     * 例如:浙A12345(奔驰) -> 浙A12345
     * 
     * @param carLicense 原始车牌号
     * @return 提取后的车牌号
     */
    private String extractPlateNumber(String carLicense)
    {
        if (StringUtils.isBlank(carLicense))
        {
            return null;
        }
 
        // 去除空格
        String license = carLicense.trim();
        
        // 查找左括号位置(支持中文和英文括号)
        int leftBracketIndex = license.indexOf('(');
        int leftBracketIndexCn = license.indexOf('(');
        
        // 取最小的有效括号位置
        int bracketIndex = -1;
        if (leftBracketIndex >= 0 && leftBracketIndexCn >= 0)
        {
            bracketIndex = Math.min(leftBracketIndex, leftBracketIndexCn);
        }
        else if (leftBracketIndex >= 0)
        {
            bracketIndex = leftBracketIndex;
        }
        else if (leftBracketIndexCn >= 0)
        {
            bracketIndex = leftBracketIndexCn;
        }
        
        // 如果找到括号,截取括号前的部分;否则返回原字符串
        if (bracketIndex > 0)
        {
            return license.substring(0, bracketIndex).trim();
        }
        
        return license;
    }
 
    /**
     * 根据车牌号查找车辆(模糊匹配)
     * 
     * @param plateNumber 车牌号
     * @return 车辆信息
     */
    private VehicleInfo findVehicleByPlateNumber(String plateNumber)
    {
        // 先尝试精确匹配
        VehicleInfo vehicle = vehicleInfoService.selectVehicleInfoByPlateNumber(plateNumber);
        
        if (vehicle != null)
        {
            return vehicle;
        }
        
        // 如果精确匹配失败,尝试模糊匹配(查询所有车辆,找到包含该车牌号的)
        VehicleInfo query = new VehicleInfo();
        List<VehicleInfo> allVehicles = vehicleInfoService.selectVehicleInfoList(query);
        
        for (VehicleInfo v : allVehicles)
        {
            if (StringUtils.isNotBlank(v.getVehicleNo()))
            {
                // 如果数据库中的车牌号包含查询的车牌号,或查询的车牌号包含数据库中的车牌号
                String dbPlateNumber = extractPlateNumber(v.getVehicleNo());
                if (plateNumber.contains(dbPlateNumber) || dbPlateNumber.contains(plateNumber))
                {
                    return v;
                }
            }
        }
        
        return null;
    }
 
    /**
     * 从 CarOrdClass 解析多个分公司关联
     * CarOrdClass格式可能是:HB、HB,TI、HB.TI等
     * 
     * @param carOrdClass 车辆单据类型编码
     * @return 车辆-分公司关联列表
     */
    private List<VehicleDept> parseVehicleDepts(String carOrdClass)
    {
        List<VehicleDept> vehicleDepts = new ArrayList<>();
        
        if (StringUtils.isBlank(carOrdClass))
        {
            log.debug("CarOrdClass为空,无法解析分公司");
            return vehicleDepts;
        }
 
        // 拆分CarOrdClass,可能的分隔符:, . 空格
        String[] codes = carOrdClass.split("[.,\\s]+");
        
        for (String code : codes)
        {
            if (StringUtils.isBlank(code))
            {
                continue;
            }
            
            code = code.trim();
            
            // 查询匹配dispatch_order_class或service_order_class的部门
            SysDept dept = findDeptByOrderClass(code);
            if (dept != null && dept.getParentId() != null && dept.getParentId() == 100L)
            {
                // 只处理分公司(parent_id=100)
                VehicleDept vehicleDept = new VehicleDept();
                vehicleDept.setDeptId(dept.getDeptId());
                vehicleDept.setOrderClass(code);
                vehicleDept.setCreateBy("system");
                vehicleDepts.add(vehicleDept);
                
                log.debug("通过order_class='{}' 找到分公司: {} (ID={})", 
                        code, dept.getDeptName(), dept.getDeptId());
            }
            else
            {
                log.debug("未找到匹配order_class='{}' 的分公司", code);
            }
        }
        
        return vehicleDepts;
    }
    
    /**
     * 同步车辆-分公司关联
     * 
     * @param vehicleId 车辆ID
     * @param vehicleDepts 分公司关联列表
     */
    private void syncVehicleDepts(Long vehicleId, List<VehicleDept> vehicleDepts)
    {
        if (vehicleId == null || vehicleDepts == null)
        {
            return;
        }
        
        // 先删除旧的关联
        vehicleInfoMapper.deleteVehicleDeptByVehicleId(vehicleId);
        
        // 再插入新的关联
        if (!vehicleDepts.isEmpty())
        {
            for (VehicleDept vd : vehicleDepts)
            {
                vd.setVehicleId(vehicleId);
            }
            vehicleInfoMapper.batchInsertVehicleDept(vehicleDepts);
            log.debug("同步车辆ID={} 的分公司关联,数量: {}", vehicleId, vehicleDepts.size());
        }
    }
 
    /**
     * 根据order_class查询部门(同时匹配dispatch_order_class和service_order_class)
     * 
     * @param orderClass 编码
     * @return 部门信息
     */
    private SysDept findDeptByOrderClass(String orderClass)
    {
        if (StringUtils.isBlank(orderClass))
        {
            return null;
        }
 
        try
        {
            // 先尝试匹配dispatch_order_class
            SysDept query = new SysDept();
            query.setDispatchOrderClass(orderClass);
            List<SysDept> depts = sysDeptMapper.selectDeptList(query);
            
            if (depts != null && !depts.isEmpty())
            {
                return depts.get(0);
            }
            
            // 如果没有找到,尝试匹配service_order_class
            query = new SysDept();
            query.setServiceOrderClass(orderClass);
            depts = sysDeptMapper.selectDeptList(query);
            
            if (depts != null && !depts.isEmpty())
            {
                return depts.get(0);
            }
        }
        catch (Exception e)
        {
            log.error("查询order_class='{}' 的部门失败", orderClass, e);
        }
        
        return null;
    }
}