| | |
| | | */ |
| | | public static void multicastEvent(ICmdDataFlowContext cmdDataFlowContext) throws Exception { |
| | | Assert.notNull(cmdDataFlowContext.getServiceCode(), "当前没有可处理的业务信息!"); |
| | | //todo 根据cmd serviceCode 发布事件 |
| | | multicastEvent(cmdDataFlowContext.getServiceCode(), cmdDataFlowContext, null); |
| | | } |
| | | |
| | |
| | | */ |
| | | public static void multicastEvent(String serviceCode, ICmdDataFlowContext dataFlowContext, String asyn) throws Exception { |
| | | try { |
| | | //todo 组装事件 |
| | | CmdEvent targetDataFlowEvent = new CmdEvent(serviceCode, dataFlowContext); |
| | | |
| | | //todo 发布事件 |
| | | multicastEvent(serviceCode, targetDataFlowEvent, asyn); |
| | | } catch (Exception e) { |
| | | logger.error("发布侦听失败,失败原因为:", e); |
| | |
| | | * @param asyn A 表示异步处理 |
| | | */ |
| | | public static void multicastEvent(String serviceCode, final CmdEvent event, String asyn) throws Exception { |
| | | //todo 根据serviceCode 去寻找 处理的Cmd处理类 如果java类中 @Java110Cmd(serviceCode = "xx.xx") 写了该注解就会被寻找到 |
| | | List<ServiceCmdListener> listeners = getListeners(serviceCode); |
| | | //这里判断 serviceCode + httpMethod 的侦听,如果没有注册直接报错。 |
| | | if (listeners == null || listeners.size() == 0) { |
| | |
| | | } |
| | | for (final ServiceCmdListener listener : listeners) { |
| | | |
| | | if (CommonConstant.PROCESS_ORDER_ASYNCHRONOUS.equals(asyn)) { //异步处理 |
| | | if (CommonConstant.PROCESS_ORDER_ASYNCHRONOUS.equals(asyn)) { //todo 异步处理,一般很少用 |
| | | |
| | | Executor executor = getTaskExecutor(); |
| | | executor.execute(new Runnable() { |
| | |
| | | }); |
| | | break; |
| | | } else { |
| | | // todo 通过同步的方式调用CMDjava类 |
| | | invokeListener(listener, event); |
| | | break; |
| | | } |
| | |
| | | } |
| | | |
| | | /** |
| | | * Invoke the given listener with the given event. |
| | | * 执行 根据serviceCode 找到的cmd 类 |
| | | * |
| | | * @param listener the ApplicationListener to invoke |
| | | * @param event the current event to propagate |
| | |
| | | @SuppressWarnings({"unchecked", "rawtypes"}) |
| | | protected static void invokeListener(ServiceCmdListener listener, CmdEvent event) throws Exception { |
| | | try { |
| | | // //这里处理业务逻辑数据 |
| | | //todo 获取 cmd 上下文对象 |
| | | ICmdDataFlowContext dataFlowContext = event.getCmdDataFlowContext(); |
| | | //获取请求数据 |
| | | //todo 获取请求数据 |
| | | JSONObject reqJson = dataFlowContext.getReqJson(); |
| | | |
| | | logger.debug("API服务 --- 请求参数为:{}", reqJson.toJSONString()); |
| | | |
| | | //todo 调用 cmd的校验方法 |
| | | listener.validate(event, dataFlowContext, reqJson); |
| | | |
| | | //todo 调用 cmd的业务处理方法 |
| | | listener.doCmd(event, dataFlowContext, reqJson); |
| | | |
| | | //logger.debug("API服务 --- 返回报文信息:{}", dataFlowContext.getResponseEntity()); |
| | |
| | | ISaveSystemErrorSMO saveSystemErrorSMOImpl; |
| | | |
| | | |
| | | /** |
| | | * 微服务 /cmd/xx.xx 接受类 |
| | | * @param service |
| | | * @param postInfo |
| | | * @param request |
| | | * @return |
| | | */ |
| | | @RequestMapping(path = "/{service:.+}", method = RequestMethod.POST) |
| | | public ResponseEntity<String> service(@PathVariable String service, |
| | | @RequestBody String postInfo, |
| | |
| | | ResponseEntity<String> responseEntity = null; |
| | | Map<String, String> headers = new HashMap<String, String>(); |
| | | try { |
| | | |
| | | //todo 头信息封装 |
| | | this.getRequestInfo(request, headers); |
| | | //todo 将serviceCode 写入到头信息 |
| | | headers.put(CommonConstant.HTTP_SERVICE, service); |
| | | //todo 将请求方式 写入到头信息 |
| | | headers.put(CommonConstant.HTTP_METHOD, CommonConstant.HTTP_METHOD_POST); |
| | | logger.debug("api:{} 请求报文为:{},header信息为:{}", service, postInfo, headers); |
| | | //todo 开始调用 cmd 类 |
| | | responseEntity = cmdServiceSMOImpl.cmd(postInfo, headers); |
| | | } catch (Throwable e) { |
| | | //todo 异常信息进行记录 |
| | | LogSystemErrorPo logSystemErrorPo = new LogSystemErrorPo(); |
| | | logSystemErrorPo.setErrId(GenerateCodeFactory.getGeneratorId(GenerateCodeFactory.CODE_PREFIX_errId)); |
| | | logSystemErrorPo.setErrType(LogSystemErrorDto.ERR_TYPE_CMD); |
| | | logSystemErrorPo.setMsg(ExceptionUtil.getStackTrace(e)); |
| | | //todo 日志 |
| | | saveSystemErrorSMOImpl.saveLog(logSystemErrorPo); |
| | | logger.error("请求post 方法[" + service + "]失败:" + postInfo, e); |
| | | responseEntity = new ResponseEntity<String>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); |
| | |
| | | public interface ICmdServiceSMO { |
| | | |
| | | /** |
| | | * 业务统一处理服务方法 |
| | | * cmd 调用 处理类 |
| | | * @param reqJson 请求报文json |
| | | * @return |
| | | */ |
| | |
| | | |
| | | |
| | | /** |
| | | * 服务调度 |
| | | * cmd 服务调度 |
| | | * |
| | | * @param reqJson 请求报文json |
| | | * @param headers |
| | |
| | | |
| | | ResponseEntity<String> responseEntity = null; |
| | | |
| | | //1.0 创建数据流 appId serviceCode |
| | | //todo 1.0 创建数据流 appId serviceCode |
| | | cmdDataFlowContext = DataFlowFactory.newInstance(CmdDataFlow.class).builder(reqJson, headers); |
| | | |
| | | |
| | | //6.0 调用下游系统 |
| | | //todo 2.0 调用下游系统 |
| | | invokeBusinessSystem(cmdDataFlowContext); |
| | | |
| | | responseEntity = cmdDataFlowContext.getResponseEntity(); |
| | |
| | | |
| | | |
| | | /** |
| | | * 6.0 调用下游系统 |
| | | * 2.0 调用下游系统 |
| | | * |
| | | * @param cmdDataFlowContext |
| | | * @throws BusinessException |
| | | */ |
| | | private void invokeBusinessSystem(ICmdDataFlowContext cmdDataFlowContext) throws Exception { |
| | | //todo 发布 cmd 事件 |
| | | ServiceCmdEventPublishing.multicastEvent(cmdDataFlowContext); |
| | | } |
| | | |
| | |
| | | private IPrivilegeSMO privilegeSMOImpl; |
| | | |
| | | /** |
| | | * 资源请求 post方式 |
| | | * 资源请求 post方式 统一入口类 |
| | | * <p> |
| | | * /app/user.listUser |
| | | * <p> |
| | |
| | | ResponseEntity<String> responseEntity = null; |
| | | try { |
| | | Map<String, String> headers = new HashMap<String, String>(); |
| | | //todo 封装请求中的 头信息 |
| | | this.getRequestInfo(request, headers); |
| | | //todo 将接口编码(服务编码) 放入头信息 通过头信息的方式 传递到后端 |
| | | headers.put(CommonConstant.HTTP_SERVICE, service); |
| | | // todo 请求方式 放入到头信息 |
| | | headers.put(CommonConstant.HTTP_METHOD, CommonConstant.HTTP_METHOD_POST); |
| | | logger.debug("api:{} 请求报文为:{},header信息为:{}", service, postInfo, headers); |
| | | //todo 获取由 PageProcessAspect aop 拦截封装的 pd 页面数据对象 |
| | | IPageData pd = (IPageData) request.getAttribute(CommonConstant.CONTEXT_PAGE_DATA); |
| | | //todo 根据登录用户 的权限 校验 用户是否有权限访问该接口,此时的"/app/" + service 为 开发这账户 菜单权限下的资源地址 |
| | | privilegeSMOImpl.hasPrivilege(restTemplate, pd, "/app/" + service); |
| | | //todo 进入 接口相关 业务处理 |
| | | responseEntity = apiSMOImpl.doApi(postInfo, headers,request); |
| | | //todo 写入 token |
| | | wirteToken(request,pd,service,responseEntity); |
| | |
| | | } |
| | | |
| | | /** |
| | | * 资源请求 get方式 |
| | | * 资源请求 get方式 统一入口类 |
| | | * |
| | | * @param service 请求接口方式 |
| | | * @param request 请求对象 查询头信息 url等信息 |
| | |
| | | ResponseEntity<String> responseEntity = null; |
| | | try { |
| | | Map<String, String> headers = new HashMap<String, String>(); |
| | | //todo 封装请求中的 头信息 |
| | | this.getRequestInfo(request, headers); |
| | | //todo 将接口编码(服务编码) 放入头信息 通过头信息的方式 传递到后端 |
| | | headers.put(CommonConstant.HTTP_SERVICE, service); |
| | | // todo 请求方式 放入到头信息 |
| | | headers.put(CommonConstant.HTTP_METHOD, CommonConstant.HTTP_METHOD_GET); |
| | | logger.debug("api:{} 请求报文为:{},header信息为:{}", "", headers); |
| | | //todo 获取由 PageProcessAspect aop 拦截封装的 pd 页面数据对象 |
| | | IPageData pd = (IPageData) request.getAttribute(CommonConstant.CONTEXT_PAGE_DATA); |
| | | //todo 根据登录用户 的权限 校验 用户是否有权限访问该接口,此时的"/app/" + service 为 开发这账户 菜单权限下的资源地址 |
| | | privilegeSMOImpl.hasPrivilege(restTemplate, pd, "/app/" + service); |
| | | //todo 进入 接口相关 业务处理 |
| | | responseEntity = apiSMOImpl.doApi(JSONObject.toJSONString(getParameterStringMap(request)), headers, request); |
| | | |
| | | } catch (Throwable e) { |
| New file |
| | |
| | | /** |
| | | * 入口控制类 |
| | | * |
| | | * // 1.0 系统基本入口类为 ./app/AppController 通过 servicePost 或者 serviceGet 拦截 请求 |
| | | * 请求地址为 /app/xxx.xxx 其中xxx.xxx 统称为 serviceCode 服务编码或者接口编码 |
| | | * |
| | | * 数据流转流程: AppController.java下的servicePost 接受请求--->ApiSMOImpl.java下的doApi---> ApiServiceSMOImpl下的service方法 --> ApiServiceSMOImpl下的dealCmd方法 |
| | | * --->java110-service 模块下的 CmdApi 类 --->CmdServiceSMOImpl.java 的 cmd方法 ---> ServiceCmdListener.java 下的cmd 类 ---> 到具体的serviceCode 对应的cmd文件 |
| | | * |
| | | * //2.0 支付 三方支付系统通知 统一有 ./app/payment 下的类来处理 |
| | | * |
| | | * // 3.0 智能电表通知 通过 ./app/smartWeter 下的类来处理 |
| | | * |
| | | * // 4.0 智能充电桩 通过./app/charge 下的类来处理 |
| | | */ |
| | | package com.java110.boot.controller; |
| | |
| | | public interface IApiServiceSMO { |
| | | |
| | | /** |
| | | * 业务统一处理服务方法 |
| | | * 业务统一处理服务方法 应用是否有接口权限校验 |
| | | * @param reqJson 请求报文json |
| | | * @return |
| | | */ |
| | |
| | | |
| | | public interface IApiSMO { |
| | | |
| | | public ResponseEntity<String> doApi(String body, Map<String, String> headers, HttpServletRequest request) throws UnsupportedEncodingException; |
| | | /** |
| | | * 主要完成员工是否有访问小区的权限校验 |
| | | * @param body |
| | | * @param headers |
| | | * @param request |
| | | * @return |
| | | * @throws UnsupportedEncodingException |
| | | */ |
| | | ResponseEntity<String> doApi(String body, Map<String, String> headers, HttpServletRequest request) throws UnsupportedEncodingException; |
| | | } |
| | |
| | | |
| | | IPageData pd = (IPageData) request.getAttribute(CommonConstant.CONTEXT_PAGE_DATA); |
| | | |
| | | |
| | | //todo 校验员工时 是否有访问小区的权限 |
| | | ComponentValidateResult result = this.validateStoreStaffCommunityRelationship(pd, restTemplate); |
| | | //todo 如果 登录用户不为空 则将 前段传递的user-id 重写 |
| | | if (!StringUtil.isEmpty(result.getLoginUserId())) { |
| | | headers.remove("user-id"); |
| | | headers.remove("user_id"); |
| | | headers.put("user-id", result.getUserId()); |
| | | headers.put("user_id", result.getUserId()); |
| | | headers.put("login-user-id",result.getLoginUserId()); |
| | | // if (!StringUtil.isEmpty(result.getUserName())) { |
| | | // headers.put("user-name", URLEncoder.encode(result.getUserName(), "UTF-8")); |
| | | // } |
| | | } |
| | | |
| | | // todo 如果 商户不为空则 商户ID写入只头信息中 这里的商户ID 可以是物业ID 或者商家ID |
| | | if (!StringUtil.isEmpty(result.getStoreId())) { |
| | | headers.remove("store-id"); |
| | | headers.put("store-id", result.getStoreId()); |
| | |
| | | headers.put("user-id", "-1"); |
| | | } |
| | | headers.put("store-id", result.getStoreId()); |
| | | // todo 应用是否有接口权限校验 |
| | | ResponseEntity<String> responseEntity = apiServiceSMOImpl.service(body, headers); |
| | | return responseEntity; |
| | | } |
| | |
| | | String resJson = ""; |
| | | |
| | | try { |
| | | //在post和 put 时才存在报文加密的情况 |
| | | //todo 在post和 put 时才存在报文加密的情况 |
| | | if ("POST,PUT".contains(headers.get(CommonConstant.HTTP_METHOD))) { |
| | | reqJson = decrypt(reqJson, headers); |
| | | } |
| | | |
| | | //1.0 创建数据流 appId serviceCode |
| | | //todo 1.0 创建数据流 appId serviceCode |
| | | dataFlow = DataFlowFactory.newInstance(ApiDataFlow.class).builder(reqJson, headers); |
| | | |
| | | //2.0 加载配置信息 |
| | | //todo 2.0 加载配置信息 |
| | | initConfigData(dataFlow); |
| | | |
| | | //3.0 校验 APPID是否有权限操作serviceCode |
| | | //todo 3.0 校验 APPID是否有权限操作serviceCode |
| | | judgeAuthority(dataFlow); |
| | | |
| | | //6.0 调用下游系统 |
| | | //todo 6.0 调用下游系统 |
| | | invokeBusinessSystem(dataFlow); |
| | | |
| | | responseEntity = dataFlow.getResponseEntity(); |
| | |
| | | DataFlowFactory.addCostTime(dataFlow, "judgeAuthority", "鉴权耗时", startDate); |
| | | throw new NoAuthorityException(ResponseConstant.RESULT_CODE_NO_AUTHORITY_ERROR, "requestTime 格式不对,遵循yyyyMMddHHmmss格式"); |
| | | } |
| | | //用户ID校验 |
| | | //todo 用户ID校验 |
| | | if (StringUtil.isNullOrNone(dataFlow.getUserId())) { |
| | | throw new NoAuthorityException(ResponseConstant.RESULT_CODE_NO_AUTHORITY_ERROR, "USER_ID 不能为空"); |
| | | } |
| | | |
| | | |
| | | //判断 AppId 是否有权限操作相应的服务 |
| | | //todo 判断 AppId 是否有权限操作相应的服务 |
| | | AppService appService = DataFlowFactory.getService(dataFlow, dataFlow.getRequestHeaders().get(CommonConstant.HTTP_SERVICE)); |
| | | |
| | | //这里调用缓存 查询缓存信息 |
| | |
| | | */ |
| | | private void invokeBusinessSystem(ApiDataFlow dataFlow) throws BusinessException { |
| | | Date startDate = DateUtil.getCurrentDate(); |
| | | //拿到当前服务 |
| | | //todo 拿到当前服务 |
| | | AppService appService = DataFlowFactory.getService(dataFlow, dataFlow.getRequestHeaders().get(CommonConstant.HTTP_SERVICE)); |
| | | //这里对透传类处理 |
| | | //todo 这里对透传类处理,目前很少用到,可以不用关注 |
| | | if ("NT".equals(appService.getIsInstance())) { |
| | | //如果是透传类 请求方式必须与接口提供方调用方式一致 |
| | | String httpMethod = dataFlow.getRequestCurrentHeaders().get(CommonConstant.HTTP_METHOD); |
| | |
| | | //dataFlow.setApiCurrentService(ServiceCodeConstant.SERVICE_CODE_DO_SERVICE_TRANSFER); |
| | | doNT(appService, dataFlow, dataFlow.getReqJson()); |
| | | return; |
| | | } else if ("T".equals(appService.getIsInstance())) { |
| | | //如果是透传类 请求方式必须与接口提供方调用方式一致 |
| | | } else if ("T".equals(appService.getIsInstance())) { // todo 通过透传方式 调用 目前很少用到,可以不用关注 |
| | | //todo 如果是透传类 请求方式必须与接口提供方调用方式一致 |
| | | String httpMethod = dataFlow.getRequestCurrentHeaders().get(CommonConstant.HTTP_METHOD); |
| | | if (!appService.getMethod().equals(httpMethod)) { |
| | | throw new ListenerExecuteException(ResponseConstant.RESULT_CODE_ERROR, |
| | |
| | | } |
| | | doTransfer(appService, dataFlow, dataFlow.getReqJson()); |
| | | return; |
| | | } else if ("CMD".equals(appService.getIsInstance())) { |
| | | } else if ("CMD".equals(appService.getIsInstance())) { // todo 微服务调用方式,目前主要用这种方式调度分发 到不同的微服务,这里是通过c_service 中配置 调用到不同的微服务 |
| | | //如果是透传类 请求方式必须与接口提供方调用方式一致 |
| | | String httpMethod = dataFlow.getRequestCurrentHeaders().get(CommonConstant.HTTP_METHOD); |
| | | if (!appService.getMethod().equals(httpMethod)) { |
| | | throw new ListenerExecuteException(ResponseConstant.RESULT_CODE_ERROR, |
| | | "服务【" + appService.getServiceCode() + "】调用方式不对请检查,当前请求方式为:" + httpMethod); |
| | | } |
| | | // todo 根据接口编码找到 appService 也就是c_service 表中的内容 |
| | | dealCmd(appService, dataFlow, dataFlow.getReqJson()); |
| | | return; |
| | | } else { |
| | |
| | | oId = headers.get(OrderDto.O_ID).get(0); |
| | | } |
| | | |
| | | //进入databus |
| | | if (!CommonConstant.HTTP_METHOD_GET.equals(appService.getMethod())) { |
| | | |
| | | // dealDatabus(serviceCode, reqJson, oId); |
| | | } |
| | | // //进入databus |
| | | // if (!CommonConstant.HTTP_METHOD_GET.equals(appService.getMethod())) { |
| | | // |
| | | // // dealDatabus(serviceCode, reqJson, oId); |
| | | // } |
| | | |
| | | |
| | | } catch (HttpStatusCodeException e) { //这里spring 框架 在4XX 或 5XX 时抛出 HttpServerErrorException 异常,需要重新封装一下 |
| | |
| | | dataFlow.setResponseEntity(responseEntity); |
| | | } |
| | | |
| | | /** |
| | | * 开始调度微服务 |
| | | * @param appService |
| | | * @param dataFlow |
| | | * @param reqJson |
| | | */ |
| | | private void dealCmd(AppService appService, ApiDataFlow dataFlow, JSONObject reqJson) { |
| | | Map<String, String> reqHeader = dataFlow.getRequestCurrentHeaders(); |
| | | HttpHeaders header = new HttpHeaders(); |
| | | //todo 对头信息重新包装 |
| | | for (String key : dataFlow.getRequestCurrentHeaders().keySet()) { |
| | | header.add(key, reqHeader.get(key)); |
| | | } |
| | | //todo 用户信息再次包装 |
| | | if (reqHeader.containsKey(CommonConstant.USER_ID) |
| | | && (!reqJson.containsKey("userId") || StringUtil.isEmpty(reqJson.getString("userId")))) { |
| | | reqJson.put("userId", reqHeader.get(CommonConstant.USER_ID)); |
| | |
| | | |
| | | serviceCode = serviceCode.startsWith("/") ? serviceCode : ("/" + serviceCode); |
| | | |
| | | //todo 组装调用微服务的地址 |
| | | String requestUrl = "http://127.0.0.1:8008/cmd" + serviceCode; |
| | | // |
| | | ResponseEntity responseEntity = null; |
| | | //todo url 带了地址这里 拼接 |
| | | if (!StringUtil.isNullOrNone(orgRequestUrl)) { |
| | | String param = orgRequestUrl.contains("?") ? orgRequestUrl.substring(orgRequestUrl.indexOf("?") + 1, orgRequestUrl.length()) : ""; |
| | | requestUrl += ("?" + param); |
| | | } |
| | | try { |
| | | //todo http的方式调用微服务,相应的java类可以到相应微服务下的cmd下根据serviceCode 的寻找 |
| | | //todo 这里会调用到 java110-service 模块下的 CmdApi 类,这个类各个微服务都会集成 |
| | | responseEntity = outRestTemplate.exchange(requestUrl, HttpMethod.POST, httpEntity, String.class); |
| | | HttpHeaders headers = responseEntity.getHeaders(); |
| | | String oId = "-1"; |
| | |
| | | oId = headers.get(OrderDto.O_ID).get(0); |
| | | } |
| | | |
| | | } catch (HttpStatusCodeException e) { //这里spring 框架 在4XX 或 5XX 时抛出 HttpServerErrorException 异常,需要重新封装一下 |
| | | } catch (HttpStatusCodeException e) { //todo 这里spring 框架 在4XX 或 5XX 时抛出 HttpServerErrorException 异常,需要重新封装一下 |
| | | logger.error("请求下游服务【" + requestUrl + "】异常,参数为" + httpEntity + e.getResponseBodyAsString(), e); |
| | | String body = e.getResponseBodyAsString(); |
| | | |
| | |
| | | if (responseEntity.getStatusCode() != HttpStatus.OK) { |
| | | responseEntity = ResultVo.createResponseEntity(ResultVo.CODE_ERROR, String.valueOf(responseEntity.getBody())); |
| | | dataFlow.setResponseEntity(responseEntity); |
| | | |
| | | return; |
| | | } |
| | | if (StringUtils.isEmpty(responseEntity.getBody() + "")) { |