diff --git a/service-api/src/main/java/com/java110/api/configuration/ServiceConfiguration.java b/service-api/src/main/java/com/java110/api/configuration/ServiceConfiguration.java index 1a50d2e..c3b46d3 100644 --- a/service-api/src/main/java/com/java110/api/configuration/ServiceConfiguration.java +++ b/service-api/src/main/java/com/java110/api/configuration/ServiceConfiguration.java @@ -142,7 +142,11 @@ public class ServiceConfiguration { - exclusions.append("/app/reportInfoAnswer/queryReportInfoAnswerByOpenId"); + exclusions.append("/app/reportInfoAnswer/queryReportInfoAnswerByOpenId,"); + // 二维码巡检打卡接口(无需登录) + exclusions.append("/app/inspection.queryStaffTodayTasks,"); + exclusions.append("/app/inspectionTaskDetail.updateInspectionTaskDetail,"); + exclusions.append("/callComponent/upload/uploadFile/uploadImage,"); final FilterRegistrationBean registrationBean = new FilterRegistrationBean(); diff --git a/service-api/src/main/java/com/java110/api/smo/impl/ApiServiceSMOImpl.java b/service-api/src/main/java/com/java110/api/smo/impl/ApiServiceSMOImpl.java index b4819f1..6084e86 100644 --- a/service-api/src/main/java/com/java110/api/smo/impl/ApiServiceSMOImpl.java +++ b/service-api/src/main/java/com/java110/api/smo/impl/ApiServiceSMOImpl.java @@ -8,6 +8,7 @@ import com.java110.core.client.RestTemplate; import com.java110.core.context.ApiDataFlow; import com.java110.core.context.DataFlow; import com.java110.core.context.Environment; +import com.java110.core.event.cmd.ServiceCmdEventPublishing; import com.java110.core.event.service.api.ServiceDataFlowEventPublishing; import com.java110.core.factory.AuthenticationFactory; import com.java110.core.factory.DataFlowFactory; @@ -339,13 +340,18 @@ public class ApiServiceSMOImpl extends LoggerEngine implements IApiServiceSMO { //todo 判断 AppId 是否有权限操作相应的服务 - AppService appService = DataFlowFactory.getService(dataFlow, dataFlow.getRequestHeaders().get(CommonConstant.HTTP_SERVICE)); + String serviceCode = dataFlow.getRequestHeaders().get(CommonConstant.HTTP_SERVICE); + AppService appService = DataFlowFactory.getService(dataFlow, serviceCode); - //这里调用缓存 查询缓存信息 + //这里调用缓存 查询缓存信息,如果缓存中没有则fallback到Cmd扫描注册表 if (appService == null || !CommonConstant.HTTP_SERVICE_API.equals(appService.getBusinessTypeCd())) { - //添加耗时 - DataFlowFactory.addCostTime(dataFlow, "judgeAuthority", "鉴权耗时", startDate); - throw new NoAuthorityException(ResponseConstant.RESULT_CODE_NO_AUTHORITY_ERROR, "AppId 没有权限访问 serviceCode = " + dataFlow.getRequestHeaders().get(CommonConstant.HTTP_SERVICE)); + // fallback: 检查是否为通过@Java110Cmd注解注册的服务(代码级注册,可能未同步到Redis) + if (ServiceCmdEventPublishing.getListeners(serviceCode).size() > 0) { + logger.debug("服务 {} 从Cmd注册表验证通过(Redis缓存未命中或businessType不匹配)", serviceCode); + } else { + DataFlowFactory.addCostTime(dataFlow, "judgeAuthority", "鉴权耗时", startDate); + throw new NoAuthorityException(ResponseConstant.RESULT_CODE_NO_AUTHORITY_ERROR, "AppId 没有权限访问 serviceCode = " + serviceCode); + } } @@ -379,6 +385,16 @@ public class ApiServiceSMOImpl extends LoggerEngine implements IApiServiceSMO { Date startDate = DateUtil.getCurrentDate(); //todo 拿到当前服务 AppService appService = DataFlowFactory.getService(dataFlow, dataFlow.getRequestHeaders().get(CommonConstant.HTTP_SERVICE)); + if (appService == null) { + // fallback: Redis未缓存但在Cmd注册表中的服务,直接CMD调度 + String svc = dataFlow.getRequestHeaders().get(CommonConstant.HTTP_SERVICE); + if (ServiceCmdEventPublishing.getListeners(svc).size() > 0) { + dealCmd(null, dataFlow, dataFlow.getReqJson()); + DataFlowFactory.addCostTime(dataFlow, "invokeBusinessSystem", "调用下游系统耗时", startDate); + return; + } + throw new BusinessException(ResponseConstant.RESULT_CODE_ERROR, "服务不存在:" + svc); + } //todo 这里对透传类处理,目前很少用到,可以不用关注 if ("NT".equals(appService.getIsInstance())) { //如果是透传类 请求方式必须与接口提供方调用方式一致 @@ -399,10 +415,9 @@ public class ApiServiceSMOImpl extends LoggerEngine implements IApiServiceSMO { } doTransfer(appService, dataFlow, dataFlow.getReqJson()); return; - } else if ("CMD".equals(appService.getIsInstance())) { // todo 微服务调用方式,目前主要用这种方式调度分发 到不同的微服务,这里是通过c_service 中配置 调用到不同的微服务 - //如果是透传类 请求方式必须与接口提供方调用方式一致 + } else if ("CMD".equals(appService.getIsInstance())) { // todo 微服务调用方式 String httpMethod = dataFlow.getRequestCurrentHeaders().get(CommonConstant.HTTP_METHOD); - if (!appService.getMethod().equals(httpMethod)) { + if (!StringUtils.isEmpty(appService.getMethod()) && !appService.getMethod().equals(httpMethod)) { throw new ListenerExecuteException(ResponseConstant.RESULT_CODE_ERROR, "服务【" + appService.getServiceCode() + "】调用方式不对请检查,当前请求方式为:" + httpMethod); } @@ -574,6 +589,13 @@ public class ApiServiceSMOImpl extends LoggerEngine implements IApiServiceSMO { * @param reqJson */ private void dealCmd(AppService appService, ApiDataFlow dataFlow, JSONObject reqJson) { + // fallback: 如果appService为null,用header中的serviceCode创建临时对象 + if (appService == null) { + appService = new AppService(); + appService.setServiceCode(dataFlow.getRequestHeaders().get(CommonConstant.HTTP_SERVICE)); + appService.setMethod(""); + appService.setIsInstance("CMD"); + } Map reqHeader = dataFlow.getRequestCurrentHeaders(); HttpHeaders header = new HttpHeaders(); //todo 对头信息重新包装 diff --git a/service-community/src/main/java/com/java110/community/cmd/inspection/QueryStaffTodayTasksCmd.java b/service-community/src/main/java/com/java110/community/cmd/inspection/QueryStaffTodayTasksCmd.java index c44be2c..1c973be 100644 --- a/service-community/src/main/java/com/java110/community/cmd/inspection/QueryStaffTodayTasksCmd.java +++ b/service-community/src/main/java/com/java110/community/cmd/inspection/QueryStaffTodayTasksCmd.java @@ -7,8 +7,10 @@ import com.java110.core.event.cmd.Cmd; import com.java110.core.event.cmd.CmdEvent; import com.java110.dto.inspection.InspectionTaskDetailDto; import com.java110.dto.inspection.InspectionTaskDto; +import com.java110.dto.user.UserDto; import com.java110.intf.community.IInspectionTaskDetailV1InnerServiceSMO; import com.java110.intf.community.IInspectionTaskV1InnerServiceSMO; +import com.java110.intf.user.IUserInnerServiceSMO; import com.java110.utils.exception.CmdException; import com.java110.utils.util.Assert; import com.java110.utils.util.BeanConvertUtil; @@ -20,15 +22,6 @@ import org.springframework.http.ResponseEntity; import java.util.ArrayList; import java.util.List; -/** - * 查询巡检人员今日任务及巡检点 - * 用于二维码扫码打卡页面加载 - * serviceCode: inspection.queryStaffTodayTasks - * - * @author Java110 - * @version 1.0 - * @since 2024 - */ @Java110Cmd(serviceCode = "inspection.queryStaffTodayTasks") public class QueryStaffTodayTasksCmd extends Cmd { @@ -38,18 +31,30 @@ public class QueryStaffTodayTasksCmd extends Cmd { @Autowired private IInspectionTaskDetailV1InnerServiceSMO inspectionTaskDetailV1InnerServiceSMOImpl; + @Autowired + private IUserInnerServiceSMO userInnerServiceSMOImpl; + @Override public void validate(CmdEvent event, ICmdDataFlowContext context, JSONObject reqJson) throws CmdException { - Assert.hasKeyAndValue(reqJson, "staffId", "请求报文中未包含staffId"); - Assert.hasKeyAndValue(reqJson, "communityId", "请求报文中未包含communityId"); + Assert.hasKeyAndValue(reqJson, "staffCode", "请求报文中未包含staffCode"); } @Override public void doCmd(CmdEvent event, ICmdDataFlowContext context, JSONObject reqJson) throws CmdException { - String staffId = reqJson.getString("staffId"); - String communityId = reqJson.getString("communityId"); + String staffCode = reqJson.getString("staffCode"); + String communityId = reqJson.getString("communityId") != null ? reqJson.getString("communityId") : ""; + + // 根据员工编号查询用户 + UserDto userDto = new UserDto(); + userDto.setName(staffCode); + List users = userInnerServiceSMOImpl.getUsers(userDto); + String staffId = "-1"; + String staffName = staffCode; + if (users != null && users.size() > 0) { + staffId = users.get(0).getUserId(); + staffName = users.get(0).getName(); + } - // 查询该巡检人员的巡检任务 InspectionTaskDto inspectionTaskDto = new InspectionTaskDto(); inspectionTaskDto.setPlanUserId(staffId); inspectionTaskDto.setCommunityId(communityId); @@ -57,24 +62,22 @@ public class QueryStaffTodayTasksCmd extends Cmd { JSONObject result = new JSONObject(); result.put("staffId", staffId); + result.put("staffName", staffName); + result.put("staffCode", staffCode); + result.put("communityId", communityId); List taskList = new ArrayList<>(); - String staffName = ""; for (InspectionTaskDto task : tasks) { - if (staffName.isEmpty() && task.getPlanUserName() != null) { - staffName = task.getPlanUserName(); - } - InspectionTaskDetailDto detailDto = new InspectionTaskDetailDto(); detailDto.setTaskId(task.getTaskId()); detailDto.setCommunityId(communityId); - List details = - BeanConvertUtil.covertBeanList( - inspectionTaskDetailV1InnerServiceSMOImpl.queryInspectionTaskDetails(detailDto), - InspectionTaskDetailDto.class); + List details = BeanConvertUtil.covertBeanList( + inspectionTaskDetailV1InnerServiceSMOImpl.queryInspectionTaskDetails(detailDto), + InspectionTaskDetailDto.class); JSONObject taskObj = new JSONObject(); taskObj.put("taskId", task.getTaskId()); + taskObj.put("communityId", communityId); taskObj.put("planName", task.getInspectionPlanName()); taskObj.put("planInsTime", task.getPlanInsTime()); taskObj.put("planEndTime", task.getPlanEndTime()); @@ -106,11 +109,9 @@ public class QueryStaffTodayTasksCmd extends Cmd { } result.put("staffName", staffName); - result.put("communityId", communityId); result.put("tasks", taskList); - ResponseEntity responseEntity = new ResponseEntity<>( - ResultVo.createResponseEntity(result).toString(), HttpStatus.OK); - context.setResponseEntity(responseEntity); + context.setResponseEntity(new ResponseEntity<>( + ResultVo.createResponseEntity(result).toString(), HttpStatus.OK)); } } diff --git a/service-community/src/main/java/com/java110/community/cmd/inspectionTaskDetail/UpdateInspectionTaskDetailCmd.java b/service-community/src/main/java/com/java110/community/cmd/inspectionTaskDetail/UpdateInspectionTaskDetailCmd.java index 3640cce..7d744bf 100644 --- a/service-community/src/main/java/com/java110/community/cmd/inspectionTaskDetail/UpdateInspectionTaskDetailCmd.java +++ b/service-community/src/main/java/com/java110/community/cmd/inspectionTaskDetail/UpdateInspectionTaskDetailCmd.java @@ -8,6 +8,7 @@ import com.java110.core.context.ICmdDataFlowContext; import com.java110.core.event.cmd.Cmd; import com.java110.core.event.cmd.CmdEvent; import com.java110.core.log.LoggerFactory; +import com.java110.dto.inspection.InspectionDto; import com.java110.dto.inspection.InspectionTaskDetailDto; import com.java110.dto.inspection.InspectionTaskDto; import com.java110.intf.common.IFileRelInnerServiceSMO; @@ -15,10 +16,13 @@ import com.java110.intf.community.IInspectionTaskDetailInnerServiceSMO; import com.java110.intf.community.IInspectionTaskDetailV1InnerServiceSMO; import com.java110.intf.community.IInspectionTaskInnerServiceSMO; import com.java110.intf.community.IInspectionTaskV1InnerServiceSMO; +import com.java110.intf.community.IInspectionPointV1InnerServiceSMO; import com.java110.po.file.FileRelPo; import com.java110.po.inspection.InspectionTaskDetailPo; import com.java110.po.inspection.InspectionTaskPo; +import com.java110.po.property.AttendanceRecordPo; import com.java110.utils.exception.CmdException; +import com.java110.utils.factory.ApplicationContextFactory; import com.java110.utils.util.Assert; import com.java110.utils.util.BeanConvertUtil; import com.java110.utils.util.DateUtil; @@ -26,6 +30,7 @@ import com.java110.utils.util.StringUtil; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; +import java.math.BigDecimal; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; @@ -59,6 +64,9 @@ public class UpdateInspectionTaskDetailCmd extends Cmd { @Autowired private IInspectionTaskV1InnerServiceSMO inspectionTaskV1InnerServiceSMOImpl; + @Autowired + private IInspectionPointV1InnerServiceSMO inspectionPointV1InnerServiceSMOImpl; + /** * 参数校验方法 * 验证请求参数中是否包含必要的字段 @@ -73,8 +81,11 @@ public class UpdateInspectionTaskDetailCmd extends Cmd { // 校验必要参数是否存在 Assert.hasKeyAndValue(reqJson, "taskDetailId", "请求报文中未包含taskDetailId"); Assert.hasKeyAndValue(reqJson, "taskId", "请求报文中未包含taskId"); - Assert.hasKeyAndValue(reqJson, "communityId", "请求报文中未包含communityId"); Assert.hasKeyAndValue(reqJson, "inspectionId", "请求报文中未包含inspectionId"); + // qrcode打卡时communityId从任务中获取,不强制要求 + if (!"qrcode".equals(reqJson.getString("source"))) { + Assert.hasKeyAndValue(reqJson, "communityId", "请求报文中未包含communityId"); + } Assert.hasKeyAndValue(reqJson, "photos", "请求报文中未包含照片"); } @@ -95,6 +106,29 @@ public class UpdateInspectionTaskDetailCmd extends Cmd { dealPhotos(reqJson); } + // GPS校验:所有打卡都必须有有效坐标 + String lng = reqJson.getString("longitude"); + String lat = reqJson.getString("latitude"); + if (StringUtil.isEmpty(lng) || "0".equals(lng) || "0.0".equals(lng) || "0.0000000".equals(lng) + || StringUtil.isEmpty(lat) || "0".equals(lat) || "0.0".equals(lat) || "0.0000000".equals(lat)) { + throw new CmdException("请开启GPS获取位置后再打卡"); + } + + // 二维码打卡时校验距离(仅H5,不影响App) + if ("qrcode".equals(reqJson.getString("source"))) { + // 从任务中获取communityId + if (StringUtil.isEmpty(reqJson.getString("communityId"))) { + InspectionTaskDto taskDto = new InspectionTaskDto(); + taskDto.setTaskId(reqJson.getString("taskId")); + List tasks = inspectionTaskInnerServiceSMOImpl.queryInspectionTasks(taskDto); + if (tasks != null && tasks.size() > 0) { + reqJson.put("communityId", tasks.get(0).getCommunityId()); + } + } + checkQrcodeDistance(reqJson); + saveQrcodeAttendanceRecord(reqJson); + } + // 设置巡检点完成状态 reqJson.put("state", "20200407"); // 巡检点完成状态码 @@ -320,10 +354,75 @@ public class UpdateInspectionTaskDetailCmd extends Cmd { // 执行更新操作 int flag = inspectionTaskV1InnerServiceSMOImpl.updateInspectionTask(inspectionTaskPo); - + // 检查更新结果 if (flag < 1) { throw new CmdException("修改任务失败"); } } + + /** + * 二维码打卡时同步写入考勤记录(attendance_record),使打卡记录在考勤页面可见 + */ + private void saveQrcodeAttendanceRecord(JSONObject reqJson) { + try { + AttendanceRecordPo po = new AttendanceRecordPo(); + po.setId(com.java110.core.factory.GenerateCodeFactory.getGeneratorId("10")); + po.setUserId(reqJson.getString("userId")); + po.setUserName(reqJson.getString("userName")); + po.setPunchType(reqJson.containsKey("punchType") ? reqJson.getString("punchType") : "ON"); + po.setPunchTime(new Date()); + try { po.setLongitude(new BigDecimal(reqJson.getString("longitude"))); } catch(Exception ex) {} + try { po.setLatitude(new BigDecimal(reqJson.getString("latitude"))); } catch(Exception ex) {} + po.setPunchAddress(reqJson.containsKey("address") ? reqJson.getString("address") : ""); + po.setStoreId(""); + po.setWorkType("qrcode"); + po.setPhotoUrl(reqJson.containsKey("photos") ? reqJson.getJSONArray("photos").getString(0) : ""); + Object dao = ApplicationContextFactory.getBean("attendanceRecordV1ServiceDaoImpl"); + if (dao != null) { + java.lang.reflect.Method m = dao.getClass().getMethod("saveAttendanceRecord", AttendanceRecordPo.class); + m.invoke(dao, po); + } + } catch (Exception e) { + logger.error("保存考勤记录失败", e); + } + } + + /** + * 二维码打卡距离校验(仅H5页面,不影响Android App) + */ + private void checkQrcodeDistance(JSONObject reqJson) { + double userLng = Double.parseDouble(reqJson.getString("longitude")); + double userLat = Double.parseDouble(reqJson.getString("latitude")); + + // 查询巡检点坐标 + InspectionDto pointDto = new InspectionDto(); + pointDto.setInspectionId(reqJson.getString("inspectionId")); + pointDto.setCommunityId(reqJson.getString("communityId")); + List points = + inspectionPointV1InnerServiceSMOImpl.queryInspectionPoints(pointDto); + if (points == null || points.size() == 0) { + throw new CmdException("巡检点不存在"); + } + String lng = points.get(0).getLng(); + String lat = points.get(0).getLat(); + if (StringUtil.isEmpty(lng) || StringUtil.isEmpty(lat)) { + return; // 无坐标时跳过距离校验 + } + double pointLng = Double.parseDouble(lng); + double pointLat = Double.parseDouble(lat); + + // Haversine距离计算 + double R = 6371; + double dLat = Math.toRadians(pointLat - userLat); + double dLng = Math.toRadians(pointLng - userLng); + double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + + Math.cos(Math.toRadians(userLat)) * Math.cos(Math.toRadians(pointLat)) + * Math.sin(dLng / 2) * Math.sin(dLng / 2); + double distance = R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + + if (distance > 2) { + throw new CmdException("距离巡检点超过2km,无法打卡(距离:" + String.format("%.2f", distance) + "km)"); + } + } } \ No newline at end of file