liujie
2026-04-18 be13b366461b31449c1488e495d7edcd25a22823
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
package com.xiaopeng.maintenance.service.impl;
 
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xiaopeng.api.model.maintenance.XpCheckLocation;
import com.xiaopeng.api.model.maintenance.XpCruiseCheckInRecord;
import com.xiaopeng.api.model.maintenance.XpCruiseCheckInRule;
import com.xiaopeng.api.model.maintenance.XpCruiseCheckInRuleLocation;
import com.xiaopeng.common.core.exception.ServiceException;
import com.xiaopeng.common.core.utils.GeodesyUtil;
import com.xiaopeng.maintenance.constant.I18nKeyConstant;
import com.xiaopeng.maintenance.mapper.XpCruiseCheckInRuleMapper;
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 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.stream.Collectors;
 
 
@Service
public class XpCruiseCheckInRuleServiceImpl extends ServiceImpl<XpCruiseCheckInRuleMapper, XpCruiseCheckInRule> implements IXpCruiseCheckInRuleService {
 
    @Resource
    private IXpCruiseCheckInRecordService xpCruiseCheckInRecordService;
 
    @Resource
    private IXpCruiseCheckInRuleLocationService xpCruiseCheckInRuleLocationService;
 
    @Resource
    private IXpCheckLocationService xpCheckLocationService;
 
 
 
    @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 void 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);
    }
 
 
 
    /**
     * 运维端实时上传位置信息,返回提醒消息
     * @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();
        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.setMessageType(1);
            vo.setCheckLocationId(cruiseCheckInRuleLocation.getCheckLocationId());
            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(2);
            vo.setCheckLocationId(cruiseCheckInRuleLocation.getCheckLocationId());
            vo.setMessage(time > 10 * 50 ? "距离打卡超时还有10分钟,请尽快前往打卡点" : "距离打卡超时还有3分钟,请尽快前往打卡点");
            return vo;
        }
        return null;
    }
 
 
    /**
     * 获取巡游打卡详情
     * @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())"));
    }
}