Commit 2527b568c7bf386cd4dfa21cd1c203ffa92b265f
1 parent
e0c2df32
feat(inspection): 添加巡检二维码打卡功能支持
- 新增 ServiceCmdEventPublishing 导入实现CMD注册表fallback机制 - 修改鉴权逻辑支持Redis缓存未命中时从CMD注册表验证服务权限 - 实现二维码巡检打卡接口无需登录认证的访问控制 - 更新巡检任务详情接口支持通过员工编号查询并返回完整打卡信息 - 添加GPS坐标校验确保打卡位置有效性 - 实现二维码打卡距离校验防止远程作弊(限制2公里内) - 集成考勤记录同步功能使打卡数据在考勤页面可见 - 优化CMD调度支持服务未缓存时的直接命令处理机制
Showing
4 changed files
with
164 additions
and
38 deletions
service-api/src/main/java/com/java110/api/configuration/ServiceConfiguration.java
| @@ -142,7 +142,11 @@ public class ServiceConfiguration { | @@ -142,7 +142,11 @@ public class ServiceConfiguration { | ||
| 142 | 142 | ||
| 143 | 143 | ||
| 144 | 144 | ||
| 145 | - exclusions.append("/app/reportInfoAnswer/queryReportInfoAnswerByOpenId"); | 145 | + exclusions.append("/app/reportInfoAnswer/queryReportInfoAnswerByOpenId,"); |
| 146 | + // 二维码巡检打卡接口(无需登录) | ||
| 147 | + exclusions.append("/app/inspection.queryStaffTodayTasks,"); | ||
| 148 | + exclusions.append("/app/inspectionTaskDetail.updateInspectionTaskDetail,"); | ||
| 149 | + exclusions.append("/callComponent/upload/uploadFile/uploadImage,"); | ||
| 146 | 150 | ||
| 147 | 151 | ||
| 148 | final FilterRegistrationBean registrationBean = new FilterRegistrationBean(); | 152 | final FilterRegistrationBean registrationBean = new FilterRegistrationBean(); |
service-api/src/main/java/com/java110/api/smo/impl/ApiServiceSMOImpl.java
| @@ -8,6 +8,7 @@ import com.java110.core.client.RestTemplate; | @@ -8,6 +8,7 @@ import com.java110.core.client.RestTemplate; | ||
| 8 | import com.java110.core.context.ApiDataFlow; | 8 | import com.java110.core.context.ApiDataFlow; |
| 9 | import com.java110.core.context.DataFlow; | 9 | import com.java110.core.context.DataFlow; |
| 10 | import com.java110.core.context.Environment; | 10 | import com.java110.core.context.Environment; |
| 11 | +import com.java110.core.event.cmd.ServiceCmdEventPublishing; | ||
| 11 | import com.java110.core.event.service.api.ServiceDataFlowEventPublishing; | 12 | import com.java110.core.event.service.api.ServiceDataFlowEventPublishing; |
| 12 | import com.java110.core.factory.AuthenticationFactory; | 13 | import com.java110.core.factory.AuthenticationFactory; |
| 13 | import com.java110.core.factory.DataFlowFactory; | 14 | import com.java110.core.factory.DataFlowFactory; |
| @@ -339,13 +340,18 @@ public class ApiServiceSMOImpl extends LoggerEngine implements IApiServiceSMO { | @@ -339,13 +340,18 @@ public class ApiServiceSMOImpl extends LoggerEngine implements IApiServiceSMO { | ||
| 339 | 340 | ||
| 340 | 341 | ||
| 341 | //todo 判断 AppId 是否有权限操作相应的服务 | 342 | //todo 判断 AppId 是否有权限操作相应的服务 |
| 342 | - AppService appService = DataFlowFactory.getService(dataFlow, dataFlow.getRequestHeaders().get(CommonConstant.HTTP_SERVICE)); | 343 | + String serviceCode = dataFlow.getRequestHeaders().get(CommonConstant.HTTP_SERVICE); |
| 344 | + AppService appService = DataFlowFactory.getService(dataFlow, serviceCode); | ||
| 343 | 345 | ||
| 344 | - //这里调用缓存 查询缓存信息 | 346 | + //这里调用缓存 查询缓存信息,如果缓存中没有则fallback到Cmd扫描注册表 |
| 345 | if (appService == null || !CommonConstant.HTTP_SERVICE_API.equals(appService.getBusinessTypeCd())) { | 347 | if (appService == null || !CommonConstant.HTTP_SERVICE_API.equals(appService.getBusinessTypeCd())) { |
| 346 | - //添加耗时 | ||
| 347 | - DataFlowFactory.addCostTime(dataFlow, "judgeAuthority", "鉴权耗时", startDate); | ||
| 348 | - throw new NoAuthorityException(ResponseConstant.RESULT_CODE_NO_AUTHORITY_ERROR, "AppId 没有权限访问 serviceCode = " + dataFlow.getRequestHeaders().get(CommonConstant.HTTP_SERVICE)); | 348 | + // fallback: 检查是否为通过@Java110Cmd注解注册的服务(代码级注册,可能未同步到Redis) |
| 349 | + if (ServiceCmdEventPublishing.getListeners(serviceCode).size() > 0) { | ||
| 350 | + logger.debug("服务 {} 从Cmd注册表验证通过(Redis缓存未命中或businessType不匹配)", serviceCode); | ||
| 351 | + } else { | ||
| 352 | + DataFlowFactory.addCostTime(dataFlow, "judgeAuthority", "鉴权耗时", startDate); | ||
| 353 | + throw new NoAuthorityException(ResponseConstant.RESULT_CODE_NO_AUTHORITY_ERROR, "AppId 没有权限访问 serviceCode = " + serviceCode); | ||
| 354 | + } | ||
| 349 | } | 355 | } |
| 350 | 356 | ||
| 351 | 357 | ||
| @@ -379,6 +385,16 @@ public class ApiServiceSMOImpl extends LoggerEngine implements IApiServiceSMO { | @@ -379,6 +385,16 @@ public class ApiServiceSMOImpl extends LoggerEngine implements IApiServiceSMO { | ||
| 379 | Date startDate = DateUtil.getCurrentDate(); | 385 | Date startDate = DateUtil.getCurrentDate(); |
| 380 | //todo 拿到当前服务 | 386 | //todo 拿到当前服务 |
| 381 | AppService appService = DataFlowFactory.getService(dataFlow, dataFlow.getRequestHeaders().get(CommonConstant.HTTP_SERVICE)); | 387 | AppService appService = DataFlowFactory.getService(dataFlow, dataFlow.getRequestHeaders().get(CommonConstant.HTTP_SERVICE)); |
| 388 | + if (appService == null) { | ||
| 389 | + // fallback: Redis未缓存但在Cmd注册表中的服务,直接CMD调度 | ||
| 390 | + String svc = dataFlow.getRequestHeaders().get(CommonConstant.HTTP_SERVICE); | ||
| 391 | + if (ServiceCmdEventPublishing.getListeners(svc).size() > 0) { | ||
| 392 | + dealCmd(null, dataFlow, dataFlow.getReqJson()); | ||
| 393 | + DataFlowFactory.addCostTime(dataFlow, "invokeBusinessSystem", "调用下游系统耗时", startDate); | ||
| 394 | + return; | ||
| 395 | + } | ||
| 396 | + throw new BusinessException(ResponseConstant.RESULT_CODE_ERROR, "服务不存在:" + svc); | ||
| 397 | + } | ||
| 382 | //todo 这里对透传类处理,目前很少用到,可以不用关注 | 398 | //todo 这里对透传类处理,目前很少用到,可以不用关注 |
| 383 | if ("NT".equals(appService.getIsInstance())) { | 399 | if ("NT".equals(appService.getIsInstance())) { |
| 384 | //如果是透传类 请求方式必须与接口提供方调用方式一致 | 400 | //如果是透传类 请求方式必须与接口提供方调用方式一致 |
| @@ -399,10 +415,9 @@ public class ApiServiceSMOImpl extends LoggerEngine implements IApiServiceSMO { | @@ -399,10 +415,9 @@ public class ApiServiceSMOImpl extends LoggerEngine implements IApiServiceSMO { | ||
| 399 | } | 415 | } |
| 400 | doTransfer(appService, dataFlow, dataFlow.getReqJson()); | 416 | doTransfer(appService, dataFlow, dataFlow.getReqJson()); |
| 401 | return; | 417 | return; |
| 402 | - } else if ("CMD".equals(appService.getIsInstance())) { // todo 微服务调用方式,目前主要用这种方式调度分发 到不同的微服务,这里是通过c_service 中配置 调用到不同的微服务 | ||
| 403 | - //如果是透传类 请求方式必须与接口提供方调用方式一致 | 418 | + } else if ("CMD".equals(appService.getIsInstance())) { // todo 微服务调用方式 |
| 404 | String httpMethod = dataFlow.getRequestCurrentHeaders().get(CommonConstant.HTTP_METHOD); | 419 | String httpMethod = dataFlow.getRequestCurrentHeaders().get(CommonConstant.HTTP_METHOD); |
| 405 | - if (!appService.getMethod().equals(httpMethod)) { | 420 | + if (!StringUtils.isEmpty(appService.getMethod()) && !appService.getMethod().equals(httpMethod)) { |
| 406 | throw new ListenerExecuteException(ResponseConstant.RESULT_CODE_ERROR, | 421 | throw new ListenerExecuteException(ResponseConstant.RESULT_CODE_ERROR, |
| 407 | "服务【" + appService.getServiceCode() + "】调用方式不对请检查,当前请求方式为:" + httpMethod); | 422 | "服务【" + appService.getServiceCode() + "】调用方式不对请检查,当前请求方式为:" + httpMethod); |
| 408 | } | 423 | } |
| @@ -574,6 +589,13 @@ public class ApiServiceSMOImpl extends LoggerEngine implements IApiServiceSMO { | @@ -574,6 +589,13 @@ public class ApiServiceSMOImpl extends LoggerEngine implements IApiServiceSMO { | ||
| 574 | * @param reqJson | 589 | * @param reqJson |
| 575 | */ | 590 | */ |
| 576 | private void dealCmd(AppService appService, ApiDataFlow dataFlow, JSONObject reqJson) { | 591 | private void dealCmd(AppService appService, ApiDataFlow dataFlow, JSONObject reqJson) { |
| 592 | + // fallback: 如果appService为null,用header中的serviceCode创建临时对象 | ||
| 593 | + if (appService == null) { | ||
| 594 | + appService = new AppService(); | ||
| 595 | + appService.setServiceCode(dataFlow.getRequestHeaders().get(CommonConstant.HTTP_SERVICE)); | ||
| 596 | + appService.setMethod(""); | ||
| 597 | + appService.setIsInstance("CMD"); | ||
| 598 | + } | ||
| 577 | Map<String, String> reqHeader = dataFlow.getRequestCurrentHeaders(); | 599 | Map<String, String> reqHeader = dataFlow.getRequestCurrentHeaders(); |
| 578 | HttpHeaders header = new HttpHeaders(); | 600 | HttpHeaders header = new HttpHeaders(); |
| 579 | //todo 对头信息重新包装 | 601 | //todo 对头信息重新包装 |
service-community/src/main/java/com/java110/community/cmd/inspection/QueryStaffTodayTasksCmd.java
| @@ -7,8 +7,10 @@ import com.java110.core.event.cmd.Cmd; | @@ -7,8 +7,10 @@ import com.java110.core.event.cmd.Cmd; | ||
| 7 | import com.java110.core.event.cmd.CmdEvent; | 7 | import com.java110.core.event.cmd.CmdEvent; |
| 8 | import com.java110.dto.inspection.InspectionTaskDetailDto; | 8 | import com.java110.dto.inspection.InspectionTaskDetailDto; |
| 9 | import com.java110.dto.inspection.InspectionTaskDto; | 9 | import com.java110.dto.inspection.InspectionTaskDto; |
| 10 | +import com.java110.dto.user.UserDto; | ||
| 10 | import com.java110.intf.community.IInspectionTaskDetailV1InnerServiceSMO; | 11 | import com.java110.intf.community.IInspectionTaskDetailV1InnerServiceSMO; |
| 11 | import com.java110.intf.community.IInspectionTaskV1InnerServiceSMO; | 12 | import com.java110.intf.community.IInspectionTaskV1InnerServiceSMO; |
| 13 | +import com.java110.intf.user.IUserInnerServiceSMO; | ||
| 12 | import com.java110.utils.exception.CmdException; | 14 | import com.java110.utils.exception.CmdException; |
| 13 | import com.java110.utils.util.Assert; | 15 | import com.java110.utils.util.Assert; |
| 14 | import com.java110.utils.util.BeanConvertUtil; | 16 | import com.java110.utils.util.BeanConvertUtil; |
| @@ -20,15 +22,6 @@ import org.springframework.http.ResponseEntity; | @@ -20,15 +22,6 @@ import org.springframework.http.ResponseEntity; | ||
| 20 | import java.util.ArrayList; | 22 | import java.util.ArrayList; |
| 21 | import java.util.List; | 23 | import java.util.List; |
| 22 | 24 | ||
| 23 | -/** | ||
| 24 | - * 查询巡检人员今日任务及巡检点 | ||
| 25 | - * 用于二维码扫码打卡页面加载 | ||
| 26 | - * serviceCode: inspection.queryStaffTodayTasks | ||
| 27 | - * | ||
| 28 | - * @author Java110 | ||
| 29 | - * @version 1.0 | ||
| 30 | - * @since 2024 | ||
| 31 | - */ | ||
| 32 | @Java110Cmd(serviceCode = "inspection.queryStaffTodayTasks") | 25 | @Java110Cmd(serviceCode = "inspection.queryStaffTodayTasks") |
| 33 | public class QueryStaffTodayTasksCmd extends Cmd { | 26 | public class QueryStaffTodayTasksCmd extends Cmd { |
| 34 | 27 | ||
| @@ -38,18 +31,30 @@ public class QueryStaffTodayTasksCmd extends Cmd { | @@ -38,18 +31,30 @@ public class QueryStaffTodayTasksCmd extends Cmd { | ||
| 38 | @Autowired | 31 | @Autowired |
| 39 | private IInspectionTaskDetailV1InnerServiceSMO inspectionTaskDetailV1InnerServiceSMOImpl; | 32 | private IInspectionTaskDetailV1InnerServiceSMO inspectionTaskDetailV1InnerServiceSMOImpl; |
| 40 | 33 | ||
| 34 | + @Autowired | ||
| 35 | + private IUserInnerServiceSMO userInnerServiceSMOImpl; | ||
| 36 | + | ||
| 41 | @Override | 37 | @Override |
| 42 | public void validate(CmdEvent event, ICmdDataFlowContext context, JSONObject reqJson) throws CmdException { | 38 | public void validate(CmdEvent event, ICmdDataFlowContext context, JSONObject reqJson) throws CmdException { |
| 43 | - Assert.hasKeyAndValue(reqJson, "staffId", "请求报文中未包含staffId"); | ||
| 44 | - Assert.hasKeyAndValue(reqJson, "communityId", "请求报文中未包含communityId"); | 39 | + Assert.hasKeyAndValue(reqJson, "staffCode", "请求报文中未包含staffCode"); |
| 45 | } | 40 | } |
| 46 | 41 | ||
| 47 | @Override | 42 | @Override |
| 48 | public void doCmd(CmdEvent event, ICmdDataFlowContext context, JSONObject reqJson) throws CmdException { | 43 | public void doCmd(CmdEvent event, ICmdDataFlowContext context, JSONObject reqJson) throws CmdException { |
| 49 | - String staffId = reqJson.getString("staffId"); | ||
| 50 | - String communityId = reqJson.getString("communityId"); | 44 | + String staffCode = reqJson.getString("staffCode"); |
| 45 | + String communityId = reqJson.getString("communityId") != null ? reqJson.getString("communityId") : ""; | ||
| 46 | + | ||
| 47 | + // 根据员工编号查询用户 | ||
| 48 | + UserDto userDto = new UserDto(); | ||
| 49 | + userDto.setName(staffCode); | ||
| 50 | + List<UserDto> users = userInnerServiceSMOImpl.getUsers(userDto); | ||
| 51 | + String staffId = "-1"; | ||
| 52 | + String staffName = staffCode; | ||
| 53 | + if (users != null && users.size() > 0) { | ||
| 54 | + staffId = users.get(0).getUserId(); | ||
| 55 | + staffName = users.get(0).getName(); | ||
| 56 | + } | ||
| 51 | 57 | ||
| 52 | - // 查询该巡检人员的巡检任务 | ||
| 53 | InspectionTaskDto inspectionTaskDto = new InspectionTaskDto(); | 58 | InspectionTaskDto inspectionTaskDto = new InspectionTaskDto(); |
| 54 | inspectionTaskDto.setPlanUserId(staffId); | 59 | inspectionTaskDto.setPlanUserId(staffId); |
| 55 | inspectionTaskDto.setCommunityId(communityId); | 60 | inspectionTaskDto.setCommunityId(communityId); |
| @@ -57,24 +62,22 @@ public class QueryStaffTodayTasksCmd extends Cmd { | @@ -57,24 +62,22 @@ public class QueryStaffTodayTasksCmd extends Cmd { | ||
| 57 | 62 | ||
| 58 | JSONObject result = new JSONObject(); | 63 | JSONObject result = new JSONObject(); |
| 59 | result.put("staffId", staffId); | 64 | result.put("staffId", staffId); |
| 65 | + result.put("staffName", staffName); | ||
| 66 | + result.put("staffCode", staffCode); | ||
| 67 | + result.put("communityId", communityId); | ||
| 60 | 68 | ||
| 61 | List<JSONObject> taskList = new ArrayList<>(); | 69 | List<JSONObject> taskList = new ArrayList<>(); |
| 62 | - String staffName = ""; | ||
| 63 | for (InspectionTaskDto task : tasks) { | 70 | for (InspectionTaskDto task : tasks) { |
| 64 | - if (staffName.isEmpty() && task.getPlanUserName() != null) { | ||
| 65 | - staffName = task.getPlanUserName(); | ||
| 66 | - } | ||
| 67 | - | ||
| 68 | InspectionTaskDetailDto detailDto = new InspectionTaskDetailDto(); | 71 | InspectionTaskDetailDto detailDto = new InspectionTaskDetailDto(); |
| 69 | detailDto.setTaskId(task.getTaskId()); | 72 | detailDto.setTaskId(task.getTaskId()); |
| 70 | detailDto.setCommunityId(communityId); | 73 | detailDto.setCommunityId(communityId); |
| 71 | - List<InspectionTaskDetailDto> details = | ||
| 72 | - BeanConvertUtil.covertBeanList( | ||
| 73 | - inspectionTaskDetailV1InnerServiceSMOImpl.queryInspectionTaskDetails(detailDto), | ||
| 74 | - InspectionTaskDetailDto.class); | 74 | + List<InspectionTaskDetailDto> details = BeanConvertUtil.covertBeanList( |
| 75 | + inspectionTaskDetailV1InnerServiceSMOImpl.queryInspectionTaskDetails(detailDto), | ||
| 76 | + InspectionTaskDetailDto.class); | ||
| 75 | 77 | ||
| 76 | JSONObject taskObj = new JSONObject(); | 78 | JSONObject taskObj = new JSONObject(); |
| 77 | taskObj.put("taskId", task.getTaskId()); | 79 | taskObj.put("taskId", task.getTaskId()); |
| 80 | + taskObj.put("communityId", communityId); | ||
| 78 | taskObj.put("planName", task.getInspectionPlanName()); | 81 | taskObj.put("planName", task.getInspectionPlanName()); |
| 79 | taskObj.put("planInsTime", task.getPlanInsTime()); | 82 | taskObj.put("planInsTime", task.getPlanInsTime()); |
| 80 | taskObj.put("planEndTime", task.getPlanEndTime()); | 83 | taskObj.put("planEndTime", task.getPlanEndTime()); |
| @@ -106,11 +109,9 @@ public class QueryStaffTodayTasksCmd extends Cmd { | @@ -106,11 +109,9 @@ public class QueryStaffTodayTasksCmd extends Cmd { | ||
| 106 | } | 109 | } |
| 107 | 110 | ||
| 108 | result.put("staffName", staffName); | 111 | result.put("staffName", staffName); |
| 109 | - result.put("communityId", communityId); | ||
| 110 | result.put("tasks", taskList); | 112 | result.put("tasks", taskList); |
| 111 | 113 | ||
| 112 | - ResponseEntity<String> responseEntity = new ResponseEntity<>( | ||
| 113 | - ResultVo.createResponseEntity(result).toString(), HttpStatus.OK); | ||
| 114 | - context.setResponseEntity(responseEntity); | 114 | + context.setResponseEntity(new ResponseEntity<>( |
| 115 | + ResultVo.createResponseEntity(result).toString(), HttpStatus.OK)); | ||
| 115 | } | 116 | } |
| 116 | } | 117 | } |
service-community/src/main/java/com/java110/community/cmd/inspectionTaskDetail/UpdateInspectionTaskDetailCmd.java
| @@ -8,6 +8,7 @@ import com.java110.core.context.ICmdDataFlowContext; | @@ -8,6 +8,7 @@ import com.java110.core.context.ICmdDataFlowContext; | ||
| 8 | import com.java110.core.event.cmd.Cmd; | 8 | import com.java110.core.event.cmd.Cmd; |
| 9 | import com.java110.core.event.cmd.CmdEvent; | 9 | import com.java110.core.event.cmd.CmdEvent; |
| 10 | import com.java110.core.log.LoggerFactory; | 10 | import com.java110.core.log.LoggerFactory; |
| 11 | +import com.java110.dto.inspection.InspectionDto; | ||
| 11 | import com.java110.dto.inspection.InspectionTaskDetailDto; | 12 | import com.java110.dto.inspection.InspectionTaskDetailDto; |
| 12 | import com.java110.dto.inspection.InspectionTaskDto; | 13 | import com.java110.dto.inspection.InspectionTaskDto; |
| 13 | import com.java110.intf.common.IFileRelInnerServiceSMO; | 14 | import com.java110.intf.common.IFileRelInnerServiceSMO; |
| @@ -15,10 +16,13 @@ import com.java110.intf.community.IInspectionTaskDetailInnerServiceSMO; | @@ -15,10 +16,13 @@ import com.java110.intf.community.IInspectionTaskDetailInnerServiceSMO; | ||
| 15 | import com.java110.intf.community.IInspectionTaskDetailV1InnerServiceSMO; | 16 | import com.java110.intf.community.IInspectionTaskDetailV1InnerServiceSMO; |
| 16 | import com.java110.intf.community.IInspectionTaskInnerServiceSMO; | 17 | import com.java110.intf.community.IInspectionTaskInnerServiceSMO; |
| 17 | import com.java110.intf.community.IInspectionTaskV1InnerServiceSMO; | 18 | import com.java110.intf.community.IInspectionTaskV1InnerServiceSMO; |
| 19 | +import com.java110.intf.community.IInspectionPointV1InnerServiceSMO; | ||
| 18 | import com.java110.po.file.FileRelPo; | 20 | import com.java110.po.file.FileRelPo; |
| 19 | import com.java110.po.inspection.InspectionTaskDetailPo; | 21 | import com.java110.po.inspection.InspectionTaskDetailPo; |
| 20 | import com.java110.po.inspection.InspectionTaskPo; | 22 | import com.java110.po.inspection.InspectionTaskPo; |
| 23 | +import com.java110.po.property.AttendanceRecordPo; | ||
| 21 | import com.java110.utils.exception.CmdException; | 24 | import com.java110.utils.exception.CmdException; |
| 25 | +import com.java110.utils.factory.ApplicationContextFactory; | ||
| 22 | import com.java110.utils.util.Assert; | 26 | import com.java110.utils.util.Assert; |
| 23 | import com.java110.utils.util.BeanConvertUtil; | 27 | import com.java110.utils.util.BeanConvertUtil; |
| 24 | import com.java110.utils.util.DateUtil; | 28 | import com.java110.utils.util.DateUtil; |
| @@ -26,6 +30,7 @@ import com.java110.utils.util.StringUtil; | @@ -26,6 +30,7 @@ import com.java110.utils.util.StringUtil; | ||
| 26 | import org.slf4j.Logger; | 30 | import org.slf4j.Logger; |
| 27 | import org.springframework.beans.factory.annotation.Autowired; | 31 | import org.springframework.beans.factory.annotation.Autowired; |
| 28 | 32 | ||
| 33 | +import java.math.BigDecimal; | ||
| 29 | import java.text.ParseException; | 34 | import java.text.ParseException; |
| 30 | import java.text.SimpleDateFormat; | 35 | import java.text.SimpleDateFormat; |
| 31 | import java.util.Date; | 36 | import java.util.Date; |
| @@ -59,6 +64,9 @@ public class UpdateInspectionTaskDetailCmd extends Cmd { | @@ -59,6 +64,9 @@ public class UpdateInspectionTaskDetailCmd extends Cmd { | ||
| 59 | @Autowired | 64 | @Autowired |
| 60 | private IInspectionTaskV1InnerServiceSMO inspectionTaskV1InnerServiceSMOImpl; | 65 | private IInspectionTaskV1InnerServiceSMO inspectionTaskV1InnerServiceSMOImpl; |
| 61 | 66 | ||
| 67 | + @Autowired | ||
| 68 | + private IInspectionPointV1InnerServiceSMO inspectionPointV1InnerServiceSMOImpl; | ||
| 69 | + | ||
| 62 | /** | 70 | /** |
| 63 | * 参数校验方法 | 71 | * 参数校验方法 |
| 64 | * 验证请求参数中是否包含必要的字段 | 72 | * 验证请求参数中是否包含必要的字段 |
| @@ -73,8 +81,11 @@ public class UpdateInspectionTaskDetailCmd extends Cmd { | @@ -73,8 +81,11 @@ public class UpdateInspectionTaskDetailCmd extends Cmd { | ||
| 73 | // 校验必要参数是否存在 | 81 | // 校验必要参数是否存在 |
| 74 | Assert.hasKeyAndValue(reqJson, "taskDetailId", "请求报文中未包含taskDetailId"); | 82 | Assert.hasKeyAndValue(reqJson, "taskDetailId", "请求报文中未包含taskDetailId"); |
| 75 | Assert.hasKeyAndValue(reqJson, "taskId", "请求报文中未包含taskId"); | 83 | Assert.hasKeyAndValue(reqJson, "taskId", "请求报文中未包含taskId"); |
| 76 | - Assert.hasKeyAndValue(reqJson, "communityId", "请求报文中未包含communityId"); | ||
| 77 | Assert.hasKeyAndValue(reqJson, "inspectionId", "请求报文中未包含inspectionId"); | 84 | Assert.hasKeyAndValue(reqJson, "inspectionId", "请求报文中未包含inspectionId"); |
| 85 | + // qrcode打卡时communityId从任务中获取,不强制要求 | ||
| 86 | + if (!"qrcode".equals(reqJson.getString("source"))) { | ||
| 87 | + Assert.hasKeyAndValue(reqJson, "communityId", "请求报文中未包含communityId"); | ||
| 88 | + } | ||
| 78 | Assert.hasKeyAndValue(reqJson, "photos", "请求报文中未包含照片"); | 89 | Assert.hasKeyAndValue(reqJson, "photos", "请求报文中未包含照片"); |
| 79 | } | 90 | } |
| 80 | 91 | ||
| @@ -95,6 +106,29 @@ public class UpdateInspectionTaskDetailCmd extends Cmd { | @@ -95,6 +106,29 @@ public class UpdateInspectionTaskDetailCmd extends Cmd { | ||
| 95 | dealPhotos(reqJson); | 106 | dealPhotos(reqJson); |
| 96 | } | 107 | } |
| 97 | 108 | ||
| 109 | + // GPS校验:所有打卡都必须有有效坐标 | ||
| 110 | + String lng = reqJson.getString("longitude"); | ||
| 111 | + String lat = reqJson.getString("latitude"); | ||
| 112 | + if (StringUtil.isEmpty(lng) || "0".equals(lng) || "0.0".equals(lng) || "0.0000000".equals(lng) | ||
| 113 | + || StringUtil.isEmpty(lat) || "0".equals(lat) || "0.0".equals(lat) || "0.0000000".equals(lat)) { | ||
| 114 | + throw new CmdException("请开启GPS获取位置后再打卡"); | ||
| 115 | + } | ||
| 116 | + | ||
| 117 | + // 二维码打卡时校验距离(仅H5,不影响App) | ||
| 118 | + if ("qrcode".equals(reqJson.getString("source"))) { | ||
| 119 | + // 从任务中获取communityId | ||
| 120 | + if (StringUtil.isEmpty(reqJson.getString("communityId"))) { | ||
| 121 | + InspectionTaskDto taskDto = new InspectionTaskDto(); | ||
| 122 | + taskDto.setTaskId(reqJson.getString("taskId")); | ||
| 123 | + List<InspectionTaskDto> tasks = inspectionTaskInnerServiceSMOImpl.queryInspectionTasks(taskDto); | ||
| 124 | + if (tasks != null && tasks.size() > 0) { | ||
| 125 | + reqJson.put("communityId", tasks.get(0).getCommunityId()); | ||
| 126 | + } | ||
| 127 | + } | ||
| 128 | + checkQrcodeDistance(reqJson); | ||
| 129 | + saveQrcodeAttendanceRecord(reqJson); | ||
| 130 | + } | ||
| 131 | + | ||
| 98 | // 设置巡检点完成状态 | 132 | // 设置巡检点完成状态 |
| 99 | reqJson.put("state", "20200407"); // 巡检点完成状态码 | 133 | reqJson.put("state", "20200407"); // 巡检点完成状态码 |
| 100 | 134 | ||
| @@ -320,10 +354,75 @@ public class UpdateInspectionTaskDetailCmd extends Cmd { | @@ -320,10 +354,75 @@ public class UpdateInspectionTaskDetailCmd extends Cmd { | ||
| 320 | 354 | ||
| 321 | // 执行更新操作 | 355 | // 执行更新操作 |
| 322 | int flag = inspectionTaskV1InnerServiceSMOImpl.updateInspectionTask(inspectionTaskPo); | 356 | int flag = inspectionTaskV1InnerServiceSMOImpl.updateInspectionTask(inspectionTaskPo); |
| 323 | - | 357 | + |
| 324 | // 检查更新结果 | 358 | // 检查更新结果 |
| 325 | if (flag < 1) { | 359 | if (flag < 1) { |
| 326 | throw new CmdException("修改任务失败"); | 360 | throw new CmdException("修改任务失败"); |
| 327 | } | 361 | } |
| 328 | } | 362 | } |
| 363 | + | ||
| 364 | + /** | ||
| 365 | + * 二维码打卡时同步写入考勤记录(attendance_record),使打卡记录在考勤页面可见 | ||
| 366 | + */ | ||
| 367 | + private void saveQrcodeAttendanceRecord(JSONObject reqJson) { | ||
| 368 | + try { | ||
| 369 | + AttendanceRecordPo po = new AttendanceRecordPo(); | ||
| 370 | + po.setId(com.java110.core.factory.GenerateCodeFactory.getGeneratorId("10")); | ||
| 371 | + po.setUserId(reqJson.getString("userId")); | ||
| 372 | + po.setUserName(reqJson.getString("userName")); | ||
| 373 | + po.setPunchType(reqJson.containsKey("punchType") ? reqJson.getString("punchType") : "ON"); | ||
| 374 | + po.setPunchTime(new Date()); | ||
| 375 | + try { po.setLongitude(new BigDecimal(reqJson.getString("longitude"))); } catch(Exception ex) {} | ||
| 376 | + try { po.setLatitude(new BigDecimal(reqJson.getString("latitude"))); } catch(Exception ex) {} | ||
| 377 | + po.setPunchAddress(reqJson.containsKey("address") ? reqJson.getString("address") : ""); | ||
| 378 | + po.setStoreId(""); | ||
| 379 | + po.setWorkType("qrcode"); | ||
| 380 | + po.setPhotoUrl(reqJson.containsKey("photos") ? reqJson.getJSONArray("photos").getString(0) : ""); | ||
| 381 | + Object dao = ApplicationContextFactory.getBean("attendanceRecordV1ServiceDaoImpl"); | ||
| 382 | + if (dao != null) { | ||
| 383 | + java.lang.reflect.Method m = dao.getClass().getMethod("saveAttendanceRecord", AttendanceRecordPo.class); | ||
| 384 | + m.invoke(dao, po); | ||
| 385 | + } | ||
| 386 | + } catch (Exception e) { | ||
| 387 | + logger.error("保存考勤记录失败", e); | ||
| 388 | + } | ||
| 389 | + } | ||
| 390 | + | ||
| 391 | + /** | ||
| 392 | + * 二维码打卡距离校验(仅H5页面,不影响Android App) | ||
| 393 | + */ | ||
| 394 | + private void checkQrcodeDistance(JSONObject reqJson) { | ||
| 395 | + double userLng = Double.parseDouble(reqJson.getString("longitude")); | ||
| 396 | + double userLat = Double.parseDouble(reqJson.getString("latitude")); | ||
| 397 | + | ||
| 398 | + // 查询巡检点坐标 | ||
| 399 | + InspectionDto pointDto = new InspectionDto(); | ||
| 400 | + pointDto.setInspectionId(reqJson.getString("inspectionId")); | ||
| 401 | + pointDto.setCommunityId(reqJson.getString("communityId")); | ||
| 402 | + List<InspectionDto> points = | ||
| 403 | + inspectionPointV1InnerServiceSMOImpl.queryInspectionPoints(pointDto); | ||
| 404 | + if (points == null || points.size() == 0) { | ||
| 405 | + throw new CmdException("巡检点不存在"); | ||
| 406 | + } | ||
| 407 | + String lng = points.get(0).getLng(); | ||
| 408 | + String lat = points.get(0).getLat(); | ||
| 409 | + if (StringUtil.isEmpty(lng) || StringUtil.isEmpty(lat)) { | ||
| 410 | + return; // 无坐标时跳过距离校验 | ||
| 411 | + } | ||
| 412 | + double pointLng = Double.parseDouble(lng); | ||
| 413 | + double pointLat = Double.parseDouble(lat); | ||
| 414 | + | ||
| 415 | + // Haversine距离计算 | ||
| 416 | + double R = 6371; | ||
| 417 | + double dLat = Math.toRadians(pointLat - userLat); | ||
| 418 | + double dLng = Math.toRadians(pointLng - userLng); | ||
| 419 | + double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) | ||
| 420 | + + Math.cos(Math.toRadians(userLat)) * Math.cos(Math.toRadians(pointLat)) | ||
| 421 | + * Math.sin(dLng / 2) * Math.sin(dLng / 2); | ||
| 422 | + double distance = R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); | ||
| 423 | + | ||
| 424 | + if (distance > 2) { | ||
| 425 | + throw new CmdException("距离巡检点超过2km,无法打卡(距离:" + String.format("%.2f", distance) + "km)"); | ||
| 426 | + } | ||
| 427 | + } | ||
| 329 | } | 428 | } |
| 330 | \ No newline at end of file | 429 | \ No newline at end of file |