xuhy
2026-04-24 080254c8bcca7efd95d67c783694fb2da3d0497f
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
package com.xiaopeng.maintenance.service.impl;
 
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xiaopeng.api.feignClient.account.XpOperationUserClient;
import com.xiaopeng.api.feignClient.account.XpOperationsUserClient;
import com.xiaopeng.api.model.account.XpOperationsUser;
import com.xiaopeng.api.model.maintenance.*;
import com.xiaopeng.common.core.exception.ServiceException;
import com.xiaopeng.common.core.utils.GeodesyUtil;
import com.xiaopeng.common.core.utils.StringUtils;
import com.xiaopeng.common.redis.service.RedisService;
import com.xiaopeng.maintenance.constant.I18nKeyConstant;
import com.xiaopeng.maintenance.dto.XpCruiseCheckInRuleDto;
import com.xiaopeng.maintenance.dto.XpCruiseCheckInRuleListPageDto;
import com.xiaopeng.maintenance.mapper.XpCruiseCheckInRuleMapper;
import com.xiaopeng.maintenance.mapper.XpCruiseCheckInRuleOperationsUserMapper;
import com.xiaopeng.maintenance.model.vo.AppXpCruiseCheckInLocationVo;
import com.xiaopeng.maintenance.model.vo.AppXpCruiseCheckInRuleVo;
import com.xiaopeng.maintenance.model.vo.CheckInMessageVo;
import com.xiaopeng.maintenance.model.vo.RealTimePositionVo;
import com.xiaopeng.maintenance.service.IXpCheckLocationService;
import com.xiaopeng.maintenance.service.IXpCruiseCheckInRecordService;
import com.xiaopeng.maintenance.service.IXpCruiseCheckInRuleLocationService;
import com.xiaopeng.maintenance.service.IXpCruiseCheckInRuleService;
import com.xiaopeng.maintenance.vo.CruiseCheckInLocationVo;
import com.xiaopeng.maintenance.vo.XpCruiseCheckInRuleInfoVo;
import com.xiaopeng.maintenance.vo.XpCruiseCheckInRuleListPage;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
 
 
@Service
public class XpCruiseCheckInRuleServiceImpl extends ServiceImpl<XpCruiseCheckInRuleMapper, XpCruiseCheckInRule> implements IXpCruiseCheckInRuleService {
 
    @Resource
    private IXpCruiseCheckInRecordService xpCruiseCheckInRecordService;
 
    @Resource
    private IXpCruiseCheckInRuleLocationService xpCruiseCheckInRuleLocationService;
 
    @Resource
    private IXpCheckLocationService xpCheckLocationService;
    @Autowired
    private RedisService redisService;
 
    @Resource
    private XpOperationUserClient xpOperationsUserClient;
 
    @Resource
    private XpCruiseCheckInRuleOperationsUserMapper xpCruiseCheckInRuleOperationsUserMapper;
 
 
 
 
    @Override
    public AppXpCruiseCheckInRuleVo getMyXpCruiseCheckInRule(Long uid) {
        List<XpCruiseCheckInRule> list = this.list(new LambdaQueryWrapper<XpCruiseCheckInRule>().eq(XpCruiseCheckInRule::getDeleted, 0)
                .last(" and (now() BETWEEN start_time and end_time or cyclic_mode = 1)"));
        if(list.isEmpty()){
            return null;
        }
        XpCruiseCheckInRule xpCruiseCheckInRule = list.get(0);
 
        AppXpCruiseCheckInRuleVo vo = new AppXpCruiseCheckInRuleVo();
        vo.setId(xpCruiseCheckInRule.getId());
        vo.setCruiseCheckInTaskTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + " "
                + xpCruiseCheckInRule.getStartTime().format(DateTimeFormatter.ofPattern("HH:mm")) + "~"
                + xpCruiseCheckInRule.getEndTime().format(DateTimeFormatter.ofPattern("HH:mm")));
        long count1 = xpCruiseCheckInRuleLocationService.count(new LambdaQueryWrapper<XpCruiseCheckInRuleLocation>()
                .eq(XpCruiseCheckInRuleLocation::getCruiseCheckInRuleId, xpCruiseCheckInRule.getId()));
        long count = xpCruiseCheckInRecordService.count(new LambdaQueryWrapper<XpCruiseCheckInRecord>()
                .eq(XpCruiseCheckInRecord::getCruiseCheckInRuleId, xpCruiseCheckInRule.getId())
                .eq(XpCruiseCheckInRecord::getDeleted, 0).eq(XpCruiseCheckInRecord::getOperationsUserId, uid));
        vo.setCheckInPointCount(count1);
        vo.setCheckInPointFinishCount(count);
        vo.setCheckInPointUnFinishCount(count1 - count);
        return vo;
    }
 
 
    /**
     * 获取APP端签到规则位置信息
     * @param ruleId
     * @param uid
     * @return
     */
    @Override
    public List<AppXpCruiseCheckInLocationVo> getAppXpCruiseCheckInLocation(Integer ruleId, Long uid) {
        //查询巡游打卡规则和点位关系数据
        List<XpCruiseCheckInRuleLocation> list = xpCruiseCheckInRuleLocationService.list(new LambdaQueryWrapper<XpCruiseCheckInRuleLocation>()
                .eq(XpCruiseCheckInRuleLocation::getCruiseCheckInRuleId, ruleId).orderByAsc(XpCruiseCheckInRuleLocation::getSort));
        //查询打卡点位数据
        List<XpCheckLocation> xpCheckLocations = xpCheckLocationService.listByIds(list.stream().map(XpCruiseCheckInRuleLocation::getCheckLocationId).collect(Collectors.toList()));
        List<AppXpCruiseCheckInLocationVo> appXpCruiseCheckInLocationVos = new ArrayList<>();
        //获取运维人员打卡记录
        List<XpCruiseCheckInRecord> list1 = xpCruiseCheckInRecordService.list(new LambdaQueryWrapper<XpCruiseCheckInRecord>()
                .eq(XpCruiseCheckInRecord::getDeleted, 0)
                .eq(XpCruiseCheckInRecord::getCruiseCheckInRuleId, ruleId)
                .eq(XpCruiseCheckInRecord::getOperationsUserId, uid)
                .last(" and DATE(check_in_time) = DATE(NOW())"));
        //构建返回结果
        for (XpCruiseCheckInRuleLocation xpCruiseCheckInRuleLocation : list) {
            XpCruiseCheckInRecord xpCruiseCheckInRecord = list1.stream().filter(x -> x.getCheckLocationId().equals(xpCruiseCheckInRuleLocation.getCheckLocationId())).findFirst().orElse(null);
            XpCheckLocation xpCheckLocation = xpCheckLocations.stream().filter(x -> x.getId().equals(xpCruiseCheckInRuleLocation.getCheckLocationId())).findFirst().orElse(null);
            AppXpCruiseCheckInLocationVo vo = new AppXpCruiseCheckInLocationVo();
            vo.setLocationId(xpCruiseCheckInRuleLocation.getCheckLocationId());
            vo.setTime(xpCruiseCheckInRuleLocation.getStartTime() + "-" + xpCruiseCheckInRuleLocation.getEndTime());
            vo.setCheckIn(0);
            if(null != xpCruiseCheckInRecord){
                vo.setCheckInTime(xpCruiseCheckInRecord.getCheckInTime().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
                vo.setCheckIn(1);
                vo.setCheckInResult(xpCruiseCheckInRecord.getCheckInResult());
            }
            vo.setLon(xpCheckLocation.getLocationLon());
            vo.setLat(xpCheckLocation.getLocationLat());
            vo.setAddress(xpCheckLocation.getLocationAddress());
            appXpCruiseCheckInLocationVos.add(vo);
        }
        return appXpCruiseCheckInLocationVos;
    }
 
 
    /**
     * 运维人员巡游打卡操作
     * @param dto
     */
    @Override
    public Long saveCruiseCheckInRecord(XpCruiseCheckInRecord dto) {
        Long checkLocationId = dto.getCheckLocationId();
        XpCruiseCheckInRuleLocation one = xpCruiseCheckInRuleLocationService.getOne(new LambdaQueryWrapper<XpCruiseCheckInRuleLocation>()
                .eq(XpCruiseCheckInRuleLocation::getCheckLocationId, checkLocationId));
        if(null == one){
            throw new ServiceException("打卡点位不存在", I18nKeyConstant.CHECK_IN_LOCATION_DOES_NOT_EXIST);
        }
        XpCruiseCheckInRecord cruiseCheckInRecord = xpCruiseCheckInRecordService.getOne(new LambdaQueryWrapper<XpCruiseCheckInRecord>()
                .eq(XpCruiseCheckInRecord::getCruiseCheckInRuleId, one.getCruiseCheckInRuleId())
                .eq(XpCruiseCheckInRecord::getDeleted, 0)
                .eq(XpCruiseCheckInRecord::getCheckLocationId, checkLocationId)
                .eq(XpCruiseCheckInRecord::getOperationsUserId, dto.getOperationsUserId())
                .last(" and DATE(check_in_time) = DATE(NOW())"));
        if(null != cruiseCheckInRecord){
            throw new ServiceException("不能重复打卡", I18nKeyConstant.CANNOT_REPEAT_CLOCKING_IN);
        }
        dto.setCruiseCheckInRuleId(one.getCruiseCheckInRuleId());
        dto.setCheckInTime(LocalDateTime.now());
        dto.setCheckInResult(1);
        //判断当前打卡时间是否超过限定时间
        Integer endTime = Integer.valueOf(one.getEndTime().replace(":", ""));
        Integer nowTime = Integer.valueOf(LocalDateTime.now().format(DateTimeFormatter.ofPattern("HHmm")));
        if(nowTime > endTime){
            dto.setCheckInResult(2);
        }
        dto.setDeleted(false);
        xpCruiseCheckInRecordService.save(dto);
        return dto.getId();
    }
 
 
 
    /**
     * 运维端实时上传位置信息,返回提醒消息
     * @param dto
     * @return
     */
    @Override
    public CheckInMessageVo uploadRealTimePosition(RealTimePositionVo dto, Long uid) {
        List<XpCruiseCheckInRuleLocation> list = xpCruiseCheckInRuleLocationService.list(new LambdaQueryWrapper<XpCruiseCheckInRuleLocation>()
                .eq(XpCruiseCheckInRuleLocation::getCruiseCheckInRuleId, dto.getId())
                .orderByAsc(XpCruiseCheckInRuleLocation::getSort));
        if(list.isEmpty()){
            return null;
        }
        //查找即将需要到达的打卡点位
        XpCruiseCheckInRecord cruiseCheckInRecord = xpCruiseCheckInRecordService.getOne(new LambdaQueryWrapper<XpCruiseCheckInRecord>()
                .eq(XpCruiseCheckInRecord::getCruiseCheckInRuleId, dto.getId())
                .eq(XpCruiseCheckInRecord::getDeleted, 0)
                .eq(XpCruiseCheckInRecord::getOperationsUserId, uid)
                .last(" and DATE(check_in_time) = DATE(NOW()) order by check_in_time desc limit 1"));
        XpCruiseCheckInRuleLocation cruiseCheckInRuleLocation = null;
        //没有打卡的情况,用第一个打卡点位
        if(null == cruiseCheckInRecord){
            cruiseCheckInRuleLocation = list.get(0);
        }else{
            Long checkLocationId = cruiseCheckInRecord.getCheckLocationId();
            for (int i = 0; i < list.size(); i++) {
                Long checkLocationId1 = list.get(i).getCheckLocationId();
                if(checkLocationId.equals(checkLocationId1) && i < list.size() - 1){
                    cruiseCheckInRuleLocation = list.get(i + 1);
                    break;
                }
            }
        }
        //没有需要打卡的点位,则返回空数据
        if(null == cruiseCheckInRuleLocation){
            return null;
        }
 
        //先判断当前定位是否到达打卡点位附近,在范围内则返回打卡消息
        XpCheckLocation xpCheckLocation = xpCheckLocationService.getById(cruiseCheckInRuleLocation.getCheckLocationId());
        if(null == xpCheckLocation){
            return null;
        }
 
        CheckInMessageVo vo = new CheckInMessageVo();
        vo.setIsInRange(false);
        Map<String, Double> distance = GeodesyUtil.getDistance(xpCheckLocation.getLocationLon() + "," + xpCheckLocation.getLocationLat(), dto.getLongitude() + "," + dto.getLatitude());
        if(xpCheckLocation.getLocationRange().compareTo(new BigDecimal(distance.get("WGS84"))) >= 0){
            vo.setIsInRange(true);
            Boolean read = redisService.getCacheObject("CHECK_IN_SCOPE:" + xpCheckLocation.getId());
            if(null == read || !read){
                vo.setMessageType(2);
                vo.setCheckLocationId(cruiseCheckInRuleLocation.getCheckLocationId());
                redisService.setCacheObject("CHECK_IN_SCOPE:" + xpCheckLocation.getId(), true, 1L, TimeUnit.HOURS);
            }
            return vo;
        }
        //判断时间小于10分钟首次触发和小于3分钟再次触发
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime endTime = now.withHour(Integer.parseInt(cruiseCheckInRuleLocation.getEndTime().split(":")[0]))
                .withMinute(Integer.parseInt(cruiseCheckInRuleLocation.getEndTime().split(":")[1]))
                .withSecond(0);
        long time = endTime.toEpochSecond(ZoneOffset.of("+8")) - now.toEpochSecond(ZoneOffset.of("+8"));
        if((time <= 10 * 60 && time > 10 * 50) || (time <= 3 * 60 && time > 3 * 50)){
            vo.setMessageType(1);
            vo.setCheckLocationId(cruiseCheckInRuleLocation.getCheckLocationId());
            vo.setMessage(time > 10 * 50 ? "距离打卡超时还有10分钟,请尽快前往打卡点" : "距离打卡超时还有3分钟,请尽快前往打卡点");
            return vo;
        }
        return vo;
    }
 
 
    /**
     * 获取巡游打卡详情
     * @param ruleId
     * @param locationId
     * @param uid
     * @return
     */
    @Override
    public XpCruiseCheckInRecord getCruiseCheckInRecordInfo(Integer ruleId, Long locationId, Long uid) {
        return xpCruiseCheckInRecordService.getOne(new LambdaQueryWrapper<XpCruiseCheckInRecord>()
                .eq(XpCruiseCheckInRecord::getCruiseCheckInRuleId, ruleId)
                .eq(XpCruiseCheckInRecord::getCheckLocationId, locationId)
                .eq(XpCruiseCheckInRecord::getDeleted, 0)
                .eq(XpCruiseCheckInRecord::getOperationsUserId, uid)
                .last(" and DATE(check_in_time) = DATE(NOW())"));
    }
 
 
    /**
     * 获取巡游打卡规则列表
     * @param pageInfo
     * @param dto
     * @return
     */
    @Override
    public List<XpCruiseCheckInRuleListPage> getXpCruiseCheckInRuleListPage(Page<XpCruiseCheckInRuleListPage> pageInfo, XpCruiseCheckInRuleListPageDto dto) {
        List<Long> ids = new ArrayList<>();
        if(StringUtils.isNotEmpty(dto.getUserName())){
            List<XpOperationsUser> data = xpOperationsUserClient.listByName(dto.getUserName()).getData();
            ids.addAll(data.stream().map(XpOperationsUser::getId).collect(Collectors.toList()));
        }
        if(StringUtils.isNotEmpty(dto.getPhoneNumber())){
            List<XpOperationsUser> data = xpOperationsUserClient.getByPhoneNumber(dto.getPhoneNumber()).getData();
            ids.addAll(data.stream().map(XpOperationsUser::getId).collect(Collectors.toList()));
        }
        if(!ids.isEmpty()){
            List<XpCruiseCheckInRuleOperationsUser> operationsUserId = xpCruiseCheckInRuleOperationsUserMapper.selectList(new QueryWrapper<XpCruiseCheckInRuleOperationsUser>().in("operations_user_id", ids));
            List<Integer> ruleIds = operationsUserId.stream().map(XpCruiseCheckInRuleOperationsUser::getCruiseCheckInRuleId).collect(Collectors.toList());
            dto.setIds(ruleIds);
        }
        List<XpCruiseCheckInRuleListPage> xpCruiseCheckInRuleListPage = this.baseMapper.getXpCruiseCheckInRuleListPage(pageInfo, dto);
        return xpCruiseCheckInRuleListPage;
    }
 
 
    /**
     * 新增巡游打卡规则
     * @param dto
     */
    @Override
    public void addXpCruiseCheckInRule(XpCruiseCheckInRuleDto dto) {
        XpCruiseCheckInRule one = this.getOne(new QueryWrapper<XpCruiseCheckInRule>().like("name", dto.getName()).eq("is_deleted", 0));
        if(null != one){
            throw new ServiceException("巡游打卡规则名称已存在");
        }
        XpCruiseCheckInRule xpCruiseCheckInRule = new XpCruiseCheckInRule();
        BeanUtils.copyProperties(dto, xpCruiseCheckInRule);
        xpCruiseCheckInRule.setDeleted(false);
        this.save(xpCruiseCheckInRule);
        dto.getPoints().forEach(s->{
            s.setCruiseCheckInRuleId(xpCruiseCheckInRule.getId());
        });
        xpCruiseCheckInRuleLocationService.saveBatch(dto.getPoints());
        Long relationId = dto.getRelationId();
 
        XpCruiseCheckInRuleOperationsUser xpCruiseCheckInRuleOperationsUser = new XpCruiseCheckInRuleOperationsUser();
        xpCruiseCheckInRuleOperationsUser.setCruiseCheckInRuleId(xpCruiseCheckInRule.getId());
        xpCruiseCheckInRuleOperationsUser.setOperationsUserId(relationId);
        xpCruiseCheckInRuleOperationsUserMapper.insert(xpCruiseCheckInRuleOperationsUser);
    }
 
 
    /**
     * 获取打卡规则详情
     * @param id
     * @return
     */
    @Override
    public XpCruiseCheckInRuleInfoVo getXpCruiseCheckInRuleInfo(Integer id) {
        XpCruiseCheckInRuleInfoVo info = new XpCruiseCheckInRuleInfoVo();
        XpCruiseCheckInRule byId = this.getById(id);
        BeanUtils.copyProperties(byId, info);
        List<XpCruiseCheckInRuleLocation> list = xpCruiseCheckInRuleLocationService.list(new QueryWrapper<XpCruiseCheckInRuleLocation>().eq("cruise_check_in_rule_id", id));
        List<XpCheckLocation> xpCheckLocations = xpCheckLocationService.listByIds(list.stream().map(XpCruiseCheckInRuleLocation::getCheckLocationId).collect(Collectors.toList()));
        List<CruiseCheckInLocationVo> collect = list.stream().map(locationList -> {
            CruiseCheckInLocationVo vo = new CruiseCheckInLocationVo();
            vo.setAddress(xpCheckLocations.stream().filter(xpCheckLocation -> xpCheckLocation.getId().equals(locationList.getCheckLocationId())).findFirst().get().getLocationAddress());
            vo.setCheckRange(xpCheckLocations.stream().filter(xpCheckLocation -> xpCheckLocation.getId().equals(locationList.getCheckLocationId())).findFirst().get().getLocationRange());
            vo.setCheckTime(StringUtils.isNotEmpty(locationList.getStartTime()) ? locationList.getStartTime() + "~" + locationList.getEndTime() : "不限");
            return vo;
 
        }).collect(Collectors.toList());
        info.setLocationList(collect);
        return info;
    }
}