Pu Zhibing
1 天以前 d0d6f8f40e5d9762d940b47c566da1876361020e
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
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
package com.stylefeng.guns.modular.system.service.impl;
 
import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.google.common.collect.Lists;
import com.stylefeng.guns.core.common.constant.factory.PageFactory;
import com.stylefeng.guns.core.exception.GunsException;
import com.stylefeng.guns.core.shiro.ShiroKit;
import com.stylefeng.guns.modular.system.controller.resp.DriverDispatchInfoResp;
import com.stylefeng.guns.modular.system.controller.util.UUIDUtil;
import com.stylefeng.guns.modular.system.dao.TAppUserMapper;
import com.stylefeng.guns.modular.system.dao.TDriverMapper;
import com.stylefeng.guns.modular.system.enums.OrderStateEnum;
import com.stylefeng.guns.modular.system.model.*;
import com.stylefeng.guns.modular.system.dao.TOrderCheckMapper;
import com.stylefeng.guns.modular.system.service.ITDriverService;
import com.stylefeng.guns.modular.system.service.ITOrderCheckService;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.stylefeng.guns.modular.system.service.ITSystemConfigService;
import com.stylefeng.guns.modular.system.util.HttpRequestUtil;
import com.stylefeng.guns.modular.system.util.PushURL;
import com.stylefeng.guns.modular.system.util.PushUtil;
import com.stylefeng.guns.modular.system.util.RedisUtil;
import org.springframework.data.redis.core.RedisTemplate;
import java.util.concurrent.TimeUnit;
import com.stylefeng.guns.modular.system.warpper.PushOrderInfoWarpper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
 
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
 
/**
 * <p>
 * 订单 服务实现类
 * </p>
 *
 * @author mitao
 * @since 2025-07-28
 */
@Slf4j
@Service
public class TOrderCheckServiceImpl extends ServiceImpl<TOrderCheckMapper, TOrderCheck> implements ITOrderCheckService {
 
    @Resource
    private TDriverMapper tDriverMapper;
 
    @Resource  
    private TAppUserMapper tAppUserMapper;
 
    @Resource
    private ITSystemConfigService systemConfigService;
 
    @Resource
    private ITDriverService driverService;
 
    @Resource
    private PushUtil pushUtil;
 
    @Resource
    private RedisUtil redisUtil;
    
    @Resource
    private RedisTemplate<String, String> redisTemplate;
    /**
     * 订单推送逻辑
     */
    public void pushOrder(Long orderId, Integer driverId) {
        TSystemConfig systemConfig = systemConfigService.selectOne(new EntityWrapper<TSystemConfig>().eq("type", 1));
        Integer num4 =30;
        if (null != systemConfig) {
            JSONObject jsonObject = JSON.parseObject(systemConfig.getContent());
            num4 = jsonObject.getInteger("num4");//接单时间
        }
//        TDriver driver = driverService.selectById(driverId);
//        TSystemConfig systemConfig6 = systemConfigService.selectOne(new EntityWrapper<TSystemConfig>().eq("type", 6)
//                .eq("companyType", 2)
//                .eq("companyId", driver.getBranchOfficeId()));
//        JSONObject jsonObject6 = JSON.parseObject(systemConfig6.getContent());
//        Double num1 = jsonObject6.getDouble("num1");
//        if (driver.getBalance().compareTo(new BigDecimal(num1)) < 0) {
//            throw new GunsException("司机余额不足");
//        }
        pushUtil.pushGrabCheckOrder(driverId, 2, orderId, num4);
    }
 
    @Override
    public Page<TOrderCheck> getOrderCheckList(String createTime, String code, Integer source, String userName, String userPhone, Integer state, String driverName) {
        //获取当前登录用户身份
        boolean admin = ShiroKit.isAdmin();
        Integer objectId = ShiroKit.getUser().getObjectId();
        String startTime = null;
        String endTime = null;
        // 开始,结束时间
        if(StringUtils.hasLength(createTime)){
            String[] split = createTime.split(" - ");
            startTime = split[0] + " 00:00:00";
            endTime = split[1] + " 23:59:59";
        }
        Page<TOrderCheck> page = new PageFactory<TOrderCheck>().defaultPage();
        page.setRecords(this.baseMapper.getOrderCheckList(page, startTime, endTime, code, source, userName, userPhone, state, driverName, admin, objectId));
        return page;
    }
 
    @Override
    public void cancel(Integer tOrderCheckId) {
        TOrderCheck tOrderCheck = selectById(tOrderCheckId);
        tOrderCheck.setState(OrderStateEnum.CANCELED.getCode());
        updateById(tOrderCheck);
        if(null != tOrderCheck.getDriverId()){
            redisUtil.delSetValue("orderService_check", tOrderCheck.getId().toString());
            TDriver tDriver = driverService.selectById(tOrderCheck.getDriverId());
            tDriver.setServerStatus(1);
            driverService.updateById(tDriver);
        }
 
        Map<String, String> map = new HashMap<>();
        if (tOrderCheck.getUserId()!=null){
            map.put("id", tOrderCheck.getUserId().toString());
        }
        map.put("type", "1");
        PushOrderInfoWarpper pushOrderInfoWarpper = new PushOrderInfoWarpper();
        pushOrderInfoWarpper.setId(tOrderCheck.getId().longValue());
        pushOrderInfoWarpper.setState(tOrderCheck.getState());
        pushOrderInfoWarpper.setCancelObject(3);
        map.put("pushOrderInfoWarpper", JSON.toJSONString(pushOrderInfoWarpper));
        String result = HttpRequestUtil.postRequest(PushURL.order_push_url, map);
        if(null != tOrderCheck.getDriverId()){
            map = new HashMap<>();
            map.put("id", tOrderCheck.getDriverId().toString());
            map.put("type", "1");
            PushOrderInfoWarpper pushOrderInfoWarpper1 = new PushOrderInfoWarpper();
            pushOrderInfoWarpper1.setId(tOrderCheck.getId().longValue());
            pushOrderInfoWarpper1.setState(tOrderCheck.getState());
            pushOrderInfoWarpper1.setCancelObject(3);
            map.put("pushOrderInfoWarpper", JSON.toJSONString(pushOrderInfoWarpper1));
            result = HttpRequestUtil.postRequest(PushURL.order_push_url, map);
        }
        //TODO 需要确定是否需要修改key
        redisUtil.setStrValue("cancelOrder", "true");
    }
 
    @Override
    public TOrderCheck getOrderInfo(Integer orderId) {
        // 查询订单信息
        TOrderCheck orderInfo = this.selectById(orderId);
        if (orderInfo == null) {
            throw new RuntimeException("订单不存在");
        }
 
        // 查询用户信息
        TAppUser tAppUser = tAppUserMapper.selectById(orderInfo.getUserId());
        if (Objects.nonNull(tAppUser)) {
            orderInfo.setUserName(tAppUser.getNickname());
            orderInfo.setUserPhone(tAppUser.getPhone());
        }
        if (orderInfo.getDriverId() != null) {
            // 查询司机信息
            TDriver tDriver = tDriverMapper.selectById(orderInfo.getDriverId());
            if (Objects.nonNull(tDriver)) {
                orderInfo.setDriverName(tDriver.getName());
                orderInfo.setDriverPhone(tDriver.getPhone());
            }
        }
        // 返回订单和用户信息
        return orderInfo;
    }
 
    @Override
    public Page<DriverDispatchInfoResp> getDispatchDriverList(Integer orderId, Integer dispatchType, String keyword) {
        // 确定排除的司机ID(改派时排除原司机)
        Integer excludeDriverId = null;
        if (dispatchType == 2) {
            // 查询订单信息,仅用于确定排除的司机ID
            TOrderCheck orderInfo = this.selectById(orderId);
            if (orderInfo == null) {
                throw new RuntimeException("订单不存在");
            }
            excludeDriverId = orderInfo.getDriverId();
        }
        Page<DriverDispatchInfoResp> page = new PageFactory<DriverDispatchInfoResp>().defaultPage();
        //获取当前登录信息
        boolean admin = ShiroKit.isAdmin();
        Integer branchOfficeId  = admin ? null : ShiroKit.getUser().getObjectId();
        // 查询有车检服务权限的司机列表(支持搜索)
        List<DriverDispatchInfoResp> driverList = tDriverMapper.queryCheckServiceDriversWithSearch(page,
                1, // checkServer = 1 (有车检服务权限)
                excludeDriverId,
                2, // approvalStatus = 2 (已审核通过)
                1, // status = 1 (正常状态)
                keyword,
                branchOfficeId //分公司id
        );
 
        // 统计总数(支持搜索)
        Long total = tDriverMapper.countCheckServiceDriversWithSearch(
                1, // checkServer = 1 (有车检服务权限)
                excludeDriverId,
                2, // approvalStatus = 2 (已审核通过)
                1, // status = 1 (正常状态)
                keyword
        );
 
        // 创建Page对象用于Bootstrap Table
        page.setRecords(driverList);
        page.setTotal(total);
        return page;
    }
 
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void executeDispatch(Integer orderId, Integer driverId, Integer dispatchType) {
        // 查询订单信息
        TOrderCheck orderInfo = this.selectById(orderId);
        if (orderInfo == null) {
            throw new GunsException("订单不存在");
        }
 
        // 检查订单状态是否允许派单
        if (dispatchType == 1 && orderInfo.getState() != 100) {
            throw new GunsException("订单状态不允许派单操作");
        }
        List<Integer> validStates = Lists.newArrayList(101, 102,  104);
        if (dispatchType == 2 && !validStates.contains(orderInfo.getState())) {
            throw new GunsException("订单状态不允许改派操作");
        }
        Integer oldDriverId = orderInfo.getDriverId();
        //推送订单
        pushOrder(orderId.longValue(), driverId);
        if (oldDriverId != null && dispatchType == 2) {
            pushUtil.pushCheckOrderCancel(oldDriverId, 2, orderId);
        }
        // 更新订单信息
        TOrderCheck updateOrder = new TOrderCheck();
        updateOrder.setId(orderId);
        updateOrder.setState(OrderStateEnum.PENDING_ORDER.getCode()); // 设置为待接单状态
        updateOrder.setOperatorId(ShiroKit.getUser().getId());
        updateOrder.setOperatorName(ShiroKit.getUser().getName());
        updateOrder.setDispatchTime(new Date());
        this.updateById(updateOrder);
    }
 
    @Override
    public List<TOrderCheck> getOrdersByIds(List<Integer> orderIds) {
        List<TOrderCheck> tOrderChecks = selectBatchIds(orderIds);
        if (CollUtil.isEmpty(tOrderChecks)) {
            return Collections.emptyList();
        }
        // 批量查询用户信息并设置到订单中
        List<Integer> appUserIds = tOrderChecks.stream()
                .map(TOrderCheck::getUserId)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        
        if (!appUserIds.isEmpty()) {
            Map<Integer, TAppUser> appUserMap = tAppUserMapper.selectBatchIds(appUserIds).stream()
                    .collect(Collectors.toMap(TAppUser::getId, Function.identity()));
            
            tOrderChecks.forEach(tOrderCheck -> {
                TAppUser tAppUser = appUserMap.get(tOrderCheck.getUserId());
                if (Objects.nonNull(tAppUser)) {
                    tOrderCheck.setUserName(tAppUser.getNickname());
                    tOrderCheck.setUserPhone(tAppUser.getPhone());
                }
            });
        }
        return tOrderChecks;
    }
 
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void executeBatchDispatch(List<Integer> orderIds, List<Integer> driverIds) {
        // 参数验证
        if (orderIds == null || driverIds == null || orderIds.isEmpty() || driverIds.isEmpty()) {
            throw new GunsException("订单或司机列表不能为空");
        }
        
        if (orderIds.size() != driverIds.size()) {
            throw new GunsException("订单数量与司机数量不匹配");
        }
        
        // 批量查询所有订单并验证
        List<TOrderCheck> orderList = this.selectBatchIds(orderIds);
        Map<Integer, TOrderCheck> orderMap = orderList.stream()
                .collect(Collectors.toMap(TOrderCheck::getId, Function.identity()));
        
        // 检查是否有不存在的订单
        List<Integer> missingOrderIds = orderIds.stream()
                .filter(orderId -> !orderMap.containsKey(orderId))
                .collect(Collectors.toList());
        if (!missingOrderIds.isEmpty()) {
            String missingOrderIdStr = missingOrderIds.stream().map(String::valueOf).collect(Collectors.joining(", "));
            throw new GunsException("订单ID:" + missingOrderIdStr + " 不存在");
        }
        
        // 检查订单状态
        List<Integer> invalidStateOrderIds = orderIds.stream()
                .filter(orderId -> orderMap.get(orderId).getState() != 100)
                .collect(Collectors.toList());
        if (!invalidStateOrderIds.isEmpty()) {
            String invalidStateOrderIdStr = invalidStateOrderIds.stream().map(String::valueOf).collect(Collectors.joining(", "));
            throw new GunsException("订单ID:" + invalidStateOrderIdStr + " 状态不允许派单操作");
        }
        
        // 批量查询所有司机并验证
        List<TDriver> driverList = driverService.selectBatchIds(driverIds);
        Map<Integer, TDriver> driverMap = driverList.stream()
                .collect(Collectors.toMap(TDriver::getId, Function.identity()));
        
        // 检查是否有不存在的司机
        List<Integer> missingDriverIds = driverIds.stream()
                .filter(driverId -> !driverMap.containsKey(driverId))
                .collect(Collectors.toList());
        if (!missingDriverIds.isEmpty()) {
            throw new GunsException("司机ID:" + missingDriverIds + " 不存在");
        }
        //校验司机是否为空闲状态
        driverList.stream().filter(item-> item.getServerStatus() != 1).findFirst().ifPresent(item -> {
            throw new GunsException("司机" + item.getName() + "正在服务中,不允许派单");
        });
        // 随机打乱司机列表,实现随机匹配
        List<Integer> shuffledDriverIds = new ArrayList<>(driverIds);
        Collections.shuffle(shuffledDriverIds);
        
        // 逐个执行派单操作
        for (int i = 0; i < orderIds.size(); i++) {
            Integer orderId = orderIds.get(i);
            Integer driverId = shuffledDriverIds.get(i);
            
            try {
                // 复用现有的派单逻辑,dispatchType=1 表示派单操作
                executeDispatch(orderId, driverId, 1);
            } catch (Exception e) {
                log.error("批量派单失败 - 订单ID: {}, 司机ID: {}, 错误: {}", orderId, driverId, e.getMessage());
                throw new GunsException("派单失败:订单ID " + orderId + " 派给司机ID " + driverId + " 时出错:" + e.getMessage());
            }
        }
    }
 
    @Override
    public void addOrder(Integer userId, String startAddress, String endAddress, String reservationTime) {
        // 102=已接单(正在前往取车地),104=到达预约点(用户待支付车检),105=开始服务,106=车检完成,107=前往还车地,108=到达还车地
        List<Integer> state = Arrays.asList(101, 102, 104, 105, 106, 107, 107, 108);
        TOrderCheck order = this.selectOne(new EntityWrapper<TOrderCheck>().eq("userId", userId).eq("status", 1).in("state", state));
        if (null != order) {
            throw new GunsException("该用户还有正在进行的订单");
        }
        // 参数验证
        if (StringUtils.isEmpty(startAddress)) {
            throw new GunsException("取车位置不能为空");
        }
        if (StringUtils.isEmpty(endAddress)) {
            throw new GunsException("还车地址不能为空");
        }
        if (StringUtils.isEmpty(reservationTime)) {
            throw new GunsException("预约时间不能为空");
        }
 
        try {
            // 创建订单对象
            order = new TOrderCheck();
            order.setUserId(userId);
 
            // 生成订单编号(使用时间戳+随机数)
            order.setCode(UUIDUtil.getTimeStr() + UUIDUtil.getNumberRandom(5));
 
            // 设置基本信息
            order.setStartAddress(startAddress);
            order.setEndAddress(endAddress);
 
            // 解析预约时间
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date reservationDate = sdf.parse(reservationTime);
            order.setReservationTime(reservationDate);
 
            // 设置订单状态为待派单
            order.setState(100);
 
            // 设置订单来源为管理后台
            order.setSource(2);
 
            // 设置创建时间
            order.setCreateTime(new Date());
 
            // 设置默认状态(正常)
            order.setStatus(1);
            //设置分公司id
            order.setBranchOfficeId(ShiroKit.getUser().getObjectId());
            //处理费用
            handleOrderPrice(order.getBranchOfficeId(), order);
            // 保存订单
            this.insert(order);
            
            // 订单创建成功后,添加到Redis通知队列
            addCheckCarOrderNotification(order.getId(), order.getBranchOfficeId());
 
        } catch (ParseException e) {
            throw new GunsException("预约时间格式错误,请使用正确的时间格式");
        } catch (Exception e) {
            log.error("新建订单失败: {}", e.getMessage(), e);
            throw new GunsException("服务器开小差啦");
        }
    }
 
    public void handleOrderPrice(Integer branchOfficeId, TOrderCheck order){
        TSystemConfig systemConfig = systemConfigService.selectOne(new EntityWrapper<TSystemConfig>()
                .eq("type", 5).eq("companyId", branchOfficeId));
        if (Objects.isNull(systemConfig)) {
            throw new GunsException("分公司未配置代检车费用,请联系管理员");
        }
        JSONObject jsonObject = JSON.parseObject(systemConfig.getContent());
        JSONObject extraCost = jsonObject.getJSONObject("ExtraCost");
 
        Double num11 = extraCost.getDouble("num11");//车检费
        Double num12 = extraCost.getDouble("num12");//服务费
 
        // TODO 计算分公司抽成
        if (num11 != null) {
            order.setCheckMoney(new BigDecimal(num11));
        }
        if (num12 != null) {
            order.setPayMoney(new BigDecimal(num12));
        }
    }
 
    @Override
    public List<TOrderCheck> exportOrderCheckList(String createTime, String code, Integer source,
                                                 String userName, String userPhone, Integer state, String driverName) {
        //获取当前登录用户身份
        boolean admin = ShiroKit.isAdmin();
        Integer objectId = ShiroKit.getUser().getObjectId();
        String startTime = null;
        String endTime = null;
        // 开始,结束时间
        if(StringUtils.hasLength(createTime)){
            String[] split = createTime.split(" - ");
            startTime = split[0] + " 00:00:00";
            endTime = split[1] + " 23:59:59";
        }
 
        List<TOrderCheck> allResults = new ArrayList<>();
        int pageNumber = 1;
        int pageSize = 1000;
        
        while (true) {
            // 创建自定义分页对象
            Page<TOrderCheck> page = new Page<>();
            page.setCurrent(pageNumber);
            page.setSize(pageSize);
            
            // 调用现有查询方法
            List<TOrderCheck> pageResults = this.baseMapper.getOrderCheckList(page, startTime, endTime, code, source, userName, userPhone, state, driverName, admin, objectId);
            
            // 如果当前页没有数据,跳出循环
            if (pageResults == null || pageResults.isEmpty()) {
                break;
            }
            
            // 将当前页数据添加到总结果中
            allResults.addAll(pageResults);
            
            // 如果当前页数据少于页大小,说明已经是最后一页
            if (pageResults.size() < pageSize) {
                break;
            }
            
            // 准备查询下一页
            pageNumber++;
        }
        
        return allResults;
    }
 
    /**
     * 添加代检车新订单通知到Redis
     * @param orderId 订单ID
     * @param branchOfficeId 分公司ID
     */
    private void addCheckCarOrderNotification(Integer orderId, Integer branchOfficeId) {
        try {
            String orderIdStr = orderId.toString();
            
            // 添加到平台通知队列(使用Redis List)
            String platformKey = "newCheckCarOrder:platform";
            List<String> existingPlatformOrders = redisTemplate.opsForList().range(platformKey, 0, -1);
            
            // 检查是否已存在该订单ID,避免重复
            if (existingPlatformOrders == null || !existingPlatformOrders.contains(orderIdStr)) {
                // 添加到列表左侧
                redisTemplate.opsForList().leftPush(platformKey, orderIdStr);
                // 设置过期时间24小时
                redisTemplate.expire(platformKey, 24, TimeUnit.HOURS);
            }
            
            // 添加到分公司通知队列(使用Redis List)
            String branchKey = "newCheckCarOrder:branch:" + branchOfficeId;
            List<String> existingBranchOrders = redisTemplate.opsForList().range(branchKey, 0, -1);
            
            // 检查是否已存在该订单ID,避免重复
            if (existingBranchOrders == null || !existingBranchOrders.contains(orderIdStr)) {
                // 添加到列表左侧
                redisTemplate.opsForList().leftPush(branchKey, orderIdStr);
                // 设置过期时间24小时
                redisTemplate.expire(branchKey, 24, TimeUnit.HOURS);
            }
            
            log.info("代检车新订单通知已添加到Redis List: 订单ID={}, 分公司ID={}", orderId, branchOfficeId);
            
        } catch (Exception e) {
            log.error("添加代检车新订单通知到Redis失败: 订单ID={}, 分公司ID={}, 错误: {}", orderId, branchOfficeId, e.getMessage(), e);
            // 通知失败不影响主流程,只记录日志
        }
    }
}