发送模板消息

url和miniprogram都是非必填字段，若都不传则模板无跳转；
若都传，会优先跳转至小程序。
开发者可根据实际需要选择其中一种跳转方式即可。
当用户的微信客户端版本不支持跳小程序时，将会跳转至url

@param toUser
@param templateId
@param url
@param miniProgram
@param messages
@return 消息ID
3700
public long send(String toUser, String templateId, String url, MiniProgram miniProgram, Map<String, Data> messages) {
        String sendUrl = WxEndpoint.get("url.template.send");
        MessageWrapper messageWrapper = new MessageWrapper();
        messageWrapper.setToUser(toUser);
        messageWrapper.setTemplateId(templateId);

        if(url != null) {
            messageWrapper.setUrl(url);
        }
        if(miniProgram != null) {
            messageWrapper.setMiniProgram(miniProgram);
        }
        messageWrapper.setData(messages);

        String json = JsonMapper.defaultMapper().toJson(messageWrapper);

        logger.debug("template message, send message: {}", json);
        String response = wxClient.post(sendUrl, json);
        Map<String, Object> result = JsonMapper.defaultMapper().json2Map(response);

        if(result.containsKey("msgid")) {
            return ((Number)result.get("msgid")).longValue();
        } else {
            throw new WxRuntimeException(999, "send template message failed.");
        }
    }
1179803
public static String userTagAction(String property, ServletRequest req) {

        CmsFlexController controller = CmsFlexController.getController(req);

        CmsObject cms = controller.getCmsObject();
        CmsUser user = cms.getRequestContext().getCurrentUser();

        if (property == null) {
            property = USER_PROPERTIES[0];
        }

        String result = null;
        switch (USER_PROPERTIES_LIST.indexOf(property)) {
            case 0: // name
                result = user.getName();
                break;
            case 1: // firstname
                result = user.getFirstname();
                break;
            case 2: // lastname
                result = user.getLastname();
                break;
            case 3: // email
                result = user.getEmail();
                break;
            case 4: // street
                result = user.getAddress();
                break;
            case 5: // zip
                result = user.getZipcode();
                break;
            case 6: // city
                result = user.getCity();
                break;
            case 7: // description
                result = user.getDescription(cms.getRequestContext().getLocale());
                break;
            // following 3 attributes are no longer supported
            case 8: // group
            case 9: // currentgroup
            case 10: // defaultgroup
                result = "";
                break;
            case 11: // otherstuff
                Iterator<String> it = user.getAdditionalInfo().keySet().iterator();
                CmsMessageContainer msgContainer = Messages.get().container(Messages.GUI_TAG_USER_ADDITIONALINFO_0);
                result = Messages.getLocalizedMessage(msgContainer, req);
                while (it.hasNext()) {
                    Object o = it.next();
                    result += " " + o + "=" + user.getAdditionalInfo((String)o);
                }
                break;
            case 12: // institution
                result = user.getInstitution();
                break;
            default:
                msgContainer = Messages.get().container(Messages.GUI_ERR_INVALID_USER_PROP_1, property);
                result = Messages.getLocalizedMessage(msgContainer, req);
        }

        return result;
    }
975750
function(userId, messageId, callback) {
      if (!userId) return callback(new Error(utils.i18n.messages.userId));
      messageId = utils.toUUID(messageId);
      if (!messageId) return callback(new Error(utils.i18n.messages.mid));
      utils.debug('Message getFromUser: ' + userId + ', ' + messageId);

      request.get({
        path: '/users/' + querystring.escape(userId) + '/messages/' + messageId
      }, callback);
    }
1182269
public MimeMessage newMessage( Session session )
    throws MessagingException, EmailException
  {
    MimeMessage message = new MimeMessage( session );

    // populate the addresses and subject.
    populate(message);

    // create the multipart content.
    MimeMultipart containingMultipart = new MimeMultipart(EmailUtil.SUBTYPE_MIXED);

    // create the message multipart content.
    MimeMultipart messageMultipart = new MimeMultipart(EmailUtil.SUBTYPE_ALTERNATIVE);
    containingMultipart.addBodyPart(EmailUtil.newMultipartBodyPart(messageMultipart));

    // create the text part.
    messageMultipart.addBodyPart(EmailUtil.newTextBodyPart(getText()));

    // create the html part.
    MimeMultipart htmlMultipart = new MimeMultipart(EmailUtil.SUBTYPE_RELATED);
    htmlMultipart.addBodyPart(EmailUtil.newHtmlBodyPart(getHtml()));
    messageMultipart.addBodyPart(EmailUtil.newMultipartBodyPart(htmlMultipart));

    /*
    // iterate over the html context, creating new elements for each.
    Iterator htmlAttachmentIterator = htmlAttachmentMap.entrySet().iterator();
    while( htmlAttachmentIterator.hasNext() ) {
      Map.Entry htmlAttachmentEntry = (Map.Entry)htmlAttachmentIterator.next();
      htmlMultipart.addBodyPart(EmailUtil.newHtmlAttachmentPart((URL)htmlAttachmentEntry.getKey(), (String)htmlAttachmentPart.getValue());
    }

    // add the html multipart to the message multipart.
    messageMultipart.addBodyPart(EmailUtil.newMultipartBodyPart(htmlMultipartBodyPart));
    */
  
      
    // create the attachments.
    for( URL attachmentUrl : attachmentUrlSet ) {
      containingMultipart.addBodyPart(EmailUtil.newAttachmentBodyPart(attachmentUrl, null));
    }
    try {
      for( StringAttachment attachment : attachmentStrings ) {
        containingMultipart.addBodyPart(EmailUtil.newAttachmentBodyPart(attachment.getContent(), null, attachment.getMimeType(), attachment.getFilename()));
      }
    } catch (IOException ioe) {
      throw new MessagingException("Unable to create attachment for string.", ioe);
    }

    message.setContent(containingMultipart);

    // save the changes.
    message.saveChanges();

    // return the email.
    return message;
  }
779630
protected void logError(String id, String messageTemplate, Object... parameters) {
    if(delegateLogger.isErrorEnabled()) {
      String msg = formatMessageTemplate(id, messageTemplate);
      delegateLogger.error(msg, parameters);
    }
  }
605394
protected URL getNearTemplateURL(String jarFile, String template) {
        if (jarFile != null) {
            Enumeration<URL> urls = IOUtils.getResources(template);
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                if (url.getPath().startsWith(jarFile)) {
                    return url;
                }
            }
        }
        return null;
    }
1101748
private ThymeLeafTemplateImplementation getTemplateByURL(URL url) {
        Collection<ThymeLeafTemplateImplementation> list = registrations.keySet();
        for (ThymeLeafTemplateImplementation template : list) {
            if (template.getURL().sameFile(url)) {
                return template;
            }
        }
        return null;
    }
402967
public synchronized HttpMessage pollPath(HttpMessage msg) {
        SiteNode resultNode = null;
        URI uri = msg.getRequestHeader().getURI();
        
        SiteNode parent = getRoot();
        String folder;
        
        try {
            String host = getHostName(uri);
            
            // no host yet
            parent = findChild(parent, host);
            if (parent == null) {
                return null;
        	}
            List<String> path = model.getSession().getTreePath(uri);
            if (path.size() == 0) {
            	// Its a top level node
            	resultNode = parent;
            }
            for (int i=0; i < path.size(); i++) {
            	folder = path.get(i);
                if (folder != null && !folder.equals("")) {
                    if (i == path.size()-1) {
                        String leafName = getLeafName(folder, msg);
                        resultNode = findChild(parent, leafName);
                    } else {
                        parent = findChild(parent, folder);
                        if (parent == null) {
                            return null;
                        }
                    }
                }
            }
        } catch (URIException e) {
            // ZAP: Added error
            log.error(e.getMessage(), e);
        }
        
        if (resultNode == null || resultNode.getHistoryReference() == null) {
            return null;
        }
        
        HttpMessage nodeMsg = null;
        try {
            nodeMsg = resultNode.getHistoryReference().getHttpMessage();
        } catch (Exception e) {
            // ZAP: Added error
            log.error(e.getMessage(), e);
        }
        return nodeMsg;
    }
1079128
public static <T> Middleware<AsyncHandler<T>, AsyncHandler<Response<ByteString>>> htmlSerialize(
      final String templateName) {
    return handler ->
        requestContext -> handler.invoke(requestContext)
            .thenApply(result -> Response
                .forPayload(serialize(templateName, result))
                .withHeader(CONTENT_TYPE, HTML));
  }
1304773
@SuppressWarnings("unchecked")
    @Override
	public <T extends ApiUser> T getUserById(int id) {
	    User sfsUser = getZone().getUserById(id);
	    if(sfsUser != null && sfsUser.containsProperty(APIKey.USER))
	        return (T) sfsUser.getProperty(APIKey.USER);
	    return null;
	}
545761
public String fetch(String code) {
        String url = WxEndpoint.get("url.template.get");
        String json = String.format("{\"template_id_short\":\"%s\"}", code);

        logger.debug("template message, get template id: {}", json);
        String response = wxClient.post(url, json);
        Map<String, Object> result = JsonMapper.defaultMapper().json2Map(response);

        if(result.containsKey("template_id")) {
            return result.get("template_id").toString();
        } else {
            throw new WxRuntimeException(999, "fetch template id failed.");
        }
    }
1188486
MessageToClient _createMessageToClient(MessageFromClient message, T session) {
		MessageToClient messageToClient = new MessageToClient();
		messageToClient.setId(message.getId());
		try {
			Class cls = Class.forName(message.getDataService());
			Object dataService = this.getDataService(session, cls);
			logger.debug("Process message {}", message);
			List<Object> arguments = getArrayList();
			Method method = methodServices.getMethodFromDataService(cls, message, arguments);
			injectSession(method.getParameterTypes(), arguments, session);
			messageToClient.setResult(method.invoke(dataService, arguments.toArray()));
			if (method.isAnnotationPresent(JsonMarshaller.class)) {
				messageToClient.setJson(argumentServices.getJsonResultFromSpecificMarshaller(method.getAnnotation(JsonMarshaller.class), messageToClient.getResponse()));
			}
			try {
				Method nonProxiedMethod = methodServices.getNonProxiedMethod(cls, method.getName(), method.getParameterTypes());
				messageToClient.setDeadline(cacheManager.processCacheAnnotations(nonProxiedMethod, message.getParameters()));
			} catch (NoSuchMethodException ex) {
				logger.error("Fail to process extra annotations (JsCacheResult, JsCacheRemove) for method : " + method.getName(), ex);
			}
			logger.debug("Method {} proceed messageToClient : {}.", method.getName(), messageToClient);
		} catch (InvocationTargetException ex) {
			Throwable cause = ex.getCause();
			if (ConstraintViolationException.class.isInstance(cause)) {
				messageToClient.setConstraints(constraintServices.extractViolations((ConstraintViolationException) cause));
			} else {
				messageToClient.setFault(faultServices.buildFault(cause));
			}
		} catch (Throwable ex) {
			messageToClient.setFault(faultServices.buildFault(ex));
		}
		return messageToClient;
	}
563786
private boolean processTemplate(ApplicationRequest appRequest,
                                    ApplicationResponse appResponse)
        throws IOException {
        
        // check if redirect or erroring out
        if (appResponse.isRedirectOrError()) {
            return false;
        }

        // set initial content type and helper attributes
        appResponse.setContentType("text/html");
        appRequest.setAttribute(this.getClass().getName(), this);

        // lookup template
        Template template = (Template)appRequest.getTemplate();

        // process as resource if no template available
        if (template == null) {
            if (!processResource(appRequest, appResponse)) {
                appResponse.sendError(404);
                return false;
            }
            return true;
        }
        
        long endTime = 0L;
        long startTime = 0L;
        long contentLength = 0;

        TemplateStats templateStats = null; 
        if (mInstrumentationEnabled) {
            templateStats = mTeaServletRequestStats.getStats(template.getName());
        }
        
        try {
	        Object[] params = null;
	        try {    
	            if (templateStats != null) {
	                templateStats.incrementServicing();
	            }

	            // Fill in the parameters to pass to the template.
	            Class<?>[] paramTypes = template.getParameterTypes();
	            if (paramTypes.length == 0) {
	                params = NO_PARAMS;
	            }
	            else {
	                params = new Object[paramTypes.length];
	                String[] paramNames = template.getParameterNames();
	                for (int i=0; i<paramNames.length; i++) {
	                    String paramName = paramNames[i];
	                    if (paramName == null) {
	                        continue;
	                    }
	                
	                    Class<?> paramType = paramTypes[i];
	                
	                    if (!paramType.isArray()) {
	                        String value = appRequest.getParameter(paramName);
	                        if (value == null || paramType == String.class) {
	                            params[i] = value;
	                        }
	                        else {
	                            params[i] = convertParameter(value, paramType);
	                        }
	                    }
	                    else {
	                        String[] values =
	                            appRequest.getParameterValues(paramName);
	                        if (values == null || paramType == String[].class) {
	                            params[i] = values;
	                        }
	                        else {
	                            paramType = paramType.getComponentType();
	                            Object converted =
	                                Array.newInstance(paramType, values.length);
	                            params[i] = converted;
	                            for (int j=0; j<values.length; j++) {
	                                Array.set
	                                    (converted, j,
	                                     convertParameter(values[j], paramType));
	                            }
	                        }
	                    }
	                }
	            }
	
	            startTime = System.currentTimeMillis();
	            try {
	                try {
	                    appRequest.getTemplate()
	                        .execute(appResponse.getHttpContext(), params);
	                }
	                catch (ContextCreationException cce) {
	                    // unwrap the inner exception
	                    throw (Exception)cce.getUndeclaredThrowable();
	                }
	            }
	            catch (AbortTemplateException e) {
	                if (DEBUG) {
	                    mLog.debug("Template execution aborted!");
	                    mLog.debug(e);
	                }
	            }
	            catch (RuntimeException e) {
	                if (getEngine()
	                    .getTemplateSource().isExceptionGuardianEnabled()) {
	                    // Just log the error and use what the template wrote out.
	                    mLog.error(e);
	                }
	                else {
	                    throw new ServletException(e);
	                }
	            }
	            catch (IOException e) {
	            	// TODO: shouldn't we be throwing this as a ServletException?
	            	//       otherwise its not logged to the TeaLog.
	                throw e;
	            }
	            catch (ServletException e) {
	                throw e;
	            }
	            catch (Exception e) {
	                throw new ServletException(e);
	            }
	            // TODO: shouldn't we be catching errors and not just exceptions?
	        	//       otherwise its not logged to the TeaLog.
	            finally {
					endTime = System.currentTimeMillis();
					if (appRequest instanceof TeaServletStats) {
						long duration = endTime - startTime;
						((TeaServletStats)appRequest).setTemplateDuration(duration);
					}
	            }
	
	            if (DEBUG) {
	                mLog.debug("Finished executing template");
	            }
	        }
	        catch (ServletException e) {
	            // Log exception
	            StringBuffer msg = new StringBuffer();
	            msg.append("Error processing request for ");
	            msg.append(appRequest.getRequestURI());
	            if (appRequest.getQueryString() != null) {
	                msg.append('?');
	                msg.append(appRequest.getQueryString());
	            }
	            mLog.error(msg.toString());
	
	            Throwable t = e;
	            while (t instanceof ServletException) {
	                e = (ServletException)t;
	                if (e.getRootCause() != null) {
	                    String message = e.getMessage();
	                    if (message != null && message.length() > 0) {
	                        mLog.error(message);
	                    }
	                    mLog.error(t = e.getRootCause());
	                }
	                else {
	                    mLog.error(e);
	                    break;
	                }
	            }
	
	            // Internal server error unless header is already set
	            if (!appResponse.isRedirectOrError()) {
	                String displayMessage = e.getLocalizedMessage();
	                if (displayMessage == null || displayMessage.length() == 0) {
	                    appResponse.sendError
	                        (ApplicationResponse.SC_INTERNAL_SERVER_ERROR);
	                }
	                else {
	                    appResponse.sendError
	                        (ApplicationResponse.SC_INTERNAL_SERVER_ERROR, displayMessage);
	                }
	            }
	          
	        }
	        contentLength = appResponse.getResponseBuffer().getByteCount();
	        appResponse.finish();
	        if (templateStats != null) {
    	        templateStats.decrementServicing();
    	        templateStats.log(startTime, endTime, contentLength, params);
	        }
        } catch (Exception e) {
            if (templateStats != null) {
                templateStats.decrementServicing();
            }
        }
		return true;
    }
100535
public SendMessageResult sendMessage(SignalServiceAddress             recipient,
                                       Optional<UnidentifiedAccessPair> unidentifiedAccess,
                                       SignalServiceDataMessage         message)
      throws UntrustedIdentityException, IOException
  {
    byte[]            content   = createMessageContent(message);
    long              timestamp = message.getTimestamp();
    SendMessageResult result    = sendMessage(recipient, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, content, false);

    if ((result.getSuccess() != null && result.getSuccess().isNeedsSync()) || (unidentifiedAccess.isPresent() && isMultiDevice.get())) {
      byte[] syncMessage = createMultiDeviceSentTranscriptContent(content, Optional.of(recipient), timestamp, Collections.singletonList(result));
      sendMessage(localAddress, Optional.<UnidentifiedAccess>absent(), timestamp, syncMessage, false);
    }

    if (message.isEndSession()) {
      store.deleteAllSessions(recipient.getNumber());

      if (eventListener.isPresent()) {
        eventListener.get().onSecurityEvent(recipient);
      }
    }

    return result;
  }
764437
@Override
    @SuppressWarnings("checkstyle:linelength")
    public Page<MessageInteraction> firstPage(final TwilioRestClient client) {
        Request request = new Request(
            HttpMethod.GET,
            Domains.PROXY.toString(),
            "/v1/Services/" + this.pathServiceSid + "/Sessions/" + this.pathSessionSid + "/Participants/" + this.pathParticipantSid + "/MessageInteractions",
            client.getRegion()
        );

        addQueryParams(request);
        return pageForRequest(client, request);
    }
872055
public function render(string $templateID, string $language, \Swift_Mime_SimpleMessage &$message)
    {
        // Find Template
        $template = $this->entityManager
            ->getRepository(MailTemplate::class)
            ->findOneBy(['templateId' => $templateID, 'status' => true, 'language' => $language]);

        if ((null === $template) && ($language !== $this->defaultLanguage)) {
            return $this->render($templateID, $this->defaultLanguage, $message);
        }

        // Render Template
        if (null !== $template) {
            // Render Body
            try {
                $message->setBody(
                    $this->twig->createTemplate($template->getTemplate())->render(unserialize($message->getBody())),
                    $message->getContentType(),
                    $message->getCharset());
            } catch (\Exception $e) {
            }

            // Set Header
            if (!empty($template->getFromName()) && !empty($template->getFromEmail())) {
                $message->setFrom($template->getFromEmail(), $template->getFromName());
            }

            // Set Subject
            if (!empty($template->getSubject())) {
                $message->setSubject($template->getSubject());
            }

            return true;
        }

        return false;
    }
835253
function sendTemplateSMS($to,$datas,$tempId)
    {
        //主帐号鉴权信息验证，对必选参数进行判空。
        $auth=$this->accAuth();
        if($auth!=""){
            return $auth;
        }
        // 拼接请求包体
        if($this->BodyType=="json"){
           $data="";
           for($i=0;$i<count($datas);$i++){
              $data = $data. "'".$datas[$i]."',";
           }
           $body= "{'to':'$to','templateId':'$tempId','appId':'$this->AppId','datas':[".$data."]}";
        }else{
           $data="";
           for($i=0;$i<count($datas);$i++){
              $data = $data. "<data>".$datas[$i]."</data>";
           }
           $body="<TemplateSMS>
                    <to>$to</to> 
                    <appId>$this->AppId</appId>
                    <templateId>$tempId</templateId>
                    <datas>".$data."</datas>
                  </TemplateSMS>";
        }
        $this->showlog("request body = ".$body);
        // 大写的sig参数
        $sig =  strtoupper(md5($this->AccountSid . $this->AccountToken . $this->Batch));
        // 生成请求URL
        $url="https://$this->ServerIP:$this->ServerPort/$this->SoftVersion/Accounts/$this->AccountSid/SMS/TemplateSMS?sig=$sig";
        $this->showlog("request url = ".$url);
        // 生成授权：主帐户Id + 英文冒号 + 时间戳。
        $authen = base64_encode($this->AccountSid . ":" . $this->Batch);
        // 生成包头
        $header = array("Accept:application/$this->BodyType","Content-Type:application/$this->BodyType;charset=utf-8","Authorization:$authen");
        // 发送请求
        $result = $this->curl_post($url,$body,$header);
        $this->showlog("response body = ".$result);
        if($this->BodyType=="json"){//JSON格式
           $datas=json_decode($result);
        }else{ //xml格式
           $datas = simplexml_load_string(trim($result," \t\n\r"));
        }
      //  if($datas == FALSE){
//            $datas = new stdClass();
//            $datas->statusCode = '172003';
//            $datas->statusMsg = '返回包体错误'; 
//        }
        //重新装填数据
        if($datas->statusCode==0){
         if($this->BodyType=="json"){
            $datas->TemplateSMS =$datas->templateSMS;
            unset($datas->templateSMS);
          }
        }

        return $datas;
    }