├── .gitignore ├── RocketMqCurrencyBootSample ├── src │ └── main │ │ ├── resources │ │ ├── application.properties │ │ ├── log4j.properties │ │ ├── applicationContext-nx.xml │ │ ├── applicationContext-zf.xml │ │ └── applicationContext-wd.xml │ │ └── java │ │ └── com │ │ └── atliwen │ │ ├── server │ │ ├── messagelistener │ │ │ ├── MqExceedCount.java │ │ │ ├── ConsumableMessageListenerConsumer.java │ │ │ ├── ForwardedMessageListConsumer.java │ │ │ └── ExternalCallConcurrentlyStatus.java │ │ ├── transaction │ │ │ ├── MyLocalTransactionExecuter.java │ │ │ └── MyTransactionCheckListener.java │ │ └── translation │ │ │ ├── myExternalCallConsumer.java │ │ │ └── myForwardedMessageListConsumer.java │ │ └── Application.java └── pom.xml ├── RocketMqCurrencyBoot ├── src │ └── main │ │ ├── java │ │ └── com │ │ │ └── currencyboot │ │ │ ├── domain │ │ │ └── mq │ │ │ │ ├── AttributeNames.java │ │ │ │ ├── HttpResponse.java │ │ │ │ └── TransferRules.java │ │ │ ├── ConfigClass.java │ │ │ ├── service │ │ │ └── rocketmq │ │ │ │ ├── messagelistener │ │ │ │ └── Interface │ │ │ │ │ ├── MqExceedCountInterface.java │ │ │ │ │ ├── ExternalCallConsumerInterface.java │ │ │ │ │ ├── ForwardedMessageListConsumerInterface.java │ │ │ │ │ ├── MessageListenerConsumerInterface.java │ │ │ │ │ └── MatchingInterface.java │ │ │ │ ├── common │ │ │ │ ├── MessageHelp.java │ │ │ │ ├── ForwardedHelp.java │ │ │ │ └── HttpRequest.java │ │ │ │ ├── spring │ │ │ │ └── SpringContextUtils.java │ │ │ │ ├── MqConsumerStart.java │ │ │ │ ├── DefaultMessageListener.java │ │ │ │ ├── MqConsumer.java │ │ │ │ └── MqProducer.java │ │ │ ├── RocketMqCurrencyBoot.java │ │ │ └── web │ │ │ ├── OrApiTest.java │ │ │ ├── MqProducerController.java │ │ │ └── MqConsumerController.java │ │ └── resources │ │ └── static │ │ ├── css │ │ ├── bootstrapValidator.min.css │ │ ├── myhome.css │ │ └── bootstrap-editable.css │ │ ├── js │ │ ├── mqjs.js │ │ ├── common.js │ │ └── bootstrapValidator.min.js │ │ └── home.html └── pom.xml ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | pom.xml.tag 3 | pom.xml.releaseBackup 4 | pom.xml.versionsBackup 5 | pom.xml.next 6 | release.properties 7 | dependency-reduced-pom.xml 8 | buildNumber.properties 9 | .mvn/timing.properties 10 | /.project 11 | /.classpath 12 | /.gitignore 13 | -------------------------------------------------------------------------------- /RocketMqCurrencyBootSample/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | #Default 12 2 | MQ.COUNT=1 3 | #Default UTF-8 4 | #MQ.Encoding=UTF-8 5 | MQ.consumerGroup=EDIConsumerGroup 6 | MQ.NamesrvAddr=10.10.6.71:9876;10.10.6.72:9876 7 | MQ.ProducerGroupName=EdiProducerGroup 8 | MQ.InstanceName=WebInstance 9 | #Default 20000 10 | #MQ.SendMsgTimeout=20000 -------------------------------------------------------------------------------- /RocketMqCurrencyBootSample/src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # Global logging configuration 2 | log4j.rootLogger=DEBUG, stdout 3 | # Console output... 4 | log4j.logger.org.mybatis = DEBUG 5 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 6 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 7 | log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n 8 | -------------------------------------------------------------------------------- /RocketMqCurrencyBoot/src/main/java/com/currencyboot/domain/mq/AttributeNames.java: -------------------------------------------------------------------------------- 1 | package com.currencyboot.domain.mq; 2 | 3 | public class AttributeNames 4 | { 5 | public AttributeNames() 6 | { 7 | } 8 | 9 | public String getKid() 10 | { 11 | return kid; 12 | } 13 | 14 | public void setKid(String kid) 15 | { 16 | this.kid = kid; 17 | } 18 | 19 | public String kid; 20 | } 21 | -------------------------------------------------------------------------------- /RocketMqCurrencyBoot/src/main/java/com/currencyboot/ConfigClass.java: -------------------------------------------------------------------------------- 1 | package com.currencyboot; 2 | 3 | /** 4 | *

Title: ConfigClass

5 | *

@Description: 配置文件载入类

6 | *

Company:

7 | * @author 李文 8 | * @date 2016年8月3日 上午11:40:28 9 | */ 10 | // @Configuration 11 | // @ImportResource(locations = "classpath:applicationContext-wd.xml") 12 | // public class ConfigClass 13 | // { 14 | // // 注意 载入那个模式的配置文件,就使用那个模式。不可以多个同时使用。 15 | // } -------------------------------------------------------------------------------- /RocketMqCurrencyBoot/src/main/resources/static/css/bootstrapValidator.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * BootstrapValidator (http://bootstrapvalidator.com) 3 | * 4 | * The best jQuery plugin to validate form fields. Designed to use with Bootstrap 3 5 | * 6 | * @version v0.4.5 7 | * @author https://twitter.com/nghuuphuoc 8 | * @copyright (c) 2013 - 2014 Nguyen Huu Phuoc 9 | * @license MIT 10 | */ 11 | 12 | 13 | .bv-form .help-block{margin-bottom:0}.nav-tabs li.bv-tab-success>a{color:#3c763d}.nav-tabs li.bv-tab-error>a{color:#a94442} -------------------------------------------------------------------------------- /RocketMqCurrencyBoot/src/main/java/com/currencyboot/service/rocketmq/messagelistener/Interface/MqExceedCountInterface.java: -------------------------------------------------------------------------------- 1 | package com.currencyboot.service.rocketmq.messagelistener.Interface; 2 | 3 | import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; 4 | import com.alibaba.rocketmq.common.message.MessageExt; 5 | 6 | /** 7 | *

Title: MqExceedCountInterface

8 | *

@Description: Mq超出 设置的处理次数 触发的 接口类

9 | *

Company:

10 | * @author 李文 11 | * @date 2016年8月24日 下午2:53:52 12 | */ 13 | public interface MqExceedCountInterface 14 | { 15 | void exceedCount(String strBody, MessageExt msg, ConsumeConcurrentlyContext context); 16 | } 17 | -------------------------------------------------------------------------------- /RocketMqCurrencyBoot/src/main/java/com/currencyboot/RocketMqCurrencyBoot.java: -------------------------------------------------------------------------------- 1 | package com.currencyboot; 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication; 4 | 5 | /** 6 | *

Title: Application

7 | *

@Description: 启动入口类

8 | *

Company:

9 | * @author 李文 10 | * @date 2016年8月3日 上午11:40:10 11 | */ 12 | @SpringBootApplication 13 | public class RocketMqCurrencyBoot 14 | { 15 | // public static void main(String[] args) 16 | // { 17 | // final ApplicationContext applicationContext = SpringApplication 18 | // .run(RocketMqCurrencyBoot.class, args); 19 | // SpringContextUtils.setApplicationContext(applicationContext); 20 | // } 21 | } 22 | -------------------------------------------------------------------------------- /RocketMqCurrencyBoot/src/main/resources/static/css/myhome.css: -------------------------------------------------------------------------------- 1 | 2 | .navbar-default { 3 | border-color: #CC1A1A; 4 | } 5 | 6 | .pText { 7 | font-weight: 900; 8 | color: #247159; 9 | } 10 | 11 | .navbar-default .navbar-nav > .active > a, .navbar-default .navbar-nav > .open > a { 12 | background-image: linear-gradient(to bottom, rgb(177, 104, 102) 0, rgb(210, 208, 208) 100%); 13 | } 14 | 15 | body { 16 | padding-top: 70px; 17 | } 18 | 19 | .focus { 20 | background-image: linear-gradient(to bottom, rgb(177, 104, 102) 0, rgb(210, 208, 208) 100%); 21 | } 22 | 23 | #bs-example-navbar-collapse-1 ul li { 24 | padding: 0px 0px; 25 | border: 0px; 26 | outline: 0px auto -webkit-focus-ring-color; 27 | } 28 | -------------------------------------------------------------------------------- /RocketMqCurrencyBootSample/src/main/java/com/atliwen/server/messagelistener/MqExceedCount.java: -------------------------------------------------------------------------------- 1 | package com.atliwen.server.messagelistener; 2 | 3 | import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; 4 | import com.alibaba.rocketmq.common.message.MessageExt; 5 | import com.currencyboot.service.rocketmq.messagelistener.Interface.MqExceedCountInterface; 6 | 7 | /** 8 | *

Title: MqExceedCount

9 | *

@Description: 消费超出 设置的容错 次数 后触发的 方法类

10 | *

Company:

11 | * @author 李文 12 | * @date 2016年9月21日 下午2:40:37 13 | */ 14 | public class MqExceedCount implements MqExceedCountInterface 15 | { 16 | @Override 17 | public void exceedCount(String strBody, MessageExt msg, ConsumeConcurrentlyContext context) 18 | { 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /RocketMqCurrencyBootSample/src/main/java/com/atliwen/server/transaction/MyLocalTransactionExecuter.java: -------------------------------------------------------------------------------- 1 | package com.atliwen.server.transaction; 2 | 3 | import com.alibaba.rocketmq.client.producer.LocalTransactionExecuter; 4 | import com.alibaba.rocketmq.client.producer.LocalTransactionState; 5 | import com.alibaba.rocketmq.common.message.Message; 6 | 7 | /** 8 | *

Title: MyLocalTransactionExecuter

9 | *

@Description: 本地事务 实例

10 | *

Company:

11 | * @author 李文 12 | * @date 2016年8月5日 下午3:22:00 13 | */ 14 | public class MyLocalTransactionExecuter implements LocalTransactionExecuter 15 | { 16 | 17 | @Override 18 | public LocalTransactionState executeLocalTransactionBranch(Message msg, Object arg) 19 | { 20 | System.out.println(" 本地事务 " + msg); 21 | return LocalTransactionState.COMMIT_MESSAGE; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /RocketMqCurrencyBootSample/src/main/java/com/atliwen/server/transaction/MyTransactionCheckListener.java: -------------------------------------------------------------------------------- 1 | package com.atliwen.server.transaction; 2 | 3 | import com.alibaba.rocketmq.client.producer.LocalTransactionState; 4 | import com.alibaba.rocketmq.client.producer.TransactionCheckListener; 5 | import com.alibaba.rocketmq.common.message.MessageExt; 6 | 7 | /** 8 | *

Title: MyTransactionCheckListener

9 | *

@Description: 未决事务,服务器回查客户端 示例

10 | *

Company:

11 | * @author 李文 12 | * @date 2016年8月5日 下午3:20:35 13 | */ 14 | public class MyTransactionCheckListener implements TransactionCheckListener 15 | { 16 | 17 | @Override 18 | public LocalTransactionState checkLocalTransactionState(MessageExt msg) 19 | { 20 | System.out.println(" 未决事务 " + msg); 21 | return LocalTransactionState.COMMIT_MESSAGE; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /RocketMqCurrencyBoot/src/main/java/com/currencyboot/service/rocketmq/messagelistener/Interface/ExternalCallConsumerInterface.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.currencyboot.service.rocketmq.messagelistener.Interface; 5 | 6 | import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; 7 | import com.alibaba.rocketmq.common.message.MessageExt; 8 | 9 | /** 10 | *

Title: BaseExternalCallConsumer

11 | *

@Description: 转译 web 服务 消息处理 接口

12 | *

Company:

13 | * @author 李文 14 | * @date 2016年8月1日 上午10:04:46 15 | */ 16 | public interface ExternalCallConsumerInterface 17 | { 18 | /** 19 | * 转译 web 服务 消息处理 20 | * @param strBody 21 | * @param msg 22 | * @param context 23 | * @return 24 | */ 25 | public String MessageConsumer(String strBody, MessageExt msg, ConsumeConcurrentlyContext context); 26 | } 27 | -------------------------------------------------------------------------------- /RocketMqCurrencyBootSample/src/main/java/com/atliwen/server/translation/myExternalCallConsumer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.atliwen.server.translation; 5 | 6 | import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; 7 | import com.alibaba.rocketmq.common.message.MessageExt; 8 | import com.currencyboot.service.rocketmq.messagelistener.Interface.ExternalCallConsumerInterface; 9 | 10 | /** 11 | *

Title: myExternalCallConsumer

12 | *

@Description: 示例 外调模式 消息加工处理类

13 | *

Company:

14 | * @author 李文 15 | * @date 2016年5月31日 下午3:41:47 16 | */ 17 | public class myExternalCallConsumer implements ExternalCallConsumerInterface 18 | { 19 | 20 | @Override 21 | public String MessageConsumer(String strBody, MessageExt msg, 22 | ConsumeConcurrentlyContext context) 23 | { 24 | 25 | return "我了个去"; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /RocketMqCurrencyBoot/src/main/java/com/currencyboot/service/rocketmq/messagelistener/Interface/ForwardedMessageListConsumerInterface.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.currencyboot.service.rocketmq.messagelistener.Interface; 5 | 6 | import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; 7 | import com.alibaba.rocketmq.common.message.MessageExt; 8 | 9 | /** 10 | *

Title: BaseForwardedMessageListConsumer

11 | *

@Description: 转译 转发 消息处理 接口

12 | *

Company:

13 | * @author 李文 14 | * @date 2016年8月1日 上午10:04:54 15 | */ 16 | public interface ForwardedMessageListConsumerInterface 17 | { 18 | 19 | /** 20 | * 转译 转发 消息处理 21 | * @param strBody 22 | * @param msg 23 | * @param context 24 | * @return 25 | */ 26 | public String MessageConsumer(String strBody, MessageExt msg, ConsumeConcurrentlyContext context); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /RocketMqCurrencyBootSample/src/main/java/com/atliwen/server/translation/myForwardedMessageListConsumer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.atliwen.server.translation; 5 | 6 | import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; 7 | import com.alibaba.rocketmq.common.message.MessageExt; 8 | import com.currencyboot.service.rocketmq.messagelistener.Interface.ForwardedMessageListConsumerInterface; 9 | 10 | /** 11 | *

Title: myForwardedMessageListConsumer

12 | *

@Description: 示例 转发模式 消息加工处理类

13 | *

Company:

14 | * @author 李文 15 | * @date 2016年5月31日 下午3:56:07 16 | */ 17 | public class myForwardedMessageListConsumer implements ForwardedMessageListConsumerInterface 18 | { 19 | 20 | @Override 21 | public String MessageConsumer(String strBody, MessageExt msg, 22 | ConsumeConcurrentlyContext context) 23 | { 24 | return strBody + "我了个去"; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /RocketMqCurrencyBoot/src/main/java/com/currencyboot/service/rocketmq/messagelistener/Interface/MessageListenerConsumerInterface.java: -------------------------------------------------------------------------------- 1 | package com.currencyboot.service.rocketmq.messagelistener.Interface; 2 | 3 | import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; 4 | import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; 5 | import com.alibaba.rocketmq.common.message.MessageExt; 6 | 7 | /** 8 | *

Title: BaseMessageListenerConsumer

9 | *

@Description: 自定义消费 接口 继承该接口 实现消费方法

10 | *

Company:

11 | * @author 李文 12 | * @date 2016年8月1日 上午10:05:04 13 | */ 14 | public interface MessageListenerConsumerInterface 15 | { 16 | 17 | /** 18 | * 自定义消费方法 19 | * 20 | * @param msg 21 | * @param context 22 | * @return 23 | */ 24 | public ConsumeConcurrentlyStatus consumeMessage(String strBody, MessageExt msg, 25 | ConsumeConcurrentlyContext context); 26 | } 27 | -------------------------------------------------------------------------------- /RocketMqCurrencyBoot/src/main/java/com/currencyboot/service/rocketmq/common/MessageHelp.java: -------------------------------------------------------------------------------- 1 | package com.currencyboot.service.rocketmq.common; 2 | 3 | import java.util.List; 4 | 5 | import com.alibaba.rocketmq.common.message.MessageExt; 6 | 7 | /** 8 | *

Title: MessageHelp

9 | *

@Description: 消息集合帮助类

10 | *

Company:

11 | * @author 李文 12 | * @date 2016年8月1日 上午10:06:08 13 | */ 14 | public class MessageHelp 15 | { 16 | 17 | /** 18 | * 将当前的消息集合 转换成字符串 方便记录日志 PS StringBuffer不是一个线程安全的类 将其放在一个方法中 局部变量希望能避免出现问题 19 | * 20 | * @param msgs 21 | * @return 22 | */ 23 | public static String GetMessagesToString(List msgs) 24 | { 25 | StringBuffer sBuffer = new StringBuffer(); 26 | sBuffer.append(" 默认只处理 下标为 0 的 消息 当前消息数量是 : ").append(msgs.size()); 27 | for (MessageExt messageExt : msgs) 28 | sBuffer.append(" 消息 :").append(messageExt.toString()).append(" "); 29 | return sBuffer.toString(); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /RocketMqCurrencyBoot/src/main/java/com/currencyboot/service/rocketmq/messagelistener/Interface/MatchingInterface.java: -------------------------------------------------------------------------------- 1 | package com.currencyboot.service.rocketmq.messagelistener.Interface; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | /** 7 | *

Title: BaseMatching

8 | *

@Description: 验证规则数据源 获取接口 数据源 》 XML 配置

9 | *

Company:

10 | * @author 李文 11 | * @date 2016年8月2日 上午10:04:21 12 | */ 13 | public interface MatchingInterface 14 | { 15 | /** 16 | * 获取 验证规则 数据 17 | * 18 | * web服务调用 消费端: 19 | * list 20 | * map 21 | * Tag = test1,test2,test3 22 | * body= 客户编码A,客户编码B 23 | * url = http://10.10.12.27 24 | * 转发消费端: 25 | * list 26 | * map 27 | * Tag = test1,test2,test3 28 | * body= 客户编码A,客户编码B 29 | * Topic= top1,top2,top3 30 | * Tags=t1||t2 或者 Tags=* 31 | * 32 | * @return 33 | */ 34 | List> getMatching(); 35 | } 36 | -------------------------------------------------------------------------------- /RocketMqCurrencyBoot/src/main/java/com/currencyboot/web/OrApiTest.java: -------------------------------------------------------------------------------- 1 | package com.currencyboot.web; 2 | 3 | import org.springframework.http.ResponseEntity; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | import org.springframework.web.bind.annotation.RequestMethod; 6 | import org.springframework.web.bind.annotation.RequestParam; 7 | import org.springframework.web.bind.annotation.RestController; 8 | 9 | /** 10 | *

Title: OrApiTest

11 | *

@Description: 用于测试的 消费外调 Api 服务

12 | *

Company:

13 | * @author 李文 14 | * @date 2016年8月2日 下午4:24:22 15 | */ 16 | @RestController 17 | public class OrApiTest 18 | { 19 | 20 | @RequestMapping(value = "/", method = RequestMethod.POST) 21 | public ResponseEntity getEdiTest(@RequestParam("Topic") String Topic, 22 | @RequestParam("Tags") String Tags, @RequestParam("Body") String Body) 23 | { 24 | System.out.println(Body); 25 | // return 26 | // ResponseEntity.status(HttpStatus.ACCEPTED).body("处理完成 并且后续不转发"); 27 | 28 | return ResponseEntity.ok("OK"); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /RocketMqCurrencyBoot/src/main/java/com/currencyboot/service/rocketmq/spring/SpringContextUtils.java: -------------------------------------------------------------------------------- 1 | package com.currencyboot.service.rocketmq.spring; 2 | 3 | import java.util.Map; 4 | 5 | import org.springframework.context.ApplicationContext; 6 | 7 | /** 8 | *

Title: SpringContextUtils

9 | *

@Description: spring 上下文获取类

10 | *

Company:

11 | * @author 李文 12 | * @date 2016年8月3日 上午11:43:18 13 | */ 14 | public class SpringContextUtils 15 | { 16 | 17 | public static void setApplicationContext(ApplicationContext arg0) 18 | { 19 | applicationContext = arg0; 20 | } 21 | 22 | public static ApplicationContext getApplicationContext() 23 | { 24 | return applicationContext; 25 | } 26 | 27 | public static Object getBeanById(String id) 28 | { 29 | return applicationContext.getBean(id); 30 | } 31 | 32 | public static Object getBeanByClass(Class c) 33 | { 34 | return applicationContext.getBean(c); 35 | } 36 | 37 | public static Map getBeansByClass(Class c) 38 | { 39 | return applicationContext.getBeansOfType(c); 40 | } 41 | 42 | private static ApplicationContext applicationContext; 43 | 44 | } 45 | -------------------------------------------------------------------------------- /RocketMqCurrencyBoot/src/main/java/com/currencyboot/service/rocketmq/MqConsumerStart.java: -------------------------------------------------------------------------------- 1 | package com.currencyboot.service.rocketmq; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.boot.CommandLineRunner; 6 | import org.springframework.stereotype.Component; 7 | 8 | import javax.servlet.http.HttpServletRequest; 9 | 10 | /** 11 | * 启动类 12 | * 13 | * @author 李文 14 | * @create 2017-03-07 13:27 15 | **/ 16 | @Component 17 | public class MqConsumerStart implements CommandLineRunner 18 | { 19 | 20 | @Autowired 21 | private MqConsumer consumer; 22 | 23 | @Autowired 24 | private HttpServletRequest request; 25 | 26 | @Value("${MQ.Topic:null}") 27 | private String Topic; 28 | @Value("${MQ.Tags:null}") 29 | private String Tags; 30 | 31 | 32 | @Override 33 | public void run(String... arg0) throws Exception { 34 | 35 | if (!"null".equals(Topic)) { 36 | String clientID; 37 | if (!"null".equals(Tags)) 38 | consumer.init(Topic, Tags); 39 | else 40 | consumer.init(Topic, "*"); 41 | } 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /RocketMqCurrencyBootSample/src/main/java/com/atliwen/server/messagelistener/ConsumableMessageListenerConsumer.java: -------------------------------------------------------------------------------- 1 | package com.atliwen.server.messagelistener; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; 7 | import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; 8 | import com.alibaba.rocketmq.common.message.MessageExt; 9 | import com.currencyboot.service.rocketmq.messagelistener.Interface.MessageListenerConsumerInterface; 10 | 11 | /** 12 | *

Title: ConsumableMessageListenerConsumer

13 | *

@Description: 内销

14 | *

Company:

15 | * @author 李文 16 | * @date 2016年8月1日 上午10:06:08 17 | */ 18 | public class ConsumableMessageListenerConsumer implements MessageListenerConsumerInterface 19 | { 20 | 21 | private static final Logger LOGGER = LoggerFactory 22 | .getLogger(ConsumableMessageListenerConsumer.class); 23 | 24 | @Override 25 | public ConsumeConcurrentlyStatus consumeMessage(String strBody, MessageExt msg, 26 | ConsumeConcurrentlyContext context) 27 | { 28 | LOGGER.info("\n 当前线程是" + Thread.currentThread().getId() + " \n 数据是" + strBody); 29 | return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /RocketMqCurrencyBootSample/src/main/resources/applicationContext-nx.xml: -------------------------------------------------------------------------------- 1 | 15 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /RocketMqCurrencyBoot/src/main/java/com/currencyboot/service/rocketmq/common/ForwardedHelp.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.currencyboot.service.rocketmq.common; 5 | 6 | import java.util.Map; 7 | 8 | /** 9 | *

Title: ForwardedHelp

10 | *

@Description: 匹配规则 校验类

11 | *

Company:

12 | * @author 李文 13 | * @date 2016年8月1日 上午10:06:08 14 | */ 15 | public class ForwardedHelp 16 | { 17 | /** 18 | * 空值的验证 19 | * 20 | * @param matchingMap 注入数据 21 | * @param keys 需要 验证的 MAP key 22 | * @throws Exception 有空值 或者 “” 抛出异常 23 | */ 24 | public static void outStr(Map matchingMap, String[] keys) throws Exception 25 | { 26 | for (String key : keys) 27 | { 28 | String val = matchingMap.get(key); 29 | if (val == null || "".equals(val)) { throw new Exception(" 验证规则 有空或者为配置 Key : " 30 | + val); } 31 | } 32 | } 33 | 34 | /** 35 | * 匹配验证 36 | * 37 | * @param MapVal 验证规则的值 38 | * @param key 被验证的数据 39 | * @return 40 | */ 41 | public static boolean isContains(String MapVal, String key) 42 | { 43 | 44 | if (MapVal == null && "".equals(MapVal)) { return false; } 45 | String[] val = key.split(","); 46 | if (val == null) { return false; } 47 | for (String string : val) 48 | { 49 | if (MapVal.contains(string.trim())) { return true; } 50 | } 51 | return false; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /RocketMqCurrencyBootSample/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.currencyboot.sample 5 | RocketMqCurrencyBootSample 6 | 0.0.1-SNAPSHOT 7 | 8 | org.springframework.boot 9 | spring-boot-starter-parent 10 | 1.4.0.RELEASE 11 | 12 | 13 | 14 | atliwen 15 | https://raw.githubusercontent.com/atliwen/maven-repo/master/repository 16 | 17 | 18 | 19 | 20 | 21 | com.currencyboot.mq 22 | currencyboot-mq 23 | 2.3.1-SNAPSHOT 24 | 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-test 29 | test 30 | 31 | 32 | 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-maven-plugin 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /RocketMqCurrencyBoot/src/main/java/com/currencyboot/domain/mq/HttpResponse.java: -------------------------------------------------------------------------------- 1 | package com.currencyboot.domain.mq; 2 | 3 | /** 4 | *

Title: HttpResponse

5 | *

@Description: HTTP请求响应数据类

6 | *

Company:

7 | * @author 李文 8 | * @date 2016年8月3日 上午11:40:56 9 | */ 10 | public class HttpResponse 11 | { 12 | 13 | /** 14 | * HTTP 响应状态 15 | */ 16 | private int state; 17 | 18 | /** 19 | * 响应是内容 20 | */ 21 | private String data; 22 | 23 | /** 24 | * 获取HTTP响应状态 25 | * @return state HTTP响应状态 26 | */ 27 | public int getState() 28 | { 29 | return state; 30 | } 31 | 32 | /** 33 | * 设置HTTP响应状态 34 | * @param state HTTP响应状态 35 | */ 36 | public void setState(int state) 37 | { 38 | this.state = state; 39 | } 40 | 41 | /** 42 | * 获取响应是内容 43 | * @return date 响应是内容 44 | */ 45 | public String getData() 46 | { 47 | return data; 48 | } 49 | 50 | /** 51 | * 设置响应是内容 52 | * @param date 响应是内容 53 | */ 54 | public void setDate(String data) 55 | { 56 | this.data = data; 57 | } 58 | 59 | /* 60 | * (non-Javadoc) 61 | * 62 | * @see java.lang.Object#toString() 63 | */ 64 | @Override 65 | public String toString() 66 | { 67 | return "HttpResponse [state=" + state + ", date=" + data + "]"; 68 | } 69 | 70 | public HttpResponse() 71 | { 72 | } 73 | 74 | public HttpResponse(int state, String data) 75 | { 76 | super(); 77 | this.state = state; 78 | this.data = data; 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /RocketMqCurrencyBoot/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.currencyboot.mq 5 | currencyboot-mq 6 | 2.4.1-SNAPSHOT 7 | 8 | 9 | org.springframework.boot 10 | spring-boot-starter-parent 11 | 1.4.4.RELEASE 12 | 13 | 14 | 1.8 15 | 16 | 17 | 18 | 19 | org.springframework.boot 20 | spring-boot-starter-web 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-test 29 | test 30 | 31 | 32 | 33 | com.alibaba.rocketmq 34 | rocketmq-client 35 | 3.2.6 36 | 37 | 38 | com.alibaba.rocketmq 39 | rocketmq-all 40 | 3.2.6 41 | pom 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /RocketMqCurrencyBootSample/src/main/resources/applicationContext-zf.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | a 28 | 29 | 30 | a 31 | 32 | 33 | orTest 34 | 35 | 36 | b 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /RocketMqCurrencyBoot/src/main/java/com/currencyboot/domain/mq/TransferRules.java: -------------------------------------------------------------------------------- 1 | package com.currencyboot.domain.mq; 2 | 3 | public class TransferRules 4 | { 5 | 6 | /** 7 | * 消费的 Tag 8 | */ 9 | private String tag; 10 | 11 | /** 12 | * Body 中包含的 字符串 13 | */ 14 | private String body; 15 | 16 | /** 17 | * 调用的 webapi 地址 18 | */ 19 | private String url; 20 | 21 | /** 22 | * 转发的 Topic 不设置 就不进行 转发消息 23 | */ 24 | private String topic; 25 | 26 | /** 27 | * 转发的 Tag 不设置 就不进行 转发消息 28 | */ 29 | private String tags; 30 | 31 | /**   32 |  * 获取消费的Tag   33 |  * @return tag 消费的Tag   34 |  */ 35 | public String getTag() 36 | { 37 | return tag; 38 | } 39 | 40 | /**   41 |  * 设置消费的Tag   42 |  * @param tag 消费的Tag   43 |  */ 44 | public void setTag(String tag) 45 | { 46 | this.tag = tag; 47 | } 48 | 49 | /**   50 |  * 获取Body中包含的字符串   51 |  * @return body Body中包含的字符串   52 |  */ 53 | public String getBody() 54 | { 55 | return body; 56 | } 57 | 58 | /**   59 |  * 设置Body中包含的字符串   60 |  * @param body Body中包含的字符串   61 |  */ 62 | public void setBody(String body) 63 | { 64 | this.body = body; 65 | } 66 | 67 | /**   68 |  * 获取调用的webapi地址   69 |  * @return url 调用的webapi地址   70 |  */ 71 | public String getUrl() 72 | { 73 | return url; 74 | } 75 | 76 | /**   77 |  * 设置调用的webapi地址   78 |  * @param url 调用的webapi地址   79 |  */ 80 | public void setUrl(String url) 81 | { 82 | this.url = url; 83 | } 84 | 85 | /**   86 |  * 获取转发的Topic不设置就不进行转发消息   87 |  * @return topic 转发的Topic不设置就不进行转发消息   88 |  */ 89 | public String getTopic() 90 | { 91 | return topic; 92 | } 93 | 94 | /**   95 |  * 设置转发的Topic不设置就不进行转发消息   96 |  * @param topic 转发的Topic不设置就不进行转发消息   97 |  */ 98 | public void setTopic(String topic) 99 | { 100 | this.topic = topic; 101 | } 102 | 103 | /**   104 |  * 获取转发的Tag不设置就不进行转发消息   105 |  * @return tags 转发的Tag不设置就不进行转发消息   106 |  */ 107 | public String getTags() 108 | { 109 | return tags; 110 | } 111 | 112 | /**   113 |  * 设置转发的Tag不设置就不进行转发消息   114 |  * @param tags 转发的Tag不设置就不进行转发消息   115 |  */ 116 | public void setTags(String tags) 117 | { 118 | this.tags = tags; 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /RocketMqCurrencyBootSample/src/main/resources/applicationContext-wd.xml: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 23 | 24 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | 34 | 35 | 37 | 38 | 39 | 40 | 41 | a 42 | 43 | 44 | * 45 | 46 | 47 | http://10.10.12.27:8080 48 | 49 | 50 | orTest 51 | 52 | 53 | b 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /RocketMqCurrencyBoot/src/main/java/com/currencyboot/web/MqProducerController.java: -------------------------------------------------------------------------------- 1 | package com.currencyboot.web; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.http.HttpStatus; 11 | import org.springframework.http.ResponseEntity; 12 | import org.springframework.web.bind.annotation.CrossOrigin; 13 | import org.springframework.web.bind.annotation.RequestMapping; 14 | import org.springframework.web.bind.annotation.RequestMethod; 15 | import org.springframework.web.bind.annotation.RequestParam; 16 | import org.springframework.web.bind.annotation.RestController; 17 | 18 | import com.alibaba.rocketmq.client.producer.SendResult; 19 | import com.currencyboot.service.rocketmq.MqProducer; 20 | 21 | /** 22 | *

Title: MqProducerController

23 | *

@Description: 生产端

24 | *

Company:

25 | * @author 李文 26 | * @date 2016年5月20日 下午7:32:25 27 | */ 28 | @RequestMapping("rest/MqProducer") 29 | @RestController 30 | @CrossOrigin 31 | public class MqProducerController 32 | { 33 | 34 | private static final Logger LOGGER = LoggerFactory.getLogger(MqProducerController.class); 35 | 36 | /** 37 | * 消费端生产类 38 | */ 39 | @Autowired(required = false) 40 | private MqProducer mqProducer; 41 | 42 | /** 43 | * 添加一个消息到MQ 44 | * 使用该方法 请注意 Topic Tags 参数 45 | * 46 | * @param Topic 队列名称 47 | * @param Tags 标签名称 48 | * @param body 实际数据 49 | * @return 50 | */ 51 | @RequestMapping(method = RequestMethod.POST) 52 | public ResponseEntity> controllerMq(@RequestParam("Topic") String Topic, 53 | @RequestParam("Tags") String Tags, @RequestParam("body") String body) 54 | { 55 | List list = new ArrayList(); 56 | try 57 | { 58 | String getRequestData = "接收到的消息是 Topic=" + Topic + " Tags= " + Tags + " body= " 59 | + body; 60 | 61 | LOGGER.debug(getRequestData); 62 | 63 | SendResult sendResult = mqProducer.send(Topic, Tags, body); 64 | LOGGER.debug("响应的数据是:" + sendResult); 65 | if (null != sendResult) 66 | { 67 | list.add("SendStatus = " + sendResult.getSendStatus() + " msgId =" 68 | + sendResult.getMsgId()); 69 | return ResponseEntity.ok(list); 70 | } 71 | list.add(" 添加到MQ 失败 !"); 72 | return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(list); 73 | } 74 | catch (UnsupportedEncodingException e) 75 | { 76 | LOGGER.error("MqProducerController 发送异常", e); 77 | list.add(e.getMessage()); 78 | return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(list); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /RocketMqCurrencyBoot/src/main/java/com/currencyboot/service/rocketmq/DefaultMessageListener.java: -------------------------------------------------------------------------------- 1 | package com.currencyboot.service.rocketmq; 2 | 3 | import java.text.MessageFormat; 4 | import java.util.List; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.beans.factory.annotation.Value; 10 | import org.springframework.stereotype.Component; 11 | 12 | import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; 13 | import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; 14 | import com.alibaba.rocketmq.client.consumer.listener.MessageListenerConcurrently; 15 | import com.alibaba.rocketmq.common.message.MessageExt; 16 | import com.currencyboot.service.rocketmq.common.MessageHelp; 17 | import com.currencyboot.service.rocketmq.messagelistener.Interface.MessageListenerConsumerInterface; 18 | import com.currencyboot.service.rocketmq.messagelistener.Interface.MqExceedCountInterface; 19 | 20 | /** 21 | *

Title: DefaultMessageListener

22 | *

@Description: 默认消费方法类 MessageListenerConcurrently 模式 23 | * 24 | * 使用Spring 来完成注入 25 | * 26 | *

27 | *

Company:

28 | * @author 李文 29 | * @date 2016年8月1日 上午10:06:08 30 | */ 31 | @Component 32 | public class DefaultMessageListener implements MessageListenerConcurrently 33 | { 34 | 35 | private final static Logger LOGGER = LoggerFactory.getLogger(DefaultMessageListener.class); 36 | 37 | /** 38 | * 消息处理次数 39 | */ 40 | @Value("${MQ.COUNT:12}") 41 | private int count; 42 | 43 | /** 44 | * 数据字符集 45 | */ 46 | @Value("${MQ.Encoding:UTF-8}") 47 | private String Encoding; 48 | 49 | /** 50 | * 自定义的消费方法 51 | */ 52 | // @Resource(name = "consumer") 53 | @Autowired 54 | private MessageListenerConsumerInterface consumer; 55 | 56 | @Autowired(required = false) 57 | private MqExceedCountInterface mqExceedCount; 58 | 59 | @Override 60 | public ConsumeConcurrentlyStatus consumeMessage(List msgs, 61 | ConsumeConcurrentlyContext context) 62 | { 63 | LOGGER.info(" 当前处理的消息是 " + MessageHelp.GetMessagesToString(msgs)); 64 | 65 | MessageExt msg = msgs.get(0); 66 | String strBody = null; 67 | try 68 | { 69 | // 直接在第一次 字节转换字符串的时候就指定为 UTF-8 编码 70 | strBody = new String(msg.getBody(), Encoding); 71 | } 72 | catch (Exception e) 73 | { 74 | LOGGER.error("获取数据为UTF-8格式出错 ", e); 75 | strBody = new String(msg.getBody()); 76 | } 77 | 78 | LOGGER.info(MessageFormat.format("当前处理的消息 实体是 {0} 队列名称是 {1} 标签是{2} ", strBody, 79 | msg.getTopic(), msg.getTags())); 80 | 81 | // 消息处理次数的处理 82 | if (msg.getReconsumeTimes() > count) 83 | { 84 | LOGGER.info("容错次数超出 msg=" + msg); 85 | try 86 | { 87 | if (mqExceedCount != null) mqExceedCount.exceedCount(strBody, msg, context); 88 | } 89 | catch (Exception e) 90 | { 91 | LOGGER.info(" 容错方法出现异常 ", e); 92 | } 93 | return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; 94 | } 95 | return consumer.consumeMessage(strBody, msg, context); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /RocketMqCurrencyBoot/src/main/java/com/currencyboot/service/rocketmq/common/HttpRequest.java: -------------------------------------------------------------------------------- 1 | package com.currencyboot.service.rocketmq.common; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.OutputStream; 7 | import java.io.UnsupportedEncodingException; 8 | import java.net.HttpURLConnection; 9 | import java.net.URL; 10 | import java.net.URLEncoder; 11 | import java.util.Map; 12 | 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | 16 | import com.currencyboot.domain.mq.HttpResponse; 17 | 18 | /** 19 | *

Title: HttpRequest

20 | *

@Description: HTTP 交互类

21 | *

Company:

22 | * @author 李文 23 | * @date 2016年8月3日 上午11:42:30 24 | */ 25 | public class HttpRequest 26 | { 27 | 28 | private static final Logger LOGGER = LoggerFactory.getLogger(HttpRequest.class); 29 | 30 | /* 31 | * params 填写的URL的参数 encode 字节编码 32 | */ 33 | public static HttpResponse sendPostMessage(String url, Map params, String encode) 34 | { 35 | 36 | StringBuffer stringBuffer = new StringBuffer(); 37 | 38 | HttpResponse responses = new HttpResponse(); 39 | 40 | if (params != null && !params.isEmpty()) 41 | { 42 | for (Map.Entry entry : params.entrySet()) 43 | { 44 | try 45 | { 46 | stringBuffer.append(entry.getKey()).append("=") 47 | .append(URLEncoder.encode(entry.getValue(), encode)).append("&"); 48 | 49 | } 50 | catch (UnsupportedEncodingException e) 51 | { 52 | // TODO Auto-generated catch block 53 | e.printStackTrace(); 54 | } 55 | } 56 | // 删掉最后一个 & 字符 57 | stringBuffer.deleteCharAt(stringBuffer.length() - 1); 58 | LOGGER.debug("将要发送的URL :" + url + " data " + stringBuffer); 59 | try 60 | { 61 | URL realUrl = new URL(url); 62 | HttpURLConnection httpURLConnection = (HttpURLConnection) realUrl.openConnection(); 63 | httpURLConnection.setConnectTimeout(3000); 64 | httpURLConnection.setDoInput(true);// 从服务器获取数据 65 | httpURLConnection.setDoOutput(true);// 向服务器写入数据 66 | 67 | // 获得上传信息的字节大小及长度 68 | byte[] mydata = stringBuffer.toString().getBytes(); 69 | // 设置请求体的类型 70 | httpURLConnection.setRequestProperty("Content-Type", 71 | "application/x-www-form-urlencoded"); 72 | httpURLConnection 73 | .setRequestProperty("Content-Lenth", String.valueOf(mydata.length)); 74 | 75 | // 获得输出流,向服务器输出数据 76 | OutputStream outputStream = (OutputStream) httpURLConnection.getOutputStream(); 77 | outputStream.write(mydata); 78 | 79 | // 获得服务器响应的结果和状态码 80 | int responseCode = httpURLConnection.getResponseCode(); 81 | responses.setState(responseCode); 82 | if (responseCode == 200) 83 | { 84 | // 获得输入流,从服务器端获得数据 85 | InputStream inputStream = (InputStream) httpURLConnection.getInputStream(); 86 | responses.setDate(changeInputStream(inputStream, encode)); 87 | } 88 | LOGGER.debug(" Web Api 服务响应的 数据 : " + responses); 89 | } 90 | catch (IOException e) 91 | { 92 | LOGGER.error(" 调用WEB 服务出现异常 url: " + url + " date:" + stringBuffer, e); 93 | responses = null; 94 | } 95 | } 96 | 97 | return responses; 98 | } 99 | 100 | /* 101 | * // 把从输入流InputStream按指定编码格式encode变成字符串String 102 | */ 103 | public static String changeInputStream(InputStream inputStream, String encode) 104 | { 105 | 106 | // ByteArrayOutputStream 一般叫做内存流 107 | ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 108 | byte[] data = new byte[1024]; 109 | int len = 0; 110 | String result = ""; 111 | if (inputStream != null) 112 | { 113 | try 114 | { 115 | while ((len = inputStream.read(data)) != -1) 116 | { 117 | byteArrayOutputStream.write(data, 0, len); 118 | 119 | } 120 | result = new String(byteArrayOutputStream.toByteArray(), encode); 121 | 122 | } 123 | catch (IOException e) 124 | { 125 | // TODO Auto-generated catch block 126 | e.printStackTrace(); 127 | } 128 | } 129 | 130 | return result; 131 | } 132 | 133 | } -------------------------------------------------------------------------------- /RocketMqCurrencyBoot/src/main/java/com/currencyboot/service/rocketmq/MqConsumer.java: -------------------------------------------------------------------------------- 1 | package com.currencyboot.service.rocketmq; 2 | 3 | import java.text.MessageFormat; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.context.annotation.Lazy; 10 | import org.springframework.context.annotation.Scope; 11 | import org.springframework.stereotype.Component; 12 | import org.springframework.util.StringUtils; 13 | 14 | import com.alibaba.rocketmq.client.consumer.DefaultMQPushConsumer; 15 | import com.alibaba.rocketmq.client.consumer.listener.MessageListenerConcurrently; 16 | import com.alibaba.rocketmq.common.consumer.ConsumeFromWhere; 17 | import com.alibaba.rocketmq.common.protocol.heartbeat.MessageModel; 18 | 19 | /** 20 | *

Title: MqConsumer

21 | *

@Description: 消费者类 22 | * 该对象是多例的 请手动 销毁 23 | * 24 | * 配置文件中需要制定 25 | * MQ.NamesrvAddr 26 | * MQ.consumerGroup 27 | * 28 | * 消费实体类是 DefaultMQPushConsumer* 29 | *

30 | *

Company: zjs

31 | * @author 李文 32 | * @date 2016年8月1日 上午10:06:08 33 | */ 34 | @Component 35 | @Scope("prototype") 36 | @Lazy(true) 37 | public class MqConsumer 38 | { 39 | private final Logger logger = LoggerFactory.getLogger(MqConsumer.class); 40 | 41 | /** 42 | * MQ的PushConsumer 消费端 主体类 43 | */ 44 | private DefaultMQPushConsumer defaultMQPushConsumer; 45 | 46 | /** 47 | * MQ地址 48 | */ 49 | @Value("${MQ.NamesrvAddr}") 50 | private String namesrvAddr; 51 | 52 | /** 53 | * 消费端组名 54 | */ 55 | @Value("${MQ.consumerGroup}") 56 | private String consumerGroup; 57 | 58 | /** 59 | * 实际消费类 60 | */ 61 | @Autowired 62 | private MessageListenerConcurrently defaultMessageListener; 63 | 64 | /** 65 | * 启动一个消费端 66 | * @param Topic 队列名称 67 | * @param Tags 标签 68 | * @throws Exception 错误消息 69 | */ 70 | public String init(String Topic, String Tags) throws Exception 71 | { 72 | return this.init(Topic, Tags, null, null); 73 | } 74 | 75 | /** 76 | * 启动一个消费端 77 | * @param Topic 队列名称 78 | * @param Tags 标签 79 | * @param consumeFromWhere 从哪里开始消费 80 | * @param messageModel 广播 / 聚集 81 | * @throws Exception 错误消息 82 | */ 83 | public String init(String Topic, String Tags, ConsumeFromWhere consumeFromWhere, 84 | MessageModel messageModel) throws Exception 85 | { 86 | 87 | // 参数信息 88 | logger.info(MessageFormat 89 | .format("消费者 初始化! consumerGroup={0} namesrvAddr={1} Topic={2} Tags={3} ConsumeFromWhere={4} MessageModel={5} ", 90 | consumerGroup, namesrvAddr, Topic, Tags, consumeFromWhere, messageModel)); 91 | 92 | // 一个应用创建一个Consumer,由应用来维护此对象,可以设置为全局对象或者单例
93 | // 注意:ConsumerGroupName需要由应用来保证唯一 94 | defaultMQPushConsumer = new DefaultMQPushConsumer(consumerGroup); 95 | defaultMQPushConsumer.setNamesrvAddr(namesrvAddr); 96 | defaultMQPushConsumer.setInstanceName(String.valueOf(System.currentTimeMillis())); 97 | 98 | // 订阅指定MyTopic下tags等于MyTag 99 | if (StringUtils.isEmpty(Topic)) throw new Exception("Topic 不能为空"); 100 | if (StringUtils.isEmpty(Tags)) { throw new Exception("Tags 不能为空"); } 101 | 102 | defaultMQPushConsumer.subscribe(Topic, Tags); 103 | 104 | // 设置Consumer第一次启动是从队列头部开始消费还是队列尾部开始消费
105 | // 如果非第一次启动,那么按照上次消费的位置继续消费 106 | if (consumeFromWhere == null) 107 | { 108 | consumeFromWhere = consumeFromWhere.CONSUME_FROM_FIRST_OFFSET; 109 | } 110 | defaultMQPushConsumer.setConsumeFromWhere(consumeFromWhere); 111 | 112 | if (messageModel == null) 113 | { 114 | messageModel = messageModel.CLUSTERING; 115 | } 116 | 117 | // 设置为集群消费(区别于广播消费) 118 | defaultMQPushConsumer.setMessageModel(messageModel); 119 | defaultMQPushConsumer.registerMessageListener(defaultMessageListener); 120 | 121 | // Consumer对象在使用之前必须要调用start初始化,初始化一次即可
122 | defaultMQPushConsumer.start(); 123 | 124 | String clientID = defaultMQPushConsumer.getClientIP() + "@" 125 | + defaultMQPushConsumer.getInstanceName(); 126 | 127 | logger.info("消费者 " + clientID + "启动成功!"); 128 | 129 | return clientID; 130 | } 131 | 132 | /** 133 | * 设置多例后 Spring 将不负责销毁 需要手动销毁 134 | */ 135 | public void destroy() 136 | { 137 | 138 | logger.info("关闭消费者 开始"); 139 | defaultMQPushConsumer.shutdown(); 140 | logger.info("关闭消费者 结束"); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /RocketMqCurrencyBoot/src/main/java/com/currencyboot/web/MqConsumerController.java: -------------------------------------------------------------------------------- 1 | package com.currencyboot.web; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Enumeration; 5 | import java.util.List; 6 | 7 | import javax.servlet.ServletContext; 8 | import javax.servlet.http.HttpServletRequest; 9 | 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.http.HttpStatus; 14 | import org.springframework.http.ResponseEntity; 15 | import org.springframework.web.bind.annotation.CrossOrigin; 16 | import org.springframework.web.bind.annotation.RequestMapping; 17 | import org.springframework.web.bind.annotation.RequestMethod; 18 | import org.springframework.web.bind.annotation.RequestParam; 19 | import org.springframework.web.bind.annotation.RestController; 20 | 21 | import com.currencyboot.domain.mq.AttributeNames; 22 | import com.currencyboot.service.rocketmq.MqConsumer; 23 | import com.currencyboot.service.rocketmq.spring.SpringContextUtils; 24 | 25 | /** 26 | *

Title: MqConsumerController

27 | *

@Description: MqConsumer 消费端 控制类

28 | *

Company:

29 | * @author 李文 30 | * @date 2016年5月20日 下午7:21:12 31 | */ 32 | @RequestMapping("rest/MqConsumer") 33 | @RestController 34 | @CrossOrigin 35 | public class MqConsumerController 36 | { 37 | 38 | private static final Logger LOGGER = LoggerFactory.getLogger(MqConsumerController.class); 39 | 40 | @Autowired 41 | private HttpServletRequest request; 42 | 43 | /** 44 | * 45 | * 开启一个 消费端 46 | * 该方法会创建一个 消费端类 ,该类 将添加到servletContext 进行管理 47 | * 重要提示 每次调用改方法都会 开启一个新的消费端 48 | * 49 | * @param Topic Topic 50 | * @param Tags Tag 标签可以 定 未获取多个 具体请看MQ 文档 51 | * @return 是否成功开启 52 | */ 53 | @RequestMapping(method = RequestMethod.POST) 54 | public ResponseEntity startMqConsumerTag(@RequestParam("Topic") String Topic, 55 | @RequestParam("Tags") String Tags) 56 | { 57 | MqConsumer consumer; 58 | String clientID; 59 | try 60 | { 61 | consumer = (MqConsumer) SpringContextUtils.getBeanByClass(MqConsumer.class); 62 | if (Tags == null || "".equals(Tags)) Tags = "*"; 63 | clientID = consumer.init(Topic, Tags); 64 | } 65 | catch (Exception e) 66 | { 67 | LOGGER.error("MqConsumerController 启动消费端异常 ", e); 68 | return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); 69 | } 70 | 71 | ServletContext servletContext = request.getServletContext(); 72 | 73 | if (servletContext.getAttribute(clientID) == null) 74 | { 75 | servletContext.setAttribute(clientID, consumer); 76 | } 77 | return ResponseEntity.status(HttpStatus.OK).build(); 78 | } 79 | 80 | /** 81 | * 销毁一个消费端 82 | * 83 | * 该方法 会通过传递的 ConsumerID 从 servletContext 查询 是否有 该对象 84 | * 然后调用 其销毁方法 85 | * 86 | * @param ConsumerID 消费者唯一标识 87 | * @return 88 | */ 89 | @RequestMapping(method = RequestMethod.DELETE) 90 | public ResponseEntity destroyMqConsumer(@RequestParam("Consumer") String Consumer) 91 | { 92 | 93 | ServletContext servletContext = request.getServletContext(); 94 | 95 | Enumeration attributeNames = servletContext.getAttributeNames(); 96 | 97 | List outList = new ArrayList(); 98 | 99 | Object AttributeConsumer = servletContext.getAttribute(Consumer); 100 | if (AttributeConsumer != null) 101 | { 102 | MqConsumer mqConsumer = (MqConsumer) AttributeConsumer; 103 | mqConsumer.destroy(); 104 | servletContext.setAttribute(Consumer, null); 105 | 106 | return ResponseEntity.status(HttpStatus.OK).body(Consumer + " 消费者销毁成功"); 107 | } 108 | 109 | return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Consumer + " 未找到 该消费者 "); 110 | } 111 | 112 | /** 113 | * 获取servletContext 中存放的 所有 消费者对象 唯一标识 114 | * 115 | * @return 返回 说有消费者唯一标识 116 | * 117 | */ 118 | @RequestMapping(method = RequestMethod.GET) 119 | public List queryMqConsumerList() 120 | { 121 | ServletContext servletContext = request.getServletContext(); 122 | List list = new ArrayList(); 123 | Enumeration attributeNames = servletContext.getAttributeNames(); 124 | while (attributeNames.hasMoreElements()) 125 | { 126 | String nameString = (String) attributeNames.nextElement(); 127 | if (nameString.contains("@")) 128 | { 129 | AttributeNames attri = new AttributeNames(); 130 | attri.setKid(nameString); 131 | list.add(attri); 132 | } 133 | } 134 | return list; 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /RocketMqCurrencyBootSample/src/main/java/com/atliwen/Application.java: -------------------------------------------------------------------------------- 1 | package com.atliwen; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | import org.springframework.boot.SpringApplication; 9 | import org.springframework.boot.autoconfigure.SpringBootApplication; 10 | import org.springframework.context.ApplicationContext; 11 | import org.springframework.context.annotation.Bean; 12 | 13 | import com.atliwen.server.messagelistener.ForwardedMessageListConsumer; 14 | import com.atliwen.server.messagelistener.MqExceedCount; 15 | import com.atliwen.server.translation.myForwardedMessageListConsumer; 16 | import com.currencyboot.RocketMqCurrencyBoot; 17 | import com.currencyboot.service.rocketmq.MqProducer; 18 | import com.currencyboot.service.rocketmq.messagelistener.Interface.MessageListenerConsumerInterface; 19 | import com.currencyboot.service.rocketmq.messagelistener.Interface.MqExceedCountInterface; 20 | import com.currencyboot.service.rocketmq.spring.SpringContextUtils; 21 | 22 | /** 23 | *

Title: Application

24 | *

@Description: 启动入口类

25 | *

Company:

26 | * @author 李文 27 | * @date 2016年8月3日 上午11:40:10 28 | */ 29 | @SpringBootApplication 30 | public class Application 31 | { 32 | public static void main(String[] args) 33 | { 34 | // SpringApplication.run 加载两个位置 当前类 位置 和 RocketMqCurrencyBoot 类 位置 35 | // 加载Spring 36 | 37 | // 如果 RocketMqCurrencyBoot 包名称 包括在 当前项目中 则 RocketMqCurrencyBoot.class 38 | // 不需要 39 | 40 | final ApplicationContext applicationContext = SpringApplication.run(new Object[] 41 | { RocketMqCurrencyBoot.class, Application.class }, args); 42 | SpringContextUtils.setApplicationContext(applicationContext); 43 | 44 | } 45 | 46 | /** 47 | * 构建 发送MQ 类 Bean 48 | * @return 49 | */ 50 | @Bean 51 | MqProducer mqProducer() 52 | { 53 | return new MqProducer(); 54 | } 55 | 56 | // ------------内销 模式 开始----------------------- 57 | 58 | // @Bean 59 | // MessageListenerConsumerInterface consumableMessageListenerConsumer() 60 | // { 61 | // return new ConsumableMessageListenerConsumer(); 62 | // } 63 | 64 | // ------------内销 模式 结束----------------------- 65 | 66 | // -------------外调 模式 开始----------------------- 67 | 68 | // /** 69 | // * 构建 web服务调用 消费端 70 | // * @return 71 | // */ 72 | // @Bean() 73 | // MessageListenerConsumerInterface externalCallConcurrentlyStatus() 74 | // { 75 | // ExternalCallConcurrentlyStatus e = new ExternalCallConcurrentlyStatus(); 76 | // List> matching = new ArrayList>(); 78 | // 79 | // // 设置验证规则 80 | // Map rule = new HashMap(); 81 | // rule.put("Tag", "a"); 82 | // rule.put("body", "*"); 83 | // rule.put("url", "http://10.10.12.27:8080"); 84 | // rule.put("Topic", "orTest"); 85 | // rule.put("Tags", "b"); 86 | // e.setMatching(matching); 87 | // // 外调模式 消息加工处理类 88 | // e.setExternalCall(new myExternalCallConsumer()); 89 | // // e.setBaseMatching(baseMatching); 这个方法是 希望通过 数据源来获取 匹配规则 90 | // return e; 91 | // } 92 | 93 | // -------------外调 模式 结束----------------------- 94 | 95 | // -------------转发 模式 开始----------------------- 96 | @Bean() 97 | MessageListenerConsumerInterface forwardedMessageListConsumer() 98 | { 99 | ForwardedMessageListConsumer f = new ForwardedMessageListConsumer(); 100 | f.setProducer(new MqProducer()); 101 | f.setForwarded(new myForwardedMessageListConsumer()); 102 | List> matching = new ArrayList>(); 103 | Map rule = new HashMap(); 104 | rule.put("Tag", "a"); 105 | rule.put("body", "a"); 106 | rule.put("Topic", "orTest"); 107 | rule.put("Tags", "b"); 108 | f.setMatching(matching); 109 | // f.setBaseMatching(); 这个方法是 希望通过 数据源来获取 匹配规则 110 | return f; 111 | } 112 | // -------------转发 模式 结束----------------------- 113 | 114 | // -------------消费超出 设置的容错 次数 处理 类 开始------ 115 | @Bean 116 | MqExceedCountInterface mqExceedCount() 117 | { 118 | // 该类不是非构建 类 不设置 消费次数超出后 自动成功。 119 | return new MqExceedCount(); 120 | } 121 | 122 | // -------------消费超出 设置的容错 次数 处理 类 结束 ------ 123 | 124 | // ------------事务 开始-------------------------- 125 | 126 | // PS 当前MQ 事务支持非常不好 没啥用的赶脚 单个事务 还得等回调处理 127 | 128 | // /** 129 | // * 构建 本地事务 处理类 130 | // * @return 131 | // */ 132 | // @Bean 133 | // LocalTransactionExecuter myLocalTransactionExecuter() 134 | // { 135 | // return new MyLocalTransactionExecuter(); 136 | // } 137 | // 138 | // /** 139 | // * 构建 未决事务,服务器回查 处理类 140 | // */ 141 | // @Bean 142 | // TransactionCheckListener myTransactionCheckListener() 143 | // { 144 | // return new MyTransactionCheckListener(); 145 | // } 146 | 147 | // ------------事务 结束-------------------------- 148 | 149 | } 150 | -------------------------------------------------------------------------------- /RocketMqCurrencyBoot/src/main/resources/static/js/mqjs.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | // 数据 5 | var MQ = Datas = { 6 | 7 | // 表单验证 发送MQ 的 表单验证数据 8 | sendTopicDate: { 9 | sendTopic: { 10 | message: "Topic证失败", 11 | validators: { 12 | notEmpty: { 13 | message: "Topic不能为空" 14 | }, 15 | regexp: { 16 | regexp: /^[a-zA-Z]+$/, 17 | message: "Topic只能包含大写、小写、字母" 18 | } 19 | } 20 | }, 21 | sendbody: { 22 | message: "body证失败", 23 | validators: { 24 | notEmpty: { 25 | message: "body不能为空" 26 | } 27 | } 28 | } 29 | }, 30 | // 表单验证 开启一个新的 消费端 的 表单验证数据 31 | newidsDate: { 32 | Topic: { 33 | message: "Topic证失败", 34 | validators: { 35 | notEmpty: { 36 | message: "Topic不能为空" 37 | }, 38 | regexp: { 39 | regexp: /^[a-zA-Z]+$/, 40 | message: "Topic只能包含大写、小写、字母" 41 | } 42 | } 43 | } 44 | }, 45 | // 表单验证 删除一个 消费端 的 表单验证数据 46 | deleteidData: { 47 | Consumer: { 48 | message: "Consumer证失败", 49 | validators: { 50 | notEmpty: { 51 | message: "Consumer不能为空" 52 | }, 53 | regexp: { 54 | regexp: /@/, 55 | message: "Consumer必须有@符合" 56 | } 57 | } 58 | } 59 | }, 60 | 61 | // 设置 Url 62 | dataUrl: { 63 | sendformidUrl:null, 64 | newidsUrl: null, 65 | deleteidUrl:null 66 | } 67 | 68 | } 69 | 70 | 71 | 72 | // 表单 验证 提交 事件 73 | var MQ = Homejq = { 74 | 75 | sendformidSubmit: function (validator, form, submitButton) { 76 | 77 | $.ajax({ 78 | type: "POST", 79 | url: Datas.dataUrl.sendformidUrl, 80 | 81 | data: { 82 | Topic: form.context.sendTopic.value, 83 | Tags: form.context.sendTags.value, 84 | body: form.context.sendbody.value 85 | }, 86 | success: function (data) { 87 | alert("成功!"); 88 | $("#sendtestid").addClass("in").html("SendResult : " + data); 89 | $(form).bootstrapValidator("disableSubmitButtons", false); 90 | }, 91 | error: function (data) { 92 | alert("失败!"); 93 | $("#sendtestid").addClass("in").html("发送失败 " + data); 94 | $(form).bootstrapValidator("disableSubmitButtons", true); 95 | } 96 | }); 97 | }, 98 | 99 | newidsSubmit: function (validator, form, submitButton) { 100 | 101 | $.ajax({ 102 | type: "POST", 103 | url: Datas.dataUrl.newidsUrl, 104 | data: { 105 | Topic: form.context.Topic.value, 106 | Tags: form.context.Tags.value 107 | }, 108 | success: function () { 109 | alert("成功!"); 110 | $("#promptid").addClass("in").html("启动成功了"); 111 | $("#homeid").click(); 112 | $("#table").bootstrapTable("refresh", true); 113 | $(form).bootstrapValidator("disableSubmitButtons", false); 114 | }, 115 | error: function (date) { 116 | alert("失败!"); 117 | $("#promptid").addClass("in").html("启动失败 " + date.responseText); 118 | $(form).bootstrapValidator("disableSubmitButtons", true); 119 | } 120 | }); 121 | }, 122 | 123 | deleteidSubmit: function (validator, form, submitButton) { 124 | $.ajax({ 125 | type: "DELETE", 126 | url: Datas.dataUrl.deleteidUrl+"?Consumer=" + form.context.Consumer.value, 127 | success: function (date) { 128 | alert(date); 129 | $("#Consumer").val(""); 130 | $("#deletepromptid").addClass("in").html(date); 131 | $("#homeid").click(); 132 | $("#table").bootstrapTable("refresh", true); 133 | $(form).bootstrapValidator("disableSubmitButtons", false); 134 | }, 135 | error: function (date) { 136 | alert("失败!"); 137 | $("#deletepromptid").addClass("in").html(date); 138 | $(form).bootstrapValidator("disableSubmitButtons", true); 139 | } 140 | }); 141 | }, 142 | 143 | bootstrapValidator: function(id, fields, submitHandler) { 144 | $(id).bootstrapValidator({ 145 | message: "This value is not valid", 146 | feedbackIcons: { 147 | valid: "glyphicon glyphicon-ok", 148 | invalid: "glyphicon glyphicon-remove", 149 | validating: "glyphicon glyphicon-refresh" 150 | }, 151 | fields: fields, 152 | submitHandler: submitHandler 153 | }); 154 | } 155 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RocketMqCurrencyBoot 2 | 对MQ高级封装。 支持规则匹配 转发消息、外调 WEB API、内销。 容错次数设置等.. 3 | 4 | 5 | 使用理念: 6 | 7 | 内销模式 8 | 9 | 建议使用场景为 消息生命末端,也就是对消息的最后的处理 。 10 | 由于需要编写 消息逻辑完全由 使用者控制。 11 | 12 | 转发模式 13 | 14 | 建议使用场景为 对消息分流控制、对消息的加工处理。 15 | 提供 16 | 消息处理接口 17 | 18 | 只要实现改接口,就可以对消息进行二次加工处理 19 | 20 | 自定义规则匹配 21 | 22 | 自定义规则控制消息的转发。如 消息中包含字符串 “客户A”,改消息将转发到 配置的对应 队列中。 23 | 24 | 匹配规则数据获取接口 25 | 26 | 只需实现改接口,并将实现类注入到改模式中。就可以通过 数据源来动态控制 规则匹配。 27 | 28 | 该接口是为了可以 动态更改匹配规则,不需要修改程序任何地方。 29 | 30 | 注: 31 | 执行流程 是先执行 消息处理 再执行 规则匹配转发 32 | 33 | 外调模式 34 | 35 | 建议使用场景为 满足非主流业务、不能统一处理、满足特殊需求 方便 进行的 动态拓展。 36 | 37 | 提供 38 | 自定义匹配条件 39 | 40 | 进行消息过滤。如 消息中包含字符串 “客户A”,改消息将调用配置中的URL 请求访问该 WebApi. 41 | 42 | 请求结果继续 转发 43 | 44 | 调用WebApi后将会 进行判断是否 需要继续 消息流转。 45 | 46 | 按照匹配规则条件 是否配置了转发 队列 和 标签,或者 转发模式 bean 47 | 48 | 匹配规则数据获取接口 49 | 50 | 只需实现改接口,并将实现类注入到改模式中。就可以通过 数据源来动态控制 规则匹配。 51 | 52 | 该接口是为了可以 动态更改匹配规则,不需要修改程序任何地方。 53 | 54 | 匹配规则权级 55 | 56 | 匹配数据获取 接口类》XML匹配配置 57 | 58 | 注: 59 | 转发模式 60 | 61 | 规则匹配成功,后续规则将不在处理。 62 | 63 | 不建议一个匹配规则中 设置 多个转发 Topic 64 | 65 | 因为如果有一个转发失败,后续将不进行转发,并且返回 消费失败。 66 | 67 | 容错次数>1,将继续 消费该消息, 原先转发成功的队列将继续转发。 68 | 69 | 会造成 一个消息多次转发到 同一个Topic 中。 70 | 71 | 外调模式 72 | 73 | 一个匹配规则 配置XML 转发 Topic 和 标签后该模式只能设置 一个 转发 Topic 74 | 75 | 注入转发模式类后 将按照转发模式中 规则转发 76 | 77 | 重要事项: 78 | 79 | 强烈建议 功能单一化。 80 | 81 | 设置多个匹配规则,和 多个转发Topic 规则后 。 82 | 83 | 只要有一个处理失败,后续将不进行处理。 84 | 85 | 并容错次数 >1 将继续消费该消息, 原先转发成功的还将继续转发。 86 | 87 | 会照成 一个消息 多次转发 到 同一个Topic 中。 88 | 89 | 90 | 91 | 版本更新 92 | 93 |   94 | 95 | 版本V2.4.1 96 | 97 | 为 方便 直接使用 不需要页面管理 添加 初始化启动生产者功能 判断条件 MQ.Topic 如果有值就直接启动 98 | 99 | 版本V2.4.0 100 | 101 | 发布私人Maven 库 使用更加方便 102 | 103 | atliwen 104 | https://raw.githubusercontent.com/atliwen/maven-repo/master/repository 105 | 106 | 107 | com.currencyboot.mq 108 | currencyboot-mq 109 | 2.4.0-SNAPSHOT 110 | 111 | 112 | 版本V2.3.1 113 | 114 | 新增 消费超出设置次数 触发的接口类 115 | 116 | 版本V2.3.0 117 | 118 | 新增 生产端 事务 支持 119 | RocketMq 对事务的支持 生产端不支持多条发送消息 事务提交啊 120 | 无必要,不推荐使用 121 | 122 | 版本V2.2.2 123 | 124 | 外调模式 请求 HTTP 响应 状态码 202 后续将不继续 转发 消息 125 | 126 | 127 | 版本V2.2.1 128 | 129 | 修改HTML 发送消息功能按钮错误名称 130 | 131 | 版本V2.2.0 132 | 133 | 新增 外调 Web Api 后续结果进行 转发消息 134 | 135 | 外调功能增强后 支持两种模式 136 | 137 | 1、调用 Web Api 后 消息生命周期结束。 138 | 2、调用 Web Api 后 得到 响应数据,消息继续流转。 139 | 140 | 版本V2.1.0 141 | 142 | 新增 匹配数据获取 接口类 143 | 144 | 通过 数据源来获取 匹配数据。 方便通过 数据源表操作就可以动态修改 匹配规则。 145 | 146 | 匹配规则权级 匹配数据获取 接口类》XML匹配配置 147 | 148 | 149 | 版本V2.0.0 (重要更新) 150 | 151 | 转换为SpringBoot 版 152 | 原因: 153 | SpringBoot通常被认为是快速开发的,其实我更加觉得它是为微服务提供的。 154 | 155 | SpringBoot有简单友好的数据源依赖,非常适合进行并不复杂的数据源操作。并且我们通常需要MQ处理的并没有复杂的操作。 156 | 157 | 如果有,建议梳理业务流程,将程序拆分,通过消息转发,外调WEB api 来处理。 158 | 159 | 微服务的核心 我觉得就是 SOA ,当然也并不能为了SOA而SOA。一切的前提是从业务需求出发 160 | 版本V1.1.1 161 | 162 | 修改了 验证方法 Bug 163 | 164 | 版本V1.1.0 (重要更新) 165 | 166 | 新增: 167 | 1 外调模式 验证规则 匹配 调用 对应的 Web服务端 168 | 169 | 2 转发模式 和 外调模式 中 增加 对 消息实体的 数据转译 处理接口 170 | 171 | 版本V1.0.1 172 | 173 | 新增: 174 | 175 | MQ在一个集群中 只能消费 一个 topic 下 一个 Tags 的 页面提示 176 | 177 | 178 | 版本V1.0.0 179 | 180 | 该程序 消费模式有三种 181 | 182 | 1 183 | 内销模式 : 184 | 185 | 该模式下 只需要继承 baseMessageListenerConsumer 接口 实现该接口的方法 即可 。 186 | 187 | 和使用 官方MQ一样 只是对其进行了包装拓展 对实体数据进行了 编码转换,和容错次数验证 。 188 | 189 | 2 190 | 外调模式 : 191 | 192 | 该模式是为了 将其调用 其他 服务 ( SOA 理念 和 restful api 理念 ) 193 | 194 | 该模式的核心理念就是 高拓展性 低耦合度 高并发 195 | 196 | 通过MQ消费端的 N线程消费,和 web服务器的 N请求处理的特性 结合实现一套 高并发的 消息处理模式。 197 | 198 | SOA(面向服务)和 restful api (一切都是资源) 非常建议 符合 改理念来编写 被调用 WEB服务端 199 | 200 | 支持N个 Web服务端 调用 但是 只要一个调用失败 将不再执行后续 调用 并且 从新开始消费 201 | (不建议 指定多个 Topic 外调) 202 | 203 | 3 204 | 转发模式 : 205 | 206 | 该模式 支持 Tags 和 实体 数据信息 匹配 定义转发的队列 名称和 标签 207 | 208 | 支持 N个转发对象和验证 但是 只要一个 转发失败 将不再 执行后续转发 并且 从新开始消费 209 | (不建议 指定多个 Topic 转发) 210 | 211 | 总结: 212 | 对阿里MQ 进行包装 是想通过 改MQ 来实现一套 动态管理消费端,轻易可拓展 ,轻易可使用的。 213 | 并且很容易就可以 分布式使用的一套 微处理程序 也可以看成是 一套微服务系统 。 214 | 215 | 216 | 问题备注: 217 | 218 | 如 HTML 页面 报错 跨域问题, 请删除 maven 库 spring 4.2 版本 重新获取 219 | 220 | PS: 221 | 222 | 随着项目越来越完善,感觉应该出个 使用指南之类的文档了。本来很喜欢 springfox 但是 这次写的项目并不是 restful API 223 | 224 | 等继续完善后,应该会出个文档。 项目 代码并不复查, 同时也劲量的减少配置 方便使用和二次开发了。 225 | 226 | -------------------------------------------------------------------------------- /RocketMqCurrencyBootSample/src/main/java/com/atliwen/server/messagelistener/ForwardedMessageListConsumer.java: -------------------------------------------------------------------------------- 1 | package com.atliwen.server.messagelistener; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.util.List; 5 | import java.util.Map; 6 | 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; 11 | import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; 12 | import com.alibaba.rocketmq.common.message.MessageExt; 13 | import com.currencyboot.service.rocketmq.MqProducer; 14 | import com.currencyboot.service.rocketmq.common.ForwardedHelp; 15 | import com.currencyboot.service.rocketmq.messagelistener.Interface.ForwardedMessageListConsumerInterface; 16 | import com.currencyboot.service.rocketmq.messagelistener.Interface.MatchingInterface; 17 | import com.currencyboot.service.rocketmq.messagelistener.Interface.MessageListenerConsumerInterface; 18 | 19 | /** 20 | *

Title: ForwardedMessageListConsumer

21 | *

@Description: 转发消费端

22 | *

Company:

23 | * @author 李文 24 | * @date 2016年8月1日 上午10:11:57 25 | */ 26 | public class ForwardedMessageListConsumer implements MessageListenerConsumerInterface 27 | { 28 | private static final Logger LOGGER = LoggerFactory 29 | .getLogger(ForwardedMessageListConsumer.class); 30 | 31 | /** 32 | * 验证规则 33 | * 34 | * list 35 | * map 36 | * Tag = test1,test2,test3 37 | * body= 客户编码A,客户编码B 38 | * Topic= top1,top2,top3 39 | * Tags=t1||t2 或者 Tags=* 40 | * 41 | */ 42 | private List> matching; 43 | 44 | /** 45 | * 生产端 46 | */ 47 | private MqProducer producer; 48 | 49 | /** 50 | * 验证规则数据源获取接口 51 | */ 52 | private MatchingInterface baseMatching; 53 | 54 | /** 55 | * 转发 消息处理 56 | */ 57 | private ForwardedMessageListConsumerInterface forwarded; 58 | 59 | @Override 60 | public ConsumeConcurrentlyStatus consumeMessage(String strBody, MessageExt msg, 61 | ConsumeConcurrentlyContext context) 62 | { 63 | if (baseMatching != null) 64 | { 65 | List> me = baseMatching.getMatching(); 66 | if (me != null && me.size() != 0) matching = me; 67 | } 68 | 69 | for (Map map : matching) 70 | { 71 | if (forwarded != null) strBody = forwarded.MessageConsumer(strBody, msg, context); 72 | return sendMqTags(map, msg.getTags(), strBody); 73 | } 74 | return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; 75 | } 76 | 77 | /** 78 | * 验证规则 并按照规则将数据 转发到对应的 队列中 79 | */ 80 | private ConsumeConcurrentlyStatus sendMqTags(Map matchingMap, String MqTags, 81 | String Mqbody) 82 | { 83 | // 验证是否有空值 84 | String[] keys = 85 | { "Tag", "body", "Topic", "Tags" }; 86 | 87 | try 88 | { 89 | ForwardedHelp.outStr(matchingMap, keys); 90 | } 91 | catch (Exception e) 92 | { 93 | LOGGER.error(" 转发消费端 验证规则 并按照规则将数据 转发到对应的 ", e.getMessage()); 94 | return ConsumeConcurrentlyStatus.RECONSUME_LATER; 95 | } 96 | 97 | return equalsTag(matchingMap, MqTags, Mqbody); 98 | 99 | } 100 | 101 | /** 102 | * 匹配 Tag 消息 Tag 的验证 103 | * 104 | * @param matchingMap 注入的 匹配 规则 数据 105 | * @param MqTags 当前消费的MQ Tags 106 | * @param Mqbody 当前转发的 消息 实体 107 | * @param Topic 需要转发到的 MQ Topic 108 | * @return 109 | */ 110 | private ConsumeConcurrentlyStatus equalsTag(Map matchingMap, String MqTags, 111 | String Mqbody) 112 | { 113 | String[] Topic = matchingMap.get("Topic").split(","); 114 | 115 | // 是否需要 匹配 Tag 116 | if ("*".equals(matchingMap.get("Tag"))) 117 | { 118 | return equalsbody(matchingMap, MqTags, Mqbody, Topic);// 不需要匹配body 119 | } 120 | else 121 | { 122 | // 进行匹配 Tag 123 | if (ForwardedHelp.isContains(MqTags, matchingMap.get("Tag"))) 124 | { 125 | // 匹配成功 进行 匹配 body 126 | return equalsbody(matchingMap, MqTags, Mqbody, Topic); 127 | } 128 | else 129 | { 130 | LOGGER.debug("Tag 匹配未成功 放弃该消息 消息内容是 " + MqTags); 131 | return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; 132 | } 133 | } 134 | } 135 | 136 | /** 137 | * 匹配 body 消息实体的验证 138 | * 139 | * @param matchingMap 注入的 匹配 规则 数据 140 | * @param MqTags 当前消费的MQ Tags 141 | * @param Mqbody 当前转发的 消息 实体 142 | * @param Topic 需要转发到的 MQ Topic 143 | * @return 144 | */ 145 | private ConsumeConcurrentlyStatus equalsbody(Map matchingMap, String MqTags, 146 | String Mqbody, String[] Topic) 147 | { 148 | // 匹配 body 149 | if ("*".equals(matchingMap.get("body"))) 150 | { 151 | // 不需要 匹配 Tag 152 | return sendMq(matchingMap, Mqbody, Topic); 153 | } 154 | 155 | if (!ForwardedHelp.isContains(Mqbody, matchingMap.get("body"))) 156 | { 157 | LOGGER.debug("body 匹配未成功 放弃该消息 消息内容是 " + MqTags); 158 | return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; 159 | } 160 | else 161 | { 162 | // 转发到MQ 163 | return sendMq(matchingMap, Mqbody, Topic); 164 | } 165 | } 166 | 167 | /** 168 | * 将数据发送给MQ 169 | * @param matchingMap 注入的配置数据 170 | * @param Mqbody 当前消息的 实体 171 | * @param Topic 转发的队列名称 172 | */ 173 | private ConsumeConcurrentlyStatus sendMq(Map matchingMap, String Mqbody, 174 | String[] Topic) 175 | { 176 | for (String topicval : Topic) 177 | { 178 | try 179 | { 180 | if (producer.send(topicval, matchingMap.get("Tags"), 181 | Mqbody) == null) { return ConsumeConcurrentlyStatus.RECONSUME_LATER; } 182 | return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; 183 | } 184 | catch (UnsupportedEncodingException e) 185 | { 186 | LOGGER.error(" 转发消费端 异常 转发消息到MQ 失败 Tag= " + matchingMap.get("Topic") + " body= " 187 | + Mqbody, e); 188 | return ConsumeConcurrentlyStatus.RECONSUME_LATER; 189 | } 190 | } 191 | return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; 192 | } 193 | 194 | /** 195 | * 设置验证规则listmapTag=test1test2test3body=客户编码A,客户编码BTopic=top1top2top3Tags=t1||t2或者Tags= 196 | * @param matching 验证规则listmapTag=test1test2test3body=客户编码A,客户编码BTopic=top1top2top3Tags=t1||t2或者Tags= 197 | */ 198 | public void setMatching(List> matching) 199 | { 200 | this.matching = matching; 201 | } 202 | 203 | /** 204 | * 设置生产端 205 | * @param producer 生产端 206 | */ 207 | public void setProducer(MqProducer producer) 208 | { 209 | this.producer = producer; 210 | } 211 | 212 | /** 213 | * 设置转发消息处理 214 | * @param forwarded 转发消息处理 215 | */ 216 | public void setForwarded(ForwardedMessageListConsumerInterface forwarded) 217 | { 218 | this.forwarded = forwarded; 219 | } 220 | 221 | /** 222 | * @param 验证规则数据源获取接口 the baseMatching to set 223 | */ 224 | public void setBaseMatching(MatchingInterface baseMatching) 225 | { 226 | this.baseMatching = baseMatching; 227 | } 228 | 229 | } 230 | -------------------------------------------------------------------------------- /RocketMqCurrencyBoot/src/main/java/com/currencyboot/service/rocketmq/MqProducer.java: -------------------------------------------------------------------------------- 1 | package com.currencyboot.service.rocketmq; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.text.MessageFormat; 5 | 6 | import javax.annotation.PostConstruct; 7 | import javax.annotation.PreDestroy; 8 | 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.beans.factory.annotation.Value; 13 | 14 | import com.alibaba.rocketmq.client.exception.MQClientException; 15 | import com.alibaba.rocketmq.client.producer.DefaultMQProducer; 16 | import com.alibaba.rocketmq.client.producer.LocalTransactionExecuter; 17 | import com.alibaba.rocketmq.client.producer.SendResult; 18 | import com.alibaba.rocketmq.client.producer.SendStatus; 19 | import com.alibaba.rocketmq.client.producer.TransactionCheckListener; 20 | import com.alibaba.rocketmq.client.producer.TransactionMQProducer; 21 | import com.alibaba.rocketmq.common.message.Message; 22 | 23 | /** 24 | *

Title: MqProducer

25 | *

@Description: 发送MQ 类 26 | * NamesrvAddr 27 | * ProducerGroupName 28 | * InstanceName 29 | * SendMsgTimeout 30 | * 需要在配置文件中设置 31 | * 32 | * 日志 建议单独配置 文件夹记录 方便问题排查 33 | * 34 | * 备注: 该类是基于SPRING 管理的 请使用 SPRING BAEN 是单例模式 35 | * 36 | * 37 | * PS: 该类不加入 没有设置注解标签 所有不会主动加入Spring 方法只使用 消费端 不使用 生产端 的环境 38 | *

39 | *

Company: zjs

40 | * @author 李文 41 | * @date 2016年8月1日 上午10:06:08 42 | */ 43 | public class MqProducer 44 | { 45 | Object producer = null; 46 | 47 | private static final Logger LOGGER = LoggerFactory.getLogger(MqProducer.class); 48 | 49 | @Value("${MQ.NamesrvAddr}") 50 | private String NamesrvAddr; 51 | 52 | @Value("${MQ.ProducerGroupName}") 53 | private String ProducerGroupName; 54 | 55 | @Value("${MQ.InstanceName}") 56 | private String InstanceName; 57 | 58 | @Value("${MQ.SendMsgTimeout:20000}") 59 | private int SendMsgTimeout; 60 | 61 | /** 62 | * Broker 回查 Producer 事务状态时, 线程池大小 63 | */ 64 | @Value("${MQ.checkThreadPoolMinSize:1}") 65 | private int checkThreadPoolMinSize; 66 | 67 | /** 68 | * Broker 回查 Producer 事务状态时, 线程池大小 69 | */ 70 | @Value("${MQ.checkThreadPoolMaxSize:1}") 71 | private int checkThreadPoolMaxSize; 72 | 73 | /** 74 | * Broker 回查 Producer 事务状态时,Producer 本地缓冲请求队列大小 75 | */ 76 | @Value("${MQ.checkRequestHoldMax:2000}") 77 | private int checkRequestHoldMax; 78 | 79 | /** 80 | * 未决事务,服务器回查客户端 81 | */ 82 | @Autowired(required = false) 83 | // @Qualifier("transactionCheckListener") 84 | private TransactionCheckListener transaction; 85 | 86 | /** 87 | * 本地事务 88 | */ 89 | @Autowired(required = false) 90 | // @Qualifier("localTransactionExecuter") 91 | private LocalTransactionExecuter transactionExecuter; 92 | 93 | @PostConstruct 94 | private void init() throws MQClientException 95 | { 96 | if (transaction == null || transactionExecuter == null) 97 | { 98 | DefaultMQProducer defaultProducer = new DefaultMQProducer(); 99 | // Producer 组名, 多个 Producer 如果属于一 个应用,发送同样的消息,则应该将它们 归为同一组 100 | defaultProducer.setProducerGroup(ProducerGroupName); 101 | // Name Server 地址列表 102 | defaultProducer.setNamesrvAddr(NamesrvAddr); 103 | // 生产者名称 104 | defaultProducer.setInstanceName(InstanceName); 105 | // 超时时间 106 | defaultProducer.setSendMsgTimeout(SendMsgTimeout); 107 | defaultProducer.start(); 108 | producer = defaultProducer; 109 | } 110 | else 111 | { 112 | TransactionMQProducer transactionProducer = new TransactionMQProducer(); 113 | // Producer 组名, 多个 Producer 如果属于一 个应用,发送同样的消息,则应该将它们 归为同一组 114 | transactionProducer.setProducerGroup(ProducerGroupName); 115 | // Name Server 地址列表 116 | transactionProducer.setNamesrvAddr(NamesrvAddr); 117 | // 生产者名称 118 | transactionProducer.setInstanceName(InstanceName); 119 | // 超时时间 120 | transactionProducer.setSendMsgTimeout(SendMsgTimeout); 121 | transactionProducer.setCheckThreadPoolMinSize(checkThreadPoolMinSize); 122 | transactionProducer.setCheckThreadPoolMaxSize(checkThreadPoolMaxSize); 123 | transactionProducer.setCheckRequestHoldMax(checkRequestHoldMax); 124 | transactionProducer.setTransactionCheckListener(transaction); 125 | transactionProducer.start(); 126 | producer = transactionProducer; 127 | } 128 | 129 | } 130 | 131 | @PreDestroy 132 | private void Destroy() 133 | { 134 | if (producer instanceof DefaultMQProducer) 135 | { 136 | DefaultMQProducer producerMq = (DefaultMQProducer) producer; 137 | if (producerMq != null) 138 | { 139 | producerMq.shutdown(); 140 | } 141 | } 142 | } 143 | 144 | /** 145 | * 发送数据到MQ方法 数据编码格式 默认UTF-8 146 | * 147 | * @param Topic 队列名称 148 | * @param Tags 标签名称 149 | * @param body 发送的数据 推荐 JSOM 或者 XML 结构 150 | * @return 响应信息进行了内部处理 确认已经保存到 MQ 并且 日志已经记录 只要值不是NULL 就是成功发送 151 | * @throws UnsupportedEncodingException 转换字符集出错 请检查是否可以转换 152 | */ 153 | public SendResult send(String Topic, String Tags, String body) 154 | throws UnsupportedEncodingException 155 | { 156 | return this.send(Topic, Tags, body, null); 157 | } 158 | 159 | /** 160 | * 发送数据到MQ方法 161 | * 162 | * @param Topic 队列名称 163 | * @param Tags 标签名称 164 | * @param body 发送的数据 推荐 JSOM 或者 XML 结构 165 | * @param Encoding 数据编码格式 默认UTF-8 166 | * @return 响应信息进行了内部处理 确认已经保存到 MQ 并且 日志已经记录 只要值不是NULL 就是成功发送 167 | * @throws UnsupportedEncodingException 转换字符集出错 请检查是否可以转换 168 | */ 169 | public SendResult send(String Topic, String Tags, String body, String Encoding) 170 | throws UnsupportedEncodingException 171 | { 172 | 173 | String loggerString = MessageFormat.format( 174 | "将要发送到Mq的数据 Topic={0} Tags={1} body={2} Encoding={3} ", Topic, Tags, body, 175 | Encoding); 176 | 177 | if (Encoding == null || "".equals(Encoding)) 178 | { 179 | Encoding = "UTF-8"; 180 | } 181 | 182 | if (Tags == null || "".equals(Tags)) 183 | { 184 | Tags = "*"; 185 | } 186 | 187 | LOGGER.info(loggerString); 188 | 189 | Message me = new Message(); 190 | // 标示 191 | me.setTopic(Topic); 192 | // 标签 193 | me.setTags(Tags); 194 | // 内容 195 | me.setBody(body.getBytes(Encoding)); 196 | // 发送信息到MQ SendResult 是当前发送的状态 官方说 不出异常 就是成功 197 | SendResult sendResult = null; 198 | try 199 | { 200 | if (producer instanceof TransactionMQProducer) 201 | { 202 | sendResult = ((TransactionMQProducer) producer).sendMessageInTransaction(me, 203 | transactionExecuter, null); 204 | 205 | } 206 | else 207 | { 208 | sendResult = ((DefaultMQProducer) producer).send(me); 209 | } 210 | } 211 | catch (Exception e) 212 | { 213 | LOGGER.error(" 发送 数据给MQ出现异常 " + loggerString, e); 214 | } 215 | // 当消息发送失败时如何处理 getSendStatus 获取发送的状态 216 | if (sendResult == null || sendResult.getSendStatus() != SendStatus.SEND_OK) 217 | { 218 | LOGGER.info(loggerString + "发送消息失败" + " MQ状态值 SendResult=" + sendResult); 219 | sendResult = null; 220 | } 221 | LOGGER.info("发送到MQ成功" + sendResult); 222 | return sendResult; 223 | } 224 | 225 | // ---- set ----- 226 | 227 | public void setNamesrvAddr(String namesrvAddr) 228 | { 229 | NamesrvAddr = namesrvAddr; 230 | } 231 | 232 | public void setProducerGroupName(String producerGroupName) 233 | { 234 | ProducerGroupName = producerGroupName; 235 | } 236 | 237 | public void setInstanceName(String instanceName) 238 | { 239 | InstanceName = instanceName; 240 | } 241 | 242 | public void setSendMsgTimeout(int sendMsgTimeout) 243 | { 244 | SendMsgTimeout = sendMsgTimeout; 245 | } 246 | 247 | } 248 | -------------------------------------------------------------------------------- /RocketMqCurrencyBoot/src/main/resources/static/js/common.js: -------------------------------------------------------------------------------- 1 | Date.prototype.format = function(format){ 2 | var o = { 3 | "M+" : this.getMonth()+1, //month 4 | "d+" : this.getDate(), //day 5 | "h+" : this.getHours(), //hour 6 | "m+" : this.getMinutes(), //minute 7 | "s+" : this.getSeconds(), //second 8 | "q+" : Math.floor((this.getMonth()+3)/3), //quarter 9 | "S" : this.getMilliseconds() //millisecond 10 | }; 11 | if(/(y+)/.test(format)){ 12 | format = format.replace(RegExp.$1, (this.getFullYear()+"").substr(4 - RegExp.$1.length)); 13 | } 14 | for(var k in o) { 15 | if(new RegExp("("+ k +")").test(format)){ 16 | format = format.replace(RegExp.$1, RegExp.$1.length==1 ? o[k] : ("00"+ o[k]).substr((""+ o[k]).length)); 17 | } 18 | } 19 | return format; 20 | }; 21 | 22 | var TT = TAOTAO = { 23 | // 编辑器参数 24 | kingEditorParams : { 25 | filePostName : "uploadFile", 26 | uploadJson : '/rest/pic/upload', 27 | dir : "image" 28 | }, 29 | // 格式化时间 30 | formatDateTime : function(val,row){ 31 | var now = new Date(val); 32 | return now.format("yyyy-MM-dd hh:mm:ss"); 33 | }, 34 | // 格式化连接 35 | formatUrl : function(val,row){ 36 | if(val){ 37 | return "查看"; 38 | } 39 | return ""; 40 | }, 41 | // 格式化价格 42 | formatPrice : function(val,row){ 43 | return (val/1000).toFixed(2); 44 | }, 45 | // 格式化商品的状态 46 | formatItemStatus : function formatStatus(val,row){ 47 | if (val == 1){ 48 | return '正常'; 49 | } else if(val == 2){ 50 | return '下架'; 51 | } else { 52 | return '未知'; 53 | } 54 | }, 55 | 56 | init : function(data){ 57 | this.initPicUpload(data); 58 | this.initItemCat(data); 59 | }, 60 | // 初始化图片上传组件 61 | initPicUpload : function(data){ 62 | $(".picFileUpload").each(function(i,e){ 63 | var _ele = $(e); 64 | _ele.siblings("div.pics").remove(); 65 | _ele.after('\ 66 |
\ 67 |
    \ 68 |
    '); 69 | // 回显图片 70 | if(data && data.pics){ 71 | var imgs = data.pics.split(","); 72 | for(var i in imgs){ 73 | if($.trim(imgs[i]).length > 0){ 74 | _ele.siblings(".pics").find("ul").append("
  • "); 75 | } 76 | } 77 | } 78 | $(e).click(function(){ 79 | var form = $(this).parentsUntil("form").parent("form"); 80 | KindEditor.editor(TT.kingEditorParams).loadPlugin('multiimage',function(){ 81 | var editor = this; 82 | editor.plugin.multiImageDialog({ 83 | clickFn : function(urlList) { 84 | var imgArray = []; 85 | KindEditor.each(urlList, function(i, data) { 86 | imgArray.push(data.url); 87 | form.find(".pics ul").append("
  • "); 88 | }); 89 | form.find("[name=image]").val(imgArray.join(",")); 90 | editor.hideDialog(); 91 | } 92 | }); 93 | }); 94 | }); 95 | }); 96 | }, 97 | 98 | // 初始化选择类目组件 99 | initItemCat : function(data){ 100 | $(".selectItemCat").each(function(i,e){ 101 | var _ele = $(e); 102 | if(data && data.cid){ 103 | _ele.after(""+data.cid+""); 104 | }else{ 105 | _ele.after(""); 106 | } 107 | _ele.unbind('click').click(function(){ 108 | $("
    ").css({padding:"5px"}).html("
      ") 109 | .window({ 110 | width:'500', 111 | height:"450", 112 | modal:true, 113 | closed:true, 114 | iconCls:'icon-save', 115 | title:'选择类目', 116 | onOpen : function(){ 117 | var _win = this; 118 | $("ul",_win).tree({ 119 | url:'/rest/item/cat/list', 120 | animate:true, 121 | onClick : function(node){ 122 | if($(this).tree("isLeaf",node.target)){ 123 | // 填写到cid中 124 | _ele.parent().find("[name=cid]").val(node.id); 125 | _ele.next().text(node.text).attr("cid",node.id); 126 | $(_win).window('close'); 127 | if(data && data.fun){ 128 | data.fun.call(this,node); 129 | } 130 | } 131 | } 132 | }); 133 | }, 134 | onClose : function(){ 135 | $(this).window("destroy"); 136 | } 137 | }).window('open'); 138 | }); 139 | }); 140 | }, 141 | 142 | createEditor : function(select){ 143 | return KindEditor.create(select, TT.kingEditorParams); 144 | }, 145 | 146 | /** 147 | * 创建一个窗口,关闭窗口后销毁该窗口对象。
      148 | * 149 | * 默认:
      150 | * width : 80%
      151 | * height : 80%
      152 | * title : (空字符串)
      153 | * 154 | * 参数:
      155 | * width :
      156 | * height :
      157 | * title :
      158 | * url : 必填参数
      159 | * onLoad : function 加载完窗口内容后执行
      160 | * 161 | * 162 | */ 163 | createWindow : function(params){ 164 | $("
      ").css({padding:"5px"}).window({ 165 | width : params.width?params.width:"80%", 166 | height : params.height?params.height:"80%", 167 | modal:true, 168 | title : params.title?params.title:" ", 169 | href : params.url, 170 | onClose : function(){ 171 | $(this).window("destroy"); 172 | }, 173 | onLoad : function(){ 174 | if(params.onLoad){ 175 | params.onLoad.call(this); 176 | } 177 | } 178 | }).window("open"); 179 | }, 180 | 181 | closeCurrentWindow : function(){ 182 | $(".panel-tool-close").click(); 183 | }, 184 | 185 | changeItemParam : function(node,formId){ 186 | $.getJSON("/rest/item/param/query/itemcatid/" + node.id,function(data){ 187 | if(data.status == 200 && data.data){ 188 | $("#"+formId+" .params").show(); 189 | var paramData = JSON.parse(data.data.paramData); 190 | var html = "
        "; 191 | for(var i in paramData){ 192 | var pd = paramData[i]; 193 | html+="
      • "; 194 | html+=""; 195 | 196 | for(var j in pd.params){ 197 | var ps = pd.params[j]; 198 | html+=""; 199 | } 200 | 201 | html+="
        "+pd.group+"
        "+ps+":
        "; 202 | } 203 | html+= "
      "; 204 | $("#"+formId+" .params td").eq(1).html(html); 205 | }else{ 206 | $("#"+formId+" .params").hide(); 207 | $("#"+formId+" .params td").eq(1).empty(); 208 | } 209 | }); 210 | }, 211 | getSelectionsIds : function (select){ 212 | var list = $(select); 213 | var sels = list.datagrid("getSelections"); 214 | var ids = []; 215 | for(var i in sels){ 216 | ids.push(sels[i].id); 217 | } 218 | ids = ids.join(","); 219 | return ids; 220 | }, 221 | 222 | /** 223 | * 初始化单图片上传组件
      224 | * 选择器为:.onePicUpload
      225 | * 上传完成后会设置input内容以及在input后面追加 226 | */ 227 | initOnePicUpload : function(){ 228 | $(".onePicUpload").click(function(){ 229 | var _self = $(this); 230 | KindEditor.editor(TT.kingEditorParams).loadPlugin('image', function() { 231 | this.plugin.imageDialog({ 232 | showRemote : false, 233 | clickFn : function(url, title, width, height, border, align) { 234 | var input = _self.siblings("input"); 235 | input.parent().find("img").remove(); 236 | input.val(url); 237 | input.after(""); 238 | this.hideDialog(); 239 | } 240 | }); 241 | }); 242 | }); 243 | } 244 | }; 245 | -------------------------------------------------------------------------------- /RocketMqCurrencyBootSample/src/main/java/com/atliwen/server/messagelistener/ExternalCallConcurrentlyStatus.java: -------------------------------------------------------------------------------- 1 | package com.atliwen.server.messagelistener; 2 | 3 | import java.util.HashMap; 4 | import java.util.List; 5 | import java.util.Map; 6 | 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.beans.factory.annotation.Value; 11 | 12 | import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; 13 | import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; 14 | import com.alibaba.rocketmq.client.producer.SendResult; 15 | import com.alibaba.rocketmq.common.message.MessageExt; 16 | import com.currencyboot.domain.mq.HttpResponse; 17 | import com.currencyboot.service.rocketmq.MqProducer; 18 | import com.currencyboot.service.rocketmq.common.ForwardedHelp; 19 | import com.currencyboot.service.rocketmq.common.HttpRequest; 20 | import com.currencyboot.service.rocketmq.messagelistener.Interface.ExternalCallConsumerInterface; 21 | import com.currencyboot.service.rocketmq.messagelistener.Interface.MatchingInterface; 22 | import com.currencyboot.service.rocketmq.messagelistener.Interface.MessageListenerConsumerInterface; 23 | 24 | /** 25 | *

      Title: ExternalCallConcurrentlyStatus

      26 | *

      @Description: web服务调用 消费端

      27 | *

      Company:

      28 | * @author 李文 29 | * @date 2016年8月1日 上午10:09:59 30 | */ 31 | public class ExternalCallConcurrentlyStatus implements MessageListenerConsumerInterface 32 | { 33 | 34 | private static final Logger LOGGER = LoggerFactory 35 | .getLogger(ExternalCallConcurrentlyStatus.class); 36 | 37 | /** 38 | * 验证规则 39 | * 40 | * list 41 | * map 42 | * Tag = test1,test2,test3 43 | * body= 客户编码A,客户编码B 44 | * url = http://10.10.12.27 45 | * 46 | */ 47 | private List> matching; 48 | 49 | /** 50 | * 数据字符集 51 | */ 52 | @Value("${MQ.Encoding:UTF-8}") 53 | private String Encoding; 54 | 55 | /** 56 | * 转译 实体数据 57 | */ 58 | private ExternalCallConsumerInterface externalCall; 59 | 60 | /** 61 | * 验证规则数据源获取接口 62 | */ 63 | private MatchingInterface baseMatching; 64 | 65 | /** 66 | * 转发模式 67 | */ 68 | private ForwardedMessageListConsumer forwarded; 69 | 70 | /** 71 | * 生产端 72 | */ 73 | @Autowired(required = false) 74 | private MqProducer producer; 75 | 76 | @Override 77 | public ConsumeConcurrentlyStatus consumeMessage(String strBody, MessageExt msg, 78 | ConsumeConcurrentlyContext context) 79 | { 80 | 81 | if (baseMatching != null) 82 | { 83 | List> me = baseMatching.getMatching(); 84 | if (me != null && me.size() != 0) matching = me; 85 | } 86 | 87 | // TODO 待完善 日志系统 最简单的方法是 使用 mongodb 存放日志数据 88 | for (Map map : matching) 89 | { 90 | Map params = new HashMap(); 91 | 92 | params.put("Topic", msg.getTopic()); 93 | params.put("Tags", msg.getTags()); 94 | 95 | if (externalCall == null) params.put("Body", strBody); 96 | else params.put("Body", externalCall.MessageConsumer(strBody, msg, context)); 97 | 98 | return sendMqTags(map, msg.getTags(), params, strBody, msg, context); 99 | } 100 | return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; 101 | 102 | } 103 | 104 | /** 105 | * 验证规则 并按照规则将数据 转发到对应的 队列中 106 | */ 107 | public ConsumeConcurrentlyStatus sendMqTags(Map matchingMap, String MqTags, 108 | Map params, String strBody, MessageExt msg, 109 | ConsumeConcurrentlyContext context) 110 | { 111 | // 验证是否有空值 112 | String[] keys = 113 | { "url", "Tag", "body" }; 114 | try 115 | { 116 | ForwardedHelp.outStr(matchingMap, keys); 117 | } 118 | catch (Exception e) 119 | { 120 | LOGGER.error(e.getMessage()); 121 | return ConsumeConcurrentlyStatus.RECONSUME_LATER; 122 | } 123 | 124 | return forwardedWebDate(matchingMap, MqTags, params, msg, context); 125 | 126 | } 127 | 128 | /** 129 | * 外调 Web API 后续结果 继续转发 转发类 权级 》 XML 配置权级 130 | * @param matchingMap 131 | * @param MqTags 132 | * @param params 133 | * @param msg 134 | * @param context 135 | * @return 136 | */ 137 | private ConsumeConcurrentlyStatus forwardedWebDate(Map matchingMap, 138 | String MqTags, Map params, MessageExt msg, 139 | ConsumeConcurrentlyContext context) 140 | { 141 | HttpResponse response = equalsTag(matchingMap, MqTags, params); 142 | if (response.getState() == 202) { return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; } 143 | if (response.getState() != 200) { return ConsumeConcurrentlyStatus.RECONSUME_LATER; } 144 | if (forwarded != null) { return forwarded.consumeMessage(response.getData(), msg, 145 | context); } 146 | 147 | String[] keyszf = 148 | { "Topic", "Tags" }; 149 | try 150 | { 151 | ForwardedHelp.outStr(matchingMap, keyszf); 152 | } 153 | catch (Exception e) 154 | { 155 | // LOGGER.debug(" 外调Web API 后续不进行转发 消息 body= " + 156 | // response.getData()); 157 | LOGGER.error("外调Web API 后续不进行转发 消息 body= " + response.getData() + " " + e.getMessage()); 158 | return ConsumeConcurrentlyStatus.RECONSUME_LATER; 159 | } 160 | 161 | try 162 | { 163 | if (producer == null) 164 | { 165 | LOGGER.error(" 外调后续转发 发送失败 , 并未配置 生产者 "); 166 | return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; 167 | } 168 | 169 | SendResult se = producer.send(matchingMap.get("Topic"), matchingMap.get("Tags"), 170 | response.getData()); 171 | if (se == null) 172 | { 173 | LOGGER.error(" 外调后续转发 发送失败 。需要转发的数据 Topic=" + matchingMap.get("Topic") + " Tags=" 174 | + matchingMap.get("Tags") + " data" + response.getData()); 175 | return ConsumeConcurrentlyStatus.RECONSUME_LATER; 176 | } 177 | } 178 | catch (Exception e) 179 | { 180 | 181 | LOGGER.error(" 外调 异常 转发消息到MQ 失败 Tag= " + matchingMap.get("Topic") + " body= " 182 | + response.getData(), e); 183 | return ConsumeConcurrentlyStatus.RECONSUME_LATER; 184 | } 185 | return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; 186 | } 187 | 188 | /** 189 | * 匹配 Tag 消息 Tag 的验证 190 | * 191 | * @param matchingMap 注入的 匹配 规则 数据 192 | * @param MqTags 当前消费的MQ Tags 193 | * @param Mqbody 当前转发的 消息 实体 194 | * @param Topic 需要转发到的 MQ Topic 195 | * @return 196 | */ 197 | private HttpResponse equalsTag(Map matchingMap, String MqTags, 198 | Map params) 199 | { 200 | String url = matchingMap.get("url"); 201 | 202 | // 是否需要 匹配 Tag 203 | if ("*".equals(matchingMap.get("Tag"))) 204 | { 205 | // 不需要匹配 206 | // 匹配 body 207 | return equalsbody(matchingMap, MqTags, url, params); 208 | 209 | } 210 | else 211 | { 212 | // 进行匹配 Tag 213 | if (ForwardedHelp.isContains(MqTags, matchingMap.get("Tag"))) 214 | { 215 | // 匹配成功 进行 匹配 body 216 | return equalsbody(matchingMap, MqTags, url, params); 217 | } 218 | else 219 | { 220 | LOGGER.debug("Tag 匹配未成功 放弃该消息 消息内容是 " + MqTags); 221 | return new HttpResponse(202, "body Tag 匹配未成功 放弃该消息"); 222 | } 223 | } 224 | } 225 | 226 | /** 227 | * 匹配 body 消息实体的验证 228 | * 229 | * @param matchingMap 注入的 匹配 规则 数据 230 | * @param MqTags 当前消费的MQ Tags 231 | * @param Mqbody 当前转发的 消息 实体 232 | * @param Topic 需要转发到的 MQ Topic 233 | * @return 234 | */ 235 | private HttpResponse equalsbody(Map matchingMap, String MqTags, String url, 236 | Map params) 237 | { 238 | // 匹配 body 239 | if ("*".equals(matchingMap.get("body"))) 240 | { 241 | // 不需要 匹配 Tag 242 | return sendMq(matchingMap, url, params); 243 | } 244 | 245 | if (!ForwardedHelp.isContains(params.get("body"), matchingMap.get("body"))) 246 | { 247 | LOGGER.debug("body 匹配未成功 放弃该消息 消息内容是 " + MqTags); 248 | return new HttpResponse(200, "body 匹配未成功 放弃该消息"); 249 | } 250 | else 251 | { 252 | // 不需要 匹配 Tag 253 | return sendMq(matchingMap, url, params); 254 | } 255 | } 256 | 257 | /** 258 | * @param matchingMap 259 | * @param mqbody 260 | * @param url 261 | * @return 262 | */ 263 | private HttpResponse sendMq(Map matchingMap, String url, 264 | Map params) 265 | { 266 | 267 | return HttpRequest.sendPostMessage(url, params, Encoding); 268 | } 269 | 270 | /** 271 | * 设置验证规则listmapTag=test1test2test3body=客户编码A,客户编码Burl 272 | * @param matching 验证规则listmapTag=test1test2test3body=客户编码A,客户编码Burl 273 | */ 274 | public void setMatching(List> matching) 275 | { 276 | this.matching = matching; 277 | } 278 | 279 | /** 280 | * 设置转译实体数据 281 | * @param externalCall 转译实体数据 282 | */ 283 | public void setExternalCall(ExternalCallConsumerInterface externalCall) 284 | { 285 | this.externalCall = externalCall; 286 | } 287 | 288 | /** 289 | * @param 转发模式 the forwarded to set 290 | */ 291 | public void setForwarded(ForwardedMessageListConsumer forwarded) 292 | { 293 | this.forwarded = forwarded; 294 | } 295 | 296 | /**   297 |  * 设置验证规则数据源获取接口   298 |  * @param baseMatching 验证规则数据源获取接口   299 |  */ 300 | public void setBaseMatching(MatchingInterface baseMatching) 301 | { 302 | this.baseMatching = baseMatching; 303 | } 304 | 305 | } 306 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /RocketMqCurrencyBoot/src/main/resources/static/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | MQ动态管理工具 9 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 21 | 23 | 24 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 |
      39 | 92 |
      93 | 94 | 95 | 96 | 97 | 98 |
      99 |
      100 |
      101 |
      103 | 104 |
      105 |
      107 |
      108 | 109 | 110 | 115 |
      116 | 117 |
      118 |
      119 |
      120 | 121 | 122 | 123 |
      124 |
      126 | 127 |
      128 | 129 |
      130 |
      131 |

      132 | New  新消费端 134 |

      135 |
      136 |
      137 | 138 |
      139 |
      140 |
      141 | 142 |
      143 | 144 |
      145 | 146 |
      147 | 149 |
      150 |
      151 | 152 | 153 | 154 |
      155 | 156 |
      157 | 159 |
      160 |
      161 | 162 |
      163 | 164 |
      165 |
      166 | 167 |
      168 |
      169 |
      170 | 171 |
      172 | 173 |
      174 |
      175 |

      注意事项:

      176 |
      177 |
      同一个消费者组中, 只能消费 Tipic 下面 一个Tags。
      178 |
      当前消费者组 消费的 Tags 以最新的 创建数据为准。
      179 |
      180 | 181 |
      182 |
      183 | 184 |
      185 | 186 | 187 |
      188 |
      189 |
      190 | 192 |
      193 |
      194 | 195 |
      196 | 197 |
      198 |
      199 | 200 | 201 | 202 |
      203 | 204 |
      206 | 207 |
      208 |
      209 |
      210 |

      211 | Delete  消费端 213 |

      214 |
      215 |
      216 |
      217 |
      218 |
      219 | 220 |
      221 | 222 |
      223 | 224 |
      225 | 227 |
      228 |
      229 | 230 |
      231 | 232 |
      233 |
      234 | 235 |
      236 |
      237 |
      238 | 239 | 240 |
      241 |
      242 |
      243 | 245 |
      246 |
      247 | 248 | 249 | 250 |
      251 | 252 |
      253 |
      254 | 255 | 256 | 257 |
      258 |
      259 | 260 |
      261 |
      262 |
      263 |

      264 | Send  消息 266 |

      267 |
      268 |
      269 |
      270 |
      271 |
      272 | 273 |
      274 | 275 | 276 |
      277 | 278 |
      279 | 281 |
      282 |
      283 | 284 | 285 | 286 |
      287 | 288 |
      289 | 291 |
      292 |
      293 | 294 | 295 | 296 |
      297 | 298 |
      299 | 301 |
      302 |
      303 | 304 | 305 |
      306 | 307 |
      308 |
      309 | 310 | 311 |
      312 |
      313 |
      314 | 315 |
      316 |
      317 |
      318 | 319 | 321 | 322 |
      323 |
      324 | 325 |
      326 | 327 |
      328 |
      329 | 330 | 331 |
      332 |
      333 |
      334 | 335 |
      336 | 337 | 338 | 339 | 340 | 341 |
      342 | 347 |
      348 | 349 | 350 | 351 | 352 | 406 | 407 | -------------------------------------------------------------------------------- /RocketMqCurrencyBoot/src/main/resources/static/css/bootstrap-editable.css: -------------------------------------------------------------------------------- 1 | /*! X-editable - v1.5.1 2 | * In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery 3 | * http://github.com/vitalets/x-editable 4 | * Copyright (c) 2013 Vitaliy Potapov; Licensed MIT */ 5 | .editableform { 6 | margin-bottom: 0; /* overwrites bootstrap margin */ 7 | } 8 | 9 | .editableform .control-group { 10 | margin-bottom: 0; /* overwrites bootstrap margin */ 11 | white-space: nowrap; /* prevent wrapping buttons on new line */ 12 | line-height: 20px; /* overwriting bootstrap line-height. See #133 */ 13 | } 14 | 15 | /* 16 | BS3 width:1005 for inputs breaks editable form in popup 17 | See: https://github.com/vitalets/x-editable/issues/393 18 | */ 19 | .editableform .form-control { 20 | width: auto; 21 | } 22 | 23 | .editable-buttons { 24 | display: inline-block; /* should be inline to take effect of parent's white-space: nowrap */ 25 | vertical-align: top; 26 | margin-left: 7px; 27 | /* inline-block emulation for IE7*/ 28 | zoom: 1; 29 | *display: inline; 30 | } 31 | 32 | .editable-buttons.editable-buttons-bottom { 33 | display: block; 34 | margin-top: 7px; 35 | margin-left: 0; 36 | } 37 | 38 | .editable-input { 39 | vertical-align: top; 40 | display: inline-block; /* should be inline to take effect of parent's white-space: nowrap */ 41 | width: auto; /* bootstrap-responsive has width: 100% that breakes layout */ 42 | white-space: normal; /* reset white-space decalred in parent*/ 43 | /* display-inline emulation for IE7*/ 44 | zoom: 1; 45 | *display: inline; 46 | } 47 | 48 | .editable-buttons .editable-cancel { 49 | margin-left: 7px; 50 | } 51 | 52 | /*for jquery-ui buttons need set height to look more pretty*/ 53 | .editable-buttons button.ui-button-icon-only { 54 | height: 24px; 55 | width: 30px; 56 | } 57 | 58 | .editableform-loading { 59 | background: url('../img/loading.gif') center center no-repeat; 60 | height: 25px; 61 | width: auto; 62 | min-width: 25px; 63 | } 64 | 65 | .editable-inline .editableform-loading { 66 | background-position: left 5px; 67 | } 68 | 69 | .editable-error-block { 70 | max-width: 300px; 71 | margin: 5px 0 0 0; 72 | width: auto; 73 | white-space: normal; 74 | } 75 | 76 | /*add padding for jquery ui*/ 77 | .editable-error-block.ui-state-error { 78 | padding: 3px; 79 | } 80 | 81 | .editable-error { 82 | color: red; 83 | } 84 | 85 | /* ---- For specific types ---- */ 86 | 87 | .editableform .editable-date { 88 | padding: 0; 89 | margin: 0; 90 | float: left; 91 | } 92 | 93 | /* move datepicker icon to center of add-on button. See https://github.com/vitalets/x-editable/issues/183 */ 94 | .editable-inline .add-on .icon-th { 95 | margin-top: 3px; 96 | margin-left: 1px; 97 | } 98 | 99 | 100 | /* checklist vertical alignment */ 101 | .editable-checklist label input[type="checkbox"], 102 | .editable-checklist label span { 103 | vertical-align: middle; 104 | margin: 0; 105 | } 106 | 107 | .editable-checklist label { 108 | white-space: nowrap; 109 | } 110 | 111 | /* set exact width of textarea to fit buttons toolbar */ 112 | .editable-wysihtml5 { 113 | width: 566px; 114 | height: 250px; 115 | } 116 | 117 | /* clear button shown as link in date inputs */ 118 | .editable-clear { 119 | clear: both; 120 | font-size: 0.9em; 121 | text-decoration: none; 122 | text-align: right; 123 | } 124 | 125 | /* IOS-style clear button for text inputs */ 126 | .editable-clear-x { 127 | background: url('../img/clear.png') center center no-repeat; 128 | display: block; 129 | width: 13px; 130 | height: 13px; 131 | position: absolute; 132 | opacity: 0.6; 133 | z-index: 100; 134 | 135 | top: 50%; 136 | right: 6px; 137 | margin-top: -6px; 138 | 139 | } 140 | 141 | .editable-clear-x:hover { 142 | opacity: 1; 143 | } 144 | 145 | .editable-pre-wrapped { 146 | white-space: pre-wrap; 147 | } 148 | .editable-container.editable-popup { 149 | max-width: none !important; /* without this rule poshytip/tooltip does not stretch */ 150 | } 151 | 152 | .editable-container.popover { 153 | width: auto; /* without this rule popover does not stretch */ 154 | } 155 | 156 | .editable-container.editable-inline { 157 | display: inline-block; 158 | vertical-align: middle; 159 | width: auto; 160 | /* inline-block emulation for IE7*/ 161 | zoom: 1; 162 | *display: inline; 163 | } 164 | 165 | .editable-container.ui-widget { 166 | font-size: inherit; /* jqueryui widget font 1.1em too big, overwrite it */ 167 | z-index: 9990; /* should be less than select2 dropdown z-index to close dropdown first when click */ 168 | } 169 | .editable-click, 170 | a.editable-click, 171 | a.editable-click:hover { 172 | text-decoration: none; 173 | border-bottom: dashed 1px #0088cc; 174 | } 175 | 176 | .editable-click.editable-disabled, 177 | a.editable-click.editable-disabled, 178 | a.editable-click.editable-disabled:hover { 179 | color: #585858; 180 | cursor: default; 181 | border-bottom: none; 182 | } 183 | 184 | .editable-empty, .editable-empty:hover, .editable-empty:focus{ 185 | font-style: italic; 186 | color: #DD1144; 187 | /* border-bottom: none; */ 188 | text-decoration: none; 189 | } 190 | 191 | .editable-unsaved { 192 | font-weight: bold; 193 | } 194 | 195 | .editable-unsaved:after { 196 | /* content: '*'*/ 197 | } 198 | 199 | .editable-bg-transition { 200 | -webkit-transition: background-color 1400ms ease-out; 201 | -moz-transition: background-color 1400ms ease-out; 202 | -o-transition: background-color 1400ms ease-out; 203 | -ms-transition: background-color 1400ms ease-out; 204 | transition: background-color 1400ms ease-out; 205 | } 206 | 207 | /*see https://github.com/vitalets/x-editable/issues/139 */ 208 | .form-horizontal .editable 209 | { 210 | padding-top: 5px; 211 | display:inline-block; 212 | } 213 | 214 | 215 | /*! 216 | * Datepicker for Bootstrap 217 | * 218 | * Copyright 2012 Stefan Petre 219 | * Improvements by Andrew Rowls 220 | * Licensed under the Apache License v2.0 221 | * http://www.apache.org/licenses/LICENSE-2.0 222 | * 223 | */ 224 | .datepicker { 225 | padding: 4px; 226 | -webkit-border-radius: 4px; 227 | -moz-border-radius: 4px; 228 | border-radius: 4px; 229 | direction: ltr; 230 | /*.dow { 231 | border-top: 1px solid #ddd !important; 232 | }*/ 233 | 234 | } 235 | .datepicker-inline { 236 | width: 220px; 237 | } 238 | .datepicker.datepicker-rtl { 239 | direction: rtl; 240 | } 241 | .datepicker.datepicker-rtl table tr td span { 242 | float: right; 243 | } 244 | .datepicker-dropdown { 245 | top: 0; 246 | left: 0; 247 | } 248 | .datepicker-dropdown:before { 249 | content: ''; 250 | display: inline-block; 251 | border-left: 7px solid transparent; 252 | border-right: 7px solid transparent; 253 | border-bottom: 7px solid #ccc; 254 | border-bottom-color: rgba(0, 0, 0, 0.2); 255 | position: absolute; 256 | top: -7px; 257 | left: 6px; 258 | } 259 | .datepicker-dropdown:after { 260 | content: ''; 261 | display: inline-block; 262 | border-left: 6px solid transparent; 263 | border-right: 6px solid transparent; 264 | border-bottom: 6px solid #ffffff; 265 | position: absolute; 266 | top: -6px; 267 | left: 7px; 268 | } 269 | .datepicker > div { 270 | display: none; 271 | } 272 | .datepicker.days div.datepicker-days { 273 | display: block; 274 | } 275 | .datepicker.months div.datepicker-months { 276 | display: block; 277 | } 278 | .datepicker.years div.datepicker-years { 279 | display: block; 280 | } 281 | .datepicker table { 282 | margin: 0; 283 | } 284 | .datepicker td, 285 | .datepicker th { 286 | text-align: center; 287 | width: 20px; 288 | height: 20px; 289 | -webkit-border-radius: 4px; 290 | -moz-border-radius: 4px; 291 | border-radius: 4px; 292 | border: none; 293 | } 294 | .table-striped .datepicker table tr td, 295 | .table-striped .datepicker table tr th { 296 | background-color: transparent; 297 | } 298 | .datepicker table tr td.day:hover { 299 | background: #eeeeee; 300 | cursor: pointer; 301 | } 302 | .datepicker table tr td.old, 303 | .datepicker table tr td.new { 304 | color: #999999; 305 | } 306 | .datepicker table tr td.disabled, 307 | .datepicker table tr td.disabled:hover { 308 | background: none; 309 | color: #999999; 310 | cursor: default; 311 | } 312 | .datepicker table tr td.today, 313 | .datepicker table tr td.today:hover, 314 | .datepicker table tr td.today.disabled, 315 | .datepicker table tr td.today.disabled:hover { 316 | background-color: #fde19a; 317 | background-image: -moz-linear-gradient(top, #fdd49a, #fdf59a); 318 | background-image: -ms-linear-gradient(top, #fdd49a, #fdf59a); 319 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fdd49a), to(#fdf59a)); 320 | background-image: -webkit-linear-gradient(top, #fdd49a, #fdf59a); 321 | background-image: -o-linear-gradient(top, #fdd49a, #fdf59a); 322 | background-image: linear-gradient(top, #fdd49a, #fdf59a); 323 | background-repeat: repeat-x; 324 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdd49a', endColorstr='#fdf59a', GradientType=0); 325 | border-color: #fdf59a #fdf59a #fbed50; 326 | border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); 327 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); 328 | color: #000; 329 | } 330 | .datepicker table tr td.today:hover, 331 | .datepicker table tr td.today:hover:hover, 332 | .datepicker table tr td.today.disabled:hover, 333 | .datepicker table tr td.today.disabled:hover:hover, 334 | .datepicker table tr td.today:active, 335 | .datepicker table tr td.today:hover:active, 336 | .datepicker table tr td.today.disabled:active, 337 | .datepicker table tr td.today.disabled:hover:active, 338 | .datepicker table tr td.today.active, 339 | .datepicker table tr td.today:hover.active, 340 | .datepicker table tr td.today.disabled.active, 341 | .datepicker table tr td.today.disabled:hover.active, 342 | .datepicker table tr td.today.disabled, 343 | .datepicker table tr td.today:hover.disabled, 344 | .datepicker table tr td.today.disabled.disabled, 345 | .datepicker table tr td.today.disabled:hover.disabled, 346 | .datepicker table tr td.today[disabled], 347 | .datepicker table tr td.today:hover[disabled], 348 | .datepicker table tr td.today.disabled[disabled], 349 | .datepicker table tr td.today.disabled:hover[disabled] { 350 | background-color: #fdf59a; 351 | } 352 | .datepicker table tr td.today:active, 353 | .datepicker table tr td.today:hover:active, 354 | .datepicker table tr td.today.disabled:active, 355 | .datepicker table tr td.today.disabled:hover:active, 356 | .datepicker table tr td.today.active, 357 | .datepicker table tr td.today:hover.active, 358 | .datepicker table tr td.today.disabled.active, 359 | .datepicker table tr td.today.disabled:hover.active { 360 | background-color: #fbf069 \9; 361 | } 362 | .datepicker table tr td.today:hover:hover { 363 | color: #000; 364 | } 365 | .datepicker table tr td.today.active:hover { 366 | color: #fff; 367 | } 368 | .datepicker table tr td.range, 369 | .datepicker table tr td.range:hover, 370 | .datepicker table tr td.range.disabled, 371 | .datepicker table tr td.range.disabled:hover { 372 | background: #eeeeee; 373 | -webkit-border-radius: 0; 374 | -moz-border-radius: 0; 375 | border-radius: 0; 376 | } 377 | .datepicker table tr td.range.today, 378 | .datepicker table tr td.range.today:hover, 379 | .datepicker table tr td.range.today.disabled, 380 | .datepicker table tr td.range.today.disabled:hover { 381 | background-color: #f3d17a; 382 | background-image: -moz-linear-gradient(top, #f3c17a, #f3e97a); 383 | background-image: -ms-linear-gradient(top, #f3c17a, #f3e97a); 384 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f3c17a), to(#f3e97a)); 385 | background-image: -webkit-linear-gradient(top, #f3c17a, #f3e97a); 386 | background-image: -o-linear-gradient(top, #f3c17a, #f3e97a); 387 | background-image: linear-gradient(top, #f3c17a, #f3e97a); 388 | background-repeat: repeat-x; 389 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f3c17a', endColorstr='#f3e97a', GradientType=0); 390 | border-color: #f3e97a #f3e97a #edde34; 391 | border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); 392 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); 393 | -webkit-border-radius: 0; 394 | -moz-border-radius: 0; 395 | border-radius: 0; 396 | } 397 | .datepicker table tr td.range.today:hover, 398 | .datepicker table tr td.range.today:hover:hover, 399 | .datepicker table tr td.range.today.disabled:hover, 400 | .datepicker table tr td.range.today.disabled:hover:hover, 401 | .datepicker table tr td.range.today:active, 402 | .datepicker table tr td.range.today:hover:active, 403 | .datepicker table tr td.range.today.disabled:active, 404 | .datepicker table tr td.range.today.disabled:hover:active, 405 | .datepicker table tr td.range.today.active, 406 | .datepicker table tr td.range.today:hover.active, 407 | .datepicker table tr td.range.today.disabled.active, 408 | .datepicker table tr td.range.today.disabled:hover.active, 409 | .datepicker table tr td.range.today.disabled, 410 | .datepicker table tr td.range.today:hover.disabled, 411 | .datepicker table tr td.range.today.disabled.disabled, 412 | .datepicker table tr td.range.today.disabled:hover.disabled, 413 | .datepicker table tr td.range.today[disabled], 414 | .datepicker table tr td.range.today:hover[disabled], 415 | .datepicker table tr td.range.today.disabled[disabled], 416 | .datepicker table tr td.range.today.disabled:hover[disabled] { 417 | background-color: #f3e97a; 418 | } 419 | .datepicker table tr td.range.today:active, 420 | .datepicker table tr td.range.today:hover:active, 421 | .datepicker table tr td.range.today.disabled:active, 422 | .datepicker table tr td.range.today.disabled:hover:active, 423 | .datepicker table tr td.range.today.active, 424 | .datepicker table tr td.range.today:hover.active, 425 | .datepicker table tr td.range.today.disabled.active, 426 | .datepicker table tr td.range.today.disabled:hover.active { 427 | background-color: #efe24b \9; 428 | } 429 | .datepicker table tr td.selected, 430 | .datepicker table tr td.selected:hover, 431 | .datepicker table tr td.selected.disabled, 432 | .datepicker table tr td.selected.disabled:hover { 433 | background-color: #9e9e9e; 434 | background-image: -moz-linear-gradient(top, #b3b3b3, #808080); 435 | background-image: -ms-linear-gradient(top, #b3b3b3, #808080); 436 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#b3b3b3), to(#808080)); 437 | background-image: -webkit-linear-gradient(top, #b3b3b3, #808080); 438 | background-image: -o-linear-gradient(top, #b3b3b3, #808080); 439 | background-image: linear-gradient(top, #b3b3b3, #808080); 440 | background-repeat: repeat-x; 441 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#b3b3b3', endColorstr='#808080', GradientType=0); 442 | border-color: #808080 #808080 #595959; 443 | border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); 444 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); 445 | color: #fff; 446 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); 447 | } 448 | .datepicker table tr td.selected:hover, 449 | .datepicker table tr td.selected:hover:hover, 450 | .datepicker table tr td.selected.disabled:hover, 451 | .datepicker table tr td.selected.disabled:hover:hover, 452 | .datepicker table tr td.selected:active, 453 | .datepicker table tr td.selected:hover:active, 454 | .datepicker table tr td.selected.disabled:active, 455 | .datepicker table tr td.selected.disabled:hover:active, 456 | .datepicker table tr td.selected.active, 457 | .datepicker table tr td.selected:hover.active, 458 | .datepicker table tr td.selected.disabled.active, 459 | .datepicker table tr td.selected.disabled:hover.active, 460 | .datepicker table tr td.selected.disabled, 461 | .datepicker table tr td.selected:hover.disabled, 462 | .datepicker table tr td.selected.disabled.disabled, 463 | .datepicker table tr td.selected.disabled:hover.disabled, 464 | .datepicker table tr td.selected[disabled], 465 | .datepicker table tr td.selected:hover[disabled], 466 | .datepicker table tr td.selected.disabled[disabled], 467 | .datepicker table tr td.selected.disabled:hover[disabled] { 468 | background-color: #808080; 469 | } 470 | .datepicker table tr td.selected:active, 471 | .datepicker table tr td.selected:hover:active, 472 | .datepicker table tr td.selected.disabled:active, 473 | .datepicker table tr td.selected.disabled:hover:active, 474 | .datepicker table tr td.selected.active, 475 | .datepicker table tr td.selected:hover.active, 476 | .datepicker table tr td.selected.disabled.active, 477 | .datepicker table tr td.selected.disabled:hover.active { 478 | background-color: #666666 \9; 479 | } 480 | .datepicker table tr td.active, 481 | .datepicker table tr td.active:hover, 482 | .datepicker table tr td.active.disabled, 483 | .datepicker table tr td.active.disabled:hover { 484 | background-color: #006dcc; 485 | background-image: -moz-linear-gradient(top, #0088cc, #0044cc); 486 | background-image: -ms-linear-gradient(top, #0088cc, #0044cc); 487 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); 488 | background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); 489 | background-image: -o-linear-gradient(top, #0088cc, #0044cc); 490 | background-image: linear-gradient(top, #0088cc, #0044cc); 491 | background-repeat: repeat-x; 492 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0); 493 | border-color: #0044cc #0044cc #002a80; 494 | border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); 495 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); 496 | color: #fff; 497 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); 498 | } 499 | .datepicker table tr td.active:hover, 500 | .datepicker table tr td.active:hover:hover, 501 | .datepicker table tr td.active.disabled:hover, 502 | .datepicker table tr td.active.disabled:hover:hover, 503 | .datepicker table tr td.active:active, 504 | .datepicker table tr td.active:hover:active, 505 | .datepicker table tr td.active.disabled:active, 506 | .datepicker table tr td.active.disabled:hover:active, 507 | .datepicker table tr td.active.active, 508 | .datepicker table tr td.active:hover.active, 509 | .datepicker table tr td.active.disabled.active, 510 | .datepicker table tr td.active.disabled:hover.active, 511 | .datepicker table tr td.active.disabled, 512 | .datepicker table tr td.active:hover.disabled, 513 | .datepicker table tr td.active.disabled.disabled, 514 | .datepicker table tr td.active.disabled:hover.disabled, 515 | .datepicker table tr td.active[disabled], 516 | .datepicker table tr td.active:hover[disabled], 517 | .datepicker table tr td.active.disabled[disabled], 518 | .datepicker table tr td.active.disabled:hover[disabled] { 519 | background-color: #0044cc; 520 | } 521 | .datepicker table tr td.active:active, 522 | .datepicker table tr td.active:hover:active, 523 | .datepicker table tr td.active.disabled:active, 524 | .datepicker table tr td.active.disabled:hover:active, 525 | .datepicker table tr td.active.active, 526 | .datepicker table tr td.active:hover.active, 527 | .datepicker table tr td.active.disabled.active, 528 | .datepicker table tr td.active.disabled:hover.active { 529 | background-color: #003399 \9; 530 | } 531 | .datepicker table tr td span { 532 | display: block; 533 | width: 23%; 534 | height: 54px; 535 | line-height: 54px; 536 | float: left; 537 | margin: 1%; 538 | cursor: pointer; 539 | -webkit-border-radius: 4px; 540 | -moz-border-radius: 4px; 541 | border-radius: 4px; 542 | } 543 | .datepicker table tr td span:hover { 544 | background: #eeeeee; 545 | } 546 | .datepicker table tr td span.disabled, 547 | .datepicker table tr td span.disabled:hover { 548 | background: none; 549 | color: #999999; 550 | cursor: default; 551 | } 552 | .datepicker table tr td span.active, 553 | .datepicker table tr td span.active:hover, 554 | .datepicker table tr td span.active.disabled, 555 | .datepicker table tr td span.active.disabled:hover { 556 | background-color: #006dcc; 557 | background-image: -moz-linear-gradient(top, #0088cc, #0044cc); 558 | background-image: -ms-linear-gradient(top, #0088cc, #0044cc); 559 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); 560 | background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); 561 | background-image: -o-linear-gradient(top, #0088cc, #0044cc); 562 | background-image: linear-gradient(top, #0088cc, #0044cc); 563 | background-repeat: repeat-x; 564 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0); 565 | border-color: #0044cc #0044cc #002a80; 566 | border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); 567 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); 568 | color: #fff; 569 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); 570 | } 571 | .datepicker table tr td span.active:hover, 572 | .datepicker table tr td span.active:hover:hover, 573 | .datepicker table tr td span.active.disabled:hover, 574 | .datepicker table tr td span.active.disabled:hover:hover, 575 | .datepicker table tr td span.active:active, 576 | .datepicker table tr td span.active:hover:active, 577 | .datepicker table tr td span.active.disabled:active, 578 | .datepicker table tr td span.active.disabled:hover:active, 579 | .datepicker table tr td span.active.active, 580 | .datepicker table tr td span.active:hover.active, 581 | .datepicker table tr td span.active.disabled.active, 582 | .datepicker table tr td span.active.disabled:hover.active, 583 | .datepicker table tr td span.active.disabled, 584 | .datepicker table tr td span.active:hover.disabled, 585 | .datepicker table tr td span.active.disabled.disabled, 586 | .datepicker table tr td span.active.disabled:hover.disabled, 587 | .datepicker table tr td span.active[disabled], 588 | .datepicker table tr td span.active:hover[disabled], 589 | .datepicker table tr td span.active.disabled[disabled], 590 | .datepicker table tr td span.active.disabled:hover[disabled] { 591 | background-color: #0044cc; 592 | } 593 | .datepicker table tr td span.active:active, 594 | .datepicker table tr td span.active:hover:active, 595 | .datepicker table tr td span.active.disabled:active, 596 | .datepicker table tr td span.active.disabled:hover:active, 597 | .datepicker table tr td span.active.active, 598 | .datepicker table tr td span.active:hover.active, 599 | .datepicker table tr td span.active.disabled.active, 600 | .datepicker table tr td span.active.disabled:hover.active { 601 | background-color: #003399 \9; 602 | } 603 | .datepicker table tr td span.old, 604 | .datepicker table tr td span.new { 605 | color: #999999; 606 | } 607 | .datepicker th.datepicker-switch { 608 | width: 145px; 609 | } 610 | .datepicker thead tr:first-child th, 611 | .datepicker tfoot tr th { 612 | cursor: pointer; 613 | } 614 | .datepicker thead tr:first-child th:hover, 615 | .datepicker tfoot tr th:hover { 616 | background: #eeeeee; 617 | } 618 | .datepicker .cw { 619 | font-size: 10px; 620 | width: 12px; 621 | padding: 0 2px 0 5px; 622 | vertical-align: middle; 623 | } 624 | .datepicker thead tr:first-child th.cw { 625 | cursor: default; 626 | background-color: transparent; 627 | } 628 | .input-append.date .add-on i, 629 | .input-prepend.date .add-on i { 630 | display: block; 631 | cursor: pointer; 632 | width: 16px; 633 | height: 16px; 634 | } 635 | .input-daterange input { 636 | text-align: center; 637 | } 638 | .input-daterange input:first-child { 639 | -webkit-border-radius: 3px 0 0 3px; 640 | -moz-border-radius: 3px 0 0 3px; 641 | border-radius: 3px 0 0 3px; 642 | } 643 | .input-daterange input:last-child { 644 | -webkit-border-radius: 0 3px 3px 0; 645 | -moz-border-radius: 0 3px 3px 0; 646 | border-radius: 0 3px 3px 0; 647 | } 648 | .input-daterange .add-on { 649 | display: inline-block; 650 | width: auto; 651 | min-width: 16px; 652 | height: 18px; 653 | padding: 4px 5px; 654 | font-weight: normal; 655 | line-height: 18px; 656 | text-align: center; 657 | text-shadow: 0 1px 0 #ffffff; 658 | vertical-align: middle; 659 | background-color: #eeeeee; 660 | border: 1px solid #ccc; 661 | margin-left: -5px; 662 | margin-right: -5px; 663 | } 664 | -------------------------------------------------------------------------------- /RocketMqCurrencyBoot/src/main/resources/static/js/bootstrapValidator.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * BootstrapValidator (http://bootstrapvalidator.com) 3 | * 4 | * The best jQuery plugin to validate form fields. Designed to use with Bootstrap 3 5 | * 6 | * @version v0.4.5 7 | * @author https://twitter.com/nghuuphuoc 8 | * @copyright (c) 2013 - 2014 Nguyen Huu Phuoc 9 | * @license MIT 10 | */ 11 | 12 | !function(a){var b=function(c,d){this.$form=a(c),this.options=a.extend({},b.DEFAULT_OPTIONS,d),this.$invalidField=null,this.$submitButton=null,this.STATUS_NOT_VALIDATED="NOT_VALIDATED",this.STATUS_VALIDATING="VALIDATING",this.STATUS_INVALID="INVALID",this.STATUS_VALID="VALID";var e=function(){for(var a=3,b=document.createElement("div"),c=b.all||[];b.innerHTML="",c[0];);return a>4?a:!a}(),f=document.createElement("div");this._changeEvent=9!==e&&"oninput"in f?"input":"keyup",this._submitIfValid=null,this._init()};b.DEFAULT_OPTIONS={elementClass:"bv-form",message:"This value is not valid",threshold:null,excluded:[":disabled",":hidden",":not(:visible)"],feedbackIcons:{valid:null,invalid:null,validating:null},submitButtons:'[type="submit"]',submitHandler:null,live:"enabled",fields:null},b.prototype={constructor:b,_init:function(){var b,c,d,e,f,g,h,i=this,j={excluded:this.$form.attr("data-bv-excluded"),trigger:this.$form.attr("data-bv-trigger"),message:this.$form.attr("data-bv-message"),submitButtons:this.$form.attr("data-bv-submitbuttons"),threshold:this.$form.attr("data-bv-threshold"),live:this.$form.attr("data-bv-live"),fields:{},feedbackIcons:{valid:this.$form.attr("data-bv-feedbackicons-valid"),invalid:this.$form.attr("data-bv-feedbackicons-invalid"),validating:this.$form.attr("data-bv-feedbackicons-validating")}};this.$form.attr("novalidate","novalidate").addClass(this.options.elementClass).on("submit.bv",function(a){a.preventDefault(),i.validate()}).on("click",this.options.submitButtons,function(){i.$submitButton=a(this),i._submitIfValid=!0}).find("[name], [data-bv-field]").each(function(){var k=a(this);if(!i._isExcluded(k)){var l=k.attr("name")||k.attr("data-bv-field"),m={};for(c in a.fn.bootstrapValidator.validators)if(b=a.fn.bootstrapValidator.validators[c],d=k.attr("data-bv-"+c.toLowerCase())+"",h="function"==typeof b.enableByHtml5?b.enableByHtml5(a(this)):null,h&&"false"!=d||h!==!0&&(""==d||"true"==d)){b.html5Attributes=b.html5Attributes||{message:"message"},m[c]=a.extend({},1==h?{}:h,m[c]);for(g in b.html5Attributes)e=b.html5Attributes[g],f=k.attr("data-bv-"+c.toLowerCase()+"-"+g),f&&("true"==f?f=!0:"false"==f&&(f=!1),m[c][e]=f)}var n={trigger:k.attr("data-bv-trigger"),message:k.attr("data-bv-message"),container:k.attr("data-bv-container"),selector:k.attr("data-bv-selector"),threshold:k.attr("data-bv-threshold"),validators:m};a.isEmptyObject(n.validators)||a.isEmptyObject(n)||(k.attr("data-bv-field",l),j.fields[l]=a.extend({},n,j.fields[l]))}}).end().find(this.options.submitButtons).each(function(){a("").attr("type","hidden").attr("name",a(this).attr("name")).val(a(this).val()).appendTo(i.$form)}),this.options=a.extend(!0,this.options,j);for(var k in this.options.fields)this._initField(k);this.setLiveMode(this.options.live)},_initField:function(b){if(null!=this.options.fields[b]&&null!=this.options.fields[b].validators){var c=this.getFieldElements(b);if(null==c)return void delete this.options.fields[b];for(var d in this.options.fields[b].validators)a.fn.bootstrapValidator.validators[d]||delete this.options.fields[b].validators[d];for(var e=this,f=c.attr("type"),g="radio"==f||"checkbox"==f||"file"==f||"SELECT"==c[0].tagName?"change":e._changeEvent,h=c.length,i=1==h||"radio"==f||"checkbox"==f,j=0;h>j;j++){var k=a(c[j]),l=k.parents(".form-group"),m=this.options.fields[b].container?l.find(this.options.fields[b].container):this._getMessageContainer(k);k.attr("data-bv-field")||k.attr("data-bv-field",b),k.on(g+".update.bv",function(){e._submitIfValid=!1,i?e.updateStatus(b,e.STATUS_NOT_VALIDATED,null):e.updateElementStatus(a(this),e.STATUS_NOT_VALIDATED,null)}),k.data("bv.messages",m);for(d in this.options.fields[b].validators)k.data("bv.result."+d,this.STATUS_NOT_VALIDATED),i&&j!=h-1||a("").css("display","none").attr("data-bv-validator",d).attr("data-bv-validator-for",b).html(this.options.fields[b].validators[d].message||this.options.fields[b].message||this.options.message).addClass("help-block").appendTo(m);if(this.options.feedbackIcons&&this.options.feedbackIcons.validating&&this.options.feedbackIcons.invalid&&this.options.feedbackIcons.valid&&(!i||j==h-1)){l.addClass("has-feedback");var n=a("").css("display","none").addClass("form-control-feedback").attr("data-bv-icon-for",b).insertAfter(k);0==l.find("label").length&&n.css("top",0)}}null==this.options.fields[b].enabled&&(this.options.fields[b].enabled=!0)}},_getMessageContainer:function(a){var b=a.parent();if(b.hasClass("form-group"))return b;var c=b.attr("class");if(!c)return this._getMessageContainer(b);c=c.split(" ");for(var d=c.length,e=0;d>e;e++)if(/^col-(xs|sm|md|lg)-\d+$/.test(c[e])||/^col-(xs|sm|md|lg)-offset-\d+$/.test(c[e]))return b;return this._getMessageContainer(b)},_submit:function(){if(this.isValid())this.options.submitHandler&&"function"==typeof this.options.submitHandler?this.options.submitHandler.call(this,this,this.$form,this.$submitButton):this.disableSubmitButtons(!0).defaultSubmit();else if("submitted"==this.options.live&&this.setLiveMode("enabled"),this.$invalidField){var b,c=this.$invalidField.parents(".tab-pane");c&&(b=c.attr("id"))&&a('a[href="#'+b+'"][data-toggle="tab"]').trigger("click.bs.tab.data-api"),this.$invalidField.focus()}},_isExcluded:function(b){if(this.options.excluded){"string"==typeof this.options.excluded&&(this.options.excluded=a.map(this.options.excluded.split(","),function(b){return a.trim(b)}));for(var c=this.options.excluded.length,d=0;c>d;d++)if("string"==typeof this.options.excluded[d]&&b.is(this.options.excluded[d])||"function"==typeof this.options.excluded[d]&&1==this.options.excluded[d].call(this,b,this))return!0}return!1},_exceedThreshold:function(a){var b=a.attr("data-bv-field"),c=this.options.fields[b].threshold||this.options.threshold;if(!c)return!0;var d=a.attr("type"),e=-1!=["button","checkbox","file","hidden","image","radio","reset","submit"].indexOf(d);return e||a.val().length>=c},getFieldElements:function(b){var c=this.options.fields[b].selector?a(this.options.fields[b].selector):this.$form.find('[name="'+b+'"]');return 0==c.length?null:c},setLiveMode:function(b){if(this.options.live=b,"submitted"==b)return this;var c=this;for(var d in this.options.fields)!function(e){var f=c.getFieldElements(e);if(f)for(var g=f.attr("type"),h=f.length,i=1==h||"radio"==g||"checkbox"==g,j=c.options.fields[d].trigger||c.options.trigger||("radio"==g||"checkbox"==g||"file"==g||"SELECT"==f[0].tagName?"change":c._changeEvent),k=a.map(j.split(" "),function(a){return a+".live.bv"}).join(" "),l=0;h>l;l++)"enabled"==b?a(f[l]).on(k,function(){c._exceedThreshold(a(this))&&(i?c.validateField(e):c.validateFieldElement(a(this),!1))}):a(f[l]).off(k)}(d);return this},disableSubmitButtons:function(a){return a?"disabled"!=this.options.live&&this.$form.find(this.options.submitButtons).attr("disabled","disabled"):this.$form.find(this.options.submitButtons).removeAttr("disabled"),this},validate:function(){if(!this.options.fields)return this;this.disableSubmitButtons(!0);for(var a in this.options.fields)this.validateField(a);return this.$submitButton&&this._submit(),this},validateField:function(b){for(var c=this.getFieldElements(b),d=c.attr("type"),e="radio"==d||"checkbox"==d?1:c.length,f=0;e>f;f++)this.validateFieldElement(a(c[f]),1==e);return this},validateFieldElement:function(b,c){var d,e,f=this,g=b.attr("data-bv-field"),h=this.options.fields[g].validators;if(!this.options.fields[g].enabled||this._isExcluded(b))return this;for(d in h){b.data("bv.dfs."+d)&&b.data("bv.dfs."+d).reject();var i=b.data("bv.result."+d);i!=this.STATUS_VALID&&i!=this.STATUS_INVALID&&(b.data("bv.result."+d,this.STATUS_VALIDATING),e=a.fn.bootstrapValidator.validators[d].validate(this,b,h[d]),"object"==typeof e?(c?this.updateStatus(g,this.STATUS_VALIDATING,d):this.updateElementStatus(b,this.STATUS_VALIDATING,d),b.data("bv.dfs."+d,e),e.done(function(a,b,d){a.removeData("bv.dfs."+b),c?f.updateStatus(a.attr("data-bv-field"),d?f.STATUS_VALID:f.STATUS_INVALID,b):f.updateElementStatus(a,d?f.STATUS_VALID:f.STATUS_INVALID,b),d&&1==f._submitIfValid&&f._submit()})):"boolean"==typeof e&&(c?this.updateStatus(g,e?this.STATUS_VALID:this.STATUS_INVALID,d):this.updateElementStatus(b,e?this.STATUS_VALID:this.STATUS_INVALID,d)))}return this},updateStatus:function(b,c,d){for(var e=this.getFieldElements(b),f=e.attr("type"),g="radio"==f||"checkbox"==f?1:e.length,h=0;g>h;h++)this.updateElementStatus(a(e[h]),c,d);return this},updateElementStatus:function(b,c,d){var e=this,f=b.attr("data-bv-field"),g=b.parents(".form-group"),h=b.data("bv.messages"),i=h.find(".help-block[data-bv-validator]"),j=g.find('.form-control-feedback[data-bv-icon-for="'+f+'"]');if(d)b.data("bv.result."+d,c);else for(var k in this.options.fields[f].validators)b.data("bv.result."+k,c);var l,m,n=b.parents(".tab-pane");switch(n&&(l=n.attr("id"))&&(m=a('a[href="#'+l+'"][data-toggle="tab"]').parent()),c){case this.STATUS_VALIDATING:this.disableSubmitButtons(!0),g.removeClass("has-success").removeClass("has-error"),d?i.filter('.help-block[data-bv-validator="'+d+'"]').hide():i.hide(),j&&j.removeClass(this.options.feedbackIcons.valid).removeClass(this.options.feedbackIcons.invalid).addClass(this.options.feedbackIcons.validating).show(),m&&m.removeClass("bv-tab-success").removeClass("bv-tab-error");break;case this.STATUS_INVALID:this.disableSubmitButtons(!0),g.removeClass("has-success").addClass("has-error"),d?i.filter('[data-bv-validator="'+d+'"]').show():i.show(),j&&j.removeClass(this.options.feedbackIcons.valid).removeClass(this.options.feedbackIcons.validating).addClass(this.options.feedbackIcons.invalid).show(),m&&m.removeClass("bv-tab-success").addClass("bv-tab-error");break;case this.STATUS_VALID:d?i.filter('[data-bv-validator="'+d+'"]').hide():i.hide();var o=0==i.filter(function(){var c=a(this).css("display"),d=a(this).attr("data-bv-validator");return"block"==c||b.data("bv.result."+d)!=e.STATUS_VALID}).length;this.disableSubmitButtons(!o),j&&j.removeClass(this.options.feedbackIcons.invalid).removeClass(this.options.feedbackIcons.validating).removeClass(this.options.feedbackIcons.valid).addClass(o?this.options.feedbackIcons.valid:this.options.feedbackIcons.invalid).show();var p=function(c){return 0==c.find(".help-block[data-bv-validator]").filter(function(){var c=a(this).css("display"),d=a(this).attr("data-bv-validator");return"block"==c||b.data("bv.result."+d)&&b.data("bv.result."+d)!=e.STATUS_VALID}).length};g.removeClass("has-error has-success").addClass(p(g)?"has-success":"has-error"),m&&m.removeClass("bv-tab-success").removeClass("bv-tab-error").addClass(p(n)?"bv-tab-success":"bv-tab-error");break;case this.STATUS_NOT_VALIDATED:default:this.disableSubmitButtons(!1),g.removeClass("has-success").removeClass("has-error"),d?i.filter('.help-block[data-bv-validator="'+d+'"]').hide():i.hide(),j&&j.removeClass(this.options.feedbackIcons.valid).removeClass(this.options.feedbackIcons.invalid).removeClass(this.options.feedbackIcons.validating).hide(),m&&m.removeClass("bv-tab-success").removeClass("bv-tab-error")}return this},isValid:function(){var b,c,d,e,f,g,h,i;for(c in this.options.fields)if(null!=this.options.fields[c]&&this.options.fields[c].enabled)for(b=this.getFieldElements(c),e=b.attr("type"),h="radio"==e||"checkbox"==e?1:b.length,i=0;h>i;i++)if(d=a(b[i]),!this._isExcluded(d))for(g in this.options.fields[c].validators){if(f=d.data("bv.result."+g),f==this.STATUS_NOT_VALIDATED||f==this.STATUS_VALIDATING)return!1;if(f==this.STATUS_INVALID)return this.$invalidField=d,!1}return!0},defaultSubmit:function(){this.$form.off("submit.bv").submit()},resetForm:function(b){var c,d,e,f,g;for(c in this.options.fields){d=this.getFieldElements(c),e=d.length;for(var h=0;e>h;h++)for(g in this.options.fields[c].validators)a(d[h]).removeData("bv.dfs."+g);this.updateStatus(c,this.STATUS_NOT_VALIDATED,null),b&&(f=d.attr("type"),"radio"==f||"checkbox"==f?d.removeAttr("checked").removeAttr("selected"):d.val(""))}return this.$invalidField=null,this.$submitButton=null,this.disableSubmitButtons(!1),this},enableFieldValidators:function(a,b){return this.options.fields[a].enabled=b,this.updateStatus(a,this.STATUS_NOT_VALIDATED,null),this}},a.fn.bootstrapValidator=function(c){var d=arguments;return this.each(function(){var e=a(this),f=e.data("bootstrapValidator"),g="object"==typeof c&&c;f||(f=new b(this,g),e.data("bootstrapValidator",f)),"string"==typeof c&&f[c].apply(f,Array.prototype.slice.call(d,1))})},a.fn.bootstrapValidator.validators={},a.fn.bootstrapValidator.Constructor=b,a.fn.bootstrapValidator.helpers={date:function(a,b,c,d){if(1e3>a||a>9999||0==b||b>12)return!1;var e=[31,28,31,30,31,30,31,31,30,31,30,31];if((a%400==0||a%100!=0&&a%4==0)&&(e[1]=29),0>c||c>e[b-1])return!1;if(d===!0){var f=new Date,g=f.getFullYear(),h=f.getMonth(),i=f.getDate();return g>a||a==g&&h>b-1||a==g&&b-1==h&&i>c}return!0},luhn:function(a){for(var b=a.length,c=0,d=[[0,1,2,3,4,5,6,7,8,9],[0,2,4,6,8,1,3,5,7,9]],e=0;b--;)e+=d[c][parseInt(a.charAt(b),10)],c^=1;return e%10===0&&e>0},mod_11_10:function(a){for(var b=5,c=a.length,d=0;c>d;d++)b=(2*(b||10)%11+parseInt(a.charAt(d),10))%10;return 1==b},mod_37_36:function(a,b){b=b||"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";for(var c=b.length,d=a.length,e=Math.floor(c/2),f=0;d>f;f++)e=(2*(e||c)%(c+1)+b.indexOf(a.charAt(f)))%c;return 1==e}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.base64={validate:function(a,b){var c=b.val();return""==c?!0:/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$/.test(c)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.between={html5Attributes:{message:"message",min:"min",max:"max",inclusive:"inclusive"},enableByHtml5:function(a){return"range"==a.attr("type")?{min:a.attr("min"),max:a.attr("max")}:!1},validate:function(a,b,c){var d=b.val();return""==d?!0:(d=parseFloat(d),c.inclusive===!0?d>c.min&&d=c.min&&d<=c.max)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.callback={validate:function(b,c,d){var e=c.val();if(d.callback&&"function"==typeof d.callback){var f=new a.Deferred;return f.resolve(c,"callback",d.callback.call(this,e,b)),f}return!0}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.choice={html5Attributes:{message:"message",min:"min",max:"max"},validate:function(a,b,c){var d=b.is("select")?a.getFieldElements(b.attr("data-bv-field")).find("option").filter(":selected").length:a.getFieldElements(b.attr("data-bv-field")).filter(":checked").length;return c.min&&dc.max?!1:!0}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.creditCard={validate:function(b,c){var d=c.val();if(""==d)return!0;if(/[^0-9-\s]+/.test(d))return!1;if(d=d.replace(/\D/g,""),!a.fn.bootstrapValidator.helpers.luhn(d))return!1;var e,f,g={AMERICAN_EXPRESS:{length:[15],prefix:["34","37"]},DINERS_CLUB:{length:[14],prefix:["300","301","302","303","304","305","36"]},DINERS_CLUB_US:{length:[16],prefix:["54","55"]},DISCOVER:{length:[16],prefix:["6011","622126","622127","622128","622129","62213","62214","62215","62216","62217","62218","62219","6222","6223","6224","6225","6226","6227","6228","62290","62291","622920","622921","622922","622923","622924","622925","644","645","646","647","648","649","65"]},JCB:{length:[16],prefix:["3528","3529","353","354","355","356","357","358"]},LASER:{length:[16,17,18,19],prefix:["6304","6706","6771","6709"]},MAESTRO:{length:[12,13,14,15,16,17,18,19],prefix:["5018","5020","5038","6304","6759","6761","6762","6763","6764","6765","6766"]},MASTERCARD:{length:[16],prefix:["51","52","53","54","55"]},SOLO:{length:[16,18,19],prefix:["6334","6767"]},UNIONPAY:{length:[16,17,18,19],prefix:["622126","622127","622128","622129","62213","62214","62215","62216","62217","62218","62219","6222","6223","6224","6225","6226","6227","6228","62290","62291","622920","622921","622922","622923","622924","622925"]},VISA:{length:[16],prefix:["4"]}};for(e in g)for(f in g[e].prefix)if(d.substr(0,g[e].prefix[f].length)==g[e].prefix[f]&&-1!=g[e].length.indexOf(d.length))return!0;return!1}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.cusip={validate:function(b,c){var d=c.val();if(""==d)return!0;if(d=d.toUpperCase(),!/^[0-9A-Z]{9}$/.test(d))return!1;for(var e=a.map(d.split(""),function(a){var b=a.charCodeAt(0);return b>="A".charCodeAt(0)&&b<="Z".charCodeAt(0)?b-"A".charCodeAt(0)+10:a}),f=e.length,g=0,h=0;f-1>h;h++){var i=parseInt(e[h]);h%2!=0&&(i*=2),i>9&&(i-=9),g+=i}return g=(10-g%10)%10,g==e[f-1]}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.cvv={html5Attributes:{message:"message",ccfield:"creditCardField"},validate:function(a,b,c){var d=b.val();if(""==d)return!0;if(!/^[0-9]{3,4}$/.test(d))return!1;if(!c.creditCardField)return!0;var e=a.getFieldElements(c.creditCardField).val();if(""==e)return!0;e=e.replace(/\D/g,"");var f,g,h={AMERICAN_EXPRESS:{length:[15],prefix:["34","37"]},DINERS_CLUB:{length:[14],prefix:["300","301","302","303","304","305","36"]},DINERS_CLUB_US:{length:[16],prefix:["54","55"]},DISCOVER:{length:[16],prefix:["6011","622126","622127","622128","622129","62213","62214","62215","62216","62217","62218","62219","6222","6223","6224","6225","6226","6227","6228","62290","62291","622920","622921","622922","622923","622924","622925","644","645","646","647","648","649","65"]},JCB:{length:[16],prefix:["3528","3529","353","354","355","356","357","358"]},LASER:{length:[16,17,18,19],prefix:["6304","6706","6771","6709"]},MAESTRO:{length:[12,13,14,15,16,17,18,19],prefix:["5018","5020","5038","6304","6759","6761","6762","6763","6764","6765","6766"]},MASTERCARD:{length:[16],prefix:["51","52","53","54","55"]},SOLO:{length:[16,18,19],prefix:["6334","6767"]},UNIONPAY:{length:[16,17,18,19],prefix:["622126","622127","622128","622129","62213","62214","62215","62216","62217","62218","62219","6222","6223","6224","6225","6226","6227","6228","62290","62291","622920","622921","622922","622923","622924","622925"]},VISA:{length:[16],prefix:["4"]}},i=null;for(f in h)for(g in h[f].prefix)if(e.substr(0,h[f].prefix[g].length)==h[f].prefix[g]&&-1!=h[f].length.indexOf(e.length)){i=f;break}return null==i?!1:"AMERICAN_EXPRESS"==i?4==d.length:3==d.length}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.date={html5Attributes:{message:"message",format:"format"},validate:function(b,c,d){var e=c.val();if(""==e)return!0;d.format=d.format||"MM/DD/YYYY";var f=d.format.split(" "),g=f[0],h=f.length>1?f[1]:null,i=f.length>2?f[2]:null,j=e.split(" "),k=j[0],l=j.length>1?j[1]:null;if(f.length!=j.length)return!1;var m=-1!=k.indexOf("/")?"/":-1!=k.indexOf("-")?"-":null;if(null==m)return!1;k=k.split(m),g=g.split(m);var n=k[g.indexOf("YYYY")],o=k[g.indexOf("MM")],p=k[g.indexOf("DD")],q=null,r=null,s=null;if(h){if(h=h.split(":"),l=l.split(":"),h.length!=l.length)return!1;if(r=l.length>0?l[0]:null,q=l.length>1?l[1]:null,s=l.length>2?l[2]:null,s&&(s=parseInt(s,10),0>s||s>60))return!1;if(r&&(r=parseInt(r,10),0>r||r>=24||i&&r>12))return!1;if(q&&(q=parseInt(q,10),0>q||q>59))return!1}return p=parseInt(p,10),o=parseInt(o,10),n=parseInt(n,10),a.fn.bootstrapValidator.helpers.date(n,o,p)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.different={html5Attributes:{message:"message",field:"field"},validate:function(a,b,c){var d=b.val();if(""==d)return!0;var e=a.getFieldElements(c.field);return null==e?!0:d!=e.val()?(a.updateStatus(c.field,a.STATUS_VALID,"different"),!0):!1}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.digits={validate:function(a,b){var c=b.val();return""==c?!0:/^\d+$/.test(c)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.ean={validate:function(a,b){var c=b.val();if(""==c)return!0;if(!/^(\d{8}|\d{12}|\d{13})$/.test(c))return!1;for(var d=c.length,e=0,f=8==d?[3,1]:[1,3],g=0;d-1>g;g++)e+=parseInt(c.charAt(g))*f[g%2];return e=10-e%10,e==c.charAt(d-1)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.emailAddress={enableByHtml5:function(a){return"email"==a.attr("type")},validate:function(a,b){var c=b.val();if(""==c)return!0;var d=/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;return d.test(c)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.file={html5Attributes:{extension:"extension",maxsize:"maxSize",message:"message",type:"type"},validate:function(a,b,c){var d=b.val();if(""==d)return!0;var e,f=c.extension?c.extension.split(","):null,g=c.type?c.type.split(","):null,h=window.File&&window.FileList&&window.FileReader;if(h)for(var i=b.get(0).files,j=i.length,k=0;j>k;k++){if(c.maxSize&&i[k].size>parseInt(c.maxSize))return!1;if(e=i[k].name.substr(i[k].name.lastIndexOf(".")+1),f&&-1==f.indexOf(e))return!1;if(g&&-1==g.indexOf(i[k].type))return!1}else if(e=d.substr(d.lastIndexOf(".")+1),f&&-1==f.indexOf(e))return!1;return!0}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.greaterThan={html5Attributes:{message:"message",value:"value",inclusive:"inclusive"},enableByHtml5:function(a){var b=a.attr("min");return b?{value:b}:!1},validate:function(a,b,c){var d=b.val();return""==d?!0:(d=parseFloat(d),c.inclusive===!0?d>c.value:d>=c.value)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.grid={validate:function(b,c){var d=c.val();return""==d?!0:(d=d.toUpperCase(),/^[GRID:]*([0-9A-Z]{2})[-\s]*([0-9A-Z]{5})[-\s]*([0-9A-Z]{10})[-\s]*([0-9A-Z]{1})$/g.test(d)?(d=d.replace(/\s/g,"").replace(/-/g,""),"GRID:"==d.substr(0,5)&&(d=d.substr(5)),a.fn.bootstrapValidator.helpers.mod_37_36(d)):!1)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.hex={validate:function(a,b){var c=b.val();return""==c?!0:/^[0-9a-fA-F]+$/.test(c)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.hexColor={enableByHtml5:function(a){return"color"==a.attr("type")},validate:function(a,b){var c=b.val();return""==c?!0:/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(c)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.iban={html5Attributes:{message:"message",country:"country"},validate:function(b,c,d){var e=c.val();if(""==e)return!0;var f={AD:"AD[0-9]{2}[0-9]{4}[0-9]{4}[A-Z0-9]{12}",AE:"AE[0-9]{2}[0-9]{3}[0-9]{16}",AL:"AL[0-9]{2}[0-9]{8}[A-Z0-9]{16}",AO:"AO[0-9]{2}[0-9]{21}",AT:"AT[0-9]{2}[0-9]{5}[0-9]{11}",AZ:"AZ[0-9]{2}[A-Z]{4}[A-Z0-9]{20}",BA:"BA[0-9]{2}[0-9]{3}[0-9]{3}[0-9]{8}[0-9]{2}",BE:"BE[0-9]{2}[0-9]{3}[0-9]{7}[0-9]{2}",BF:"BF[0-9]{2}[0-9]{23}",BG:"BG[0-9]{2}[A-Z]{4}[0-9]{4}[0-9]{2}[A-Z0-9]{8}",BH:"BH[0-9]{2}[A-Z]{4}[A-Z0-9]{14}",BI:"BI[0-9]{2}[0-9]{12}",BJ:"BJ[0-9]{2}[A-Z]{1}[0-9]{23}",BR:"BR[0-9]{2}[0-9]{8}[0-9]{5}[0-9]{10}[A-Z][A-Z0-9]",CH:"CH[0-9]{2}[0-9]{5}[A-Z0-9]{12}",CI:"CI[0-9]{2}[A-Z]{1}[0-9]{23}",CM:"CM[0-9]{2}[0-9]{23}",CR:"CR[0-9]{2}[0-9]{3}[0-9]{14}",CV:"CV[0-9]{2}[0-9]{21}",CY:"CY[0-9]{2}[0-9]{3}[0-9]{5}[A-Z0-9]{16}",CZ:"CZ[0-9]{2}[0-9]{20}",DE:"DE[0-9]{2}[0-9]{8}[0-9]{10}",DK:"DK[0-9]{2}[0-9]{14}",DO:"DO[0-9]{2}[A-Z0-9]{4}[0-9]{20}",DZ:"DZ[0-9]{2}[0-9]{20}",EE:"EE[0-9]{2}[0-9]{2}[0-9]{2}[0-9]{11}[0-9]{1}",ES:"ES[0-9]{2}[0-9]{4}[0-9]{4}[0-9]{1}[0-9]{1}[0-9]{10}",FI:"FI[0-9]{2}[0-9]{6}[0-9]{7}[0-9]{1}",FO:"FO[0-9]{2}[0-9]{4}[0-9]{9}[0-9]{1}",FR:"FR[0-9]{2}[0-9]{5}[0-9]{5}[A-Z0-9]{11}[0-9]{2}",GB:"GB[0-9]{2}[A-Z]{4}[0-9]{6}[0-9]{8}",GE:"GE[0-9]{2}[A-Z]{2}[0-9]{16}",GI:"GI[0-9]{2}[A-Z]{4}[A-Z0-9]{15}",GL:"GL[0-9]{2}[0-9]{4}[0-9]{9}[0-9]{1}",GR:"GR[0-9]{2}[0-9]{3}[0-9]{4}[A-Z0-9]{16}",GT:"GT[0-9]{2}[A-Z0-9]{4}[A-Z0-9]{20}",HR:"HR[0-9]{2}[0-9]{7}[0-9]{10}",HU:"HU[0-9]{2}[0-9]{3}[0-9]{4}[0-9]{1}[0-9]{15}[0-9]{1}",IE:"IE[0-9]{2}[A-Z]{4}[0-9]{6}[0-9]{8}",IL:"IL[0-9]{2}[0-9]{3}[0-9]{3}[0-9]{13}",IR:"IR[0-9]{2}[0-9]{22}",IS:"IS[0-9]{2}[0-9]{4}[0-9]{2}[0-9]{6}[0-9]{10}",IT:"IT[0-9]{2}[A-Z]{1}[0-9]{5}[0-9]{5}[A-Z0-9]{12}",JO:"JO[0-9]{2}[A-Z]{4}[0-9]{4}[0]{8}[A-Z0-9]{10}",KW:"KW[0-9]{2}[A-Z]{4}[0-9]{22}",KZ:"KZ[0-9]{2}[0-9]{3}[A-Z0-9]{13}",LB:"LB[0-9]{2}[0-9]{4}[A-Z0-9]{20}",LI:"LI[0-9]{2}[0-9]{5}[A-Z0-9]{12}",LT:"LT[0-9]{2}[0-9]{5}[0-9]{11}",LU:"LU[0-9]{2}[0-9]{3}[A-Z0-9]{13}",LV:"LV[0-9]{2}[A-Z]{4}[A-Z0-9]{13}",MC:"MC[0-9]{2}[0-9]{5}[0-9]{5}[A-Z0-9]{11}[0-9]{2}",MD:"MD[0-9]{2}[A-Z0-9]{20}",ME:"ME[0-9]{2}[0-9]{3}[0-9]{13}[0-9]{2}",MG:"MG[0-9]{2}[0-9]{23}",MK:"MK[0-9]{2}[0-9]{3}[A-Z0-9]{10}[0-9]{2}",ML:"ML[0-9]{2}[A-Z]{1}[0-9]{23}",MR:"MR13[0-9]{5}[0-9]{5}[0-9]{11}[0-9]{2}",MT:"MT[0-9]{2}[A-Z]{4}[0-9]{5}[A-Z0-9]{18}",MU:"MU[0-9]{2}[A-Z]{4}[0-9]{2}[0-9]{2}[0-9]{12}[0-9]{3}[A-Z]{3}",MZ:"MZ[0-9]{2}[0-9]{21}",NL:"NL[0-9]{2}[A-Z]{4}[0-9]{10}",NO:"NO[0-9]{2}[0-9]{4}[0-9]{6}[0-9]{1}",PK:"PK[0-9]{2}[A-Z]{4}[A-Z0-9]{16}",PL:"PL[0-9]{2}[0-9]{8}[0-9]{16}",PS:"PS[0-9]{2}[A-Z]{4}[A-Z0-9]{21}",PT:"PT[0-9]{2}[0-9]{4}[0-9]{4}[0-9]{11}[0-9]{2}",QA:"QA[0-9]{2}[A-Z]{4}[A-Z0-9]{21}",RO:"RO[0-9]{2}[A-Z]{4}[A-Z0-9]{16}",RS:"RS[0-9]{2}[0-9]{3}[0-9]{13}[0-9]{2}",SA:"SA[0-9]{2}[0-9]{2}[A-Z0-9]{18}",SE:"SE[0-9]{2}[0-9]{3}[0-9]{16}[0-9]{1}",SI:"SI[0-9]{2}[0-9]{5}[0-9]{8}[0-9]{2}",SK:"SK[0-9]{2}[0-9]{4}[0-9]{6}[0-9]{10}",SM:"SM[0-9]{2}[A-Z]{1}[0-9]{5}[0-9]{5}[A-Z0-9]{12}",SN:"SN[0-9]{2}[A-Z]{1}[0-9]{23}",TN:"TN59[0-9]{2}[0-9]{3}[0-9]{13}[0-9]{2}",TR:"TR[0-9]{2}[0-9]{5}[A-Z0-9]{1}[A-Z0-9]{16}",VG:"VG[0-9]{2}[A-Z]{4}[0-9]{16}"};e=e.replace(/[^a-zA-Z0-9]/g,"").toUpperCase();var g=d.country||e.substr(0,2);if(!f[g])return!1;if(!new RegExp("^"+f[g]+"$").test(e))return!1;e=e.substr(4)+e.substr(0,4),e=a.map(e.split(""),function(a){var b=a.charCodeAt(0);return b>="A".charCodeAt(0)&&b<="Z".charCodeAt(0)?b-"A".charCodeAt(0)+10:a}),e=e.join("");for(var h=parseInt(e.substr(0,1),10),i=e.length,j=1;i>j;++j)h=(10*h+parseInt(e.substr(j,1),10))%97;return 1==h}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.id={html5Attributes:{message:"message",country:"country"},validate:function(a,b,c){var d=b.val();if(""==d)return!0;var e=c.country||d.substr(0,2),f=["_",e.toLowerCase()].join("");return this[f]&&"function"==typeof this[f]?this[f](d):!0},_validateJMBG:function(a,b){if(!/^\d{13}$/.test(a))return!1;var c=parseInt(a.substr(0,2),10),d=parseInt(a.substr(2,2),10),e=(parseInt(a.substr(4,3),10),parseInt(a.substr(7,2),10)),f=parseInt(a.substr(12,1),10);if(c>31||d>12)return!1;for(var g=0,h=0;6>h;h++)g+=(7-h)*(parseInt(a.charAt(h))+parseInt(a.charAt(h+6)));if(g=11-g%11,(10==g||11==g)&&(g=0),g!=f)return!1;switch(b.toUpperCase()){case"BA":return e>=10&&19>=e;case"MK":return e>=41&&49>=e;case"ME":return e>=20&&29>=e;case"RS":return e>=70&&99>=e;case"SI":return e>=50&&59>=e;default:return!0}},_ba:function(a){return this._validateJMBG(a,"BA")},_mk:function(a){return this._validateJMBG(a,"MK")},_me:function(a){return this._validateJMBG(a,"ME")},_rs:function(a){return this._validateJMBG(a,"RS")},_si:function(a){return this._validateJMBG(a,"SI")},_bg:function(b){if(!/^\d{10}$/.test(b)&&!/^\d{6}\s\d{3}\s\d{1}$/.test(b))return!1;b=b.replace(/\s/g,"");var c=parseInt(b.substr(0,2),10)+1900,d=parseInt(b.substr(2,2),10),e=parseInt(b.substr(4,2),10);if(d>40?(c+=100,d-=40):d>20&&(c-=100,d-=20),!a.fn.bootstrapValidator.helpers.date(c,d,e))return!1;for(var f=0,g=[2,4,8,5,10,9,7,3,6],h=0;9>h;h++)f+=parseInt(b.charAt(h))*g[h];return f=f%11%10,f==b.substr(9,1)},_br:function(a){if(/^1{11}|2{11}|3{11}|4{11}|5{11}|6{11}|7{11}|8{11}|9{11}|0{11}$/.test(a))return!1;if(!/^\d{11}$/.test(a)&&!/^\d{3}\.\d{3}\.\d{3}-\d{2}$/.test(a))return!1;a=a.replace(/\./g,"").replace(/-/g,"");for(var b=0,c=0;9>c;c++)b+=(10-c)*parseInt(a.charAt(c));if(b=11-b%11,(10==b||11==b)&&(b=0),b!=a.charAt(9))return!1;var d=0;for(c=0;10>c;c++)d+=(11-c)*parseInt(a.charAt(c));return d=11-d%11,(10==d||11==d)&&(d=0),d==a.charAt(10)},_ch:function(a){if(!/^756[\.]{0,1}[0-9]{4}[\.]{0,1}[0-9]{4}[\.]{0,1}[0-9]{2}$/.test(a))return!1;a=a.replace(/\D/g,"").substr(3);for(var b=a.length,c=0,d=8==b?[3,1]:[1,3],e=0;b-1>e;e++)c+=parseInt(a.charAt(e))*d[e%2];return c=10-c%10,c==a.charAt(b-1)},_cl:function(a){if(!/^\d{7,8}[-]{0,1}[0-9K]$/.test(a))return!1;for(a=a.replace(/\D/g,"");a.length<9;)a="0"+a;for(var b=0,c=[3,2,7,6,5,4,3,2],d=0;8>d;d++)b+=parseInt(a.charAt(d))*c[d];return b=11-b%11,11==b?b=0:10==b&&(b="K"),b==a.charAt(8)},_cz:function(b){if(!/^\d{9,10}$/.test(b))return!1;var c=1900+parseInt(b.substr(0,2)),d=parseInt(b.substr(2,2))%50%20,e=parseInt(b.substr(4,2));if(9==b.length){if(c>=1980&&(c-=100),c>1953)return!1}else 1954>c&&(c+=100);if(!a.fn.bootstrapValidator.helpers.date(c,d,e))return!1;if(10==b.length){var f=parseInt(b.substr(0,9),10)%11;return 1985>c&&(f%=10),f==b.substr(9,1)}return!0},_dk:function(b){if(!/^[0-9]{6}[-]{0,1}[0-9]{4}$/.test(b))return!1;b=b.replace(/-/g,"");var c=parseInt(b.substr(0,2),10),d=parseInt(b.substr(2,2),10),e=parseInt(b.substr(4,2),10);switch(!0){case-1!="5678".indexOf(b.charAt(6))&&e>=58:e+=1800;break;case-1!="0123".indexOf(b.charAt(6)):case-1!="49".indexOf(b.charAt(6))&&e>=37:e+=1900;break;default:e+=2e3}return a.fn.bootstrapValidator.helpers.date(e,d,c)},_ee:function(a){return this._lt(a)},_es:function(a){if(!/^[0-9A-Z]{8}[-]{0,1}[0-9A-Z]$/.test(a)&&!/^[XYZ][-]{0,1}[0-9]{7}[-]{0,1}[0-9A-Z]$/.test(a))return!1;a=a.replace(/-/g,"");var b="XYZ".indexOf(a.charAt(0));-1!=b&&(a=b+a.substr(1)+"");var c=parseInt(a.substr(0,8),10);return c="TRWAGMYFPDXBNJZSQVHLCKE"[c%23],c==a.substr(8,1)},_fi:function(b){if(!/^[0-9]{6}[-+A][0-9]{3}[0-9ABCDEFHJKLMNPRSTUVWXY]$/.test(b))return!1;var c=parseInt(b.substr(0,2),10),d=parseInt(b.substr(2,2),10),e=parseInt(b.substr(4,2),10),f={"+":1800,"-":1900,A:2e3};if(e=f[b.charAt(6)]+e,!a.fn.bootstrapValidator.helpers.date(e,d,c))return!1;var g=parseInt(b.substr(7,3));if(2>g)return!1;var h=b.substr(0,6)+b.substr(7,3)+"";return h=parseInt(h),"0123456789ABCDEFHJKLMNPRSTUVWXY".charAt(h%31)==b.charAt(10)},_hr:function(b){return/^[0-9]{11}$/.test(b)?a.fn.bootstrapValidator.helpers.mod_11_10(b):!1},_ie:function(a){if(!/^\d{7}[A-W][AHWTX]?$/.test(a))return!1;var b=function(a){for(;a.length<7;)a="0"+a;for(var b="WABCDEFGHIJKLMNOPQRSTUV",c=0,d=0;7>d;d++)c+=parseInt(a.charAt(d))*(8-d);return c+=9*b.indexOf(a.substr(7)),b[c%23]};return 9!=a.length||"A"!=a.charAt(8)&&"H"!=a.charAt(8)?a.charAt(7)==b(a.substr(0,7)):a.charAt(7)==b(a.substr(0,7)+a.substr(8)+"")},_is:function(b){if(!/^[0-9]{6}[-]{0,1}[0-9]{4}$/.test(b))return!1;b=b.replace(/-/g,"");var c=parseInt(b.substr(0,2),10),d=parseInt(b.substr(2,2),10),e=parseInt(b.substr(4,2),10),f=parseInt(b.charAt(9));if(e=9==f?1900+e:100*(20+f)+e,!a.fn.bootstrapValidator.helpers.date(e,d,c,!0))return!1;for(var g=0,h=[3,2,7,6,5,4,3,2],i=0;8>i;i++)g+=parseInt(b.charAt(i))*h[i];return g=11-g%11,g==b.charAt(8)},_lt:function(b){if(!/^[0-9]{11}$/.test(b))return!1;var c=parseInt(b.charAt(0)),d=parseInt(b.substr(1,2),10),e=parseInt(b.substr(3,2),10),f=parseInt(b.substr(5,2),10),g=c%2==0?17+c/2:17+(c+1)/2;if(d=100*g+d,!a.fn.bootstrapValidator.helpers.date(d,e,f,!0))return!1;for(var h=0,i=[1,2,3,4,5,6,7,8,9,1],j=0;10>j;j++)h+=parseInt(b.charAt(j))*i[j];if(h%=11,10!=h)return h==b.charAt(10);for(h=0,i=[3,4,5,6,7,8,9,1,2,3],j=0;10>j;j++)h+=parseInt(b.charAt(j))*i[j];return h%=11,10==h&&(h=0),h==b.charAt(10)},_lv:function(b){if(!/^[0-9]{6}[-]{0,1}[0-9]{5}$/.test(b))return!1;b=b.replace(/\D/g,"");var c=parseInt(b.substr(0,2)),d=parseInt(b.substr(2,2)),e=parseInt(b.substr(4,2));if(e=e+1800+100*parseInt(b.charAt(6)),!a.fn.bootstrapValidator.helpers.date(e,d,c,!0))return!1;for(var f=0,g=[10,5,8,4,2,1,6,3,7,9],h=0;10>h;h++)f+=parseInt(b.charAt(h))*g[h];return f=(f+1)%11%10,f==b.charAt(10)},_nl:function(a){for(;a.length<9;)a="0"+a;if(!/^[0-9]{4}[.]{0,1}[0-9]{2}[.]{0,1}[0-9]{3}$/.test(a))return!1;if(a=a.replace(/\./g,""),0==parseInt(a,10))return!1;for(var b=0,c=a.length,d=0;c-1>d;d++)b+=(9-d)*parseInt(a.charAt(d));return b%=11,10==b&&(b=0),b==a.charAt(c-1)},_ro:function(b){if(!/^[0-9]{13}$/.test(b))return!1;var c=parseInt(b.charAt(0));if(0==c||7==c||8==c)return!1;var d=parseInt(b.substr(1,2),10),e=parseInt(b.substr(3,2),10),f=parseInt(b.substr(5,2),10),g={1:1900,2:1900,3:1800,4:1800,5:2e3,6:2e3};if(f>31&&e>12)return!1;if(9!=c&&(d=g[c+""]+d,!a.fn.bootstrapValidator.helpers.date(d,e,f)))return!1;for(var h=0,i=[2,7,9,1,4,6,3,5,8,2,7,9],j=b.length,k=0;j-1>k;k++)h+=parseInt(b.charAt(k))*i[k]; 13 | return h%=11,10==h&&(h=1),h==b.charAt(j-1)},_se:function(b){if(!/^[0-9]{10}$/.test(b)&&!/^[0-9]{6}[-|+][0-9]{4}$/.test(b))return!1;b=b.replace(/[^0-9]/g,"");var c=parseInt(b.substr(0,2))+1900,d=parseInt(b.substr(2,2)),e=parseInt(b.substr(4,2));return a.fn.bootstrapValidator.helpers.date(c,d,e)?a.fn.bootstrapValidator.helpers.luhn(b):!1},_sk:function(a){return this._cz(a)},_sm:function(a){return/^\d{5}$/.test(a)},_za:function(b){if(!/^[0-9]{10}[0|1][8|9][0-9]$/.test(b))return!1;var c=parseInt(b.substr(0,2)),d=(new Date).getFullYear()%100,e=parseInt(b.substr(2,2)),f=parseInt(b.substr(4,2));return c=c>=d?c+1900:c+2e3,a.fn.bootstrapValidator.helpers.date(c,e,f)?a.fn.bootstrapValidator.helpers.luhn(b):!1}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.identical={html5Attributes:{message:"message",field:"field"},validate:function(a,b,c){var d=b.val();if(""==d)return!0;var e=a.getFieldElements(c.field);return null==e?!0:d==e.val()?(a.updateStatus(c.field,a.STATUS_VALID,"identical"),!0):!1}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.imei={validate:function(b,c){var d=c.val();if(""==d)return!0;switch(!0){case/^\d{15}$/.test(d):case/^\d{2}-\d{6}-\d{6}-\d{1}$/.test(d):case/^\d{2}\s\d{6}\s\d{6}\s\d{1}$/.test(d):return d=d.replace(/[^0-9]/g,""),a.fn.bootstrapValidator.helpers.luhn(d);case/^\d{14}$/.test(d):case/^\d{16}$/.test(d):case/^\d{2}-\d{6}-\d{6}(|-\d{2})$/.test(d):case/^\d{2}\s\d{6}\s\d{6}(|\s\d{2})$/.test(d):return!0;default:return!1}}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.integer={enableByHtml5:function(a){return"number"==a.attr("type")},validate:function(a,b){var c=b.val();return""==c?!0:/^(?:-?(?:0|[1-9][0-9]*))$/.test(c)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.ip={html5Attributes:{message:"message",ipv4:"ipv4",ipv6:"ipv6"},validate:function(b,c,d){var e=c.val();return""==e?!0:(d=a.extend({},{ipv4:!0,ipv6:!0},d),d.ipv4?/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(e):d.ipv6?/^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/.test(str):!1)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.isbn={validate:function(a,b){var c=b.val();if(""==c)return!0;var d;switch(!0){case/^\d{9}[\dX]$/.test(c):case 13==c.length&&/^(\d+)-(\d+)-(\d+)-([\dX])$/.test(c):case 13==c.length&&/^(\d+)\s(\d+)\s(\d+)\s([\dX])$/.test(c):d="ISBN10";break;case/^(978|979)\d{9}[\dX]$/.test(c):case 17==c.length&&/^(978|979)-(\d+)-(\d+)-(\d+)-([\dX])$/.test(c):case 17==c.length&&/^(978|979)\s(\d+)\s(\d+)\s(\d+)\s([\dX])$/.test(c):d="ISBN13";break;default:return!1}c=c.replace(/[^0-9X]/gi,"");var e,f=c.split(""),g=f.length,h=0;switch(d){case"ISBN10":h=0;for(var i=0;g-1>i;i++)h+=(10-i)*parseInt(f[i]);return e=11-h%11,11==e?e=0:10==e&&(e="X"),e+""==f[g-1];case"ISBN13":h=0;for(var i=0;g-1>i;i++)h+=i%2==0?parseInt(f[i]):3*parseInt(f[i]);return e=10-h%10,10==e&&(e="0"),e+""==f[g-1];default:return!1}}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.isin={COUNTRY_CODES:"AF|AX|AL|DZ|AS|AD|AO|AI|AQ|AG|AR|AM|AW|AU|AT|AZ|BS|BH|BD|BB|BY|BE|BZ|BJ|BM|BT|BO|BQ|BA|BW|BV|BR|IO|BN|BG|BF|BI|KH|CM|CA|CV|KY|CF|TD|CL|CN|CX|CC|CO|KM|CG|CD|CK|CR|CI|HR|CU|CW|CY|CZ|DK|DJ|DM|DO|EC|EG|SV|GQ|ER|EE|ET|FK|FO|FJ|FI|FR|GF|PF|TF|GA|GM|GE|DE|GH|GI|GR|GL|GD|GP|GU|GT|GG|GN|GW|GY|HT|HM|VA|HN|HK|HU|IS|IN|ID|IR|IQ|IE|IM|IL|IT|JM|JP|JE|JO|KZ|KE|KI|KP|KR|KW|KG|LA|LV|LB|LS|LR|LY|LI|LT|LU|MO|MK|MG|MW|MY|MV|ML|MT|MH|MQ|MR|MU|YT|MX|FM|MD|MC|MN|ME|MS|MA|MZ|MM|NA|NR|NP|NL|NC|NZ|NI|NE|NG|NU|NF|MP|NO|OM|PK|PW|PS|PA|PG|PY|PE|PH|PN|PL|PT|PR|QA|RE|RO|RU|RW|BL|SH|KN|LC|MF|PM|VC|WS|SM|ST|SA|SN|RS|SC|SL|SG|SX|SK|SI|SB|SO|ZA|GS|SS|ES|LK|SD|SR|SJ|SZ|SE|CH|SY|TW|TJ|TZ|TH|TL|TG|TK|TO|TT|TN|TR|TM|TC|TV|UG|UA|AE|GB|US|UM|UY|UZ|VU|VE|VN|VG|VI|WF|EH|YE|ZM|ZW",validate:function(a,b){var c=b.val();if(""==c)return!0;c=c.toUpperCase();var d=new RegExp("^("+this.COUNTRY_CODES+")[0-9A-Z]{10}$");if(!d.test(c))return!1;for(var e="",f=c.length,g=0;f-1>g;g++){var h=c.charCodeAt(g);e+=h>57?(h-55).toString():c.charAt(g)}var i="",j=e.length,k=j%2!=0?0:1;for(g=0;j>g;g++)i+=parseInt(e[g])*(g%2==k?2:1)+"";var l=0;for(g=0;gh;h++)f+=parseInt(c.charAt(h))*g[h%2];return f=10-f%10,f==c.charAt(e-1)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.issn={validate:function(a,b){var c=b.val();if(""==c)return!0;if(!/^\d{4}\-\d{3}[\dX]$/.test(c))return!1;c=c.replace(/[^0-9X]/gi,"");var d=c.split(""),e=d.length,f=0;"X"==d[7]&&(d[7]=10);for(var g=0;e>g;g++)f+=(8-g)*parseInt(d[g]);return f%11==0}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.lessThan={html5Attributes:{message:"message",value:"value",inclusive:"inclusive"},enableByHtml5:function(a){var b=a.attr("max");return b?{value:b}:!1},validate:function(a,b,c){var d=b.val();return""==d?!0:(d=parseFloat(d),c.inclusive===!1?d<=c.value:d0:""!=a.trim(c.val())}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.numeric={html5Attributes:{message:"message",separator:"separator"},validate:function(a,b,c){var d=b.val();if(""==d)return!0;var e=c.separator||".";return"."!=e&&(d=d.replace(e,".")),!isNaN(parseFloat(d))&&isFinite(d)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.phone={html5Attributes:{message:"message",country:"country"},validate:function(a,b,c){var d=b.val();if(""==d)return!0;var e=(c.country||"US").toUpperCase();switch(e){case"US":default:return d=d.replace(/\D/g,""),/^(?:(1\-?)|(\+1 ?))?\(?(\d{3})[\)\-\.]?(\d{3})[\-\.]?(\d{4})$/.test(d)&&10==d.length}}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.regexp={html5Attributes:{message:"message",regexp:"regexp"},enableByHtml5:function(a){var b=a.attr("pattern");return b?{regexp:b}:!1},validate:function(a,b,c){var d=b.val();if(""==d)return!0;var e="string"==typeof c.regexp?new RegExp(c.regexp):c.regexp;return e.test(d)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.remote={html5Attributes:{message:"message",url:"url",name:"name"},validate:function(b,c,d){var e=c.val();if(""==e)return!0;var f=c.attr("data-bv-field"),g=d.data;null==g&&(g={}),"function"==typeof g&&(g=g.call(this,b)),g[d.name||f]=e;var h=new a.Deferred,i=a.ajax({type:"POST",url:d.url,dataType:"json",data:g});return i.then(function(a){h.resolve(c,"remote",a.valid===!0||"true"===a.valid)}),h.fail(function(){i.abort()}),h}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.rtn={validate:function(a,b){var c=b.val();if(""==c)return!0;if(!/^\d{9}$/.test(c))return!1;for(var d=0,e=0;eg;g++)d+=e[g]*parseInt(c.charAt(g),36);return d=(10-d%10)%10,d==c.charAt(f-1)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.siren={validate:function(b,c){var d=c.val();return""==d?!0:/^\d{9}$/.test(d)?a.fn.bootstrapValidator.helpers.luhn(d):!1}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.siret={validate:function(a,b){var c=b.val();if(""==c)return!0;for(var d,e=0,f=c.length,g=0;f>g;g++)d=parseInt(c.charAt(g),10),g%2==0&&(d=2*d,d>9&&(d-=9)),e+=d;return e%10==0}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.step={html5Attributes:{message:"message",base:"baseValue",step:"step"},validate:function(b,c,d){var e=c.val();if(""==e)return!0;if(d=a.extend({},{baseValue:0,step:1},d),e=parseFloat(e),isNaN(e)||!isFinite(e))return!1;var f=function(a,b){var c=Math.pow(10,b);a*=c;var d=a>0|-(0>a),e=a%1===.5*d;return e?(Math.floor(a)+(d>0))/c:Math.round(a)/c},g=function(a,b){if(0==b)return 1;var c=(a+"").split("."),d=(b+"").split("."),e=(1==c.length?0:c[1].length)+(1==d.length?0:d[1].length);return f(a-b*Math.floor(a/b),e)},h=g(e-d.baseValue,d.step);return 0==h||h==d.step}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.stringCase={html5Attributes:{message:"message","case":"case"},validate:function(a,b,c){var d=b.val();if(""==d)return!0;var e=(c["case"]||"lower").toLowerCase();switch(e){case"upper":return d===d.toUpperCase();case"lower":default:return d===d.toLowerCase()}}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.stringLength={html5Attributes:{message:"message",min:"min",max:"max"},enableByHtml5:function(a){var b=a.attr("maxlength");return b?{max:parseInt(b,10)}:!1},validate:function(b,c,d){var e=c.val();if(""==e)return!0;var f=a.trim(e).length;return d.min&&fd.max?!1:!0}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.uri={enableByHtml5:function(a){return"url"==a.attr("type")},validate:function(a,b){var c=b.val();if(""==c)return!0;var d=new RegExp("^(?:(?:https?|ftp)://)(?:\\S+(?::\\S*)?@)?(?:(?!10(?:\\.\\d{1,3}){3})(?!127(?:\\.\\d{1,3}){3})(?!169\\.254(?:\\.\\d{1,3}){2})(?!192\\.168(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))(?::\\d{2,5})?(?:/[^\\s]*)?$","i");return d.test(c)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.uuid={html5Attributes:{message:"message",version:"version"},validate:function(a,b,c){var d=b.val();if(""==d)return!0;var e={3:/^[0-9A-F]{8}-[0-9A-F]{4}-3[0-9A-F]{3}-[0-9A-F]{4}-[0-9A-F]{12}$/i,4:/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,5:/^[0-9A-F]{8}-[0-9A-F]{4}-5[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,all:/^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i},f=c.version?c.version+"":"all";return null==e[f]?!0:e[f].test(d)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.vat={html5Attributes:{message:"message",country:"country"},validate:function(a,b,c){var d=b.val();if(""==d)return!0;var e=c.country||d.substr(0,2),f=["_",e.toLowerCase()].join("");return this[f]&&"function"==typeof this[f]?this[f](d):!0},_at:function(a){if(!/^ATU[0-9]{8}$/.test(a))return!1;a=a.substr(3);for(var b=0,c=[1,2,1,2,1,2,1],d=0,e=0;7>e;e++)d=parseInt(a.charAt(e))*c[e],d>9&&(d=Math.floor(d/10)+d%10),b+=d;return b=10-(b+4)%10,10==b&&(b=0),b==a.substr(7,1)},_be:function(a){if(!/^BE[0]{0,1}[0-9]{9}$/.test(a))return!1;if(a=a.substr(2),9==a.length&&(a="0"+a),0==a.substr(1,1))return!1;var b=parseInt(a.substr(0,8),10)+parseInt(a.substr(8,2),10);return b%97==0},_bg:function(b){if(!/^BG[0-9]{9,10}$/.test(b))return!1;b=b.substr(2);var c=0,d=0;if(9==b.length){for(d=0;8>d;d++)c+=parseInt(b.charAt(d))*(d+1);if(c%=11,10==c)for(c=0,d=0;8>d;d++)c+=parseInt(b.charAt(d))*(d+3);return c%=10,c==b.substr(8)}if(10==b.length){var e=function(b){var c=parseInt(b.substr(0,2),10)+1900,d=parseInt(b.substr(2,2),10),e=parseInt(b.substr(4,2),10);if(d>40?(c+=100,d-=40):d>20&&(c-=100,d-=20),!a.fn.bootstrapValidator.helpers.date(c,d,e))return!1;for(var f=0,g=[2,4,8,5,10,9,7,3,6],h=0;9>h;h++)f+=parseInt(b.charAt(h))*g[h];return f=f%11%10,f==b.substr(9,1)},f=function(a){for(var b=0,c=[21,19,17,13,11,9,7,3,1],d=0;9>d;d++)b+=parseInt(a.charAt(d))*c[d];return b%=10,b==a.substr(9,1)},g=function(a){for(var b=0,c=[4,3,2,7,6,5,4,3,2],d=0;9>d;d++)b+=parseInt(a.charAt(d))*c[d];return b=11-b%11,10==b?!1:(11==b&&(b=0),b==a.substr(9,1))};return e(b)||f(b)||g(b)}return!1},_ch:function(a){if(!/^CHE[0-9]{9}(MWST)?$/.test(a))return!1;a=a.substr(3);for(var b=0,c=[5,4,3,2,7,6,5,4],d=0;8>d;d++)b+=parseInt(a.charAt(d),10)*c[d];return b=11-b%11,10==b?!1:(11==b&&(b=0),b==a.substr(8,1))},_cy:function(a){if(!/^CY[0-5|9]{1}[0-9]{7}[A-Z]{1}$/.test(a))return!1;if(a=a.substr(2),"12"==a.substr(0,2))return!1;for(var b=0,c={0:1,1:0,2:5,3:7,4:9,5:13,6:15,7:17,8:19,9:21},d=0;8>d;d++){var e=parseInt(a.charAt(d),10);d%2==0&&(e=c[e+""]),b+=e}return b="ABCDEFGHIJKLMNOPQRSTUVWXYZ"[b%26],b==a.substr(8,1)},_cz:function(b){if(!/^CZ[0-9]{8,10}$/.test(b))return!1;b=b.substr(2);var c=0,d=0;if(8==b.length){if(b.charAt(0)+""=="9")return!1;for(c=0,d=0;7>d;d++)c+=parseInt(b.charAt(d),10)*(8-d);return c=11-c%11,10==c&&(c=0),11==c&&(c=1),c==b.substr(7,1)}if(9==b.length&&b.charAt(0)+""=="6"){for(c=0,d=0;7>d;d++)c+=parseInt(b.charAt(d+1),10)*(8-d);return c=11-c%11,10==c&&(c=0),11==c&&(c=1),c=[8,7,6,5,4,3,2,1,0,9,10][c-1],c==b.substr(8,1)}if(9==b.length||10==b.length){var e=1900+parseInt(b.substr(0,2)),f=parseInt(b.substr(2,2))%50%20,g=parseInt(b.substr(4,2));if(9==b.length){if(e>=1980&&(e-=100),e>1953)return!1}else 1954>e&&(e+=100);if(!a.fn.bootstrapValidator.helpers.date(e,f,g))return!1;if(10==b.length){var h=parseInt(b.substr(0,9),10)%11;return 1985>e&&(h%=10),h==b.substr(9,1)}return!0}return!1},_de:function(b){return/^DE[0-9]{9}$/.test(b)?(b=b.substr(2),a.fn.bootstrapValidator.helpers.mod_11_10(b)):!1},_dk:function(a){if(!/^DK[0-9]{8}$/.test(a))return!1;a=a.substr(2);for(var b=0,c=[2,7,6,5,4,3,2,1],d=0;8>d;d++)b+=parseInt(a.charAt(d),10)*c[d];return b%11==0},_ee:function(a){if(!/^EE[0-9]{9}$/.test(a))return!1;a=a.substr(2);for(var b=0,c=[3,7,1,3,7,1,3,7,1],d=0;9>d;d++)b+=parseInt(a.charAt(d))*c[d];return b%10==0},_es:function(a){if(!/^ES[0-9A-Z][0-9]{7}[0-9A-Z]$/.test(a))return!1;a=a.substr(2);var b=function(a){var b=parseInt(a.substr(0,8),10);return b="TRWAGMYFPDXBNJZSQVHLCKE"[b%23],b==a.substr(8,1)},c=function(a){var b=["XYZ".indexOf(a.charAt(0)),a.substr(1)].join("");return b=parseInt(b,10),b="TRWAGMYFPDXBNJZSQVHLCKE"[b%23],b==a.substr(8,1)},d=function(a){var b,c=a.charAt(0);if(-1!="KLM".indexOf(c))return b=parseInt(a.substr(1,8),10),b="TRWAGMYFPDXBNJZSQVHLCKE"[b%23],b==a.substr(8,1);if(-1!="ABCDEFGHJNPQRSUVW".indexOf(c)){for(var d=0,e=[2,1,2,1,2,1,2],f=0,g=0;7>g;g++)f=parseInt(a.charAt(g+1))*e[g],f>9&&(f=Math.floor(f/10)+f%10),d+=f;return d=10-d%10,d==a.substr(8,1)||"JABCDEFGHI"[d]==a.substr(8,1)}return!1},e=a.charAt(0);return/^[0-9]$/.test(e)?b(a):/^[XYZ]$/.test(e)?c(a):d(a)},_fi:function(a){if(!/^FI[0-9]{8}$/.test(a))return!1;a=a.substr(2);for(var b=0,c=[7,9,10,5,8,4,2,1],d=0;8>d;d++)b+=parseInt(a.charAt(d))*c[d];return b%11==0},_fr:function(b){if(!/^FR[0-9A-Z]{2}[0-9]{9}$/.test(b))return!1;if(b=b.substr(2),!a.fn.bootstrapValidator.helpers.luhn(b.substr(2)))return!1;if(/^[0-9]{2}$/.test(b.substr(0,2)))return b.substr(0,2)==parseInt(b.substr(2)+"12",10)%97;var c,d="0123456789ABCDEFGHJKLMNPQRSTUVWXYZ";return c=/^[0-9]{1}$/.test(b.charAt(0))?24*d.indexOf(b.charAt(0))+d.indexOf(b.charAt(1))-10:34*d.indexOf(b.charAt(0))+d.indexOf(b.charAt(1))-100,(parseInt(b.substr(2),10)+1+Math.floor(c/11))%11==c%11},_gb:function(a){if(!(/^GB[0-9]{9}$/.test(a)||/^GB[0-9]{12}$/.test(a)||/^GBGD[0-9]{3}$/.test(a)||/^GBHA[0-9]{3}$/.test(a)||/^GB(GD|HA)8888[0-9]{5}$/.test(a)))return!1;a=a.substr(2);var b=a.length;if(5==b){var c=a.substr(0,2),d=parseInt(a.substr(2));return"GD"==c&&500>d||"HA"==c&&d>=500}if(11==b&&("GD8888"==a.substr(0,6)||"HA8888"==a.substr(0,6)))return"GD"==a.substr(0,2)&&parseInt(a.substr(6,3))>=500||"HA"==a.substr(0,2)&&parseInt(a.substr(6,3))<500?!1:parseInt(a.substr(6,3))%97==parseInt(a.substr(9,2));if(9==b||12==b){for(var e=0,f=[8,7,6,5,4,3,2,10,1],g=0;9>g;g++)e+=parseInt(a.charAt(g))*f[g];return e%=97,parseInt(a.substr(0,3))>=100?0==e||42==e||55==e:0==e}return!0},_gr:function(a){if(!/^GR[0-9]{9}$/.test(a))return!1;a=a.substr(2),8==a.length&&(a="0"+a);for(var b=0,c=[256,128,64,32,16,8,4,2],d=0;8>d;d++)b+=parseInt(a.charAt(d))*c[d];return b=b%11%10,b==a.substr(8,1)},_el:function(a){return/^EL[0-9]{9}$/.test(a)?(a="GR"+a.substr(2),this._gr(a)):!1},_hu:function(a){if(!/^HU[0-9]{8}$/.test(a))return!1;a=a.substr(2);for(var b=0,c=[9,7,3,1,9,7,3,1],d=0;8>d;d++)b+=parseInt(a.charAt(d))*c[d];return b%10==0},_hr:function(b){return/^HR[0-9]{11}$/.test(b)?(b=b.substr(2),a.fn.bootstrapValidator.helpers.mod_11_10(b)):!1},_ie:function(a){if(!/^IE[0-9]{1}[0-9A-Z\*\+]{1}[0-9]{5}[A-Z]{1,2}$/.test(a))return!1;a=a.substr(2);var b=function(a){for(;a.length<7;)a="0"+a;for(var b="WABCDEFGHIJKLMNOPQRSTUV",c=0,d=0;7>d;d++)c+=parseInt(a.charAt(d))*(8-d);return c+=9*b.indexOf(a.substr(7)),b[c%23]};return/^[0-9]+$/.test(a.substr(0,7))?a.charAt(7)==b(a.substr(0,7)+a.substr(8)+""):-1!="ABCDEFGHIJKLMNOPQRSTUVWXYZ+*".indexOf(a.charAt(1))?a.charAt(7)==b(a.substr(2,5)+a.substr(0,1)+""):!0},_it:function(b){if(!/^IT[0-9]{11}$/.test(b))return!1;if(b=b.substr(2),0==parseInt(b.substr(0,7)))return!1;var c=parseInt(b.substr(7,3));return 1>c||c>201&&999!=c&&888!=c?!1:a.fn.bootstrapValidator.helpers.luhn(b)},_lt:function(a){if(!/^LT([0-9]{7}1[0-9]{1}|[0-9]{10}1[0-9]{1})$/.test(a))return!1;a=a.substr(2);for(var b=a.length,c=0,d=0;b-1>d;d++)c+=parseInt(a.charAt(d))*(1+d%9);var e=c%11;if(10==e){c=0;for(var d=0;b-1>d;d++)c+=parseInt(a.charAt(d))*(1+(d+2)%9)}return e=e%11%10,e==a.charAt(b-1)},_lu:function(a){return/^LU[0-9]{8}$/.test(a)?(a=a.substr(2),a.substr(0,6)%89==a.substr(6,2)):!1},_lv:function(b){if(!/^LV[0-9]{11}$/.test(b))return!1;b=b.substr(2);var c=parseInt(b.charAt(0)),d=0,e=[],f=0,g=b.length;if(c>3){for(d=0,e=[9,1,4,8,3,10,2,5,7,6,1],f=0;g>f;f++)d+=parseInt(b.charAt(f))*e[f];return d%=11,3==d}var h=parseInt(b.substr(0,2)),i=parseInt(b.substr(2,2)),j=parseInt(b.substr(4,2));if(j=j+1800+100*parseInt(b.charAt(6)),!a.fn.bootstrapValidator.helpers.date(j,i,h))return!1;for(d=0,e=[10,5,8,4,2,1,6,3,7,9],f=0;g-1>f;f++)d+=parseInt(b.charAt(f))*e[f];return d=(d+1)%11%10,d==b.charAt(g-1)},_mt:function(a){if(!/^MT[0-9]{8}$/.test(a))return!1;a=a.substr(2);for(var b=0,c=[3,4,6,7,8,9,10,1],d=0;8>d;d++)b+=parseInt(a.charAt(d))*c[d];return b%37==0},_nl:function(a){if(!/^NL[0-9]{9}B[0-9]{2}$/.test(a))return!1;a=a.substr(2);for(var b=0,c=[9,8,7,6,5,4,3,2],d=0;8>d;d++)b+=parseInt(a.charAt(d))*c[d];return b%=11,b>9&&(b=0),b==a.substr(8,1)},_no:function(a){if(!/^NO[0-9]{9}$/.test(a))return!1;a=a.substr(2);for(var b=0,c=[3,2,7,6,5,4,3,2],d=0;8>d;d++)b+=parseInt(a.charAt(d))*c[d];return b=11-b%11,11==b&&(b=0),b==a.substr(8,1)},_pl:function(a){if(!/^PL[0-9]{10}$/.test(a))return!1;a=a.substr(2);for(var b=0,c=[6,5,7,2,3,4,5,6,7,-1],d=0;10>d;d++)b+=parseInt(a.charAt(d))*c[d];return b%11==0},_pt:function(a){if(!/^PT[0-9]{9}$/.test(a))return!1;a=a.substr(2);for(var b=0,c=[9,8,7,6,5,4,3,2],d=0;8>d;d++)b+=parseInt(a.charAt(d))*c[d];return b=11-b%11,b>9&&(b=0),b==a.substr(8,1)},_ro:function(a){if(!/^RO[1-9][0-9]{1,9}$/.test(a))return!1;a=a.substr(2);for(var b=a.length,c=[7,5,3,2,1,7,5,3,2].slice(10-b),d=0,e=0;b-1>e;e++)d+=parseInt(a.charAt(e))*c[e];return d=10*d%11%10,d==a.substr(b-1,1)},_ru:function(a){if(!/^RU([0-9]{9}|[0-9]{12})$/.test(a))return!1;if(a=a.substr(2),10==a.length){for(var b=0,c=[2,4,10,3,5,9,4,6,8,0],d=0;10>d;d++)b+=parseInt(a.charAt(d))*c[d];return b%=11,b>9&&(b%=10),b==a.substr(9,1)}if(12==a.length){for(var e=0,f=[7,2,4,10,3,5,9,4,6,8,0],g=0,h=[3,7,2,4,10,3,5,9,4,6,8,0],d=0;11>d;d++)e+=parseInt(a.charAt(d))*f[d],g+=parseInt(a.charAt(d))*h[d];return e%=11,e>9&&(e%=10),g%=11,g>9&&(g%=10),e==a.substr(10,1)&&g==a.substr(11,1)}return!1},_rs:function(a){if(!/^RS[0-9]{9}$/.test(a))return!1;a=a.substr(2);for(var b=10,c=0,d=0;8>d;d++)c=(parseInt(a.charAt(d))+b)%10,0==c&&(c=10),b=2*c%11;return(b+parseInt(a.substr(8,1)))%10==1},_se:function(b){return/^SE[0-9]{10}01$/.test(b)?(b=b.substr(2,10),a.fn.bootstrapValidator.helpers.luhn(b)):!1},_si:function(a){if(!/^SI[0-9]{8}$/.test(a))return!1;a=a.substr(2);for(var b=0,c=[8,7,6,5,4,3,2],d=0;7>d;d++)b+=parseInt(a.charAt(d))*c[d];return b=11-b%11,10==b&&(b=0),b==a.substr(7,1)},_sk:function(a){return/^SK[1-9][0-9][(2-4)|(6-9)][0-9]{7}$/.test(a)?(a=a.substr(2),a%11==0):!1}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.vin={validate:function(a,b){var c=b.val();if(""==c)return!0;if(!/^[a-hj-npr-z0-9]{8}[0-9xX][a-hj-npr-z0-9]{8}$/i.test(c))return!1;c=c.toUpperCase();for(var d={A:1,B:2,C:3,D:4,E:5,F:6,G:7,H:8,J:1,K:2,L:3,M:4,N:5,P:7,R:9,S:2,T:3,U:4,V:5,W:6,X:7,Y:8,Z:9,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,0:0},e=[8,7,6,5,4,3,2,10,0,9,8,7,6,5,4,3,2],f=0,g=c.length,h=0;g>h;h++)f+=d[c.charAt(h)+""]*e[h];var i=f%11;return 10==i&&(i="X"),i==c.charAt(8)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.zipCode={html5Attributes:{message:"message",country:"country"},validate:function(a,b,c){var d=b.val();if(""==d||!c.country)return!0;var e=(c.country||"US").toUpperCase();switch(e){case"CA":return/(?:A|B|C|E|G|J|K|L|M|N|P|R|S|T|V|X|Y){1}[0-9]{1}(?:A|B|C|E|G|J|K|L|M|N|P|R|S|T|V|X|Y){1}\s?[0-9]{1}(?:A|B|C|E|G|J|K|L|M|N|P|R|S|T|V|X|Y){1}[0-9]{1}/i.test(d);case"DK":return/^(DK(-|\s)?)?\d{4}$/i.test(d);case"GB":return this._gb(d);case"IT":return/^(I-|IT-)?\d{5}$/i.test(d);case"NL":return/^[1-9][0-9]{3} ?(?!sa|sd|ss)[a-z]{2}$/i.test(d);case"SE":return/^(S-)?\d{3}\s?\d{2}$/i.test(d);case"US":default:return/^\d{4,5}([\-]\d{4})?$/.test(d)}},_gb:function(a){for(var b="[ABCDEFGHIJKLMNOPRSTUWYZ]",c="[ABCDEFGHKLMNOPQRSTUVWXY]",d="[ABCDEFGHJKPMNRSTUVWXY]",e="[ABEHMNPRVWXY]",f="[ABDEFGHJLNPQRSTUWXYZ]",g=[new RegExp("^("+b+"{1}"+c+"?[0-9]{1,2})(\\s*)([0-9]{1}"+f+"{2})$","i"),new RegExp("^("+b+"{1}[0-9]{1}"+d+"{1})(\\s*)([0-9]{1}"+f+"{2})$","i"),new RegExp("^("+b+"{1}"+c+"{1}?[0-9]{1}"+e+"{1})(\\s*)([0-9]{1}"+f+"{2})$","i"),new RegExp("^(BF1)(\\s*)([0-6]{1}[ABDEFGHJLNPQRST]{1}[ABDEFGHJLNPQRSTUWZYZ]{1})$","i"),/^(GIR)(\s*)(0AA)$/i,/^(BFPO)(\s*)([0-9]{1,4})$/i,/^(BFPO)(\s*)(c\/o\s*[0-9]{1,3})$/i,/^([A-Z]{4})(\s*)(1ZZ)$/i,/^(AI-2640)$/i],h=0;h