├── demo.png ├── order-charge-gateway-notify ├── src │ ├── main │ │ ├── resources │ │ │ ├── application.properties │ │ │ ├── mybatis-config.xml │ │ │ ├── META-INF │ │ │ │ ├── applicationContext-mapper.xml │ │ │ │ └── applicationContext-bean.xml │ │ │ └── mapper │ │ │ │ └── WalletMapper.xml │ │ └── java │ │ │ └── com │ │ │ └── snowalker │ │ │ └── notify │ │ │ ├── NotifyApplication.java │ │ │ ├── common │ │ │ ├── constant │ │ │ │ └── NotifyConstant.java │ │ │ ├── util │ │ │ │ ├── LogExceptionWapper.java │ │ │ │ └── DateUtil.java │ │ │ ├── service │ │ │ │ ├── WalletService.java │ │ │ │ └── impl │ │ │ │ │ └── WalletServiceImpl.java │ │ │ ├── dao │ │ │ │ ├── WalletMapper.java │ │ │ │ └── dataobject │ │ │ │ │ ├── ChargeRecordEntity.java │ │ │ │ │ ├── WalletEntity.java │ │ │ │ │ └── OrderEntity.java │ │ │ └── config │ │ │ │ └── RestTemplateConfig.java │ │ │ └── mq │ │ │ ├── notify │ │ │ └── NotifySendConsumer.java │ │ │ └── payment │ │ │ ├── consumer │ │ │ ├── WalletPaymentConsumer.java │ │ │ └── listener │ │ │ │ └── WalletPaymentMsgListenerImpl.java │ │ │ └── producer │ │ │ ├── OrderStatusUpdateProducer.java │ │ │ └── listener │ │ │ └── LocalTranListenerImpl.java │ └── test │ │ └── java │ │ └── com │ │ └── snowalker │ │ └── notify │ │ └── OrderChargeNotifyApplicationTests.java ├── .gitignore ├── pom.xml └── mvnw.cmd ├── order-charge-gateway-server ├── src │ ├── main │ │ ├── resources │ │ │ ├── application.properties │ │ │ ├── mybatis-config.xml │ │ │ ├── mapper │ │ │ │ ├── MerchantInfoMapper.xml │ │ │ │ └── OrderChargeMapper.xml │ │ │ └── META-INF │ │ │ │ ├── applicationContext-mapper.xml │ │ │ │ └── applicationContext-bean.xml │ │ └── java │ │ │ └── com │ │ │ └── snowalker │ │ │ └── order │ │ │ ├── common │ │ │ ├── dao │ │ │ │ ├── MerchantInfoMapper.java │ │ │ │ ├── OrderChargeMapper.java │ │ │ │ └── dataobject │ │ │ │ │ ├── MerchantInfoDO.java │ │ │ │ │ ├── OrderInfoDobj.java │ │ │ │ │ └── OrderInfoDO.java │ │ │ ├── service │ │ │ │ ├── MerchantInfoService.java │ │ │ │ ├── impl │ │ │ │ │ └── MerchantInfoServiceImpl.java │ │ │ │ └── OrderChargeService.java │ │ │ ├── util │ │ │ │ └── LogExceptionWapper.java │ │ │ ├── config │ │ │ │ ├── ProductVO.java │ │ │ │ ├── ProductConfig.java │ │ │ │ └── MerchantInfoConfig.java │ │ │ └── dto │ │ │ │ ├── Result.java │ │ │ │ └── CodeMsg.java │ │ │ ├── ServerApplication.java │ │ │ ├── mq │ │ │ ├── notify │ │ │ │ └── producer │ │ │ │ │ └── OrderNotifySendProducer.java │ │ │ └── payment │ │ │ │ └── producer │ │ │ │ ├── ChargeOrderPaymentTranProducer.java │ │ │ │ └── listener │ │ │ │ └── ChargeOrderTranListenerImpl.java │ │ │ └── portal │ │ │ └── OrderChargeController.java │ └── test │ │ └── java │ │ └── com │ │ └── snowalker │ │ └── order │ │ └── OrderChargeGatewayApplicationTests.java ├── .gitignore └── pom.xml ├── order-charge-gateway-merchant ├── src │ ├── main │ │ ├── resources │ │ │ ├── application.properties │ │ │ ├── mybatis-config.xml │ │ │ ├── META-INF │ │ │ │ ├── applicationContext-mapper.xml │ │ │ │ └── applicationContext-bean.xml │ │ │ └── mapper │ │ │ │ └── OrderInfoMapper.xml │ │ └── java │ │ │ └── com │ │ │ └── snowalker │ │ │ └── gateway │ │ │ └── merchant │ │ │ ├── order │ │ │ ├── manager │ │ │ │ ├── package-info.java │ │ │ │ └── OrderChargeManager.java │ │ │ ├── constant │ │ │ │ ├── NotifyConstant.java │ │ │ │ └── OrderStatusEnum.java │ │ │ ├── exception │ │ │ │ └── ApiException.java │ │ │ ├── util │ │ │ │ └── LogExceptionWapper.java │ │ │ ├── mapper │ │ │ │ └── OrderInfoMapper.java │ │ │ ├── config │ │ │ │ └── RestTemplateConfig.java │ │ │ ├── handler │ │ │ │ ├── GlobalExceptionHandler.java │ │ │ │ └── RequestParamValidateHandler.java │ │ │ ├── dto │ │ │ │ ├── Result.java │ │ │ │ ├── CodeMsg.java │ │ │ │ └── ChargeOrderDto.java │ │ │ ├── service │ │ │ │ └── OrderService.java │ │ │ ├── OrderChargeController.java │ │ │ ├── OrderNotifyController.java │ │ │ └── dataobject │ │ │ │ └── ChargeOrderDO.java │ │ │ ├── MerchantApplication.java │ │ │ └── goods │ │ │ ├── ProductConfig.java │ │ │ └── ProductVO.java │ └── test │ │ └── java │ │ └── com │ │ └── snowalker │ │ └── gateway │ │ └── merchant │ │ └── OrderChargeGatewayMerchantApplicationTests.java ├── .gitignore └── pom.xml ├── order-charge-sdk ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── snowalker │ │ │ ├── App.java │ │ │ └── order │ │ │ └── charge │ │ │ ├── constant │ │ │ └── ResponseCodeEnum.java │ │ │ ├── response │ │ │ └── ChargeOrderResponse.java │ │ │ └── request │ │ │ ├── ChargeNotifyRequest.java │ │ │ └── ChargeOrderRequest.java │ └── test │ │ └── java │ │ └── com │ │ └── snowalker │ │ └── AppTest.java ├── .gitignore ├── order-charge-sdk.iml └── pom.xml ├── order-charge-message-protocol ├── src │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── snowalker │ │ │ └── AppTest.java │ └── main │ │ └── java │ │ └── com │ │ └── snowalker │ │ └── order │ │ └── charge │ │ └── message │ │ ├── constant │ │ ├── UpdateEventTypeConst.java │ │ └── MessageProtocolConst.java │ │ └── protocol │ │ ├── BaseMsg.java │ │ ├── OrderStatusUpdateProtocol.java │ │ └── WalletPaymentProtocol.java ├── .gitignore ├── order-charge-message-protocol.iml └── pom.xml ├── .gitignore ├── pom.xml ├── readme.md └── script └── demo.sql /demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaXueWWL/order-charge-notify/HEAD/demo.png -------------------------------------------------------------------------------- /order-charge-gateway-notify/src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaXueWWL/order-charge-notify/HEAD/order-charge-gateway-notify/src/main/resources/application.properties -------------------------------------------------------------------------------- /order-charge-gateway-server/src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaXueWWL/order-charge-notify/HEAD/order-charge-gateway-server/src/main/resources/application.properties -------------------------------------------------------------------------------- /order-charge-gateway-merchant/src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaXueWWL/order-charge-notify/HEAD/order-charge-gateway-merchant/src/main/resources/application.properties -------------------------------------------------------------------------------- /order-charge-gateway-merchant/src/main/java/com/snowalker/gateway/merchant/order/manager/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author snowalker 3 | * 防腐层 4 | */ 5 | package com.snowalker.gateway.merchant.order.manager; -------------------------------------------------------------------------------- /order-charge-sdk/src/main/java/com/snowalker/App.java: -------------------------------------------------------------------------------- 1 | package com.snowalker; 2 | 3 | /** 4 | * Hello world! 5 | * 6 | */ 7 | public class App 8 | { 9 | public static void main( String[] args ) 10 | { 11 | System.out.println( "Hello World!" ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /order-charge-message-protocol/src/test/java/com/snowalker/AppTest.java: -------------------------------------------------------------------------------- 1 | package com.snowalker; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | 5 | import org.junit.Test; 6 | 7 | /** 8 | * Unit test for simple App. 9 | */ 10 | public class AppTest 11 | { 12 | /** 13 | * Rigorous Test :-) 14 | */ 15 | @Test 16 | public void shouldAnswerWithTrue() 17 | { 18 | assertTrue( true ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /order-charge-gateway-notify/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | /target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | .sts4-cache 13 | 14 | ### IntelliJ IDEA ### 15 | .idea 16 | *.iws 17 | *.iml 18 | *.ipr 19 | 20 | ### NetBeans ### 21 | /nbproject/private/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ 26 | /build/ 27 | 28 | ### VS Code ### 29 | .vscode/ 30 | -------------------------------------------------------------------------------- /order-charge-gateway-server/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | /target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | .sts4-cache 13 | 14 | ### IntelliJ IDEA ### 15 | .idea 16 | *.iws 17 | *.iml 18 | *.ipr 19 | 20 | ### NetBeans ### 21 | /nbproject/private/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ 26 | /build/ 27 | 28 | ### VS Code ### 29 | .vscode/ 30 | -------------------------------------------------------------------------------- /order-charge-gateway-merchant/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | /target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | .sts4-cache 13 | 14 | ### IntelliJ IDEA ### 15 | .idea 16 | *.iws 17 | *.iml 18 | *.ipr 19 | 20 | ### NetBeans ### 21 | /nbproject/private/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ 26 | /build/ 27 | 28 | ### VS Code ### 29 | .vscode/ 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Java template 3 | # Compiled class file 4 | *.class 5 | 6 | # Log file 7 | *.log 8 | 9 | # BlueJ files 10 | *.ctxt 11 | 12 | # Mobile Tools for Java (J2ME) 13 | .mtj.tmp/ 14 | 15 | # Package Files # 16 | *.jar 17 | *.war 18 | *.nar 19 | *.ear 20 | *.zip 21 | *.tar.gz 22 | *.rar 23 | .idea 24 | 25 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 26 | hs_err_pid* 27 | 28 | -------------------------------------------------------------------------------- /order-charge-sdk/.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Java template 3 | # Compiled class file 4 | *.class 5 | 6 | # Log file 7 | *.log 8 | 9 | # BlueJ files 10 | *.ctxt 11 | 12 | # Mobile Tools for Java (J2ME) 13 | .mtj.tmp/ 14 | 15 | # Package Files # 16 | *.jar 17 | *.war 18 | *.nar 19 | *.ear 20 | *.zip 21 | *.tar.gz 22 | *.rar 23 | .idea 24 | 25 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 26 | hs_err_pid* 27 | 28 | -------------------------------------------------------------------------------- /order-charge-message-protocol/.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Java template 3 | # Compiled class file 4 | *.class 5 | 6 | # Log file 7 | *.log 8 | 9 | # BlueJ files 10 | *.ctxt 11 | 12 | # Mobile Tools for Java (J2ME) 13 | .mtj.tmp/ 14 | 15 | # Package Files # 16 | *.jar 17 | *.war 18 | *.nar 19 | *.ear 20 | *.zip 21 | *.tar.gz 22 | *.rar 23 | .idea 24 | 25 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 26 | hs_err_pid* 27 | 28 | -------------------------------------------------------------------------------- /order-charge-gateway-notify/src/test/java/com/snowalker/notify/OrderChargeNotifyApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.notify; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class OrderChargeNotifyApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /order-charge-gateway-notify/src/main/resources/mybatis-config.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /order-charge-gateway-server/src/main/resources/mybatis-config.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /order-charge-gateway-merchant/src/main/resources/mybatis-config.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /order-charge-gateway-merchant/src/test/java/com/snowalker/gateway/merchant/OrderChargeGatewayMerchantApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.gateway.merchant; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class OrderChargeGatewayMerchantApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /order-charge-gateway-server/src/main/java/com/snowalker/order/common/dao/MerchantInfoMapper.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.order.common.dao; 2 | 3 | import com.snowalker.order.common.dao.dataobject.MerchantInfoDO; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author snowalker 9 | * @version 1.0 10 | * @date 2019/6/11 14:06 11 | * @className MerchantInfoMapper 12 | * @desc 商户基本信息Mapper 13 | */ 14 | public interface MerchantInfoMapper { 15 | 16 | /** 17 | * 查询商户信息列表 18 | * @return 19 | */ 20 | List queryMerchantList(); 21 | } 22 | -------------------------------------------------------------------------------- /order-charge-gateway-server/src/main/java/com/snowalker/order/common/service/MerchantInfoService.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.order.common.service; 2 | 3 | import com.snowalker.order.common.dao.dataobject.MerchantInfoDO; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author snowalker 9 | * @version 1.0 10 | * @date 2019/6/11 14:13 11 | * @className MerchantInfoService 12 | * @desc 商户服务接口 13 | */ 14 | public interface MerchantInfoService { 15 | 16 | /** 17 | * 查询商户列表 18 | * @return 19 | */ 20 | List queryMerchantList(); 21 | } 22 | -------------------------------------------------------------------------------- /order-charge-gateway-notify/src/main/java/com/snowalker/notify/NotifyApplication.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.notify; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.context.annotation.ImportResource; 6 | 7 | /** 8 | * 通知服务启动器 9 | * @author snowalker 10 | */ 11 | @SpringBootApplication 12 | @ImportResource({"classpath*:META-INF/applicationContext-bean.xml"}) 13 | public class NotifyApplication { 14 | 15 | public static void main(String[] args) { 16 | SpringApplication.run(NotifyApplication.class, args); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /order-charge-gateway-server/src/main/java/com/snowalker/order/ServerApplication.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.order; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.context.annotation.ImportResource; 6 | 7 | /** 8 | * 上游收单网关启动器 9 | * @author snowalker 10 | */ 11 | @SpringBootApplication 12 | @ImportResource({"classpath*:META-INF/applicationContext-bean.xml"}) 13 | public class ServerApplication { 14 | 15 | public static void main(String[] args) { 16 | SpringApplication.run(ServerApplication.class, args); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /order-charge-gateway-notify/src/main/java/com/snowalker/notify/common/constant/NotifyConstant.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.notify.common.constant; 2 | 3 | /** 4 | * @author snowalker 5 | * @version 1.0 6 | * @date 2019/6/11 12:54 7 | * @className NotifyConstant 8 | * @desc 通知常量 9 | */ 10 | public abstract class NotifyConstant { 11 | 12 | private NotifyConstant() {} 13 | 14 | /**通知结果返回*/ 15 | public static final String NOTIFY_RETURN_SUCC = "T"; 16 | public static final String NOTIFY_RETURN_FAIL = "F"; 17 | /**订单结果通知状态*/ 18 | public static final String NOTIFY_SUCCESS = "SUCCESS"; 19 | public static final String NOTIFY_FAIL = "FAIL"; 20 | } 21 | -------------------------------------------------------------------------------- /order-charge-gateway-merchant/src/main/java/com/snowalker/gateway/merchant/MerchantApplication.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.gateway.merchant; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.context.annotation.ImportResource; 6 | 7 | /** 8 | * 渠道收单网关启动器 9 | * @author snowalker 10 | */ 11 | @SpringBootApplication 12 | @ImportResource({"classpath*:META-INF/applicationContext-bean.xml"}) 13 | public class MerchantApplication { 14 | 15 | public static void main(String[] args) { 16 | SpringApplication.run(MerchantApplication.class, args); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /order-charge-gateway-merchant/src/main/java/com/snowalker/gateway/merchant/order/constant/NotifyConstant.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.gateway.merchant.order.constant; 2 | 3 | /** 4 | * @author snowalker 5 | * @version 1.0 6 | * @date 2019/6/11 12:54 7 | * @className NotifyConstant 8 | * @desc 9 | */ 10 | public abstract class NotifyConstant { 11 | 12 | private NotifyConstant() {} 13 | 14 | /**通知结果返回*/ 15 | public static final String NOTIFY_RETURN_SUCC = "T"; 16 | public static final String NOTIFY_RETURN_FAIL = "F"; 17 | /**订单结果通知状态*/ 18 | public static final String NOTIFY_SUCCESS = "SUCCESS"; 19 | public static final String NOTIFY_FAIL = "FAIL"; 20 | } 21 | -------------------------------------------------------------------------------- /order-charge-gateway-server/src/main/resources/mapper/MerchantInfoMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 16 | 17 | -------------------------------------------------------------------------------- /order-charge-sdk/src/main/java/com/snowalker/order/charge/constant/ResponseCodeEnum.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.order.charge.constant; 2 | 3 | /** 4 | * @author snowalker 5 | * @version 1.0 6 | * @date 2019/6/10 11:49 7 | * @className OrderStatusEnum 8 | * @desc 同步下单及异步回调状态码枚举 9 | */ 10 | public enum ResponseCodeEnum { 11 | 12 | SUCCESS("10000", "成功"), 13 | FAIL("20000", "失败"), 14 | UNKNOWN("40004", "未知"); 15 | 16 | private String code; 17 | private String desc; 18 | 19 | ResponseCodeEnum(String code, String desc) { 20 | this.code = code; 21 | this.desc = desc; 22 | } 23 | 24 | public String getCode() { 25 | return code; 26 | } 27 | 28 | public String getDesc() { 29 | return desc; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /order-charge-gateway-notify/src/main/resources/META-INF/applicationContext-mapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /order-charge-gateway-server/src/main/resources/META-INF/applicationContext-mapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /order-charge-gateway-merchant/src/main/resources/META-INF/applicationContext-mapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /order-charge-gateway-merchant/src/main/java/com/snowalker/gateway/merchant/order/exception/ApiException.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.gateway.merchant.order.exception; 2 | 3 | import com.snowalker.gateway.merchant.order.dto.CodeMsg; 4 | 5 | /** 6 | * @author snowalker 7 | * @version 1.0 8 | * @date 2019/6/10 10:15 9 | * @className ApiException 10 | * @desc 自定义接口异常 11 | */ 12 | public class ApiException extends RuntimeException { 13 | 14 | private static final long serialVersionUID = 1L; 15 | 16 | private CodeMsg codeMsg; 17 | 18 | public ApiException(CodeMsg codeMsg) { 19 | super(codeMsg.toString()); 20 | this.codeMsg = codeMsg; 21 | } 22 | 23 | public ApiException() {} 24 | 25 | public CodeMsg getCodeMsg() { 26 | return codeMsg; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /order-charge-gateway-merchant/src/main/java/com/snowalker/gateway/merchant/order/constant/OrderStatusEnum.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.gateway.merchant.order.constant; 2 | 3 | /** 4 | * @author snowalker 5 | * @version 1.0 6 | * @date 2019/6/10 11:49 7 | * @className OrderStatusEnum 8 | * @desc 订单状态枚举 9 | */ 10 | public enum OrderStatusEnum { 11 | 12 | SUCCESS(0, "成功"), 13 | INIT(1, "初始化"), 14 | DEALING(2, "处理中"), 15 | FAIL(3, "失败"); 16 | 17 | private Integer code; 18 | private String desc; 19 | 20 | OrderStatusEnum(Integer code, String desc) { 21 | this.code = code; 22 | this.desc = desc; 23 | } 24 | 25 | public Integer getCode() { 26 | return code; 27 | } 28 | 29 | public String getDesc() { 30 | return desc; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /order-charge-gateway-server/src/main/java/com/snowalker/order/common/util/LogExceptionWapper.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.order.common.util; 2 | 3 | import java.io.PrintWriter; 4 | import java.io.StringWriter; 5 | import java.io.Writer; 6 | 7 | /** 8 | * @author snowalker 9 | * @date 2018/9/20 10 | * @desc 日志工具 11 | */ 12 | public class LogExceptionWapper { 13 | 14 | private LogExceptionWapper() {} 15 | 16 | /** 17 | * 获取完整栈轨迹 18 | * 19 | * @param aThrowable 20 | * @return 21 | */ 22 | public static String getStackTrace(Throwable aThrowable) { 23 | final Writer result = new StringWriter(); 24 | final PrintWriter printWriter = new PrintWriter(result); 25 | aThrowable.printStackTrace(printWriter); 26 | return result.toString(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /order-charge-gateway-notify/src/main/java/com/snowalker/notify/common/util/LogExceptionWapper.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.notify.common.util; 2 | 3 | import java.io.PrintWriter; 4 | import java.io.StringWriter; 5 | import java.io.Writer; 6 | 7 | /** 8 | * @author snowalker 9 | * @date 2018/9/20 10 | * @desc 日志工具 11 | */ 12 | public class LogExceptionWapper { 13 | 14 | private LogExceptionWapper() {} 15 | 16 | /** 17 | * 获取完整栈轨迹 18 | * 19 | * @param aThrowable 20 | * @return 21 | */ 22 | public static String getStackTrace(Throwable aThrowable) { 23 | final Writer result = new StringWriter(); 24 | final PrintWriter printWriter = new PrintWriter(result); 25 | aThrowable.printStackTrace(printWriter); 26 | return result.toString(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /order-charge-gateway-merchant/src/main/java/com/snowalker/gateway/merchant/order/util/LogExceptionWapper.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.gateway.merchant.order.util; 2 | 3 | import java.io.PrintWriter; 4 | import java.io.StringWriter; 5 | import java.io.Writer; 6 | 7 | /** 8 | * @author snowalker 9 | * @date 2018/9/20 10 | * @desc 日志工具 11 | */ 12 | public class LogExceptionWapper { 13 | 14 | private LogExceptionWapper() {} 15 | 16 | /** 17 | * 获取完整栈轨迹 18 | * 19 | * @param aThrowable 20 | * @return 21 | */ 22 | public static String getStackTrace(Throwable aThrowable) { 23 | final Writer result = new StringWriter(); 24 | final PrintWriter printWriter = new PrintWriter(result); 25 | aThrowable.printStackTrace(printWriter); 26 | return result.toString(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /order-charge-gateway-notify/src/main/resources/META-INF/applicationContext-bean.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /order-charge-gateway-server/src/main/resources/META-INF/applicationContext-bean.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /order-charge-gateway-merchant/src/main/resources/META-INF/applicationContext-bean.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /order-charge-gateway-merchant/src/main/java/com/snowalker/gateway/merchant/order/mapper/OrderInfoMapper.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.gateway.merchant.order.mapper; 2 | 3 | import com.snowalker.gateway.merchant.order.dataobject.ChargeOrderDO; 4 | import com.snowalker.gateway.merchant.order.dto.ChargeOrderDto; 5 | 6 | /** 7 | * @author snowalker 8 | * @version 1.0 9 | * @date 2019/6/10 13:46 10 | * @className OrderMapper 11 | * @desc 订单本地Mapper 12 | */ 13 | public interface OrderInfoMapper { 14 | 15 | /** 16 | * 订单入库 17 | * @param chargeOrderDto 18 | * @return 19 | */ 20 | int insertOrderInfo(ChargeOrderDto chargeOrderDto); 21 | 22 | /** 23 | * 订单状态更新 24 | * @param chargeOrderDto 25 | * @return 26 | */ 27 | int updateOrderStatus(ChargeOrderDto chargeOrderDto); 28 | 29 | /** 30 | * 查询订单信息 31 | * @param chargeOrderDto 32 | * @return 33 | */ 34 | ChargeOrderDO queryOrderByOrderId(ChargeOrderDto chargeOrderDto); 35 | } 36 | -------------------------------------------------------------------------------- /order-charge-message-protocol/src/main/java/com/snowalker/order/charge/message/constant/UpdateEventTypeConst.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.order.charge.message.constant; 2 | 3 | /** 4 | * @author snowalker 5 | * @version 1.0 6 | * @date 2019/6/11 14:45 7 | * @className MessageProtocolConst 8 | * @desc 消息协议常量 9 | */ 10 | public enum UpdateEventTypeConst { 11 | 12 | /**支付状态更新事件*/ 13 | EVENT_UPDATE_PAY_STATUS("EVENT_UPDATE_PAY_STATUS", "EVENT_UPDATE_PAY_STATUS"), 14 | /**通知状态及订单状态更新事件*/ 15 | EVENT_UPDATE_NOTIFY_OD_STATUS("EVENT_UPDATE_NOTIFY_OD_STATUS", "EVENT_UPDATE_NOTIFY_OD_STATUS") 16 | ; 17 | 18 | /**更新事件*/ 19 | private String eventType; 20 | /**事件描述*/ 21 | private String eventDesc; 22 | 23 | UpdateEventTypeConst(String eventType, String eventDesc) { 24 | this.eventType = eventType; 25 | this.eventDesc = eventDesc; 26 | } 27 | 28 | public String getEventType() { 29 | return eventType; 30 | } 31 | 32 | public String getEventDesc() { 33 | return eventDesc; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /order-charge-gateway-server/src/test/java/com/snowalker/order/OrderChargeGatewayApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.order; 2 | 3 | import com.snowalker.order.common.dao.dataobject.OrderInfoDO; 4 | import com.snowalker.order.common.dao.dataobject.OrderInfoDobj; 5 | import com.snowalker.order.common.service.OrderChargeService; 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.boot.test.context.SpringBootTest; 10 | import org.springframework.test.context.junit4.SpringRunner; 11 | 12 | @RunWith(SpringRunner.class) 13 | @SpringBootTest 14 | public class OrderChargeGatewayApplicationTests { 15 | 16 | @Autowired 17 | OrderChargeService orderChargeService; 18 | 19 | @Test 20 | public void contextLoads() { 21 | OrderInfoDO orderInfoDO = new OrderInfoDO().setOrderId("00001"); 22 | OrderInfoDobj orderInfoDobj = orderChargeService.queryOrderInfo(orderInfoDO); 23 | System.out.println(orderInfoDobj.toString()); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /order-charge-gateway-server/src/main/java/com/snowalker/order/common/service/impl/MerchantInfoServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.order.common.service.impl; 2 | 3 | import com.snowalker.order.common.dao.MerchantInfoMapper; 4 | import com.snowalker.order.common.dao.dataobject.MerchantInfoDO; 5 | import com.snowalker.order.common.service.MerchantInfoService; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Service; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * @author snowalker 13 | * @version 1.0 14 | * @date 2019/6/11 14:21 15 | * @className MerchantInfoServiceImpl 16 | * @desc 商户信息服务实现 17 | */ 18 | @Service(value = "merchantInfoService") 19 | public class MerchantInfoServiceImpl implements MerchantInfoService { 20 | 21 | @Autowired 22 | MerchantInfoMapper merchantInfoMapper; 23 | 24 | /** 25 | * 商户列表查询 26 | * @return 27 | */ 28 | @Override 29 | public List queryMerchantList() { 30 | return merchantInfoMapper.queryMerchantList(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /order-charge-gateway-notify/src/main/java/com/snowalker/notify/common/service/WalletService.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.notify.common.service; 2 | 3 | import com.snowalker.notify.common.dao.dataobject.ChargeRecordEntity; 4 | import com.snowalker.notify.common.dao.dataobject.WalletEntity; 5 | 6 | /** 7 | * @author snowalker 8 | * @version 1.0 9 | * @date 2019/6/12 9:15 10 | * @className WalletService 11 | * @desc 钱包相关接口定义 12 | */ 13 | public interface WalletService { 14 | 15 | /** 16 | * 新增用户钱包 17 | * @param walletEntity 18 | */ 19 | void insertWallet(WalletEntity walletEntity); 20 | 21 | /** 22 | * 修改用户钱包数据 23 | * @param walletEntity 24 | * @return 25 | */ 26 | boolean updateWallet(WalletEntity walletEntity, String orderId); 27 | 28 | /** 29 | * 查询扣款流水 30 | * @param orderId 31 | * @return 32 | */ 33 | ChargeRecordEntity queryChargeRecordByOrderId(String orderId); 34 | 35 | /** 36 | * 查询钱包信息 37 | * @param purseId 38 | * @return 39 | */ 40 | WalletEntity queryWalletInfoByPurseId(String purseId); 41 | } 42 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | com.snowalker 8 | order-charge-notify 9 | 1.0-SNAPSHOT 10 | pom 11 | 12 | order-charge-notify 13 | 14 | 15 | 1.8 16 | 1.8 17 | UTF-8 18 | UTF-8 19 | 20 | 21 | 22 | order-charge-gateway-server 23 | order-charge-gateway-notify 24 | order-charge-gateway-merchant 25 | order-charge-sdk 26 | order-charge-message-protocol 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /order-charge-gateway-server/src/main/java/com/snowalker/order/common/dao/OrderChargeMapper.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.order.common.dao; 2 | 3 | import com.snowalker.order.common.dao.dataobject.OrderInfoDO; 4 | import com.snowalker.order.common.dao.dataobject.OrderInfoDobj; 5 | 6 | /** 7 | * @author snowalker 8 | * @version 1.0 9 | * @date 2019/6/11 20:01 10 | * @className OrderChargeMapper 11 | * @desc 订单交易Mapper 12 | */ 13 | public interface OrderChargeMapper { 14 | 15 | /** 16 | * 订单入库 17 | * @param orderInfoDO 18 | * @return 19 | */ 20 | int insertOrder(OrderInfoDO orderInfoDO); 21 | 22 | /** 23 | * 订单查询 24 | * @param orderInfoDO 25 | * @return 26 | */ 27 | OrderInfoDobj queryOrderInfoByOrderId(OrderInfoDO orderInfoDO); 28 | 29 | /** 30 | * 订单状态修改为处理中 31 | * @param orderInfoDO 32 | * @return 33 | */ 34 | int updateOrderDealing(OrderInfoDO orderInfoDO); 35 | 36 | /** 37 | * 支付状态修改为成功 38 | * @param orderInfoDO 39 | * @return 40 | */ 41 | int updateOrderPayStatusSucc(OrderInfoDO orderInfoDO); 42 | 43 | /** 44 | * 通知状态改成功 45 | * @param orderInfoDO 46 | * @return 47 | */ 48 | int updateOrderNotifyStatusSucc(OrderInfoDO orderInfoDO); 49 | } 50 | -------------------------------------------------------------------------------- /order-charge-message-protocol/src/main/java/com/snowalker/order/charge/message/protocol/BaseMsg.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.order.charge.message.protocol; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | /** 7 | * @author snowalker 8 | * @date 2018/9/16 9 | * @desc 基础协议类 10 | */ 11 | public abstract class BaseMsg { 12 | 13 | public Logger LOGGER = LoggerFactory.getLogger(this.getClass()); 14 | 15 | /**版本号,默认1.0*/ 16 | private String version = "1.0"; 17 | /**主题名*/ 18 | private String topicName; 19 | 20 | public abstract String encode(); 21 | 22 | public abstract void decode(String msg); 23 | 24 | public String getVersion() { 25 | return version; 26 | } 27 | 28 | public void setVersion(String version) { 29 | this.version = version; 30 | } 31 | 32 | public String getTopicName() { 33 | return topicName; 34 | } 35 | 36 | public void setTopicName(String topicName) { 37 | this.topicName = topicName; 38 | } 39 | 40 | @Override 41 | public String toString() { 42 | return "BaseMsg{" + 43 | "version='" + version + '\'' + 44 | ", topicName='" + topicName + '\'' + 45 | '}'; 46 | } 47 | } 48 | 49 | 50 | -------------------------------------------------------------------------------- /order-charge-gateway-notify/src/main/java/com/snowalker/notify/common/dao/WalletMapper.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.notify.common.dao; 2 | 3 | import com.snowalker.notify.common.dao.dataobject.ChargeRecordEntity; 4 | import com.snowalker.notify.common.dao.dataobject.OrderEntity; 5 | import com.snowalker.notify.common.dao.dataobject.WalletEntity; 6 | import java.util.Map; 7 | 8 | /** 9 | * @author snowalker 10 | * @version 1.0 11 | * @date 2019/2/18 9:30 12 | * @className WalletMapper 13 | * @desc 钱包Mapper 14 | */ 15 | public interface WalletMapper { 16 | 17 | /** 18 | * 新增用户钱包 19 | * @param walletEntity 20 | */ 21 | void insertWallet(WalletEntity walletEntity); 22 | 23 | /** 24 | * 更新用户钱包 25 | * @param params 26 | * @return 27 | */ 28 | int updateWallet(Map params); 29 | 30 | /** 31 | * 插入扣款流水 32 | */ 33 | void insertChargeRecord(Map params); 34 | 35 | /** 36 | * 查询扣款流水 37 | * @param orderEntity 38 | * @return 39 | */ 40 | ChargeRecordEntity queryChargeRecordByOrderId(OrderEntity orderEntity); 41 | 42 | /** 43 | * 查询钱包信息 44 | * @param params 45 | * @return 46 | */ 47 | WalletEntity queryWalletInfoByPurseId(Map params); 48 | } 49 | -------------------------------------------------------------------------------- /order-charge-sdk/src/test/java/com/snowalker/AppTest.java: -------------------------------------------------------------------------------- 1 | package com.snowalker; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | 5 | import com.alibaba.fastjson.JSON; 6 | import com.snowalker.order.charge.request.ChargeOrderRequest; 7 | import org.junit.Test; 8 | 9 | import java.text.SimpleDateFormat; 10 | import java.util.Date; 11 | import java.util.UUID; 12 | 13 | /** 14 | * Unit test for simple App. 15 | */ 16 | public class AppTest { 17 | /** 18 | * Rigorous Test :-) 19 | */ 20 | @Test 21 | public void shouldAnswerWithTrue() { 22 | assertTrue( true ); 23 | } 24 | 25 | @Test 26 | public void generateChargeOrderRequestJson() { 27 | ChargeOrderRequest chargeOrderRequest = new ChargeOrderRequest(); 28 | chargeOrderRequest.setChannelOrderId(UUID.randomUUID().toString()) 29 | .setChargePrice("123.14") 30 | .setMerchantName("SNOWALKER") 31 | .setPurseId("NO00000001") 32 | .setUserPhoneNum("13912312343") 33 | .setTimestamp(new SimpleDateFormat("yyyyMMddHHmmss").format(new Date(System.currentTimeMillis()))); 34 | chargeOrderRequest.setSign(chargeOrderRequest.sign("asdaseasdaddada")); 35 | String json = JSON.toJSONString(chargeOrderRequest); 36 | System.out.println(json); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /order-charge-gateway-notify/src/main/java/com/snowalker/notify/common/config/RestTemplateConfig.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.notify.common.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.http.client.ClientHttpRequestFactory; 6 | import org.springframework.http.client.SimpleClientHttpRequestFactory; 7 | import org.springframework.web.client.RestTemplate; 8 | 9 | /** 10 | * @author snowalker 11 | * @version 1.0 12 | * @date 2019/6/10 13:37 13 | * @className RestTemplateConfig 14 | * @desc RestTemplate配置 15 | */ 16 | @Configuration 17 | public class RestTemplateConfig { 18 | 19 | @Bean 20 | public RestTemplate restTemplate(ClientHttpRequestFactory factory){ 21 | return new RestTemplate(factory); 22 | } 23 | 24 | @Bean 25 | public RestTemplate restTemplateNew(ClientHttpRequestFactory factory){ 26 | return new RestTemplate(factory); 27 | } 28 | 29 | @Bean 30 | public ClientHttpRequestFactory simpleClientHttpRequestFactory(){ 31 | SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); 32 | /**读超时单位为ms*/ 33 | factory.setReadTimeout(10000); 34 | /**连接超时单位为ms*/ 35 | factory.setConnectTimeout(10000); 36 | return factory; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /order-charge-gateway-merchant/src/main/java/com/snowalker/gateway/merchant/order/config/RestTemplateConfig.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.gateway.merchant.order.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.http.client.ClientHttpRequestFactory; 6 | import org.springframework.http.client.SimpleClientHttpRequestFactory; 7 | import org.springframework.web.client.RestTemplate; 8 | 9 | /** 10 | * @author snowalker 11 | * @version 1.0 12 | * @date 2019/6/10 13:37 13 | * @className RestTemplateConfig 14 | * @desc RestTemplate配置 15 | */ 16 | @Configuration 17 | public class RestTemplateConfig { 18 | 19 | @Bean 20 | public RestTemplate restTemplate(ClientHttpRequestFactory factory){ 21 | return new RestTemplate(factory); 22 | } 23 | 24 | @Bean 25 | public RestTemplate restTemplateNew(ClientHttpRequestFactory factory){ 26 | return new RestTemplate(factory); 27 | } 28 | 29 | @Bean 30 | public ClientHttpRequestFactory simpleClientHttpRequestFactory(){ 31 | SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); 32 | /**读超时单位为ms*/ 33 | factory.setReadTimeout(10000); 34 | /**连接超时单位为ms*/ 35 | factory.setConnectTimeout(10000); 36 | return factory; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /order-charge-gateway-server/src/main/java/com/snowalker/order/common/config/ProductVO.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.order.common.config; 2 | 3 | /** 4 | * @author snowalker 5 | * @version 1.0 6 | * @date 2019/6/10 9:45 7 | * @className ProductBean 8 | * @desc 商品配置领域 9 | */ 10 | public class ProductVO { 11 | 12 | /**商品id*/ 13 | private String productId; 14 | /**商品名*/ 15 | private String productName; 16 | /**商品库存*/ 17 | private Integer productStock; 18 | 19 | public String getProductId() { 20 | return productId; 21 | } 22 | 23 | public ProductVO setProductId(String productId) { 24 | this.productId = productId; 25 | return this; 26 | } 27 | 28 | public String getProductName() { 29 | return productName; 30 | } 31 | 32 | public ProductVO setProductName(String productName) { 33 | this.productName = productName; 34 | return this; 35 | } 36 | 37 | public Integer getProductStock() { 38 | return productStock; 39 | } 40 | 41 | public ProductVO setProductStock(Integer productStock) { 42 | this.productStock = productStock; 43 | return this; 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | return "ProductVO{" + 49 | "productId='" + productId + '\'' + 50 | ", productName='" + productName + '\'' + 51 | ", productStock=" + productStock + 52 | '}'; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /order-charge-gateway-server/src/main/java/com/snowalker/order/common/config/ProductConfig.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.order.common.config; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | import javax.annotation.PostConstruct; 9 | import java.util.Map; 10 | import java.util.concurrent.ConcurrentHashMap; 11 | 12 | /** 13 | * @author snowalker 14 | * @version 1.0 15 | * @date 2019/6/10 9:41 16 | * @className GoodsConfig 17 | * @desc 商品配置 18 | * prod_id:BJ_SY_JJC_01 19 | * prod_name:南航北京-三亚经济舱 20 | * prod_stock:999 21 | */ 22 | @Configuration 23 | public class ProductConfig { 24 | 25 | private static final Logger LOGGER = LoggerFactory.getLogger(ProductConfig.class); 26 | 27 | private static final Map PRODUCTS_MAP = new ConcurrentHashMap<>(16); 28 | 29 | @PostConstruct 30 | public void init() { 31 | ProductVO productVO = new ProductVO(); 32 | String productId = "BJ_SY_JJC_01"; 33 | productVO.setProductId(productId) 34 | .setProductName("南航北京-三亚经济舱") 35 | .setProductStock(200); 36 | PRODUCTS_MAP.put(productId, productVO); 37 | LOGGER.info("平台商品配置加载完毕,productConfig={}", JSON.toJSONString(PRODUCTS_MAP)); 38 | } 39 | 40 | public static Map getProductsMap() { 41 | return PRODUCTS_MAP; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Readme 2 | 3 | > 场景为:示例项目以客户端模拟下单成功,下单成功后服务端回调客户端修改订单状态场景进行讲解。 4 | 即:模拟下单并处理异步通知 5 | 6 | ## 主要技术 7 | 8 | 消息队列(RocketMQ): 作用,长流程异步化,提升吞吐量,削峰填谷 9 | |-普通消息的发布及订阅 10 | |-事务消息的发布及订阅 11 | 线程池 12 | |-事务消息回查 13 | SpringBoot 14 | |-配置资源预加载 15 | RestTemplate 16 | |-application/json格式数据的发送 17 | |-application/x-www-form-urlencoded格式数据的发送 18 | 19 | ## 思路 20 | 21 | 业务流程图如下 22 | 23 | ![项目流程图](demo.png) 24 | 25 | 26 | ## 模块描述 27 | 28 | | 模块 | 说明 | 29 | | :------ | :------ | 30 | | order-charge-gateway-merchant | 商户收单网关,2C业务,对用户提供下单接口;提供对下单平台的充值回调接口 | 31 | | order-charge-gateway-server | 核心收单平台,2B业务,进行业务正式下单,下单完成后投递通知消息到MQ | 32 | | order-charge-gateway-notify | 核心通知平台,消费通知消息发送通知到order-charge-gateway-merchant的回调接口 | 33 | | order-charge-message-protocol | 消息协议封装 | 34 | | order-charge-sdk | HTTP下单接口sdk及通知sdk | 35 | | script | 数据库初始化脚本 | 36 | 37 | ## 业务描述 38 | 1. 用户访问第三方售票网关,进行购票操作。用户发起下单操作,执行支付转账操作 39 | 40 | 扣减用户账户,增加商户账户金额 41 | 42 | 2. 转账完成之后,商户发起下单操作,售票平台进行下单操作及扣款操作,(此处为讲解重点-- **事务消息,分布式事务场景**)返回订单提交成功 43 | 3. 售票平台下单扣款完成后,认为售票成功,则返回结果通知给第三方售票网关(此处为讲解重点-- **普通消息,异步通知**) 44 | 4. 第三方售票网关自行给用户通知即可,未实现 45 | 46 | > 注意:商品信息直接在初始化的时候记载到第三方售票网关和售票平台的缓存中即可。 47 | 48 | 49 | ## 说明 50 | 51 | 下单与扣款通过事务消息保证一致性,保证成功率 52 | 53 | 通知过程通过MQ进行异步解耦,使用普通消息即可,因为通知过程本身是为了最大努力送达,属于最终一致性的范畴,不要求数据的强一致性。 54 | 55 | 如果通知达到上限阈值,则停止通知,等待商户侧发起主动查询即可。通过通知回调+主动查询,能够在跨网络的交易场景下,实现端与端之间的订单状态的最终一致。 56 | 57 | 在平台内部,跨服务之间的分布式事务,通过RMQ的事务消息得到保证,事务消息原理可简单介绍。 58 | 59 | -------------------------------------------------------------------------------- /order-charge-gateway-merchant/src/main/java/com/snowalker/gateway/merchant/order/handler/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.gateway.merchant.order.handler; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.snowalker.gateway.merchant.order.dto.CodeMsg; 5 | import com.snowalker.gateway.merchant.order.dto.Result; 6 | import com.snowalker.gateway.merchant.order.exception.ApiException; 7 | import org.springframework.validation.BindException; 8 | import org.springframework.validation.ObjectError; 9 | import org.springframework.web.bind.annotation.ControllerAdvice; 10 | import org.springframework.web.bind.annotation.ExceptionHandler; 11 | import org.springframework.web.bind.annotation.ResponseBody; 12 | 13 | import java.util.List; 14 | 15 | /** 16 | * @author snowalker 17 | * 全局异常解析 18 | */ 19 | @ControllerAdvice 20 | @ResponseBody 21 | public class GlobalExceptionHandler { 22 | 23 | @ExceptionHandler(value = Exception.class) 24 | public Result exceptionHandler(Exception e){ 25 | if (e instanceof ApiException) { 26 | ApiException apiException = (ApiException) e; 27 | return Result.error(apiException.getCodeMsg()); 28 | } else if(e instanceof BindException) { 29 | BindException ex = (BindException)e; 30 | List errors = ex.getAllErrors(); 31 | ObjectError error = errors.get(0); 32 | String msg = error.getDefaultMessage(); 33 | return Result.error(CodeMsg.BIND_ERROR.fillArgs(msg)); 34 | } else { 35 | return Result.error(CodeMsg.SERVER_ERROR); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /order-charge-gateway-server/src/main/java/com/snowalker/order/common/dto/Result.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.order.common.dto; 2 | 3 | /** 4 | * 返回体包装 5 | * @author snowalker 6 | * @param 7 | */ 8 | public class Result { 9 | 10 | private String code; 11 | private String msg; 12 | private T data; 13 | 14 | /** 15 | * 失败时候的调用 16 | */ 17 | public static Result error(CodeMsg codeMsg) { 18 | return new Result(codeMsg); 19 | } 20 | 21 | public Result(T data) { 22 | this.data = data; 23 | } 24 | 25 | public Result(String code, String msg) { 26 | this.code = code; 27 | this.msg = msg; 28 | } 29 | 30 | public Result(String code, String msg, T t) { 31 | this.code = code; 32 | this.msg = msg; 33 | this.data = t; 34 | } 35 | 36 | private Result(CodeMsg codeMsg) { 37 | if (codeMsg != null) { 38 | this.code = codeMsg.getCode(); 39 | this.msg = codeMsg.getMsg(); 40 | this.data = null; 41 | } 42 | } 43 | 44 | public String getCode() { 45 | return code; 46 | } 47 | 48 | public Result setCode(String code) { 49 | this.code = code; 50 | return this; 51 | } 52 | 53 | public String getMsg() { 54 | return msg; 55 | } 56 | 57 | public Result setMsg(String msg) { 58 | this.msg = msg; 59 | return this; 60 | } 61 | 62 | public T getData() { 63 | return data; 64 | } 65 | 66 | public Result setData(T data) { 67 | this.data = data; 68 | return this; 69 | } 70 | } -------------------------------------------------------------------------------- /order-charge-gateway-merchant/src/main/java/com/snowalker/gateway/merchant/order/dto/Result.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.gateway.merchant.order.dto; 2 | 3 | /** 4 | * 返回体包装 5 | * @author snowalker 6 | * @param 7 | */ 8 | public class Result { 9 | 10 | private String code; 11 | private String msg; 12 | private T data; 13 | 14 | /** 15 | * 失败时候的调用 16 | */ 17 | public static Result error(CodeMsg codeMsg) { 18 | return new Result(codeMsg); 19 | } 20 | 21 | public Result(T data) { 22 | this.data = data; 23 | } 24 | 25 | public Result(String code, String msg) { 26 | this.code = code; 27 | this.msg = msg; 28 | } 29 | 30 | public Result(String code, String msg, T t) { 31 | this.code = code; 32 | this.msg = msg; 33 | this.data = t; 34 | } 35 | 36 | private Result(CodeMsg codeMsg) { 37 | if (codeMsg != null) { 38 | this.code = codeMsg.getCode(); 39 | this.msg = codeMsg.getMsg(); 40 | this.data = null; 41 | } 42 | } 43 | 44 | public String getCode() { 45 | return code; 46 | } 47 | 48 | public Result setCode(String code) { 49 | this.code = code; 50 | return this; 51 | } 52 | 53 | public String getMsg() { 54 | return msg; 55 | } 56 | 57 | public Result setMsg(String msg) { 58 | this.msg = msg; 59 | return this; 60 | } 61 | 62 | public T getData() { 63 | return data; 64 | } 65 | 66 | public Result setData(T data) { 67 | this.data = data; 68 | return this; 69 | } 70 | } -------------------------------------------------------------------------------- /order-charge-gateway-merchant/src/main/java/com/snowalker/gateway/merchant/order/service/OrderService.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.gateway.merchant.order.service; 2 | 3 | import com.snowalker.gateway.merchant.order.dataobject.ChargeOrderDO; 4 | import com.snowalker.gateway.merchant.order.dto.ChargeOrderDto; 5 | import com.snowalker.gateway.merchant.order.dto.Result; 6 | 7 | import java.sql.SQLException; 8 | 9 | /** 10 | * @author snowalker 11 | * @version 1.0 12 | * @date 2019/6/10 11:48 13 | * @className OrderService 14 | * @desc 订单交易 15 | */ 16 | public interface OrderService { 17 | 18 | /** 19 | * 下单 20 | * @param phoneNum 21 | * @param chargeMoney 22 | * @param prodId 23 | * @return 24 | */ 25 | Result chargeOrder(String phoneNum, String chargeMoney, String prodId); 26 | 27 | /** 28 | * 本地订单入库 29 | * @param chargeOrderDto 30 | * @return 31 | */ 32 | boolean insertOrderInfo(ChargeOrderDto chargeOrderDto); 33 | 34 | /** 35 | * 订单状态更新 36 | * @param chargeOrderDto 37 | * @return 38 | */ 39 | boolean updateOrderStatus(ChargeOrderDto chargeOrderDto); 40 | 41 | /** 42 | * 订单详情查询 43 | * @param channelOrderId 44 | * @return 45 | */ 46 | ChargeOrderDO queryOrderByOrderId(String channelOrderId) throws SQLException; 47 | 48 | /** 49 | * 通知逻辑 50 | * @param sessionId 51 | * @param orderStatus 52 | * @param channelOrderId 53 | * @param platOrderId 54 | * @param finishTime 55 | * @return 56 | */ 57 | String doNotify(String sessionId, String orderStatus, String channelOrderId, String platOrderId, String finishTime); 58 | } 59 | 60 | -------------------------------------------------------------------------------- /order-charge-message-protocol/src/main/java/com/snowalker/order/charge/message/constant/MessageProtocolConst.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.order.charge.message.constant; 2 | 3 | /** 4 | * @author snowalker 5 | * @version 1.0 6 | * @date 2019/6/11 14:45 7 | * @className MessageProtocolConst 8 | * @desc 消息协议常量 9 | */ 10 | public enum MessageProtocolConst { 11 | 12 | /**WALLET_PAYMENT_TOPIC 钱包扣款协议*/ 13 | WALLET_PAYMENT_TOPIC("WALLET_PAYMENT_TOPIC", "PID_WALLET_PAYMENT", "CID_WALLET_PAYMENT", "钱包扣款协议"), 14 | /**ORDER_STATUS_UPDATE_TOPIC 订单状态修改协议*/ 15 | ORDER_STATUS_UPDATE_TOPIC("ORDER_STATUS_UPDATE_TOPIC", "PID_ORDER_STATUS_UPDATE", "CID_ORDER_STATUS_UPDATE", "订单状态修改协议"), 16 | /**ORDER_RESULT_NOTIFY_TOPIC 订单结果通知协议*/ 17 | ORDER_RESULT_NOTIFY_TOPIC("ORDER_RESULT_NOTIFY_TOPIC", "PID_ORDER_RESULT_NOTIFY", "CID_ORDER_RESULT_NOTIFY", "订单结果通知协议") 18 | ; 19 | /**消息主题*/ 20 | private String topic; 21 | /**生产者组*/ 22 | private String producerGroup; 23 | /**消费者组*/ 24 | private String consumerGroup; 25 | /**消息描述*/ 26 | private String desc; 27 | 28 | MessageProtocolConst(String topic, String producerGroup, String consumerGroup, String desc) { 29 | this.topic = topic; 30 | this.producerGroup = producerGroup; 31 | this.consumerGroup = consumerGroup; 32 | this.desc = desc; 33 | } 34 | 35 | public String getTopic() { 36 | return topic; 37 | } 38 | 39 | public String getProducerGroup() { 40 | return producerGroup; 41 | } 42 | 43 | public String getDesc() { 44 | return desc; 45 | } 46 | 47 | public String getConsumerGroup() { 48 | return consumerGroup; 49 | }} 50 | -------------------------------------------------------------------------------- /order-charge-gateway-server/src/main/java/com/snowalker/order/common/dto/CodeMsg.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.order.common.dto; 2 | 3 | /** 4 | * @author snowalker 5 | * 错误码封装 6 | */ 7 | public class CodeMsg { 8 | 9 | private String code; 10 | private String msg; 11 | 12 | /**通用的错误码*/ 13 | public static CodeMsg SUCCESS = new CodeMsg("10000", "SUCCESS"); 14 | public static CodeMsg UNKNOWN_ERROR = new CodeMsg("40004", "UNKNOWN_ERROR"); 15 | public static CodeMsg SERVER_ERROR = new CodeMsg("40004", "服务端异常"); 16 | public static CodeMsg BIND_ERROR = new CodeMsg("40006", "参数校验异常:s%"); 17 | /**订单入库失败*/ 18 | public static CodeMsg INSERT_ORDER_ERROR = new CodeMsg("40009", "订单入库异常"); 19 | /**参数非法*/ 20 | public static final String PARAM_ILLEGAL = "40006"; 21 | /**产品不存在*/ 22 | public static final String PRODUCT_NOT_EXIST = "40007"; 23 | /**库存不足*/ 24 | public static final String PRODUCT_STOCK_NOT_ENOUGH = "40008"; 25 | 26 | public CodeMsg( ) { 27 | } 28 | 29 | public CodeMsg(String code, String msg ) { 30 | this.code = code; 31 | this.msg = msg; 32 | } 33 | 34 | public String getCode() { 35 | return code; 36 | } 37 | 38 | public CodeMsg setCode(String code) { 39 | this.code = code; 40 | return this; 41 | } 42 | 43 | public String getMsg() { 44 | return msg; 45 | } 46 | 47 | public CodeMsg setMsg(String msg) { 48 | this.msg = msg; 49 | return this; 50 | } 51 | 52 | public CodeMsg fillArgs(Object ... args) { 53 | String code = this.code; 54 | String message = String.format(this.msg, args); 55 | return new CodeMsg(code, message); 56 | } 57 | 58 | @Override 59 | public String toString() { 60 | return "CodeMsg [code=" + code + ", msg=" + msg + "]"; 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /order-charge-gateway-server/src/main/java/com/snowalker/order/common/service/OrderChargeService.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.order.common.service; 2 | 3 | import com.snowalker.order.charge.request.ChargeOrderRequest; 4 | import com.snowalker.order.common.dao.dataobject.OrderInfoDO; 5 | import com.snowalker.order.common.dao.dataobject.OrderInfoDobj; 6 | import com.snowalker.order.common.dto.Result; 7 | 8 | /** 9 | * @author snowalker 10 | * @version 1.0 11 | * @date 2019/6/11 15:12 12 | * @className OrderChargeService 13 | * @desc 14 | */ 15 | public interface OrderChargeService { 16 | 17 | /** 18 | * 下单前置校验 19 | * @param chargeOrderRequest 20 | * @param sessionId 21 | * @return 22 | */ 23 | boolean checkValidBeforeChargeOrder(ChargeOrderRequest chargeOrderRequest, String sessionId); 24 | 25 | /** 26 | * 下单并发送扣款事务消息 27 | * @param chargeOrderRequest 28 | * @param sessionId 29 | * @return 30 | */ 31 | Result sendPaymentTransactionMsg(ChargeOrderRequest chargeOrderRequest, String sessionId); 32 | 33 | /** 34 | * 订单入库 35 | * @param orderInfoDO 36 | * @return 37 | */ 38 | boolean insertOrder(OrderInfoDO orderInfoDO); 39 | 40 | /** 41 | * 订单查询 42 | * @param orderInfoDO 43 | * @return 44 | */ 45 | OrderInfoDobj queryOrderInfo(OrderInfoDO orderInfoDO); 46 | 47 | /** 48 | * 订单状态修改为处理中 49 | * @param orderInfoDO 50 | * @return 51 | */ 52 | boolean updateOrderDealing(OrderInfoDO orderInfoDO); 53 | 54 | /** 55 | * 支付状态修改为成功 56 | * @param orderInfoDO 57 | * @return 58 | */ 59 | boolean updateOrderPayStatusSucc(OrderInfoDO orderInfoDO); 60 | 61 | /** 62 | * 订单通知状态修改成功 63 | * @param orderInfoDO 64 | * @return 65 | */ 66 | boolean updateOrderNotifyStatusSucc(OrderInfoDO orderInfoDO); 67 | } 68 | -------------------------------------------------------------------------------- /order-charge-gateway-server/src/main/java/com/snowalker/order/mq/notify/producer/OrderNotifySendProducer.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.order.mq.notify.producer; 2 | 3 | import com.snowalker.order.charge.message.constant.MessageProtocolConst; 4 | import com.snowalker.order.common.util.LogExceptionWapper; 5 | import org.apache.rocketmq.client.exception.MQClientException; 6 | import org.apache.rocketmq.client.producer.DefaultMQProducer; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.beans.factory.annotation.Value; 10 | import org.springframework.stereotype.Component; 11 | 12 | import javax.annotation.PostConstruct; 13 | 14 | /** 15 | * @author snowalker 16 | * @version 1.0 17 | * @date 2019/6/12 11:35 18 | * @className SendOrderNotifyProducer 19 | * @desc 订单通知发送生产者 20 | */ 21 | @Component 22 | public class OrderNotifySendProducer { 23 | 24 | private static final Logger LOGGER = LoggerFactory.getLogger(OrderNotifySendProducer.class); 25 | 26 | @Value("${rocketmq.nameServer}") 27 | String nameSrvAddr; 28 | 29 | private DefaultMQProducer defaultMQProducer; 30 | 31 | @PostConstruct 32 | public void init() { 33 | 34 | defaultMQProducer = 35 | new DefaultMQProducer(MessageProtocolConst.ORDER_RESULT_NOTIFY_TOPIC.getProducerGroup()); 36 | defaultMQProducer.setNamesrvAddr(nameSrvAddr); 37 | try { 38 | defaultMQProducer.start(); 39 | } catch (MQClientException e) { 40 | LOGGER.error("[订单通知发送生产者]--OrderNotifySendProducer加载异常!e={}", LogExceptionWapper.getStackTrace(e)); 41 | throw new RuntimeException("[订单通知发送生产者]--OrderNotifySendProducer加载异常!", e); 42 | } 43 | LOGGER.info("[订单通知发送生产者]--OrderNotifySendProducer加载完成!"); 44 | } 45 | 46 | public DefaultMQProducer getProducer() { 47 | return defaultMQProducer; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /order-charge-gateway-server/src/main/java/com/snowalker/order/common/config/MerchantInfoConfig.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.order.common.config; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.snowalker.order.common.dao.dataobject.MerchantInfoDO; 5 | import com.snowalker.order.common.service.MerchantInfoService; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Component; 10 | 11 | import javax.annotation.PostConstruct; 12 | import javax.annotation.Resource; 13 | import java.util.List; 14 | import java.util.Map; 15 | import java.util.concurrent.ConcurrentHashMap; 16 | 17 | /** 18 | * @author snowalker 19 | * @version 1.0 20 | * @date 2019/6/11 14:10 21 | * @className MerchantInfoConfig 22 | * @desc 23 | */ 24 | @Component 25 | public class MerchantInfoConfig { 26 | 27 | private static final Logger LOGGER = LoggerFactory.getLogger(MerchantInfoConfig.class); 28 | 29 | private static final Map MERCHANTS_MAP = new ConcurrentHashMap<>(16); 30 | 31 | @Resource(name = "merchantInfoService") 32 | MerchantInfoService merchantInfoService; 33 | 34 | @PostConstruct 35 | public void init() { 36 | 37 | List list = merchantInfoService.queryMerchantList(); 38 | if (list != null && list.size() > 0) { 39 | list.stream().forEach((merchantInfoDO -> { 40 | String key = merchantInfoDO.getMerchantPurseId(); 41 | MERCHANTS_MAP.put(key, merchantInfoDO); 42 | })); 43 | LOGGER.info("商户配置信息初始化完成,MERCHANTS_MAP={}", JSON.toJSONString(MERCHANTS_MAP)); 44 | } else { 45 | LOGGER.info("商户配置信息为空, 请及时添加"); 46 | } 47 | } 48 | 49 | public static Map getMerchantsMap() { 50 | return MERCHANTS_MAP; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /order-charge-gateway-merchant/src/main/java/com/snowalker/gateway/merchant/goods/ProductConfig.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.gateway.merchant.goods; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | import javax.annotation.PostConstruct; 9 | import java.util.Map; 10 | import java.util.concurrent.ConcurrentHashMap; 11 | 12 | /** 13 | * @author snowalker 14 | * @version 1.0 15 | * @date 2019/6/10 9:41 16 | * @className GoodsConfig 17 | * @desc 商品配置 18 | * prod_id:0001 19 | * prod_name:南方航空北京-三亚经济舱 20 | * prod_stock:100 21 | * prod_out_id:BJ_SY_JJC_01 22 | * prod_out_name:南航北京-三亚经济舱 23 | * 24 | * prod_id:BJ_SY_JJC_01 25 | * prod_name:南航北京-三亚经济舱 26 | * prod_stock:999 27 | */ 28 | @Configuration 29 | public class ProductConfig { 30 | 31 | private static final Logger LOGGER = LoggerFactory.getLogger(ProductConfig.class); 32 | 33 | private static final Map PRODUCTS_MAP = new ConcurrentHashMap<>(16); 34 | 35 | /**渠道id:南方航空*/ 36 | public static final String CHANNEL_ID_CHINA_CSAIR = "CHINA_CSAIR"; 37 | 38 | @PostConstruct 39 | public void init() { 40 | ProductVO productVO = new ProductVO(); 41 | String productId = "0001"; 42 | productVO.setProductId(productId) 43 | .setProductName("南方航空北京-三亚经济舱") 44 | .setProductOutId("BJ_SY_JJC_01") 45 | .setProductOutName("南航北京-三亚经济舱") 46 | .setProductStock(100) 47 | .setProductChannelId(CHANNEL_ID_CHINA_CSAIR); 48 | PRODUCTS_MAP.put(productId, productVO); 49 | LOGGER.info("渠道商品配置加载完毕,productConfig={}", JSON.toJSONString(PRODUCTS_MAP)); 50 | } 51 | 52 | public static Map getProductsMap() { 53 | return PRODUCTS_MAP; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /order-charge-sdk/order-charge-sdk.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /order-charge-gateway-server/src/main/java/com/snowalker/order/common/dao/dataobject/MerchantInfoDO.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.order.common.dao.dataobject; 2 | 3 | /** 4 | * @author snowalker 5 | * @version 1.0 6 | * @date 2019/6/11 14:15 7 | * @className MerchantInfoDO 8 | * @desc 商户配置信息 9 | */ 10 | public class MerchantInfoDO { 11 | 12 | private String merchantPurseId; 13 | private String merchantName; 14 | private String merchantNotifyUrl; 15 | private String merchantPrivateKey; 16 | 17 | public String getMerchantPurseId() { 18 | return merchantPurseId; 19 | } 20 | 21 | public MerchantInfoDO setMerchantPurseId(String merchantPurseId) { 22 | this.merchantPurseId = merchantPurseId; 23 | return this; 24 | } 25 | 26 | public String getMerchantName() { 27 | return merchantName; 28 | } 29 | 30 | public MerchantInfoDO setMerchantName(String merchantName) { 31 | this.merchantName = merchantName; 32 | return this; 33 | } 34 | 35 | public String getMerchantNotifyUrl() { 36 | return merchantNotifyUrl; 37 | } 38 | 39 | public MerchantInfoDO setMerchantNotifyUrl(String merchantNotifyUrl) { 40 | this.merchantNotifyUrl = merchantNotifyUrl; 41 | return this; 42 | } 43 | 44 | public String getMerchantPrivateKey() { 45 | return merchantPrivateKey; 46 | } 47 | 48 | public MerchantInfoDO setMerchantPrivateKey(String merchantPrivateKey) { 49 | this.merchantPrivateKey = merchantPrivateKey; 50 | return this; 51 | } 52 | 53 | @Override 54 | public String toString() { 55 | return "MerchantInfoDO{" + 56 | "merchantPurseId='" + merchantPurseId + '\'' + 57 | ", merchantName='" + merchantName + '\'' + 58 | ", merchantNotifyUrl='" + merchantNotifyUrl + '\'' + 59 | ", merchantPrivateKey='" + merchantPrivateKey + '\'' + 60 | '}'; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /order-charge-gateway-merchant/src/main/java/com/snowalker/gateway/merchant/order/dto/CodeMsg.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.gateway.merchant.order.dto; 2 | 3 | /** 4 | * @author snowalker 5 | * 错误码封装 6 | */ 7 | public class CodeMsg { 8 | 9 | private String code; 10 | private String msg; 11 | 12 | /**通用的错误码*/ 13 | public static CodeMsg SUCCESS = new CodeMsg("10000", "SUCCESS"); 14 | public static CodeMsg UNKNOWN_ERROR = new CodeMsg("40004", "UNKNOWN_ERROR"); 15 | public static CodeMsg SERVER_ERROR = new CodeMsg("40004", "服务端异常"); 16 | public static CodeMsg BIND_ERROR = new CodeMsg("40006", "参数校验异常:s%"); 17 | /**订单入库失败*/ 18 | public static CodeMsg INSERT_ORDER_ERROR = new CodeMsg("40004", "订单入库异常"); 19 | public static CodeMsg UPDATE_ORDER_ERROR = new CodeMsg("40004", "订单入库异常"); 20 | public static CodeMsg CHARGE_ORDER_FAIL = new CodeMsg("20000", "下单失败"); 21 | /**参数非法*/ 22 | public static final String PARAM_ILLEGAL = "40006"; 23 | /**产品不存在*/ 24 | public static final String PRODUCT_NOT_EXIST = "40007"; 25 | /**库存不足*/ 26 | public static final String PRODUCT_STOCK_NOT_ENOUGH = "40008"; 27 | 28 | public CodeMsg( ) { 29 | } 30 | 31 | public CodeMsg(String code, String msg ) { 32 | this.code = code; 33 | this.msg = msg; 34 | } 35 | 36 | public String getCode() { 37 | return code; 38 | } 39 | 40 | public CodeMsg setCode(String code) { 41 | this.code = code; 42 | return this; 43 | } 44 | 45 | public String getMsg() { 46 | return msg; 47 | } 48 | 49 | public CodeMsg setMsg(String msg) { 50 | this.msg = msg; 51 | return this; 52 | } 53 | 54 | public CodeMsg fillArgs(Object ... args) { 55 | String code = this.code; 56 | String message = String.format(this.msg, args); 57 | return new CodeMsg(code, message); 58 | } 59 | 60 | @Override 61 | public String toString() { 62 | return "CodeMsg [code=" + code + ", msg=" + msg + "]"; 63 | } 64 | 65 | } -------------------------------------------------------------------------------- /order-charge-gateway-server/src/main/java/com/snowalker/order/portal/OrderChargeController.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.order.portal; 2 | 3 | import com.snowalker.order.common.dto.Result; 4 | import com.snowalker.order.charge.request.ChargeOrderRequest; 5 | import com.snowalker.order.common.service.OrderChargeService; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.stereotype.Controller; 9 | import org.springframework.web.bind.annotation.RequestBody; 10 | import org.springframework.web.bind.annotation.RequestMapping; 11 | import org.springframework.web.bind.annotation.RequestMethod; 12 | import org.springframework.web.bind.annotation.ResponseBody; 13 | import org.springframework.web.context.request.RequestContextHolder; 14 | import org.springframework.web.context.request.ServletRequestAttributes; 15 | 16 | import javax.annotation.Resource; 17 | 18 | /** 19 | * @author snowalker 20 | * @version 1.0 21 | * @date 2019/6/10 16:13 22 | * @className OrderChargeController 23 | * @desc 订单充值接口 24 | */ 25 | @Controller 26 | @RequestMapping("api") 27 | public class OrderChargeController { 28 | 29 | private static final Logger LOGGER = LoggerFactory.getLogger(OrderChargeController.class); 30 | 31 | @Resource(name = "orderChargeService") 32 | OrderChargeService orderChargeService; 33 | 34 | /** 35 | * 平台下单接口 36 | * @param chargeOrderRequest 37 | * @return 38 | */ 39 | @RequestMapping(value = "charge.do", method = {RequestMethod.POST}) 40 | public @ResponseBody Result chargeRequst(@RequestBody ChargeOrderRequest chargeOrderRequest) { 41 | ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); 42 | String sessionId = attributes.getSessionId(); 43 | // 下单前置校验 44 | if (!orderChargeService.checkValidBeforeChargeOrder(chargeOrderRequest, sessionId)) { 45 | return new Result("20000", "FAIL", null); 46 | } 47 | return orderChargeService.sendPaymentTransactionMsg(chargeOrderRequest, sessionId); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /order-charge-message-protocol/order-charge-message-protocol.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /order-charge-sdk/src/main/java/com/snowalker/order/charge/response/ChargeOrderResponse.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.order.charge.response; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * @author snowalker 7 | * @version 1.0 8 | * @date 2019/6/10 16:03 9 | * @className ChargeOrderResponse 10 | * @desc 下单sdk接口返回参数 11 | */ 12 | public class ChargeOrderResponse implements Serializable { 13 | 14 | private static final long serialVersionUID = -5685058946404699059L; 15 | 16 | /**商户订单号*/ 17 | private String channelOrderId; 18 | private String userPhoneNum; 19 | private String chargePrice; 20 | private String merchantName; 21 | /**票务服务订单号*/ 22 | private String platOrderId; 23 | 24 | public String getChannelOrderId() { 25 | return channelOrderId; 26 | } 27 | 28 | public ChargeOrderResponse setChannelOrderId(String channelOrderId) { 29 | this.channelOrderId = channelOrderId; 30 | return this; 31 | } 32 | 33 | public String getUserPhoneNum() { 34 | return userPhoneNum; 35 | } 36 | 37 | public ChargeOrderResponse setUserPhoneNum(String userPhoneNum) { 38 | this.userPhoneNum = userPhoneNum; 39 | return this; 40 | } 41 | 42 | public String getChargePrice() { 43 | return chargePrice; 44 | } 45 | 46 | public ChargeOrderResponse setChargePrice(String chargePrice) { 47 | this.chargePrice = chargePrice; 48 | return this; 49 | } 50 | 51 | public String getMerchantName() { 52 | return merchantName; 53 | } 54 | 55 | public ChargeOrderResponse setMerchantName(String merchantName) { 56 | this.merchantName = merchantName; 57 | return this; 58 | } 59 | 60 | public String getPlatOrderId() { 61 | return platOrderId; 62 | } 63 | 64 | public ChargeOrderResponse setPlatOrderId(String platOrderId) { 65 | this.platOrderId = platOrderId; 66 | return this; 67 | } 68 | 69 | @Override 70 | public String toString() { 71 | return "ChargeOrderResponse{" + 72 | "channelOrderId='" + channelOrderId + '\'' + 73 | ", userPhoneNum='" + userPhoneNum + '\'' + 74 | ", chargePrice='" + chargePrice + '\'' + 75 | ", merchantName='" + merchantName + '\'' + 76 | ", platOrderId='" + platOrderId + '\'' + 77 | '}'; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /order-charge-gateway-merchant/src/main/java/com/snowalker/gateway/merchant/order/OrderChargeController.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.gateway.merchant.order; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.snowalker.gateway.merchant.order.dto.CodeMsg; 5 | import com.snowalker.gateway.merchant.order.dto.Result; 6 | import com.snowalker.gateway.merchant.order.handler.RequestParamValidateHandler; 7 | import com.snowalker.gateway.merchant.order.service.OrderService; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.stereotype.Controller; 12 | import org.springframework.web.bind.annotation.RequestMapping; 13 | import org.springframework.web.bind.annotation.RequestMethod; 14 | import org.springframework.web.bind.annotation.ResponseBody; 15 | 16 | import javax.annotation.Resource; 17 | import javax.servlet.http.HttpServletRequest; 18 | 19 | /** 20 | * @author snowalker 21 | * @version 1.0 22 | * @date 2019/6/10 10:06 23 | * @className OrderChargeController 24 | * @desc 商户下单接口 25 | */ 26 | @Controller 27 | @RequestMapping(value = "api") 28 | public class OrderChargeController { 29 | 30 | private static final Logger LOGGER = LoggerFactory.getLogger(OrderChargeController.class); 31 | 32 | @Autowired 33 | RequestParamValidateHandler requestParamValidateHandler; 34 | 35 | @Resource(name = "orderService") 36 | OrderService orderService; 37 | 38 | /** 39 | * 第三方渠道下单 40 | * @param request 41 | */ 42 | @RequestMapping(value = "charge", method = {RequestMethod.POST}) 43 | public @ResponseBody Result chargeRequst(HttpServletRequest request) { 44 | 45 | String phoneNum = request.getParameter("phone_num"); 46 | String chargeMoney = request.getParameter("charge_money"); 47 | String prodId = request.getParameter("prod_id"); 48 | 49 | Result result; 50 | // 前置校验:校验参数、产品、库存等 51 | if (requestParamValidateHandler.checkRequestParams(phoneNum, chargeMoney, prodId)) { 52 | LOGGER.info("下单接口入参:phoneNum={},chargeMoney={},prodId={}", phoneNum, chargeMoney, prodId); 53 | result = orderService.chargeOrder(phoneNum, chargeMoney, prodId); 54 | } else { 55 | result = Result.error(CodeMsg.UNKNOWN_ERROR); 56 | } 57 | LOGGER.info("下单接口出参:result={}", JSON.toJSONString(result)); 58 | return result; 59 | } 60 | 61 | 62 | } 63 | -------------------------------------------------------------------------------- /order-charge-gateway-merchant/src/main/java/com/snowalker/gateway/merchant/order/handler/RequestParamValidateHandler.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.gateway.merchant.order.handler; 2 | 3 | import com.snowalker.gateway.merchant.goods.ProductConfig; 4 | import com.snowalker.gateway.merchant.goods.ProductVO; 5 | import com.snowalker.gateway.merchant.order.dto.CodeMsg; 6 | import com.snowalker.gateway.merchant.order.exception.ApiException; 7 | import org.apache.commons.lang3.StringUtils; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.stereotype.Component; 12 | import org.springframework.web.client.RestTemplate; 13 | 14 | import javax.annotation.Resource; 15 | 16 | /** 17 | * @author snowalker 18 | * @version 1.0 19 | * @date 2019/6/10 10:28 20 | * @className RequestParamValidateHandler 21 | * @desc 请求参数校验 22 | */ 23 | @Component 24 | public class RequestParamValidateHandler { 25 | 26 | private static final Logger LOGGER = LoggerFactory.getLogger(RequestParamValidateHandler.class); 27 | 28 | /** 29 | * 下单请求参数校验 30 | * @param phoneNum 31 | * @param chargeMoney 32 | * @param prodId 33 | */ 34 | public boolean checkRequestParams(String phoneNum, String chargeMoney, String prodId) { 35 | // 非空校验 36 | if (StringUtils.isBlank(phoneNum) || StringUtils.isBlank(chargeMoney) || StringUtils.isBlank(prodId)) { 37 | String errorMsg = String.format("下单请求入参存在空值, phoneNum=%s, chargeMoney=%s, prodId=%s", phoneNum, chargeMoney, prodId); 38 | LOGGER.error(errorMsg); 39 | throw new ApiException(new CodeMsg(CodeMsg.PARAM_ILLEGAL, errorMsg)); 40 | } 41 | // 产品校验 42 | ProductVO productVO = ProductConfig.getProductsMap().get(prodId); 43 | if (productVO == null) { 44 | String errorMsg = String.format("产品不存在!, prodId=%s", prodId); 45 | LOGGER.error(errorMsg); 46 | throw new ApiException(new CodeMsg(CodeMsg.PRODUCT_NOT_EXIST, errorMsg)); 47 | } 48 | // 库存校验 49 | int productStock = productVO.getProductStock(); 50 | if (productStock <= 0) { 51 | String errorMsg = String.format("产品库存不足!, prodId=%s", prodId); 52 | LOGGER.error(errorMsg); 53 | throw new ApiException(new CodeMsg(CodeMsg.PRODUCT_STOCK_NOT_ENOUGH, errorMsg)); 54 | } 55 | return true; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /order-charge-gateway-merchant/src/main/resources/mapper/OrderInfoMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | insert into t_user_order3rd 9 | ( 10 | order_id, 11 | order_status, 12 | user_phoneno, 13 | prod_id, 14 | prod_name, 15 | out_prod_id, 16 | out_prod_name, 17 | charge_money 18 | ) 19 | values 20 | ( 21 | #{orderId}, 22 | #{orderStatus}, 23 | #{phoneNum}, 24 | #{productId}, 25 | #{productName}, 26 | #{outProductId}, 27 | #{outProductName}, 28 | #{chargeMoney} 29 | ) 30 | 31 | 32 | 33 | 35 | update t_user_order3rd t 36 | set t.order_status = #{afterUpdateOrderStatus} 37 | 38 | ,t.finish_time = #{finishTime} 39 | 40 | 41 | ,t.out_order_id = #{outOrderId} 42 | 43 | where t.order_status = #{beforeUpdateOrderStatus} 44 | AND t.order_id = #{orderId} 45 | 46 | 47 | 48 | 68 | 69 | -------------------------------------------------------------------------------- /order-charge-gateway-merchant/src/main/java/com/snowalker/gateway/merchant/goods/ProductVO.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.gateway.merchant.goods; 2 | 3 | /** 4 | * @author snowalker 5 | * @version 1.0 6 | * @date 2019/6/10 9:45 7 | * @className ProductBean 8 | * @desc 商品配置领域 9 | */ 10 | public class ProductVO { 11 | 12 | /**商品id*/ 13 | private String productId; 14 | /**商品名*/ 15 | private String productName; 16 | /**供应商商品id*/ 17 | private String productOutId; 18 | /**供应商商品名*/ 19 | private String productOutName; 20 | /**商品库存*/ 21 | private Integer productStock; 22 | /**供应商渠道id*/ 23 | private String productChannelId; 24 | 25 | public String getProductId() { 26 | return productId; 27 | } 28 | 29 | public ProductVO setProductId(String productId) { 30 | this.productId = productId; 31 | return this; 32 | } 33 | 34 | public String getProductName() { 35 | return productName; 36 | } 37 | 38 | public ProductVO setProductName(String productName) { 39 | this.productName = productName; 40 | return this; 41 | } 42 | 43 | public String getProductOutId() { 44 | return productOutId; 45 | } 46 | 47 | public ProductVO setProductOutId(String productOutId) { 48 | this.productOutId = productOutId; 49 | return this; 50 | } 51 | 52 | public String getProductOutName() { 53 | return productOutName; 54 | } 55 | 56 | public ProductVO setProductOutName(String productOutName) { 57 | this.productOutName = productOutName; 58 | return this; 59 | } 60 | 61 | public Integer getProductStock() { 62 | return productStock; 63 | } 64 | 65 | public ProductVO setProductStock(Integer productStock) { 66 | this.productStock = productStock; 67 | return this; 68 | } 69 | 70 | public String getProductChannelId() { 71 | return productChannelId; 72 | } 73 | 74 | public ProductVO setProductChannelId(String productChannelId) { 75 | this.productChannelId = productChannelId; 76 | return this; 77 | } 78 | 79 | @Override 80 | public String toString() { 81 | return "ProductVO{" + 82 | "productId='" + productId + '\'' + 83 | ", productName='" + productName + '\'' + 84 | ", productOutId='" + productOutId + '\'' + 85 | ", productOutName='" + productOutName + '\'' + 86 | ", productStock=" + productStock + 87 | ", productChannelId='" + productChannelId + '\'' + 88 | '}'; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /order-charge-gateway-notify/src/main/java/com/snowalker/notify/mq/notify/NotifySendConsumer.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.notify.mq.notify; 2 | 3 | import com.snowalker.notify.common.util.LogExceptionWapper; 4 | import com.snowalker.notify.mq.payment.producer.OrderStatusUpdateProducer; 5 | import com.snowalker.order.charge.message.constant.MessageProtocolConst; 6 | import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; 7 | import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently; 8 | import org.apache.rocketmq.client.exception.MQClientException; 9 | import org.apache.rocketmq.common.consumer.ConsumeFromWhere; 10 | import org.apache.rocketmq.common.protocol.heartbeat.MessageModel; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | import org.springframework.beans.factory.annotation.Value; 14 | import org.springframework.stereotype.Component; 15 | 16 | import javax.annotation.PostConstruct; 17 | import javax.annotation.Resource; 18 | 19 | /** 20 | * @author snowalker 21 | * @version 1.0 22 | * @date 2019/6/12 14:14 23 | * @className NotifySendConsumer 24 | * @desc 订单结果通知消息消费者 25 | * TODO 需要学员实现 26 | */ 27 | @Component 28 | public class NotifySendConsumer { 29 | 30 | private static final Logger LOGGER = LoggerFactory.getLogger(NotifySendConsumer.class); 31 | 32 | @Value("${rocketmq.nameServer}") 33 | String nameSrvAddr; 34 | 35 | private DefaultMQPushConsumer defaultMQPushConsumer; 36 | 37 | @Resource(name = "notifySendListenerImpl") 38 | private MessageListenerConcurrently messageListener; 39 | 40 | @PostConstruct 41 | public void init() { 42 | defaultMQPushConsumer = new DefaultMQPushConsumer(MessageProtocolConst.ORDER_RESULT_NOTIFY_TOPIC.getConsumerGroup()); 43 | defaultMQPushConsumer.setNamesrvAddr(nameSrvAddr); 44 | // 从头开始消费 45 | defaultMQPushConsumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET); 46 | // 消费模式:集群模式 47 | defaultMQPushConsumer.setMessageModel(MessageModel.CLUSTERING); 48 | // 注册监听器 49 | defaultMQPushConsumer.registerMessageListener(messageListener); 50 | // 订阅所有消息 51 | try { 52 | defaultMQPushConsumer.subscribe(MessageProtocolConst.ORDER_RESULT_NOTIFY_TOPIC.getTopic(), "*"); 53 | defaultMQPushConsumer.start(); 54 | } catch (MQClientException e) { 55 | LOGGER.error("[订单结果通知消息消费者]--NotifySendConsumer加载异常!e={}", LogExceptionWapper.getStackTrace(e)); 56 | throw new RuntimeException("[订单结果通知消息消费者]--NotifySendConsumer加载异常!", e); 57 | } 58 | LOGGER.info("[订单结果通知消息消费者]--NotifySendConsumer加载完成!"); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /order-charge-gateway-notify/src/main/java/com/snowalker/notify/mq/payment/consumer/WalletPaymentConsumer.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.notify.mq.payment.consumer; 2 | 3 | import com.snowalker.notify.common.service.WalletService; 4 | import com.snowalker.notify.common.util.LogExceptionWapper; 5 | import com.snowalker.order.charge.message.constant.MessageProtocolConst; 6 | import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; 7 | import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently; 8 | import org.apache.rocketmq.client.exception.MQClientException; 9 | import org.apache.rocketmq.common.consumer.ConsumeFromWhere; 10 | import org.apache.rocketmq.common.protocol.heartbeat.MessageModel; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.beans.factory.annotation.Value; 15 | import org.springframework.stereotype.Component; 16 | 17 | import javax.annotation.PostConstruct; 18 | import javax.annotation.Resource; 19 | 20 | /** 21 | * @author snowalker 22 | * @version 1.0 23 | * @date 2019/6/12 9:46 24 | * @className WalletPaymentConsumer 25 | * @desc 扣款消息消费者 26 | */ 27 | @Component 28 | public class WalletPaymentConsumer { 29 | 30 | private static final Logger LOGGER = LoggerFactory.getLogger(WalletPaymentConsumer.class); 31 | 32 | @Value("${rocketmq.nameServer}") 33 | String nameSrvAddr; 34 | 35 | @Autowired 36 | WalletService walletService; 37 | 38 | @Resource(name = "walletPaymentMsgListenerImpl") 39 | private MessageListenerConcurrently messageListener; 40 | 41 | private DefaultMQPushConsumer defaultMQPushConsumer; 42 | 43 | @PostConstruct 44 | public void init() { 45 | defaultMQPushConsumer = new DefaultMQPushConsumer(MessageProtocolConst.WALLET_PAYMENT_TOPIC.getConsumerGroup()); 46 | defaultMQPushConsumer.setNamesrvAddr(nameSrvAddr); 47 | // 从头开始消费 48 | defaultMQPushConsumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET); 49 | // 消费模式:集群模式 50 | defaultMQPushConsumer.setMessageModel(MessageModel.CLUSTERING); 51 | // 注册监听器 52 | defaultMQPushConsumer.registerMessageListener(messageListener); 53 | // 订阅所有消息 54 | try { 55 | defaultMQPushConsumer.subscribe(MessageProtocolConst.WALLET_PAYMENT_TOPIC.getTopic(), "*"); 56 | defaultMQPushConsumer.start(); 57 | } catch (MQClientException e) { 58 | LOGGER.error("[扣款消息消费者]--WalletPaymentConsumer加载异常!e={}", LogExceptionWapper.getStackTrace(e)); 59 | throw new RuntimeException("[扣款消息消费者]--WalletPaymentConsumer加载异常!", e); 60 | } 61 | LOGGER.info("[扣款消息消费者]--WalletPaymentConsumer加载完成!"); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /order-charge-sdk/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | order-charge-notify 7 | com.snowalker 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | com.snowalker 13 | order-charge-sdk 14 | 1.0.0 15 | order-charge-sdk 16 | 17 | 18 | 1.8 19 | 1.8 20 | UTF-8 21 | UTF-8 22 | 29.0-jre 23 | 24 | 25 | 26 | 27 | junit 28 | junit 29 | 4.13.1 30 | test 31 | 32 | 33 | 34 | 35 | org.apache.commons 36 | commons-lang3 37 | 3.4 38 | 39 | 40 | 41 | 42 | com.alibaba 43 | fastjson 44 | 1.2.58 45 | 46 | 47 | 48 | 49 | com.google.guava 50 | guava 51 | ${guava-version} 52 | 53 | 54 | 55 | 56 | commons-codec 57 | commons-codec 58 | 1.12 59 | 60 | 61 | 62 | 63 | 64 | 65 | org.apache.maven.plugins 66 | maven-compiler-plugin 67 | 68 | 8 69 | 8 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /order-charge-gateway-notify/src/main/resources/mapper/WalletMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | INSERT INTO t_merchant_wallet ( 8 | purse_id, 9 | merchant_name, 10 | balance_account, 11 | account_status) 12 | VALUES 13 | ( 14 | #{purseId}, 15 | #{merchantName}, 16 | #{balanceAccount}, 17 | 0 18 | ) 19 | 20 | 21 | 22 | 23 | UPDATE t_merchant_wallet 24 | SET balance_account = balance_account - #{chargeMoney}, 25 | version = version + 1 26 | WHERE 27 | version = #{version} 28 | AND purse_id = #{purseId} 29 | 30 | 31 | 32 | 33 | INSERT INTO t_merchant_charge_record ( 34 | record_id, 35 | order_id, 36 | purse_id, 37 | merchant_name, 38 | charge_price) 39 | VALUES 40 | ( 41 | #{recordId}, 42 | #{orderId}, 43 | #{purseId}, 44 | #{merchantName}, 45 | #{chargeMoney} 46 | ) 47 | 48 | 49 | 50 | 65 | 66 | 67 | 83 | -------------------------------------------------------------------------------- /order-charge-gateway-notify/src/main/java/com/snowalker/notify/mq/payment/producer/OrderStatusUpdateProducer.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.notify.mq.payment.producer; 2 | 3 | import com.snowalker.order.charge.message.constant.MessageProtocolConst; 4 | import org.apache.rocketmq.client.exception.MQClientException; 5 | import org.apache.rocketmq.client.producer.TransactionListener; 6 | import org.apache.rocketmq.client.producer.TransactionMQProducer; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.beans.factory.annotation.Value; 10 | import org.springframework.stereotype.Component; 11 | 12 | import javax.annotation.PostConstruct; 13 | import javax.annotation.Resource; 14 | import java.util.concurrent.ExecutorService; 15 | import java.util.concurrent.LinkedBlockingQueue; 16 | import java.util.concurrent.ThreadPoolExecutor; 17 | import java.util.concurrent.TimeUnit; 18 | 19 | /** 20 | * @author snowalker 21 | * @version 1.0 22 | * @date 2019/6/12 9:39 23 | * @className OrderStatusUpdateProducer 24 | * @desc 订单状态修改生产者 25 | */ 26 | @Component 27 | public class OrderStatusUpdateProducer { 28 | 29 | private static final Logger LOGGER = LoggerFactory.getLogger(OrderStatusUpdateProducer.class); 30 | 31 | /**事务回查线程池*/ 32 | private ExecutorService executorService; 33 | /**事务消息生产者*/ 34 | private TransactionMQProducer transactionMQProducer; 35 | 36 | @Value("${rocketmq.nameServer}") 37 | private String nameSrvAddr; 38 | 39 | @Resource(name = "localTranListenerImpl") 40 | TransactionListener transactionListener; 41 | 42 | @PostConstruct 43 | public void init() { 44 | // 初始化回查线程池 45 | executorService = new ThreadPoolExecutor( 46 | 5, 47 | 512, 48 | 10000L, 49 | TimeUnit.MILLISECONDS, 50 | new LinkedBlockingQueue<>(512), 51 | runnable -> { 52 | Thread thread = new Thread(runnable); 53 | thread.setName(MessageProtocolConst.ORDER_STATUS_UPDATE_TOPIC.getProducerGroup() + "-check-thread"); 54 | return null; 55 | }); 56 | 57 | transactionMQProducer = new TransactionMQProducer(MessageProtocolConst.ORDER_STATUS_UPDATE_TOPIC.getProducerGroup()); 58 | transactionMQProducer.setNamesrvAddr(nameSrvAddr); 59 | transactionMQProducer.setExecutorService(executorService); 60 | transactionMQProducer.setTransactionListener(transactionListener); 61 | try { 62 | transactionMQProducer.start(); 63 | } catch (MQClientException e) { 64 | throw new RuntimeException("启动[订单状态修改生产者]OrderStatusUpdateProducer异常", e); 65 | } 66 | LOGGER.info("启动[订单状态修改生产者]OrderStatusUpdateProducer成功, topic={}", MessageProtocolConst.ORDER_STATUS_UPDATE_TOPIC.getTopic()); 67 | } 68 | 69 | public TransactionMQProducer getProducer() { 70 | return transactionMQProducer; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /order-charge-gateway-server/src/main/java/com/snowalker/order/mq/payment/producer/ChargeOrderPaymentTranProducer.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.order.mq.payment.producer; 2 | 3 | import com.snowalker.order.charge.message.constant.MessageProtocolConst; 4 | import org.apache.rocketmq.client.exception.MQClientException; 5 | import org.apache.rocketmq.client.producer.TransactionListener; 6 | import org.apache.rocketmq.client.producer.TransactionMQProducer; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.beans.factory.annotation.Value; 10 | import org.springframework.stereotype.Component; 11 | 12 | import javax.annotation.PostConstruct; 13 | import javax.annotation.Resource; 14 | import java.util.concurrent.ExecutorService; 15 | import java.util.concurrent.LinkedBlockingQueue; 16 | import java.util.concurrent.ThreadPoolExecutor; 17 | import java.util.concurrent.TimeUnit; 18 | 19 | /** 20 | * @author snowalker 21 | * @version 1.0 22 | * @date 2019/6/11 15:46 23 | * @className PaymentTransactionProducer 24 | * @desc 扣款事务消息生产者 25 | * TODO 需要学员实现 26 | */ 27 | @Component 28 | public class ChargeOrderPaymentTranProducer { 29 | 30 | private static final Logger LOGGER = LoggerFactory.getLogger(ChargeOrderPaymentTranProducer.class); 31 | 32 | /**事务回查线程池*/ 33 | private ExecutorService executorService; 34 | /**事务消息生产者*/ 35 | private TransactionMQProducer transactionMQProducer; 36 | 37 | @Value("${rocketmq.nameServer}") 38 | private String nameSrvAddr; 39 | 40 | @Resource(name = "chargeOrderTranListenerImpl") 41 | TransactionListener transactionListener; 42 | 43 | @PostConstruct 44 | public void init() { 45 | // 初始化回查线程池 46 | executorService = new ThreadPoolExecutor( 47 | 5, 48 | 512, 49 | 10000L, 50 | TimeUnit.MILLISECONDS, 51 | new LinkedBlockingQueue<>(512), 52 | runnable -> { 53 | Thread thread = new Thread(runnable); 54 | thread.setName(MessageProtocolConst.WALLET_PAYMENT_TOPIC.getProducerGroup() + "-check-thread"); 55 | return null; 56 | }); 57 | 58 | transactionMQProducer = new TransactionMQProducer(MessageProtocolConst.WALLET_PAYMENT_TOPIC.getProducerGroup()); 59 | transactionMQProducer.setNamesrvAddr(nameSrvAddr); 60 | transactionMQProducer.setExecutorService(executorService); 61 | transactionMQProducer.setTransactionListener(transactionListener); 62 | try { 63 | transactionMQProducer.start(); 64 | } catch (MQClientException e) { 65 | throw new RuntimeException("启动[扣款事务消息生产者]ChargeOrderPaymentTranProducer异常", e); 66 | } 67 | LOGGER.info("启动[扣款事务消息生产者]ChargeOrderPaymentTranProducer成功, topic={}", MessageProtocolConst.WALLET_PAYMENT_TOPIC.getTopic()); 68 | } 69 | 70 | public TransactionMQProducer getProducer() { 71 | return transactionMQProducer; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /order-charge-gateway-server/src/main/resources/mapper/OrderChargeMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | insert into t_merchant_order 9 | ( 10 | order_id, 11 | channel_order_id, 12 | order_status, 13 | notify_status, 14 | pay_status, 15 | user_phoneno, 16 | charge_price, 17 | purse_id, 18 | merchant_name 19 | ) 20 | values 21 | ( 22 | #{orderId}, 23 | #{channelOrderId}, 24 | 1, 25 | 1, 26 | 1, 27 | #{userPhoneNo}, 28 | #{chargeMoney}, 29 | #{purseId}, 30 | #{merchantName} 31 | ) 32 | 33 | 34 | 35 | 51 | 52 | 53 | 54 | UPDATE t_merchant_order 55 | SET order_status = 2, 56 | pay_status = 2 57 | WHERE 58 | order_status = 1 59 | AND pay_status = 1 60 | AND channel_order_id = #{channelOrderId} 61 | 62 | 63 | 64 | 65 | UPDATE t_merchant_order 66 | SET pay_status = 0, 67 | order_status = 0, 68 | notify_status = 2 69 | WHERE 70 | pay_status = 2 71 | AND order_status = 2 72 | AND notify_status = 1 73 | AND order_id = #{orderId} 74 | 75 | 76 | 77 | 78 | UPDATE t_merchant_order 79 | SET notify_status = 0 80 | WHERE 81 | order_status = 0 82 | AND notify_status = 2 83 | AND order_id = #{orderId} 84 | 85 | -------------------------------------------------------------------------------- /order-charge-gateway-notify/src/main/java/com/snowalker/notify/common/dao/dataobject/ChargeRecordEntity.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.notify.common.dao.dataobject; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.Date; 5 | 6 | /** 7 | * @author snowalker 8 | * @version 1.0 9 | * @date 2019/2/15 17:21 10 | * @className ChargeRecordEntity 11 | * @desc 模拟钱包扣款流水 12 | */ 13 | public class ChargeRecordEntity { 14 | 15 | private int id; 16 | /**扣款流水id*/ 17 | private String recordId; 18 | private String orderId; 19 | private String purseId; 20 | private String merchantName; 21 | /**本次交易流水额度*/ 22 | private BigDecimal chargePrice; 23 | private Date gmtCreate; 24 | private Date gmtUpdate; 25 | 26 | public String getMerchantName() { 27 | return merchantName; 28 | } 29 | 30 | public ChargeRecordEntity setMerchantName(String merchantName) { 31 | this.merchantName = merchantName; 32 | return this; 33 | } 34 | 35 | public int getId() { 36 | return id; 37 | } 38 | 39 | public ChargeRecordEntity setId(int id) { 40 | this.id = id; 41 | return this; 42 | } 43 | 44 | public String getRecordId() { 45 | return recordId; 46 | } 47 | 48 | public ChargeRecordEntity setRecordId(String recordId) { 49 | this.recordId = recordId; 50 | return this; 51 | } 52 | 53 | public String getOrderId() { 54 | return orderId; 55 | } 56 | 57 | public ChargeRecordEntity setOrderId(String orderId) { 58 | this.orderId = orderId; 59 | return this; 60 | } 61 | 62 | public String getPurseId() { 63 | return purseId; 64 | } 65 | 66 | public ChargeRecordEntity setPurseId(String purseId) { 67 | this.purseId = purseId; 68 | return this; 69 | } 70 | 71 | 72 | public BigDecimal getChargePrice() { 73 | return chargePrice; 74 | } 75 | 76 | public ChargeRecordEntity setChargePrice(BigDecimal chargePrice) { 77 | this.chargePrice = chargePrice; 78 | return this; 79 | } 80 | 81 | public Date getGmtCreate() { 82 | return gmtCreate; 83 | } 84 | 85 | public ChargeRecordEntity setGmtCreate(Date gmtCreate) { 86 | this.gmtCreate = gmtCreate; 87 | return this; 88 | } 89 | 90 | public Date getGmtUpdate() { 91 | return gmtUpdate; 92 | } 93 | 94 | public ChargeRecordEntity setGmtUpdate(Date gmtUpdate) { 95 | this.gmtUpdate = gmtUpdate; 96 | return this; 97 | } 98 | 99 | @Override 100 | public String toString() { 101 | return "ChargeRecordEntity{" + 102 | "id=" + id + 103 | ", recordId='" + recordId + '\'' + 104 | ", orderId='" + orderId + '\'' + 105 | ", purseId='" + purseId + '\'' + 106 | ", merchantName='" + merchantName + '\'' + 107 | ", chargePrice=" + chargePrice + 108 | ", gmtCreate=" + gmtCreate + 109 | ", gmtUpdate=" + gmtUpdate + 110 | '}'; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /order-charge-gateway-notify/src/main/java/com/snowalker/notify/common/dao/dataobject/WalletEntity.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.notify.common.dao.dataobject; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.Date; 5 | 6 | /** 7 | * @author snowalker 8 | * @version 1.0 9 | * @date 2019/2/15 16:54 10 | * @className PurseEntity 11 | * @desc 模拟钱包数据库操作实体 12 | */ 13 | public class WalletEntity { 14 | 15 | private int id; 16 | private String purseId; 17 | private String merchantName; 18 | private BigDecimal balanceAccount; 19 | private BigDecimal chargeMoney; 20 | /**账户状态 账户状态 0 正常 1 异常*/ 21 | private int accountStatus; 22 | private Date gmtCreate; 23 | private Date gmtUpdate; 24 | private int version; 25 | 26 | public BigDecimal getChargeMoney() { 27 | return chargeMoney; 28 | } 29 | 30 | public WalletEntity setChargeMoney(BigDecimal chargeMoney) { 31 | this.chargeMoney = chargeMoney; 32 | return this; 33 | } 34 | 35 | public int getVersion() { 36 | return version; 37 | } 38 | 39 | public WalletEntity setVersion(int version) { 40 | this.version = version; 41 | return this; 42 | } 43 | 44 | public int getId() { 45 | return id; 46 | } 47 | 48 | public WalletEntity setId(int id) { 49 | this.id = id; 50 | return this; 51 | } 52 | 53 | public String getPurseId() { 54 | return purseId; 55 | } 56 | 57 | public WalletEntity setPurseId(String purseId) { 58 | this.purseId = purseId; 59 | return this; 60 | } 61 | 62 | public BigDecimal getBalanceAccount() { 63 | return balanceAccount; 64 | } 65 | 66 | public WalletEntity setBalanceAccount(BigDecimal balanceAccount) { 67 | this.balanceAccount = balanceAccount; 68 | return this; 69 | } 70 | 71 | public int getAccountStatus() { 72 | return accountStatus; 73 | } 74 | 75 | public WalletEntity setAccountStatus(int accountStatus) { 76 | this.accountStatus = accountStatus; 77 | return this; 78 | } 79 | 80 | public Date getGmtCreate() { 81 | return gmtCreate; 82 | } 83 | 84 | public WalletEntity setGmtCreate(Date gmtCreate) { 85 | this.gmtCreate = gmtCreate; 86 | return this; 87 | } 88 | 89 | public Date getGmtUpdate() { 90 | return gmtUpdate; 91 | } 92 | 93 | public WalletEntity setGmtUpdate(Date gmtUpdate) { 94 | this.gmtUpdate = gmtUpdate; 95 | return this; 96 | } 97 | 98 | public String getMerchantName() { 99 | return merchantName; 100 | } 101 | 102 | public WalletEntity setMerchantName(String merchantName) { 103 | this.merchantName = merchantName; 104 | return this; 105 | } 106 | 107 | @Override 108 | public String toString() { 109 | return "WalletEntity{" + 110 | "id=" + id + 111 | ", purseId='" + purseId + '\'' + 112 | ", merchantName='" + merchantName + '\'' + 113 | ", balanceAccount=" + balanceAccount + 114 | ", chargeMoney=" + chargeMoney + 115 | ", accountStatus=" + accountStatus + 116 | ", gmtCreate=" + gmtCreate + 117 | ", gmtUpdate=" + gmtUpdate + 118 | ", version=" + version + 119 | '}'; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /order-charge-gateway-server/src/main/java/com/snowalker/order/common/dao/dataobject/OrderInfoDobj.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.order.common.dao.dataobject; 2 | 3 | /** 4 | * @author snowalker 5 | * @version 1.0 6 | * @date 2019/6/11 19:50 7 | * @className OrderInfoDO 8 | * @desc 订单数据库映射实体 9 | */ 10 | public class OrderInfoDobj { 11 | 12 | private Integer orderStatus; 13 | private Integer notifyStatus; 14 | private Integer payStatus; 15 | private String orderId; 16 | private String channelOrderId; 17 | private String userPhoneNo; 18 | private String chargeMoney; 19 | private String purseId; 20 | private String merchantName; 21 | 22 | public Integer getOrderStatus() { 23 | return orderStatus; 24 | } 25 | 26 | public OrderInfoDobj setOrderStatus(Integer orderStatus) { 27 | this.orderStatus = orderStatus; 28 | return this; 29 | } 30 | 31 | public Integer getNotifyStatus() { 32 | return notifyStatus; 33 | } 34 | 35 | public OrderInfoDobj setNotifyStatus(Integer notifyStatus) { 36 | this.notifyStatus = notifyStatus; 37 | return this; 38 | } 39 | 40 | public Integer getPayStatus() { 41 | return payStatus; 42 | } 43 | 44 | public OrderInfoDobj setPayStatus(Integer payStatus) { 45 | this.payStatus = payStatus; 46 | return this; 47 | } 48 | 49 | public String getOrderId() { 50 | return orderId; 51 | } 52 | 53 | public OrderInfoDobj setOrderId(String orderId) { 54 | this.orderId = orderId; 55 | return this; 56 | } 57 | 58 | public String getChannelOrderId() { 59 | return channelOrderId; 60 | } 61 | 62 | public OrderInfoDobj setChannelOrderId(String channelOrderId) { 63 | this.channelOrderId = channelOrderId; 64 | return this; 65 | } 66 | 67 | public String getUserPhoneNo() { 68 | return userPhoneNo; 69 | } 70 | 71 | public OrderInfoDobj setUserPhoneNo(String userPhoneNo) { 72 | this.userPhoneNo = userPhoneNo; 73 | return this; 74 | } 75 | 76 | public String getChargeMoney() { 77 | return chargeMoney; 78 | } 79 | 80 | public OrderInfoDobj setChargeMoney(String chargeMoney) { 81 | this.chargeMoney = chargeMoney; 82 | return this; 83 | } 84 | 85 | public String getPurseId() { 86 | return purseId; 87 | } 88 | 89 | public OrderInfoDobj setPurseId(String purseId) { 90 | this.purseId = purseId; 91 | return this; 92 | } 93 | 94 | public String getMerchantName() { 95 | return merchantName; 96 | } 97 | 98 | public OrderInfoDobj setMerchantName(String merchantName) { 99 | this.merchantName = merchantName; 100 | return this; 101 | } 102 | 103 | @Override 104 | public String toString() { 105 | return "OrderInfoDobj{" + 106 | "orderStatus=" + orderStatus + 107 | ", notifyStatus=" + notifyStatus + 108 | ", payStatus=" + payStatus + 109 | ", orderId='" + orderId + '\'' + 110 | ", channelOrderId='" + channelOrderId + '\'' + 111 | ", userPhoneNo='" + userPhoneNo + '\'' + 112 | ", chargeMoney='" + chargeMoney + '\'' + 113 | ", purseId='" + purseId + '\'' + 114 | ", merchantName='" + merchantName + '\'' + 115 | '}'; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /order-charge-gateway-merchant/src/main/java/com/snowalker/gateway/merchant/order/OrderNotifyController.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.gateway.merchant.order; 2 | 3 | import com.snowalker.gateway.merchant.order.constant.NotifyConstant; 4 | import com.snowalker.gateway.merchant.order.service.OrderService; 5 | import com.snowalker.order.charge.request.ChargeNotifyRequest; 6 | import org.apache.commons.lang3.StringUtils; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.beans.factory.annotation.Value; 10 | import org.springframework.stereotype.Controller; 11 | import org.springframework.web.bind.annotation.*; 12 | import org.springframework.web.context.request.RequestContextHolder; 13 | import org.springframework.web.context.request.ServletRequestAttributes; 14 | 15 | import javax.annotation.Resource; 16 | /** 17 | * @author snowalker 18 | * @version 1.0 19 | * @date 2019/6/10 10:07 20 | * @className OrderNotifyController 21 | * @desc 订单充值回调接口 22 | * 接收并处理成功返回T 23 | * 其余情况返回F,上游充值平台重试 24 | */ 25 | @Controller 26 | @RequestMapping(value = "api") 27 | public class OrderNotifyController { 28 | 29 | private static final Logger LOGGER = LoggerFactory.getLogger(OrderNotifyController.class); 30 | 31 | @Resource(name = "orderService") 32 | OrderService orderService; 33 | 34 | @Value("${agent.config.private.key}") 35 | private String privateKey; 36 | 37 | /** 38 | * 接受充值结果通知 39 | * @param chargeNotifyRequest 40 | * orderStatus 订单状态 41 | * channelOrderId 本平台订单 42 | * platOrderId 充值平台订单 43 | * finishTime 订单结束时间,时间戳yyyyMMddHHmmss 44 | */ 45 | @RequestMapping(value = "callback", method = {RequestMethod.POST}) 46 | public @ResponseBody String chargeNotify(@ModelAttribute ChargeNotifyRequest chargeNotifyRequest) { 47 | ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); 48 | String sessionId = attributes.getSessionId(); 49 | 50 | if (chargeNotifyRequest == null) { 51 | LOGGER.info("sessionId={},通知请求参数chargeNotifyDto==null,不进行处理。", sessionId); 52 | return NotifyConstant.NOTIFY_RETURN_FAIL; 53 | } 54 | LOGGER.info("sessionId={},充值结果通知处理开始,请求入参:chargeNotifyRequest={}", sessionId, chargeNotifyRequest.toString()); 55 | // 获取详细通知参数 56 | String orderStatus = chargeNotifyRequest.getOrder_status(); 57 | String channelOrderId = chargeNotifyRequest.getChannel_orderid(); 58 | String platOrderId = chargeNotifyRequest.getPlat_orderid(); 59 | String finishTime = chargeNotifyRequest.getFinish_time(); 60 | 61 | if (StringUtils.isBlank(orderStatus) || 62 | StringUtils.isBlank(channelOrderId) || 63 | StringUtils.isBlank(platOrderId) || 64 | StringUtils.isBlank(finishTime)) { 65 | LOGGER.info("sessionId={},通知请求参数存在空值,不进行处理。", sessionId); 66 | return NotifyConstant.NOTIFY_RETURN_FAIL; 67 | } 68 | // 签名校验 69 | String originSign = chargeNotifyRequest.getSign(); 70 | String localSign = chargeNotifyRequest.sign(privateKey); 71 | if (!localSign.equals(originSign)) { 72 | LOGGER.info("sessionId={},签名校验失败,不进行处理。originSign={},localSign={}", sessionId, originSign, localSign); 73 | return NotifyConstant.NOTIFY_RETURN_FAIL; 74 | } 75 | LOGGER.info("sessionId={},签名校验成功,准备进行通知处理。originSign={},localSign={}", sessionId, originSign, localSign); 76 | // 执行通知处理逻辑 77 | return orderService.doNotify(sessionId, orderStatus, channelOrderId, platOrderId, finishTime); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /order-charge-message-protocol/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | order-charge-notify 7 | com.snowalker 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | com.snowalker 13 | order-charge-message-protocol 14 | 1.0.0 15 | 16 | order-charge-message-protocol 17 | 18 | 19 | 1.8 20 | 1.8 21 | UTF-8 22 | UTF-8 23 | 24 | 25 | 26 | 27 | junit 28 | junit 29 | 4.13.1 30 | test 31 | 32 | 33 | 34 | 35 | org.apache.commons 36 | commons-lang3 37 | 3.7 38 | 39 | 40 | 41 | 42 | com.google.guava 43 | guava 44 | 29.0-jre 45 | 46 | 47 | 48 | 49 | org.slf4j 50 | slf4j-api 51 | 1.7.21 52 | 53 | 54 | 55 | junit 56 | junit 57 | 4.13.1 58 | test 59 | 60 | 61 | 62 | 63 | com.fasterxml.jackson.core 64 | jackson-databind 65 | 2.12.6.1 66 | 67 | 68 | 69 | com.fasterxml.jackson.core 70 | jackson-core 71 | 2.9.9 72 | 73 | 74 | 75 | com.fasterxml.jackson.core 76 | jackson-annotations 77 | 2.9.9 78 | 79 | 80 | 81 | 82 | 83 | 84 | org.apache.maven.plugins 85 | maven-compiler-plugin 86 | 87 | 8 88 | 8 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /order-charge-sdk/src/main/java/com/snowalker/order/charge/request/ChargeNotifyRequest.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.order.charge.request; 2 | 3 | import com.google.common.base.Preconditions; 4 | import org.apache.commons.codec.digest.DigestUtils; 5 | 6 | import java.io.Serializable; 7 | import java.util.Map; 8 | import java.util.TreeMap; 9 | 10 | /** 11 | * @author snowalker 12 | * @version 1.0 13 | * @date 2019/6/11 10:57 14 | * @className ChargeNotifyDto 15 | * @desc 订单结果通知入参 16 | */ 17 | public class ChargeNotifyRequest implements Serializable { 18 | 19 | private static final long serialVersionUID = -2278245695213335505L; 20 | 21 | /**订单状态*/ 22 | private String order_status; 23 | /**渠道订单id*/ 24 | private String channel_orderid; 25 | /**平台订单id*/ 26 | private String plat_orderid; 27 | /**订单结束时间*/ 28 | private String finish_time; 29 | /**签名*/ 30 | private String sign; 31 | 32 | public String getSign() { 33 | return sign; 34 | } 35 | 36 | public ChargeNotifyRequest setSign(String sign) { 37 | this.sign = sign; 38 | return this; 39 | } 40 | 41 | public String getOrder_status() { 42 | return order_status; 43 | } 44 | 45 | public ChargeNotifyRequest setOrder_status(String order_status) { 46 | this.order_status = order_status; 47 | return this; 48 | } 49 | 50 | public String getChannel_orderid() { 51 | return channel_orderid; 52 | } 53 | 54 | public ChargeNotifyRequest setChannel_orderid(String channel_orderid) { 55 | this.channel_orderid = channel_orderid; 56 | return this; 57 | } 58 | 59 | public String getPlat_orderid() { 60 | return plat_orderid; 61 | } 62 | 63 | public ChargeNotifyRequest setPlat_orderid(String plat_orderid) { 64 | this.plat_orderid = plat_orderid; 65 | return this; 66 | } 67 | 68 | public String getFinish_time() { 69 | return finish_time; 70 | } 71 | 72 | public ChargeNotifyRequest setFinish_time(String finish_time) { 73 | this.finish_time = finish_time; 74 | return this; 75 | } 76 | 77 | /** 78 | * MD5签名 79 | */ 80 | public String sign(String privateKey) { 81 | Preconditions.checkNotNull(this.getChannel_orderid()); 82 | Preconditions.checkNotNull(this.getFinish_time()); 83 | Preconditions.checkNotNull(this.getOrder_status()); 84 | Preconditions.checkNotNull(this.getPlat_orderid()); 85 | Preconditions.checkNotNull(privateKey); 86 | // 参数排序 87 | Map params = new TreeMap<>(); 88 | params.put("order_status", this.getOrder_status()); 89 | params.put("channel_orderid", this.getChannel_orderid()); 90 | params.put("plat_orderid", this.getPlat_orderid()); 91 | params.put("finish_time", this.getFinish_time()); 92 | params.put("privateKey", privateKey); 93 | // 参数拼装并MD5签名 94 | StringBuilder signSourceBuilder = new StringBuilder(); 95 | for (String key : params.keySet()) { 96 | signSourceBuilder.append(key).append("=").append(params.get(key)).append("&"); 97 | } 98 | // 去除最后一个& 99 | String signSource = signSourceBuilder.toString(); 100 | String beforeSign = signSource.substring(0, signSource.length() - 1); 101 | // md5签名 102 | return DigestUtils.md5Hex(beforeSign); 103 | } 104 | 105 | @Override 106 | public String toString() { 107 | return "ChargeNotifyRequest{" + 108 | "order_status='" + order_status + '\'' + 109 | ", channel_orderid='" + channel_orderid + '\'' + 110 | ", plat_orderid='" + plat_orderid + '\'' + 111 | ", finish_time='" + finish_time + '\'' + 112 | ", sign='" + sign + '\'' + 113 | '}'; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /order-charge-gateway-merchant/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 2.1.5.RELEASE 10 | 11 | 12 | 13 | com.snowalker 14 | order-charge-gateway-merchant 15 | 1.0.0 16 | order-charge-gateway-merchant 17 | Demo project for Spring Boot 18 | 19 | 20 | 1.8 21 | 1.8 22 | UTF-8 23 | UTF-8 24 | 1.3.2 25 | 29.0-jre 26 | 27 | 28 | 29 | 30 | 31 | 32 | com.snowalker 33 | order-charge-sdk 34 | 1.0.0 35 | 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-starter-web 40 | 41 | 42 | 43 | org.springframework.boot 44 | spring-boot-starter-test 45 | test 46 | 47 | 48 | 49 | org.springframework.boot 50 | spring-boot-starter-jdbc 51 | 52 | 53 | 54 | mysql 55 | mysql-connector-java 56 | runtime 57 | 58 | 59 | 60 | 61 | org.apache.commons 62 | commons-lang3 63 | 3.4 64 | 65 | 66 | 67 | 68 | com.alibaba 69 | fastjson 70 | 1.2.58 71 | 72 | 73 | 74 | 75 | com.google.guava 76 | guava 77 | ${guava-version} 78 | 79 | 80 | 81 | 82 | commons-codec 83 | commons-codec 84 | 1.12 85 | 86 | 87 | 88 | 89 | org.mybatis.spring.boot 90 | mybatis-spring-boot-starter 91 | ${mybatis-spring-boot-starter-version} 92 | 93 | 94 | 95 | 96 | 97 | 98 | org.springframework.boot 99 | spring-boot-maven-plugin 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /order-charge-gateway-server/src/main/java/com/snowalker/order/mq/payment/producer/listener/ChargeOrderTranListenerImpl.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.order.mq.payment.producer.listener; 2 | 3 | /** 4 | * @author snowalker 5 | * @version 1.0 6 | * @date 2019/6/11 15:51 7 | * @className ChargeOrderTranListenerImpl 8 | * @desc 订单交易事务监听器回调实现 9 | */ 10 | import com.snowalker.order.charge.message.protocol.WalletPaymentProtocol; 11 | import com.snowalker.order.common.dao.dataobject.OrderInfoDO; 12 | import com.snowalker.order.common.dao.dataobject.OrderInfoDobj; 13 | import com.snowalker.order.common.service.OrderChargeService; 14 | import com.snowalker.order.common.util.LogExceptionWapper; 15 | import org.apache.rocketmq.client.producer.LocalTransactionState; 16 | import org.apache.rocketmq.client.producer.TransactionListener; 17 | import org.apache.rocketmq.common.message.Message; 18 | import org.apache.rocketmq.common.message.MessageExt; 19 | import org.slf4j.Logger; 20 | import org.slf4j.LoggerFactory; 21 | import org.springframework.beans.BeanUtils; 22 | import org.springframework.stereotype.Component; 23 | 24 | import javax.annotation.Resource; 25 | 26 | /** 27 | * 下单事务消息本地回调实现 28 | * 主要实现:本地订单入库、订单事务回查 29 | * TODO 需要学员实现 30 | * @author snowalker 31 | * @date 2019/6/11 16:09 32 | */ 33 | @Component(value = "chargeOrderTranListenerImpl") 34 | public class ChargeOrderTranListenerImpl implements TransactionListener { 35 | 36 | private static final Logger LOGGER = LoggerFactory.getLogger(ChargeOrderTranListenerImpl.class); 37 | 38 | @Resource(name = "orderChargeService") 39 | OrderChargeService orderChargeService; 40 | 41 | /** 42 | * 执行本地订单入库操作 43 | * @param msg 44 | * @param arg 45 | * @return 46 | */ 47 | @Override 48 | public LocalTransactionState executeLocalTransaction(Message msg, Object arg) { 49 | // 消息解码 50 | String message = new String(msg.getBody()); 51 | WalletPaymentProtocol walletPaymentProtocol = new WalletPaymentProtocol(); 52 | walletPaymentProtocol.decode(message); 53 | LOGGER.info("订单入库实体WalletPaymentProtocol={}", walletPaymentProtocol.toString()); 54 | // 组装下单实体 55 | OrderInfoDO orderInfoDO = new OrderInfoDO(); 56 | BeanUtils.copyProperties(walletPaymentProtocol, orderInfoDO); 57 | String orderId = orderInfoDO.getOrderId(); 58 | // 执行下单操作 59 | try { 60 | if (!orderChargeService.insertOrder(orderInfoDO)) { 61 | LOGGER.error("订单入库失败,事务消息回滚,LocalTransactionState={},orderId={}", LocalTransactionState.ROLLBACK_MESSAGE, orderId); 62 | return LocalTransactionState.ROLLBACK_MESSAGE; 63 | } 64 | } catch (Exception e) { 65 | LOGGER.error("订单入库异常,等待回查发起,orderId={},e={}", orderId, LogExceptionWapper.getStackTrace(e)); 66 | return LocalTransactionState.UNKNOW; 67 | } 68 | LOGGER.info("订单入库成功,orderId={}", orderId); 69 | return LocalTransactionState.COMMIT_MESSAGE; 70 | } 71 | 72 | /** 73 | * 根据订单号进行回查 74 | * @param msg 75 | * @return 76 | */ 77 | @Override 78 | public LocalTransactionState checkLocalTransaction(MessageExt msg) { 79 | String message = new String(msg.getBody()); 80 | String msgId = msg.getMsgId(); 81 | int reconsumeTimes = msg.getReconsumeTimes(); 82 | LOGGER.info("订单入库本地事务回查--接收到消息, msgId={},message={},reconsumeTimes={}", msgId, message, reconsumeTimes); 83 | // 消息解码 84 | WalletPaymentProtocol walletPaymentProtocol = new WalletPaymentProtocol(); 85 | walletPaymentProtocol.decode(message); 86 | String orderId = walletPaymentProtocol.getOrderId(); 87 | // 订单查询 88 | OrderInfoDO orderInfoDO = new OrderInfoDO().setOrderId(orderId); 89 | OrderInfoDobj orderInfoDobj = orderChargeService.queryOrderInfo(orderInfoDO); 90 | if (orderInfoDobj == null) { 91 | LOGGER.info("订单入库本地事务回查--本地不存在订单,[消息回滚],orderId={},msgId={}", orderId, msgId); 92 | return LocalTransactionState.ROLLBACK_MESSAGE; 93 | } 94 | LOGGER.info("订单入库本地事务回查--本地存在订单信息,orderInfoDobj={},msgId={},[消息提交]", orderInfoDobj, msgId); 95 | return LocalTransactionState.COMMIT_MESSAGE; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /order-charge-gateway-notify/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.1.5.RELEASE 9 | 10 | 11 | com.snowalker 12 | order-charge-notify 13 | 1.0.0 14 | order-charge-notify 15 | Demo project for Spring Boot 16 | 17 | 18 | 1.8 19 | 1.8 20 | UTF-8 21 | UTF-8 22 | 1.3.2 23 | 29.0-jre 24 | 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | 34 | 35 | com.snowalker 36 | order-charge-sdk 37 | 1.0.0 38 | 39 | 40 | 41 | 42 | org.apache.rocketmq 43 | rocketmq-client 44 | 4.3.2 45 | 46 | 47 | 48 | com.snowalker 49 | order-charge-message-protocol 50 | 1.0.0 51 | 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-starter-test 56 | test 57 | 58 | 59 | 60 | org.springframework.boot 61 | spring-boot-starter-jdbc 62 | 63 | 64 | 65 | mysql 66 | mysql-connector-java 67 | runtime 68 | 69 | 70 | 71 | 72 | org.apache.commons 73 | commons-lang3 74 | 3.4 75 | 76 | 77 | 78 | 79 | com.alibaba 80 | fastjson 81 | 1.2.58 82 | 83 | 84 | 85 | 86 | com.google.guava 87 | guava 88 | ${guava-version} 89 | 90 | 91 | 92 | 93 | commons-codec 94 | commons-codec 95 | 1.12 96 | 97 | 98 | 99 | 100 | org.mybatis.spring.boot 101 | mybatis-spring-boot-starter 102 | ${mybatis-spring-boot-starter-version} 103 | 104 | 105 | 106 | 107 | 108 | 109 | org.springframework.boot 110 | spring-boot-maven-plugin 111 | 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /order-charge-gateway-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 2.1.5.RELEASE 10 | 11 | 12 | 13 | com.snowalker 14 | order-charge-gateway 15 | 1.0.0 16 | order-charge-gateway 17 | Demo project for Spring Boot 18 | 19 | 20 | 1.8 21 | 1.8 22 | UTF-8 23 | UTF-8 24 | 1.3.2 25 | 29.0-jre 26 | 27 | 28 | 29 | 30 | 31 | 32 | com.snowalker 33 | order-charge-sdk 34 | 1.0.0 35 | 36 | 37 | 38 | 39 | org.apache.rocketmq 40 | rocketmq-client 41 | 4.3.2 42 | 43 | 44 | 45 | com.snowalker 46 | order-charge-message-protocol 47 | 1.0.0 48 | 49 | 50 | 51 | org.springframework.boot 52 | spring-boot-starter-web 53 | 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-starter-test 58 | test 59 | 60 | 61 | 62 | org.springframework.boot 63 | spring-boot-starter-jdbc 64 | 65 | 66 | 67 | mysql 68 | mysql-connector-java 69 | runtime 70 | 71 | 72 | 73 | 74 | org.apache.commons 75 | commons-lang3 76 | 3.4 77 | 78 | 79 | 80 | 81 | com.alibaba 82 | fastjson 83 | 1.2.58 84 | 85 | 86 | 87 | 88 | com.google.guava 89 | guava 90 | ${guava-version} 91 | 92 | 93 | 94 | 95 | commons-codec 96 | commons-codec 97 | 1.12 98 | 99 | 100 | 101 | 102 | org.mybatis.spring.boot 103 | mybatis-spring-boot-starter 104 | ${mybatis-spring-boot-starter-version} 105 | 106 | 107 | 108 | 109 | 110 | 111 | org.springframework.boot 112 | spring-boot-maven-plugin 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /order-charge-gateway-notify/src/main/java/com/snowalker/notify/common/service/impl/WalletServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.notify.common.service.impl; 2 | 3 | import com.snowalker.notify.common.dao.WalletMapper; 4 | import com.snowalker.notify.common.dao.dataobject.ChargeRecordEntity; 5 | import com.snowalker.notify.common.dao.dataobject.OrderEntity; 6 | import com.snowalker.notify.common.dao.dataobject.WalletEntity; 7 | import com.snowalker.notify.common.service.WalletService; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.stereotype.Service; 12 | import org.springframework.transaction.annotation.Transactional; 13 | 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | import java.util.UUID; 17 | 18 | /** 19 | * @author snowalker 20 | * @version 1.0 21 | * @date 2019/6/12 9:20 22 | * @className WalletServiceImpl 23 | * @desc 钱包接口实现 24 | */ 25 | @Service(value = "walletService") 26 | public class WalletServiceImpl implements WalletService { 27 | 28 | private static final Logger LOGGER = LoggerFactory.getLogger(WalletServiceImpl.class); 29 | 30 | @Autowired 31 | WalletMapper walletMapper; 32 | 33 | /** 34 | * 新增用户钱包 35 | * @param walletEntity 36 | */ 37 | @Transactional(rollbackFor = Exception.class) 38 | @Override 39 | public void insertWallet(WalletEntity walletEntity) { 40 | String purseId = walletEntity.getPurseId(); 41 | try { 42 | walletMapper.insertWallet(walletEntity); 43 | LOGGER.info("钱包数据插入成功,purseId={}", purseId); 44 | } catch (Exception e) { 45 | LOGGER.error("钱包数据插入异常,purseId={},e={}", purseId, e); 46 | throw e; 47 | } 48 | } 49 | 50 | /** 51 | * 修改用户钱包数据 52 | * @param walletEntity 53 | * @return 54 | */ 55 | @Transactional(rollbackFor = Exception.class) 56 | @Override 57 | public boolean updateWallet(WalletEntity walletEntity, String orderId) { 58 | // 插入扣款流水 59 | Map paramsInsert = new HashMap<>(16); 60 | String recordId = UUID.randomUUID().toString().replace("-", ""); 61 | paramsInsert.put("recordId", recordId); 62 | paramsInsert.put("orderId", orderId); 63 | paramsInsert.put("purseId", walletEntity.getPurseId()); 64 | paramsInsert.put("merchantName", walletEntity.getMerchantName()); 65 | paramsInsert.put("chargeMoney", walletEntity.getChargeMoney()); 66 | try { 67 | walletMapper.insertChargeRecord(paramsInsert); 68 | LOGGER.info("扣款流水插入成功,purseId={},recordId={}", walletEntity.getPurseId(), recordId); 69 | } catch (Exception e) { 70 | LOGGER.error("扣款流水插入异常,purseId={},recordId={},e={}", walletEntity.getPurseId(), recordId, e); 71 | throw e; 72 | } 73 | // 执行扣款 74 | Map paramsUpdate = new HashMap<>(16); 75 | paramsUpdate.put("chargeMoney", walletEntity.getChargeMoney()); 76 | paramsUpdate.put("version", walletEntity.getVersion()); 77 | paramsUpdate.put("purseId", walletEntity.getPurseId()); 78 | int updateCount = walletMapper.updateWallet(paramsUpdate); 79 | if (updateCount == 1) { 80 | LOGGER.info("钱包数据修改成功,purseId={}", walletEntity.getPurseId()); 81 | return true; 82 | } else { 83 | LOGGER.error("并发修改钱包数据修改异常,purseId={}", walletEntity.getPurseId()); 84 | throw new RuntimeException("Exception occurred while updating wallet, purseId=" + walletEntity.getPurseId()); 85 | } 86 | } 87 | 88 | /** 89 | * 查询扣款流水 90 | * @param orderId 91 | * @return 92 | */ 93 | @Override 94 | public ChargeRecordEntity queryChargeRecordByOrderId(String orderId) { 95 | OrderEntity orderEntity = new OrderEntity(); 96 | orderEntity.setOrderId(orderId); 97 | return walletMapper.queryChargeRecordByOrderId(orderEntity); 98 | } 99 | 100 | /** 101 | * 查询钱包信息 102 | * @param purseId 103 | * @return 104 | */ 105 | @Override 106 | public WalletEntity queryWalletInfoByPurseId(String purseId) { 107 | Map queryParams = new HashMap<>(16); 108 | queryParams.put("purseId", purseId); 109 | WalletEntity walletEntity = walletMapper.queryWalletInfoByPurseId(queryParams); 110 | LOGGER.info("根据钱包purseId={},查询到钱包信息:walletInfo={}", purseId, walletEntity.toString()); 111 | return walletEntity; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /order-charge-gateway-notify/src/main/java/com/snowalker/notify/mq/payment/producer/listener/LocalTranListenerImpl.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.notify.mq.payment.producer.listener; 2 | 3 | import com.snowalker.notify.common.dao.dataobject.ChargeRecordEntity; 4 | import com.snowalker.notify.common.dao.dataobject.WalletEntity; 5 | import com.snowalker.notify.common.service.WalletService; 6 | import com.snowalker.notify.common.util.LogExceptionWapper; 7 | import com.snowalker.order.charge.message.protocol.OrderStatusUpdateProtocol; 8 | import com.snowalker.order.charge.message.protocol.WalletPaymentProtocol; 9 | import org.apache.rocketmq.client.producer.LocalTransactionState; 10 | import org.apache.rocketmq.client.producer.TransactionListener; 11 | import org.apache.rocketmq.common.message.Message; 12 | import org.apache.rocketmq.common.message.MessageExt; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | import org.springframework.beans.factory.annotation.Autowired; 16 | import org.springframework.stereotype.Component; 17 | 18 | import java.math.BigDecimal; 19 | 20 | /** 21 | * @author snowalker 22 | * @version 1.0 23 | * @date 2019/6/12 9:41 24 | * @className PaymentTranListenerImpl 25 | * @desc 扣款本地事务监听回调 26 | */ 27 | @Component(value = "localTranListenerImpl") 28 | public class LocalTranListenerImpl implements TransactionListener { 29 | 30 | private static final Logger LOGGER = LoggerFactory.getLogger(LocalTranListenerImpl.class); 31 | 32 | @Autowired 33 | WalletService walletService; 34 | 35 | /** 36 | * 扣款本地事务 37 | * @param msg 38 | * @param arg 39 | * @return 40 | */ 41 | @Override 42 | public LocalTransactionState executeLocalTransaction(Message msg, Object arg) { 43 | String message = new String(msg.getBody()); 44 | LOGGER.info("[扣款本地事务监听回调]执行逻辑--接收到消息, message={}", message); 45 | OrderStatusUpdateProtocol protocol = new OrderStatusUpdateProtocol(); 46 | protocol.decode(message); 47 | // 扣款 48 | String purseId = protocol.getPurseId(); 49 | String merchantName = protocol.getMerchantName(); 50 | LOGGER.info("[扣款本地事务监听回调]反序列化扣款消息成功,开始进行扣款操作,purseId={}", purseId); 51 | WalletEntity walletEntity = new WalletEntity(); 52 | walletEntity.setChargeMoney( 53 | new BigDecimal(protocol.getChargeMoney())) 54 | .setPurseId(purseId) 55 | .setMerchantName(merchantName); 56 | // 查询当前账户信息获取版本号,基于乐观锁更新 57 | WalletEntity realWalletInfo = walletService.queryWalletInfoByPurseId(protocol.getPurseId()); 58 | // 判断是否足够扣减 59 | BigDecimal currBalanceAccount = realWalletInfo.getBalanceAccount(); 60 | if (currBalanceAccount.subtract(new BigDecimal(protocol.getChargeMoney())).longValue() < 0) { 61 | LOGGER.error("执行钱包扣款,账户不足扣减,消息回滚.purseId={}", purseId); 62 | return LocalTransactionState.ROLLBACK_MESSAGE; 63 | } 64 | // 足够扣减,进行账户扣减 65 | int version = realWalletInfo.getVersion(); 66 | walletEntity.setVersion(version); 67 | try { 68 | if (!walletService.updateWallet(walletEntity, protocol.getOrderId())) { 69 | return LocalTransactionState.ROLLBACK_MESSAGE; 70 | } 71 | } catch (Exception e) { 72 | LOGGER.error("执行钱包扣款本地扣款异常,purseId={},e={}", purseId, LogExceptionWapper.getStackTrace(e)); 73 | return LocalTransactionState.UNKNOW; 74 | } 75 | LOGGER.info("[扣款本地事务监听回调]扣款本地操作成功,本地事务提交,purseId={}", purseId); 76 | return LocalTransactionState.COMMIT_MESSAGE; 77 | } 78 | 79 | /** 80 | * 本地事务回查: 回查依据是否存在扣款流水 81 | * @param msg 82 | * @return 83 | */ 84 | @Override 85 | public LocalTransactionState checkLocalTransaction(MessageExt msg) { 86 | // 本地事务回查,查询扣款流水 87 | String message = new String(msg.getBody()); 88 | String msgId = msg.getMsgId(); 89 | LOGGER.info("[扣款本地事务回查]-接收到消息,msgId={},message={}", msgId, message); 90 | WalletPaymentProtocol payProtocol = new WalletPaymentProtocol(); 91 | payProtocol.decode(message); 92 | // 获取订单号 93 | String orderId = payProtocol.getOrderId(); 94 | ChargeRecordEntity chargeRecordEntity = 95 | walletService.queryChargeRecordByOrderId(orderId); 96 | if (chargeRecordEntity == null) { 97 | LOGGER.info("[扣款本地事务回查]-本地不存在扣款流水,消息回滚,msgId={}", msgId); 98 | return LocalTransactionState.ROLLBACK_MESSAGE; 99 | } 100 | LOGGER.info("[扣款本地事务回查]-本地存在扣款流水,消息提交,msgId={}", msgId); 101 | return LocalTransactionState.COMMIT_MESSAGE; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /order-charge-gateway-merchant/src/main/java/com/snowalker/gateway/merchant/order/dataobject/ChargeOrderDO.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.gateway.merchant.order.dataobject; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.Date; 5 | 6 | /** 7 | * @author snowalker 8 | * @version 1.0 9 | * @date 2019/6/11 11:26 10 | * @className ChargeOrderDO 11 | * @desc 订单数据库DO 12 | */ 13 | public class ChargeOrderDO { 14 | 15 | private Integer id; 16 | private Date gmtCreate; 17 | private Date gmtUpdate; 18 | private String orderId; 19 | private Integer orderStatus; 20 | private String userPhoneNo; 21 | private String prodId; 22 | private String prodName; 23 | private String outProdId; 24 | private String outProdName; 25 | private BigDecimal chargeMoney; 26 | private Date finishTime; 27 | private String outOrderId; 28 | 29 | public Integer getId() { 30 | return id; 31 | } 32 | 33 | public ChargeOrderDO setId(Integer id) { 34 | this.id = id; 35 | return this; 36 | } 37 | 38 | public Date getGmtCreate() { 39 | return gmtCreate; 40 | } 41 | 42 | public ChargeOrderDO setGmtCreate(Date gmtCreate) { 43 | this.gmtCreate = gmtCreate; 44 | return this; 45 | } 46 | 47 | public Date getGmtUpdate() { 48 | return gmtUpdate; 49 | } 50 | 51 | public ChargeOrderDO setGmtUpdate(Date gmtUpdate) { 52 | this.gmtUpdate = gmtUpdate; 53 | return this; 54 | } 55 | 56 | public String getOrderId() { 57 | return orderId; 58 | } 59 | 60 | public ChargeOrderDO setOrderId(String orderId) { 61 | this.orderId = orderId; 62 | return this; 63 | } 64 | 65 | public Integer getOrderStatus() { 66 | return orderStatus; 67 | } 68 | 69 | public ChargeOrderDO setOrderStatus(Integer orderStatus) { 70 | this.orderStatus = orderStatus; 71 | return this; 72 | } 73 | 74 | public String getUserPhoneNo() { 75 | return userPhoneNo; 76 | } 77 | 78 | public ChargeOrderDO setUserPhoneNo(String userPhoneNo) { 79 | this.userPhoneNo = userPhoneNo; 80 | return this; 81 | } 82 | 83 | public String getProdId() { 84 | return prodId; 85 | } 86 | 87 | public ChargeOrderDO setProdId(String prodId) { 88 | this.prodId = prodId; 89 | return this; 90 | } 91 | 92 | public String getProdName() { 93 | return prodName; 94 | } 95 | 96 | public ChargeOrderDO setProdName(String prodName) { 97 | this.prodName = prodName; 98 | return this; 99 | } 100 | 101 | public String getOutProdId() { 102 | return outProdId; 103 | } 104 | 105 | public ChargeOrderDO setOutProdId(String outProdId) { 106 | this.outProdId = outProdId; 107 | return this; 108 | } 109 | 110 | public String getOutProdName() { 111 | return outProdName; 112 | } 113 | 114 | public ChargeOrderDO setOutProdName(String outProdName) { 115 | this.outProdName = outProdName; 116 | return this; 117 | } 118 | 119 | public BigDecimal getChargeMoney() { 120 | return chargeMoney; 121 | } 122 | 123 | public ChargeOrderDO setChargeMoney(BigDecimal chargeMoney) { 124 | this.chargeMoney = chargeMoney; 125 | return this; 126 | } 127 | 128 | public Date getFinishTime() { 129 | return finishTime; 130 | } 131 | 132 | public ChargeOrderDO setFinishTime(Date finishTime) { 133 | this.finishTime = finishTime; 134 | return this; 135 | } 136 | 137 | public String getOutOrderId() { 138 | return outOrderId; 139 | } 140 | 141 | public ChargeOrderDO setOutOrderId(String outOrderId) { 142 | this.outOrderId = outOrderId; 143 | return this; 144 | } 145 | 146 | @Override 147 | public String toString() { 148 | return "ChargeOrderDO{" + 149 | "id=" + id + 150 | ", gmtCreate=" + gmtCreate + 151 | ", gmtUpdate=" + gmtUpdate + 152 | ", orderId='" + orderId + '\'' + 153 | ", orderStatus=" + orderStatus + 154 | ", userPhoneNo='" + userPhoneNo + '\'' + 155 | ", prodId='" + prodId + '\'' + 156 | ", prodName='" + prodName + '\'' + 157 | ", outProdId='" + outProdId + '\'' + 158 | ", outProdName='" + outProdName + '\'' + 159 | ", chargeMoney=" + chargeMoney + 160 | ", finishTime=" + finishTime + 161 | ", outOrderId='" + outOrderId + '\'' + 162 | '}'; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /order-charge-gateway-merchant/src/main/java/com/snowalker/gateway/merchant/order/dto/ChargeOrderDto.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.gateway.merchant.order.dto; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.Date; 5 | 6 | /** 7 | * @author snowalker 8 | * @version 1.0 9 | * @date 2019/6/10 13:37 10 | * @className ChargeOrderDto 11 | * @desc 本地下单入参 12 | */ 13 | public class ChargeOrderDto { 14 | 15 | private String orderId; 16 | private String phoneNum; 17 | private int orderStatus = 1; 18 | private String productId; 19 | private String productName; 20 | private String outProductId; 21 | private String outProductName; 22 | private BigDecimal chargeMoney; 23 | 24 | /**更新前状态*/ 25 | private int beforeUpdateOrderStatus; 26 | /**欲更新状态*/ 27 | private int afterUpdateOrderStatus; 28 | /**结束时间*/ 29 | private Date finishTime; 30 | /**外部订单号*/ 31 | private String outOrderId; 32 | 33 | public String getOutOrderId() { 34 | return outOrderId; 35 | } 36 | 37 | public ChargeOrderDto setOutOrderId(String outOrderId) { 38 | this.outOrderId = outOrderId; 39 | return this; 40 | } 41 | 42 | public Date getFinishTime() { 43 | return finishTime; 44 | } 45 | 46 | public ChargeOrderDto setFinishTime(Date finishTime) { 47 | this.finishTime = finishTime; 48 | return this; 49 | } 50 | 51 | public int getBeforeUpdateOrderStatus() { 52 | return beforeUpdateOrderStatus; 53 | } 54 | 55 | public ChargeOrderDto setBeforeUpdateOrderStatus(int beforeUpdateOrderStatus) { 56 | this.beforeUpdateOrderStatus = beforeUpdateOrderStatus; 57 | return this; 58 | } 59 | 60 | public int getAfterUpdateOrderStatus() { 61 | return afterUpdateOrderStatus; 62 | } 63 | 64 | public ChargeOrderDto setAfterUpdateOrderStatus(int afterUpdateOrderStatus) { 65 | this.afterUpdateOrderStatus = afterUpdateOrderStatus; 66 | return this; 67 | } 68 | 69 | public BigDecimal getChargeMoney() { 70 | return chargeMoney; 71 | } 72 | 73 | public ChargeOrderDto setChargeMoney(BigDecimal chargeMoney) { 74 | this.chargeMoney = chargeMoney; 75 | return this; 76 | } 77 | 78 | public String getOrderId() { 79 | return orderId; 80 | } 81 | 82 | public ChargeOrderDto setOrderId(String orderId) { 83 | this.orderId = orderId; 84 | return this; 85 | } 86 | 87 | public String getPhoneNum() { 88 | return phoneNum; 89 | } 90 | 91 | public ChargeOrderDto setPhoneNum(String phoneNum) { 92 | this.phoneNum = phoneNum; 93 | return this; 94 | } 95 | 96 | public int getOrderStatus() { 97 | return orderStatus; 98 | } 99 | 100 | public ChargeOrderDto setOrderStatus(int orderStatus) { 101 | this.orderStatus = orderStatus; 102 | return this; 103 | } 104 | 105 | public String getProductId() { 106 | return productId; 107 | } 108 | 109 | public ChargeOrderDto setProductId(String productId) { 110 | this.productId = productId; 111 | return this; 112 | } 113 | 114 | public String getProductName() { 115 | return productName; 116 | } 117 | 118 | public ChargeOrderDto setProductName(String productName) { 119 | this.productName = productName; 120 | return this; 121 | } 122 | 123 | public String getOutProductId() { 124 | return outProductId; 125 | } 126 | 127 | public ChargeOrderDto setOutProductId(String outProductId) { 128 | this.outProductId = outProductId; 129 | return this; 130 | } 131 | 132 | public String getOutProductName() { 133 | return outProductName; 134 | } 135 | 136 | public ChargeOrderDto setOutProductName(String outProductName) { 137 | this.outProductName = outProductName; 138 | return this; 139 | } 140 | 141 | @Override 142 | public String toString() { 143 | return "ChargeOrderDto{" + 144 | "orderId='" + orderId + '\'' + 145 | ", phoneNum='" + phoneNum + '\'' + 146 | ", orderStatus=" + orderStatus + 147 | ", productId='" + productId + '\'' + 148 | ", productName='" + productName + '\'' + 149 | ", outProductId='" + outProductId + '\'' + 150 | ", outProductName='" + outProductName + '\'' + 151 | ", chargeMoney=" + chargeMoney + 152 | ", beforeUpdateOrderStatus=" + beforeUpdateOrderStatus + 153 | ", afterUpdateOrderStatus=" + afterUpdateOrderStatus + 154 | ", finishTime=" + finishTime + 155 | ", outOrderId='" + outOrderId + '\'' + 156 | '}'; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /order-charge-gateway-server/src/main/java/com/snowalker/order/common/dao/dataobject/OrderInfoDO.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.order.common.dao.dataobject; 2 | 3 | /** 4 | * @author snowalker 5 | * @version 1.0 6 | * @date 2019/6/11 19:50 7 | * @className OrderInfoDO 8 | * @desc 订单数据库映射实体 9 | */ 10 | public class OrderInfoDO { 11 | 12 | private String orderId; 13 | private String channelOrderId; 14 | private String userPhoneNo; 15 | private String chargeMoney; 16 | private String purseId; 17 | private String merchantName; 18 | 19 | /**修改前后的订单状态*/ 20 | private Integer beforeOrderStatus; 21 | private Integer afterOrderStatus; 22 | /**修改前后的通知状态*/ 23 | private Integer beforeNotifyStatus; 24 | private Integer afterNotifyStatus; 25 | /**修改前后的支付状态*/ 26 | private Integer beforePayStatus; 27 | private Integer afterPayStatus; 28 | 29 | public Integer getBeforeOrderStatus() { 30 | return beforeOrderStatus; 31 | } 32 | 33 | public OrderInfoDO setBeforeOrderStatus(Integer beforeOrderStatus) { 34 | this.beforeOrderStatus = beforeOrderStatus; 35 | return this; 36 | } 37 | 38 | public Integer getAfterOrderStatus() { 39 | return afterOrderStatus; 40 | } 41 | 42 | public OrderInfoDO setAfterOrderStatus(Integer afterOrderStatus) { 43 | this.afterOrderStatus = afterOrderStatus; 44 | return this; 45 | } 46 | 47 | public Integer getBeforeNotifyStatus() { 48 | return beforeNotifyStatus; 49 | } 50 | 51 | public OrderInfoDO setBeforeNotifyStatus(Integer beforeNotifyStatus) { 52 | this.beforeNotifyStatus = beforeNotifyStatus; 53 | return this; 54 | } 55 | 56 | public Integer getAfterNotifyStatus() { 57 | return afterNotifyStatus; 58 | } 59 | 60 | public OrderInfoDO setAfterNotifyStatus(Integer afterNotifyStatus) { 61 | this.afterNotifyStatus = afterNotifyStatus; 62 | return this; 63 | } 64 | 65 | public Integer getBeforePayStatus() { 66 | return beforePayStatus; 67 | } 68 | 69 | public OrderInfoDO setBeforePayStatus(Integer beforePayStatus) { 70 | this.beforePayStatus = beforePayStatus; 71 | return this; 72 | } 73 | 74 | public Integer getAfterPayStatus() { 75 | return afterPayStatus; 76 | } 77 | 78 | public OrderInfoDO setAfterPayStatus(Integer afterPayStatus) { 79 | this.afterPayStatus = afterPayStatus; 80 | return this; 81 | } 82 | 83 | public String getOrderId() { 84 | return orderId; 85 | } 86 | 87 | public OrderInfoDO setOrderId(String orderId) { 88 | this.orderId = orderId; 89 | return this; 90 | } 91 | 92 | public String getChannelOrderId() { 93 | return channelOrderId; 94 | } 95 | 96 | public OrderInfoDO setChannelOrderId(String channelOrderId) { 97 | this.channelOrderId = channelOrderId; 98 | return this; 99 | } 100 | 101 | public String getUserPhoneNo() { 102 | return userPhoneNo; 103 | } 104 | 105 | public OrderInfoDO setUserPhoneNo(String userPhoneNo) { 106 | this.userPhoneNo = userPhoneNo; 107 | return this; 108 | } 109 | 110 | public String getChargeMoney() { 111 | return chargeMoney; 112 | } 113 | 114 | public OrderInfoDO setChargeMoney(String chargeMoney) { 115 | this.chargeMoney = chargeMoney; 116 | return this; 117 | } 118 | 119 | public String getPurseId() { 120 | return purseId; 121 | } 122 | 123 | public OrderInfoDO setPurseId(String purseId) { 124 | this.purseId = purseId; 125 | return this; 126 | } 127 | 128 | public String getMerchantName() { 129 | return merchantName; 130 | } 131 | 132 | public OrderInfoDO setMerchantName(String merchantName) { 133 | this.merchantName = merchantName; 134 | return this; 135 | } 136 | 137 | @Override 138 | public String toString() { 139 | return "OrderInfoDO{" + 140 | "orderId='" + orderId + '\'' + 141 | ", channelOrderId='" + channelOrderId + '\'' + 142 | ", userPhoneNo='" + userPhoneNo + '\'' + 143 | ", chargeMoney='" + chargeMoney + '\'' + 144 | ", purseId='" + purseId + '\'' + 145 | ", merchantName='" + merchantName + '\'' + 146 | ", beforeOrderStatus=" + beforeOrderStatus + 147 | ", afterOrderStatus=" + afterOrderStatus + 148 | ", beforeNotifyStatus=" + beforeNotifyStatus + 149 | ", afterNotifyStatus=" + afterNotifyStatus + 150 | ", beforePayStatus=" + beforePayStatus + 151 | ", afterPayStatus=" + afterPayStatus + 152 | '}'; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /order-charge-gateway-notify/src/main/java/com/snowalker/notify/common/dao/dataobject/OrderEntity.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.notify.common.dao.dataobject; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.Date; 5 | 6 | /** 7 | * @author snowalker 8 | * @version 1.0 9 | * @date 2019/2/15 16:46 10 | * @className OrderEntity 11 | * @desc 模拟订单数据库实体 12 | */ 13 | public class OrderEntity { 14 | 15 | private int id; 16 | private String orderId; 17 | /**支付状态 1 初始化 2 处理中 3 失败 0 成功*/ 18 | private int orderStatus; 19 | /**支付状态 1 初始化 2 处理中 3 失败 0 成功*/ 20 | private int payStatus; 21 | private String userName; 22 | private BigDecimal chargeMoney; 23 | /**钱包id*/ 24 | private String purseId; 25 | private Date gmtCreate; 26 | private Date gmtUpdate; 27 | /**通知状态,1 初始化 2 通知处理中 3 失败 0 成功 -1 不需要通知*/ 28 | private Integer notifyStatus; 29 | private String channelOrderId; 30 | private String userPhoneNo; 31 | private String merchantName; 32 | 33 | public Integer getNotifyStatus() { 34 | return notifyStatus; 35 | } 36 | 37 | public OrderEntity setNotifyStatus(Integer notifyStatus) { 38 | this.notifyStatus = notifyStatus; 39 | return this; 40 | } 41 | 42 | public String getChannelOrderId() { 43 | return channelOrderId; 44 | } 45 | 46 | public OrderEntity setChannelOrderId(String channelOrderId) { 47 | this.channelOrderId = channelOrderId; 48 | return this; 49 | } 50 | 51 | public String getUserPhoneNo() { 52 | return userPhoneNo; 53 | } 54 | 55 | public OrderEntity setUserPhoneNo(String userPhoneNo) { 56 | this.userPhoneNo = userPhoneNo; 57 | return this; 58 | } 59 | 60 | public String getMerchantName() { 61 | return merchantName; 62 | } 63 | 64 | public OrderEntity setMerchantName(String merchantName) { 65 | this.merchantName = merchantName; 66 | return this; 67 | } 68 | 69 | public BigDecimal getChargeMoney() { 70 | return chargeMoney; 71 | } 72 | 73 | public OrderEntity setChargeMoney(BigDecimal chargeMoney) { 74 | this.chargeMoney = chargeMoney; 75 | return this; 76 | } 77 | 78 | public String getPurseId() { 79 | return purseId; 80 | } 81 | 82 | public OrderEntity setPurseId(String purseId) { 83 | this.purseId = purseId; 84 | return this; 85 | } 86 | 87 | public int getId() { 88 | return id; 89 | } 90 | 91 | public OrderEntity setId(int id) { 92 | this.id = id; 93 | return this; 94 | } 95 | 96 | public String getOrderId() { 97 | return orderId; 98 | } 99 | 100 | public OrderEntity setOrderId(String orderId) { 101 | this.orderId = orderId; 102 | return this; 103 | } 104 | 105 | public int getOrderStatus() { 106 | return orderStatus; 107 | } 108 | 109 | public OrderEntity setOrderStatus(int orderStatus) { 110 | this.orderStatus = orderStatus; 111 | return this; 112 | } 113 | 114 | public int getPayStatus() { 115 | return payStatus; 116 | } 117 | 118 | public OrderEntity setPayStatus(int payStatus) { 119 | this.payStatus = payStatus; 120 | return this; 121 | } 122 | 123 | public String getUserName() { 124 | return userName; 125 | } 126 | 127 | public OrderEntity setUserName(String userName) { 128 | this.userName = userName; 129 | return this; 130 | } 131 | 132 | public Date getGmtCreate() { 133 | return gmtCreate; 134 | } 135 | 136 | public OrderEntity setGmtCreate(Date gmtCreate) { 137 | this.gmtCreate = gmtCreate; 138 | return this; 139 | } 140 | 141 | public Date getGmtUpdate() { 142 | return gmtUpdate; 143 | } 144 | 145 | public OrderEntity setGmtUpdate(Date gmtUpdate) { 146 | this.gmtUpdate = gmtUpdate; 147 | return this; 148 | } 149 | 150 | @Override 151 | public String toString() { 152 | return "OrderEntity{" + 153 | "id=" + id + 154 | ", orderId='" + orderId + '\'' + 155 | ", orderStatus=" + orderStatus + 156 | ", payStatus=" + payStatus + 157 | ", userName='" + userName + '\'' + 158 | ", chargeMoney=" + chargeMoney + 159 | ", purseId='" + purseId + '\'' + 160 | ", gmtCreate=" + gmtCreate + 161 | ", gmtUpdate=" + gmtUpdate + 162 | ", notifyStatus=" + notifyStatus + 163 | ", channelOrderId='" + channelOrderId + '\'' + 164 | ", userPhoneNo='" + userPhoneNo + '\'' + 165 | ", merchantName='" + merchantName + '\'' + 166 | '}'; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /order-charge-sdk/src/main/java/com/snowalker/order/charge/request/ChargeOrderRequest.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.order.charge.request; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.google.common.base.Preconditions; 5 | import org.apache.commons.codec.digest.DigestUtils; 6 | 7 | import java.io.Serializable; 8 | import java.text.SimpleDateFormat; 9 | import java.util.Date; 10 | import java.util.Map; 11 | import java.util.TreeMap; 12 | import java.util.UUID; 13 | 14 | /** 15 | * @author snowalker 16 | * @version 1.0 17 | * @date 2019/6/10 14:33 18 | * @className ChargeOrderRequest 19 | * @desc 下单sdk接口请求参数 20 | */ 21 | public class ChargeOrderRequest implements Serializable { 22 | 23 | private static final long serialVersionUID = 2596328097263464531L; 24 | 25 | /**商户订单号*/ 26 | private String channelOrderId; 27 | private String userPhoneNum; 28 | private String chargePrice; 29 | private String purseId; 30 | private String merchantName; 31 | private String timestamp; 32 | private String prodId; 33 | private String sign; 34 | 35 | public String getProdId() { 36 | return prodId; 37 | } 38 | 39 | public ChargeOrderRequest setProdId(String prodId) { 40 | this.prodId = prodId; 41 | return this; 42 | } 43 | 44 | public String getTimestamp() { 45 | return timestamp; 46 | } 47 | 48 | public ChargeOrderRequest setTimestamp(String timestamp) { 49 | this.timestamp = timestamp; 50 | return this; 51 | } 52 | 53 | public String getChannelOrderId() { 54 | return channelOrderId; 55 | } 56 | 57 | public ChargeOrderRequest setChannelOrderId(String channelOrderId) { 58 | this.channelOrderId = channelOrderId; 59 | return this; 60 | } 61 | 62 | public String getUserPhoneNum() { 63 | return userPhoneNum; 64 | } 65 | 66 | public ChargeOrderRequest setUserPhoneNum(String userPhoneNum) { 67 | this.userPhoneNum = userPhoneNum; 68 | return this; 69 | } 70 | 71 | public String getChargePrice() { 72 | return chargePrice; 73 | } 74 | 75 | public ChargeOrderRequest setChargePrice(String chargePrice) { 76 | this.chargePrice = chargePrice; 77 | return this; 78 | } 79 | 80 | public String getPurseId() { 81 | return purseId; 82 | } 83 | 84 | public ChargeOrderRequest setPurseId(String purseId) { 85 | this.purseId = purseId; 86 | return this; 87 | } 88 | 89 | public String getMerchantName() { 90 | return merchantName; 91 | } 92 | 93 | public ChargeOrderRequest setMerchantName(String merchantName) { 94 | this.merchantName = merchantName; 95 | return this; 96 | } 97 | 98 | public String getSign() { 99 | return sign; 100 | } 101 | 102 | public ChargeOrderRequest setSign(String sign) { 103 | this.sign = sign; 104 | return this; 105 | } 106 | 107 | /** 108 | * MD5签名 109 | */ 110 | public String sign(String privateKey) { 111 | Preconditions.checkNotNull(this.getChannelOrderId()); 112 | Preconditions.checkNotNull(this.getUserPhoneNum()); 113 | Preconditions.checkNotNull(this.getChargePrice()); 114 | Preconditions.checkNotNull(this.getPurseId()); 115 | Preconditions.checkNotNull(this.getMerchantName()); 116 | Preconditions.checkNotNull(this.getTimestamp()); 117 | Preconditions.checkNotNull(this.getProdId()); 118 | Preconditions.checkNotNull(privateKey); 119 | // 参数排序 120 | Map params = new TreeMap<>(); 121 | params.put("channelOrderId", this.getChannelOrderId()); 122 | params.put("userPhoneNum", this.getUserPhoneNum()); 123 | params.put("chargePrice", this.getChargePrice()); 124 | params.put("purseId", this.getPurseId()); 125 | params.put("merchantName", this.getMerchantName()); 126 | params.put("timestamp", this.getTimestamp()); 127 | params.put("prodId", this.getProdId()); 128 | params.put("privateKey", privateKey); 129 | // 参数拼装并MD5签名 130 | StringBuilder signSourceBuilder = new StringBuilder(); 131 | for (String key : params.keySet()) { 132 | signSourceBuilder.append(key).append("=").append(params.get(key)).append("&"); 133 | } 134 | // 去除最后一个& 135 | String signSource = signSourceBuilder.toString(); 136 | String beforeSign = signSource.substring(0, signSource.length() - 1); 137 | // md5签名 138 | return DigestUtils.md5Hex(beforeSign); 139 | } 140 | 141 | @Override 142 | public String toString() { 143 | return "ChargeOrderRequest{" + 144 | "channelOrderId='" + channelOrderId + '\'' + 145 | ", userPhoneNum='" + userPhoneNum + '\'' + 146 | ", chargePrice='" + chargePrice + '\'' + 147 | ", purseId='" + purseId + '\'' + 148 | ", merchantName='" + merchantName + '\'' + 149 | ", timestamp='" + timestamp + '\'' + 150 | ", prodId='" + prodId + '\'' + 151 | ", sign='" + sign + '\'' + 152 | '}'; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /order-charge-message-protocol/src/main/java/com/snowalker/order/charge/message/protocol/OrderStatusUpdateProtocol.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.order.charge.message.protocol; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.JsonNode; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import com.google.common.base.Preconditions; 7 | import com.google.common.collect.ImmutableMap; 8 | import com.snowalker.order.charge.message.constant.MessageProtocolConst; 9 | 10 | import java.io.IOException; 11 | import java.io.Serializable; 12 | import java.util.Map; 13 | 14 | /** 15 | * @author snowalker 16 | * @version 1.0 17 | * @date 2019/2/18 14:35 18 | * @className OrderStatusUpdateProtocol 19 | * @desc 订单状态修改消息协议 20 | */ 21 | public class OrderStatusUpdateProtocol extends BaseMsg implements Serializable { 22 | 23 | private static final long serialVersionUID = -6415079919585308245L; 24 | 25 | private String purseId; 26 | private String merchantName; 27 | private String orderId; 28 | private String chargeMoney; 29 | private String eventType; 30 | 31 | private Map header; 32 | private Map body; 33 | 34 | @Override 35 | public String encode() { 36 | // 组装消息协议头 37 | ImmutableMap.Builder headerBuilder = new ImmutableMap.Builder() 38 | .put("version", this.getVersion()) 39 | .put("topicName", MessageProtocolConst.ORDER_STATUS_UPDATE_TOPIC.getTopic()); 40 | header = headerBuilder.build(); 41 | 42 | body = new ImmutableMap.Builder() 43 | .put("purseId", this.getPurseId()) 44 | .put("merchantName", this.getMerchantName()) 45 | .put("chargeMoney", this.getChargeMoney()) 46 | .put("orderId", this.getOrderId()) 47 | .put("eventType", this.getEventType()) 48 | .build(); 49 | 50 | ImmutableMap map = new ImmutableMap.Builder() 51 | .put("header", header) 52 | .put("body", body) 53 | .build(); 54 | // 返回序列化消息Json串 55 | String ret_string = null; 56 | ObjectMapper objectMapper = new ObjectMapper(); 57 | try { 58 | ret_string = objectMapper.writeValueAsString(map); 59 | } catch (JsonProcessingException e) { 60 | throw new RuntimeException("OrderStatusUpdateProtocol消息序列化json异常", e); 61 | } 62 | return ret_string; 63 | } 64 | 65 | @Override 66 | public void decode(String msg) { 67 | Preconditions.checkNotNull(msg); 68 | ObjectMapper mapper = new ObjectMapper(); 69 | try { 70 | JsonNode root = mapper.readTree(msg); 71 | // header 72 | this.setVersion(root.get("header").get("version").asText()); 73 | this.setTopicName(root.get("header").get("topicName").asText()); 74 | // body 75 | this.setPurseId(root.get("body").get("purseId").asText()); 76 | this.setMerchantName(root.get("body").get("merchantName").asText()); 77 | this.setChargeMoney(root.get("body").get("chargeMoney").asText()); 78 | this.setOrderId(root.get("body").get("orderId").asText()); 79 | this.setEventType(root.get("body").get("eventType").asText()); 80 | } catch (IOException e) { 81 | throw new RuntimeException("OrderStatusUpdateProtocol消息反序列化异常", e); 82 | } 83 | } 84 | 85 | public String getOrderId() { 86 | return orderId; 87 | } 88 | 89 | public OrderStatusUpdateProtocol setOrderId(String orderId) { 90 | this.orderId = orderId; 91 | return this; 92 | } 93 | 94 | public String getPurseId() { 95 | return purseId; 96 | } 97 | 98 | public OrderStatusUpdateProtocol setPurseId(String purseId) { 99 | this.purseId = purseId; 100 | return this; 101 | } 102 | 103 | public String getMerchantName() { 104 | return merchantName; 105 | } 106 | 107 | public OrderStatusUpdateProtocol setMerchantName(String merchantName) { 108 | this.merchantName = merchantName; 109 | return this; 110 | } 111 | 112 | public String getChargeMoney() { 113 | return chargeMoney; 114 | } 115 | 116 | public OrderStatusUpdateProtocol setChargeMoney(String chargeMoney) { 117 | this.chargeMoney = chargeMoney; 118 | return this; 119 | } 120 | 121 | public String getEventType() { 122 | return eventType; 123 | } 124 | 125 | public OrderStatusUpdateProtocol setEventType(String eventType) { 126 | this.eventType = eventType; 127 | return this; 128 | } 129 | 130 | @Override 131 | public String toString() { 132 | return "OrderStatusUpdateProtocol{" + 133 | "purseId='" + purseId + '\'' + 134 | ", merchantName='" + merchantName + '\'' + 135 | ", orderId='" + orderId + '\'' + 136 | ", chargeMoney='" + chargeMoney + '\'' + 137 | ", eventType='" + eventType + '\'' + 138 | ", header=" + header + 139 | ", body=" + body + 140 | "} " + super.toString(); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /order-charge-gateway-merchant/src/main/java/com/snowalker/gateway/merchant/order/manager/OrderChargeManager.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.gateway.merchant.order.manager; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.snowalker.gateway.merchant.order.dto.ChargeOrderDto; 5 | import com.snowalker.gateway.merchant.order.dto.Result; 6 | import com.snowalker.gateway.merchant.order.util.DateUtil; 7 | import com.snowalker.gateway.merchant.order.util.LogExceptionWapper; 8 | import com.snowalker.order.charge.constant.ResponseCodeEnum; 9 | import com.snowalker.order.charge.request.ChargeOrderRequest; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.beans.factory.annotation.Value; 14 | import org.springframework.http.HttpEntity; 15 | import org.springframework.http.HttpHeaders; 16 | import org.springframework.http.MediaType; 17 | import org.springframework.http.ResponseEntity; 18 | import org.springframework.stereotype.Component; 19 | import org.springframework.web.client.RestTemplate; 20 | 21 | import java.util.Date; 22 | import java.util.HashMap; 23 | import java.util.Map; 24 | 25 | /** 26 | * @author snowalker 27 | * @version 1.0 28 | * @date 2019/6/10 16:39 29 | * @className OrderChargeManager 30 | * @desc 远程下单交互防腐层 31 | */ 32 | @Component 33 | public class OrderChargeManager { 34 | 35 | private static final Logger LOGGER = LoggerFactory.getLogger(OrderChargeManager.class); 36 | 37 | @Autowired 38 | RestTemplate restTemplate; 39 | 40 | @Value("${agent.config.purseid}") 41 | private String purseId; 42 | 43 | @Value("${agent.config.merchant.name}") 44 | private String merchantName; 45 | 46 | @Value("${agent.config.private.key}") 47 | private String privateKey; 48 | 49 | @Value("${agent.config.request.url}") 50 | private String requestUrl; 51 | 52 | /** 53 | * 下单接口调用 54 | * @param chargeOrderDto 55 | * @return 56 | */ 57 | public ResponseCodeEnum chargeRequest(ChargeOrderDto chargeOrderDto) { 58 | 59 | // 组装请求参数 60 | ChargeOrderRequest chargeOrderRequest = new ChargeOrderRequest(); 61 | chargeOrderRequest.setChannelOrderId(chargeOrderDto.getOrderId()) 62 | .setChargePrice(chargeOrderDto.getChargeMoney().toString()) 63 | .setPurseId(purseId) 64 | .setMerchantName(merchantName) 65 | .setUserPhoneNum(chargeOrderDto.getPhoneNum()) 66 | .setTimestamp(DateUtil.formatDate(new Date(System.currentTimeMillis()))) 67 | .setProdId(chargeOrderDto.getOutProductId()) 68 | .setSign(chargeOrderRequest.sign(privateKey)); 69 | // 1. 设置请求头 70 | HttpHeaders headers = new HttpHeaders(); 71 | headers.setContentType(MediaType.APPLICATION_JSON); 72 | // 2. 设置请求参数 73 | Map requestParam = new HashMap<>(); 74 | requestParam.put("channelOrderId", chargeOrderRequest.getChannelOrderId()); 75 | requestParam.put("chargePrice", chargeOrderRequest.getChargePrice()); 76 | requestParam.put("merchantName", chargeOrderRequest.getMerchantName()); 77 | requestParam.put("purseId", chargeOrderRequest.getPurseId()); 78 | requestParam.put("timestamp", chargeOrderRequest.getTimestamp()); 79 | requestParam.put("userPhoneNum", chargeOrderRequest.getUserPhoneNum()); 80 | requestParam.put("prodId", chargeOrderRequest.getProdId()); 81 | requestParam.put("sign", chargeOrderRequest.getSign()); 82 | LOGGER.info("请求远端下单地址:{}, 下单入参:{}", requestUrl, requestParam.toString()); 83 | // 3. 请求开始 84 | HttpEntity> entity = new HttpEntity<>(requestParam, headers); 85 | ResponseEntity responseEntity = null; 86 | try { 87 | responseEntity = restTemplate.postForEntity(requestUrl, entity, String.class); 88 | LOGGER.info("请求远端下单地址:{}, 下单出参:{}", requestUrl, JSON.toJSONString(responseEntity)); 89 | if (responseEntity == null) { 90 | LOGGER.error("请求远端下单,返回为NULL,下单地址:{},返回下单状态未知:{}", requestUrl, ResponseCodeEnum.UNKNOWN); 91 | return ResponseCodeEnum.UNKNOWN; 92 | } 93 | // 解析返回参 94 | String responseBody = responseEntity.getBody(); 95 | // 转换返回体到对象 96 | Result result = JSON.parseObject(responseBody, Result.class); 97 | String code = result.getCode(); 98 | String msg = result.getMsg(); 99 | if (ResponseCodeEnum.UNKNOWN.getCode().equals(code)) { 100 | LOGGER.info("请求远端下单,返回下单未知,code={},msg={}", code, msg); 101 | return ResponseCodeEnum.UNKNOWN; 102 | } 103 | if (ResponseCodeEnum.FAIL.getCode().equals(code)) { 104 | LOGGER.info("请求远端下单,返回下单失败,code={},msg={}", code, msg); 105 | return ResponseCodeEnum.FAIL; 106 | } 107 | if (ResponseCodeEnum.SUCCESS.getCode().equals(code)) { 108 | LOGGER.info("请求远端下单,返回下单成功,code={},msg={}", code, msg); 109 | return ResponseCodeEnum.SUCCESS; 110 | } else { 111 | LOGGER.info("请求远端下单,其他未知情况,code={},msg={}", code, msg); 112 | return ResponseCodeEnum.UNKNOWN; 113 | } 114 | } catch (Exception e) { 115 | LOGGER.error("请求远端下单异常,e={}", LogExceptionWapper.getStackTrace(e)); 116 | return ResponseCodeEnum.UNKNOWN; 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /order-charge-message-protocol/src/main/java/com/snowalker/order/charge/message/protocol/WalletPaymentProtocol.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.order.charge.message.protocol; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.JsonNode; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import com.google.common.base.Preconditions; 7 | import com.google.common.collect.ImmutableMap; 8 | import com.snowalker.order.charge.message.constant.MessageProtocolConst; 9 | 10 | import java.io.IOException; 11 | import java.io.Serializable; 12 | import java.util.Map; 13 | 14 | /** 15 | * @author snowalker 16 | * @version 1.0 17 | * @date 2019/2/18 14:35 18 | * @className WalletPayProtocol 19 | * @desc 钱包支付(扣款)消息协议 20 | */ 21 | public class WalletPaymentProtocol extends BaseMsg implements Serializable { 22 | 23 | private static final long serialVersionUID = -6415079919585308245L; 24 | 25 | private String orderId; 26 | private String channelOrderId; 27 | private String userPhoneNo; 28 | private String chargeMoney; 29 | private String purseId; 30 | private String merchantName; 31 | 32 | 33 | private Map header; 34 | private Map body; 35 | 36 | @Override 37 | public String encode() { 38 | // 组装消息协议头 39 | ImmutableMap.Builder headerBuilder = new ImmutableMap.Builder() 40 | .put("version", this.getVersion()) 41 | .put("topicName", MessageProtocolConst.WALLET_PAYMENT_TOPIC.getTopic()); 42 | header = headerBuilder.build(); 43 | 44 | body = new ImmutableMap.Builder() 45 | .put("purseId", this.getPurseId()) 46 | .put("merchantName", this.getMerchantName()) 47 | .put("chargeMoney", this.getChargeMoney()) 48 | .put("channelOrderId", this.getChannelOrderId()) 49 | .put("orderId", this.getOrderId()) 50 | .put("userPhoneNo", this.getUserPhoneNo()) 51 | .build(); 52 | 53 | ImmutableMap map = new ImmutableMap.Builder() 54 | .put("header", header) 55 | .put("body", body) 56 | .build(); 57 | // 返回序列化消息Json串 58 | String ret_string = null; 59 | ObjectMapper objectMapper = new ObjectMapper(); 60 | try { 61 | ret_string = objectMapper.writeValueAsString(map); 62 | } catch (JsonProcessingException e) { 63 | throw new RuntimeException("WalletPaymentProtocol消息序列化json异常", e); 64 | } 65 | return ret_string; 66 | } 67 | 68 | @Override 69 | public void decode(String msg) { 70 | Preconditions.checkNotNull(msg); 71 | ObjectMapper mapper = new ObjectMapper(); 72 | try { 73 | JsonNode root = mapper.readTree(msg); 74 | // header 75 | this.setVersion(root.get("header").get("version").asText()); 76 | this.setTopicName(root.get("header").get("topicName").asText()); 77 | // body 78 | this.setPurseId(root.get("body").get("purseId").asText()); 79 | this.setMerchantName(root.get("body").get("merchantName").asText()); 80 | this.setChargeMoney(root.get("body").get("chargeMoney").asText()); 81 | this.setChannelOrderId(root.get("body").get("channelOrderId").asText()); 82 | this.setOrderId(root.get("body").get("orderId").asText()); 83 | this.setUserPhoneNo(root.get("body").get("userPhoneNo").asText()); 84 | } catch (IOException e) { 85 | throw new RuntimeException("WalletPaymentProtocol反序列化消息异常", e); 86 | } 87 | } 88 | 89 | public String getOrderId() { 90 | return orderId; 91 | } 92 | 93 | public WalletPaymentProtocol setOrderId(String orderId) { 94 | this.orderId = orderId; 95 | return this; 96 | } 97 | 98 | public String getUserPhoneNo() { 99 | return userPhoneNo; 100 | } 101 | 102 | public WalletPaymentProtocol setUserPhoneNo(String userPhoneNo) { 103 | this.userPhoneNo = userPhoneNo; 104 | return this; 105 | } 106 | 107 | public String getPurseId() { 108 | return purseId; 109 | } 110 | 111 | public WalletPaymentProtocol setPurseId(String purseId) { 112 | this.purseId = purseId; 113 | return this; 114 | } 115 | 116 | public String getMerchantName() { 117 | return merchantName; 118 | } 119 | 120 | public WalletPaymentProtocol setMerchantName(String merchantName) { 121 | this.merchantName = merchantName; 122 | return this; 123 | } 124 | 125 | public String getChannelOrderId() { 126 | return channelOrderId; 127 | } 128 | 129 | public WalletPaymentProtocol setChannelOrderId(String channelOrderId) { 130 | this.channelOrderId = channelOrderId; 131 | return this; 132 | } 133 | 134 | public String getChargeMoney() { 135 | return chargeMoney; 136 | } 137 | 138 | public WalletPaymentProtocol setChargeMoney(String chargeMoney) { 139 | this.chargeMoney = chargeMoney; 140 | return this; 141 | } 142 | 143 | @Override 144 | public String toString() { 145 | return "WalletPaymentProtocol{" + 146 | "purseId='" + purseId + '\'' + 147 | ", merchantName='" + merchantName + '\'' + 148 | ", channelOrderId='" + channelOrderId + '\'' + 149 | ", chargeMoney='" + chargeMoney + '\'' + 150 | ", header=" + header + 151 | ", body=" + body + 152 | "} " + super.toString(); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /order-charge-gateway-notify/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" 124 | FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( 125 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 126 | ) 127 | 128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 130 | if exist %WRAPPER_JAR% ( 131 | echo Found %WRAPPER_JAR% 132 | ) else ( 133 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 134 | echo Downloading from: %DOWNLOAD_URL% 135 | powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" 136 | echo Finished downloading %WRAPPER_JAR% 137 | ) 138 | @REM End of extension 139 | 140 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 141 | if ERRORLEVEL 1 goto error 142 | goto end 143 | 144 | :error 145 | set ERROR_CODE=1 146 | 147 | :end 148 | @endlocal & set ERROR_CODE=%ERROR_CODE% 149 | 150 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 151 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 152 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 153 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 154 | :skipRcPost 155 | 156 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 157 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 158 | 159 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 160 | 161 | exit /B %ERROR_CODE% 162 | -------------------------------------------------------------------------------- /order-charge-gateway-notify/src/main/java/com/snowalker/notify/mq/payment/consumer/listener/WalletPaymentMsgListenerImpl.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.notify.mq.payment.consumer.listener; 2 | 3 | import com.snowalker.notify.common.dao.dataobject.ChargeRecordEntity; 4 | import com.snowalker.notify.common.service.WalletService; 5 | import com.snowalker.notify.common.util.LogExceptionWapper; 6 | import com.snowalker.notify.mq.payment.producer.OrderStatusUpdateProducer; 7 | import com.snowalker.order.charge.message.constant.MessageProtocolConst; 8 | import com.snowalker.order.charge.message.constant.UpdateEventTypeConst; 9 | import com.snowalker.order.charge.message.protocol.OrderStatusUpdateProtocol; 10 | import com.snowalker.order.charge.message.protocol.WalletPaymentProtocol; 11 | import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; 12 | import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; 13 | import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently; 14 | import org.apache.rocketmq.client.producer.LocalTransactionState; 15 | import org.apache.rocketmq.client.producer.TransactionSendResult; 16 | import org.apache.rocketmq.common.message.Message; 17 | import org.apache.rocketmq.common.message.MessageExt; 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | import org.springframework.beans.factory.annotation.Autowired; 21 | import org.springframework.stereotype.Component; 22 | 23 | import java.util.List; 24 | 25 | /** 26 | * @author snowalker 27 | * @version 1.0 28 | * @date 2019/6/12 9:52 29 | * @className WalletPaymentMsgListenerImpl 30 | * @desc 消息消费监听回调实现 31 | */ 32 | @Component(value = "walletPaymentMsgListenerImpl") 33 | public class WalletPaymentMsgListenerImpl implements MessageListenerConcurrently { 34 | 35 | private static final Logger LOGGER = LoggerFactory.getLogger(WalletPaymentMsgListenerImpl.class); 36 | 37 | @Autowired 38 | OrderStatusUpdateProducer orderStatusUpdateProducer; 39 | 40 | @Autowired 41 | WalletService walletService; 42 | 43 | /** 44 | * 钱包扣款关键逻辑 45 | * @param msgs 46 | * @param context 47 | * @return 48 | */ 49 | @Override 50 | public ConsumeConcurrentlyStatus consumeMessage(List msgs, ConsumeConcurrentlyContext context) { 51 | try { 52 | // 默认msgs只有一条消息 53 | for (MessageExt msg : msgs) { 54 | // 消费次数 55 | int reconsumeTimes = msg.getReconsumeTimes(); 56 | String msgId = msg.getMsgId(); 57 | LOGGER.info("===============msgId={},消费次数={}===============", msgId, reconsumeTimes); 58 | return walletCharge(msg, msgId); 59 | } 60 | return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; 61 | } catch (Exception e) { 62 | LOGGER.error("钱包扣款消费异常,e={}", e); 63 | return ConsumeConcurrentlyStatus.RECONSUME_LATER; 64 | } 65 | } 66 | 67 | /** 68 | * 钱包扣款,并插入扣款流水 69 | * 70 | * @param msg 71 | */ 72 | private ConsumeConcurrentlyStatus walletCharge(MessageExt msg, String msgId) { 73 | String message = new String(msg.getBody()); 74 | LOGGER.info("msgId={},钱包扣款消费者接收到消息,message={}", msgId, message); 75 | WalletPaymentProtocol payProtocol = new WalletPaymentProtocol(); 76 | payProtocol.decode(message); 77 | 78 | // 幂等消费逻辑: 根据订单号查询扣款流水,如果存在则直接返回消费成功 79 | String orderId = payProtocol.getOrderId(); 80 | ChargeRecordEntity chargeRecordEntity = walletService.queryChargeRecordByOrderId(orderId); 81 | if (chargeRecordEntity != null) { 82 | LOGGER.info("[扣款本地事务回查]-本地已经存在orderId=[{}]对应的扣款流水,不需要重复消费,msgId={}", orderId, msgId); 83 | return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; 84 | } 85 | 86 | try { 87 | // 组装半消息:扣款成功修改订单状态为成功,消息事件=修改订单支付状态 88 | OrderStatusUpdateProtocol orderStatusUpdateProtocol = new OrderStatusUpdateProtocol(); 89 | orderStatusUpdateProtocol.setTopicName(MessageProtocolConst.ORDER_STATUS_UPDATE_TOPIC.getTopic()); 90 | orderStatusUpdateProtocol.setOrderId(payProtocol.getOrderId()) 91 | .setChargeMoney(payProtocol.getChargeMoney()) 92 | .setPurseId(payProtocol.getPurseId()) 93 | .setMerchantName(payProtocol.getMerchantName()) 94 | .setEventType(UpdateEventTypeConst.EVENT_UPDATE_PAY_STATUS.getEventType()); 95 | 96 | Message updateOrderStatusMsg = 97 | new Message(MessageProtocolConst.ORDER_STATUS_UPDATE_TOPIC.getTopic(), 98 | orderStatusUpdateProtocol.encode().getBytes()); 99 | // 半消息发送 100 | TransactionSendResult transactionSendResult = orderStatusUpdateProducer.getProducer() 101 | /** 102 | * sendMessageInTransaction(final Message msg, 103 | * final Object arg) 第二个参数为回调参数,可以为null, 104 | * 该参数和LocalTransactionState executeLocalTransaction(Message msg, Object arg)第二个参数为同一个值 105 | */ 106 | .sendMessageInTransaction(updateOrderStatusMsg, null); 107 | if (transactionSendResult == null) { 108 | // 发送未知重新消费 109 | LOGGER.info("msgId={},订单状态更新半消息发送状态未知,消费状态[RECONSUME_LATER],等待重新消费,orderId={}", msgId, payProtocol.getOrderId(), message); 110 | return ConsumeConcurrentlyStatus.RECONSUME_LATER; 111 | } 112 | if (transactionSendResult.getLocalTransactionState().equals(LocalTransactionState.COMMIT_MESSAGE)) { 113 | LOGGER.info("msgId={},订单状态更新半消息发送成功,消费状态[CONSUME_SUCCESS],orderId={},sendResult={}", msgId, payProtocol.getOrderId(), transactionSendResult); 114 | return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; 115 | } 116 | if (transactionSendResult.getLocalTransactionState().equals(LocalTransactionState.UNKNOW)) { 117 | LOGGER.warn("msgId={},订单状态更新本地事务执行状态未知,半消息发送未知,消费状态[RECONSUME_LATER],orderId={},sendResult={}", msgId, payProtocol.getOrderId(), transactionSendResult); 118 | return ConsumeConcurrentlyStatus.RECONSUME_LATER; 119 | } 120 | } catch (Exception e) { 121 | LOGGER.error("msgId={},订单状态更新半消息发送异常,消费状态[RECONSUME_LATER],e={}", msgId, LogExceptionWapper.getStackTrace(e)); 122 | } 123 | return ConsumeConcurrentlyStatus.RECONSUME_LATER; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /script/demo.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat MySQL Data Transfer 3 | 4 | Source Server : localhost-3306 5 | Source Server Version : 50505 6 | Source Host : 127.0.0.1:3306 7 | Source Database : demo 8 | 9 | Target Server Type : MYSQL 10 | Target Server Version : 50505 11 | File Encoding : 65001 12 | 13 | Date: 2019-06-13 09:37:53 14 | */ 15 | 16 | SET FOREIGN_KEY_CHECKS=0; 17 | 18 | -- ---------------------------- 19 | -- Table structure for t_merchant_charge_record 20 | -- ---------------------------- 21 | DROP TABLE IF EXISTS `t_merchant_charge_record`; 22 | CREATE TABLE `t_merchant_charge_record` ( 23 | `id` int(11) NOT NULL AUTO_INCREMENT, 24 | `gmt_create` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 25 | `gmt_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 26 | `record_id` varchar(255) CHARACTER SET utf32 DEFAULT '-1' COMMENT '扣款流水号', 27 | `order_id` varchar(255) DEFAULT '-1' COMMENT '交易订单号', 28 | `purse_id` varchar(255) DEFAULT '-1' COMMENT '钱包(账户)id', 29 | `merchant_name` varchar(255) DEFAULT '-1' COMMENT '商户名', 30 | `charge_price` decimal(15,3) DEFAULT NULL, 31 | PRIMARY KEY (`id`) 32 | ) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8; 33 | 34 | -- ---------------------------- 35 | -- Table structure for t_merchant_info 36 | -- ---------------------------- 37 | DROP TABLE IF EXISTS `t_merchant_info`; 38 | CREATE TABLE `t_merchant_info` ( 39 | `id` int(11) NOT NULL AUTO_INCREMENT, 40 | `gmt_create` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 41 | `gmt_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 42 | `merchant_name` varchar(255) NOT NULL DEFAULT '-1' COMMENT '供应商名', 43 | `purse_id` varchar(255) NOT NULL DEFAULT '-1' COMMENT '钱包账户id', 44 | `notify_url` varchar(512) NOT NULL DEFAULT '-1' COMMENT '通知地址', 45 | `private_key` varchar(1024) NOT NULL DEFAULT '-1' COMMENT '签名密钥', 46 | PRIMARY KEY (`id`), 47 | UNIQUE KEY `purseid_index` (`purse_id`) USING BTREE 48 | ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; 49 | 50 | -- ---------------------------- 51 | -- Table structure for t_merchant_order 52 | -- ---------------------------- 53 | DROP TABLE IF EXISTS `t_merchant_order`; 54 | CREATE TABLE `t_merchant_order` ( 55 | `id` int(11) NOT NULL AUTO_INCREMENT, 56 | `gmt_create` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 57 | `gmt_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 58 | `order_id` varchar(255) NOT NULL DEFAULT '-1' COMMENT '平台订单号', 59 | `channel_order_id` varchar(255) NOT NULL DEFAULT '-1' COMMENT '渠道订单号', 60 | `order_status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '订单状态,1 初始化 2 处理中 3 失败 0 成功', 61 | `notify_status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '通知状态,1 初始化 2 通知处理中 3 失败 0 成功 -1 不需要通知', 62 | `pay_status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '支付状态,1 初始化 2 处理中 3 失败 0 成功', 63 | `user_phoneno` varchar(11) NOT NULL DEFAULT '-1' COMMENT '用户手机号', 64 | `charge_price` decimal(10,3) NOT NULL DEFAULT '0.000' COMMENT '订单金额', 65 | `purse_id` varchar(255) NOT NULL DEFAULT '-1' COMMENT '下单商户钱包id', 66 | `merchant_name` varchar(255) DEFAULT '-1' COMMENT '用户名', 67 | PRIMARY KEY (`id`), 68 | UNIQUE KEY `order_id_index` (`order_id`) USING BTREE, 69 | KEY `channel_orderid_index` (`channel_order_id`) USING BTREE 70 | ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; 71 | 72 | -- ---------------------------- 73 | -- Table structure for t_merchant_renotify 74 | -- ---------------------------- 75 | DROP TABLE IF EXISTS `t_merchant_renotify`; 76 | CREATE TABLE `t_merchant_renotify` ( 77 | `id` int(11) NOT NULL AUTO_INCREMENT, 78 | `gmt_create` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 79 | `gmt_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 80 | `notify_id` varchar(255) NOT NULL DEFAULT '-1' COMMENT '通知id', 81 | `order_id` varchar(255) NOT NULL DEFAULT '-1' COMMENT '订单id', 82 | `channel_order_id` varchar(255) NOT NULL DEFAULT '-1' COMMENT '渠道订单号', 83 | `user_phoneno` varchar(11) NOT NULL DEFAULT '-1' COMMENT '用户手机号', 84 | `order_status` tinyint(1) NOT NULL DEFAULT '-1' COMMENT '订单状态 3 失败 0 成功', 85 | `order_status_desc` varchar(255) NOT NULL DEFAULT '-1' COMMENT '订单状态描述', 86 | `charge_price` decimal(10,3) NOT NULL DEFAULT '0.000' COMMENT '交易金额', 87 | `notify_time` int(3) DEFAULT '0' COMMENT '通知次数', 88 | PRIMARY KEY (`id`), 89 | UNIQUE KEY `notify_index` (`notify_id`) USING BTREE, 90 | KEY `notify_order_id_index` (`order_id`) USING BTREE 91 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商户通知重发表,对于通知达到一定阈值的失败请求入该重发表。'; 92 | 93 | -- ---------------------------- 94 | -- Table structure for t_merchant_wallet 95 | -- ---------------------------- 96 | DROP TABLE IF EXISTS `t_merchant_wallet`; 97 | CREATE TABLE `t_merchant_wallet` ( 98 | `id` int(11) NOT NULL AUTO_INCREMENT, 99 | `purse_id` varchar(255) CHARACTER SET utf8 NOT NULL COMMENT '用户钱包id', 100 | `merchant_name` varchar(255) DEFAULT '-1' COMMENT '商户名', 101 | `balance_account` decimal(10,3) NOT NULL DEFAULT '0.000' COMMENT '账户余额', 102 | `account_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '账户状态 0 正常 1 异常', 103 | `version` int(11) NOT NULL DEFAULT '0', 104 | `gmt_create` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 105 | `gmt_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 106 | PRIMARY KEY (`id`) 107 | ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1; 108 | 109 | -- ---------------------------- 110 | -- Table structure for t_user_order3rd 111 | -- ---------------------------- 112 | DROP TABLE IF EXISTS `t_user_order3rd`; 113 | CREATE TABLE `t_user_order3rd` ( 114 | `id` int(11) NOT NULL AUTO_INCREMENT, 115 | `gmt_create` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 116 | `gmt_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 117 | `order_id` varchar(255) NOT NULL DEFAULT '-1' COMMENT '代理商订单号', 118 | `order_status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '订单状态,1 初始化 2 处理中 3 失败 0 成功', 119 | `user_phoneno` varchar(11) NOT NULL DEFAULT '-1' COMMENT '用户手机号', 120 | `prod_id` varchar(255) NOT NULL DEFAULT '-1' COMMENT '商品id', 121 | `prod_name` varchar(255) NOT NULL DEFAULT '-1' COMMENT '商品名称', 122 | `out_prod_id` varchar(255) NOT NULL DEFAULT '-1' COMMENT '外部商品id', 123 | `out_prod_name` varchar(255) NOT NULL DEFAULT '-1' COMMENT '外部商品名称', 124 | `charge_money` decimal(10,3) NOT NULL DEFAULT '0.000' COMMENT '交易金额', 125 | `finish_time` datetime DEFAULT NULL COMMENT '订单结束时间', 126 | `out_order_id` varchar(255) NOT NULL DEFAULT '-1' COMMENT '上游订单号', 127 | PRIMARY KEY (`id`) 128 | ) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=utf8; 129 | -------------------------------------------------------------------------------- /order-charge-gateway-notify/src/main/java/com/snowalker/notify/common/util/DateUtil.java: -------------------------------------------------------------------------------- 1 | package com.snowalker.notify.common.util; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.text.DateFormat; 7 | import java.text.ParseException; 8 | import java.text.SimpleDateFormat; 9 | import java.util.Calendar; 10 | import java.util.Date; 11 | 12 | /** 13 | * @author snowalker 14 | * @date 2018/9/20 15 | * @desc 日期工具 16 | */ 17 | public class DateUtil { 18 | 19 | private DateUtil() {} 20 | 21 | private static final Logger LOGGER = LoggerFactory.getLogger(DateUtil.class); 22 | 23 | /** 24 | * 线程安全方式 25 | */ 26 | private static final ThreadLocal DATE_FORMAT_THREAD_LOCAL 27 | = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMddHHmmss")); 28 | 29 | private static final ThreadLocal DATE_FORMATY_YYYMMDD 30 | = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMdd")); 31 | 32 | private static final ThreadLocal DATE_FORMAT_YYYYMM 33 | = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMM")); 34 | 35 | private static final ThreadLocal DATE_FORMAT_NORMAL_THREAD_LOCAL 36 | = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); 37 | 38 | private static final ThreadLocal DATE_FORMATY_YYYMMDD2 39 | = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd")); 40 | /** 41 | * 格式化时间为时间戳:yyyyMMddHHmmss 42 | * @param date 43 | * @return 44 | */ 45 | public static String formatDate(Date date) { 46 | return DATE_FORMAT_THREAD_LOCAL.get().format(date); 47 | } 48 | 49 | /** 50 | * 解析字符串形式的时间戳为java.util.Date 51 | * @param timeStampStr 52 | * @return 53 | */ 54 | public static Date parseDateFromStr(String timeStampStr) { 55 | try { 56 | return DATE_FORMAT_THREAD_LOCAL.get().parse(timeStampStr); 57 | } catch (ParseException e) { 58 | LOGGER.error("解析'yyyyMMddHHmmss'形式时间戳为java.util.Date失败,e={}", e); 59 | } 60 | return null; 61 | } 62 | 63 | public static void remove() { 64 | DATE_FORMAT_THREAD_LOCAL.remove(); 65 | } 66 | 67 | 68 | /** 69 | * 格式化时间为时间戳:yyyyMMdd 70 | * @param date 71 | * @return 72 | */ 73 | public static String formatDateYyyyMMdd(Date date) { 74 | return DATE_FORMATY_YYYMMDD.get().format(date); 75 | } 76 | 77 | /** 78 | * 解析字符串形式的时间戳为java.util.Date 79 | * @param timeStampStr 80 | * @return 81 | */ 82 | public static Date parseDateFromStrYyyyMMdd(String timeStampStr) { 83 | try { 84 | return DATE_FORMATY_YYYMMDD.get().parse(timeStampStr); 85 | } catch (ParseException e) { 86 | LOGGER.error("解析'yyyyMMdd'形式时间戳为java.util.Date失败,e={}", e); 87 | } 88 | return null; 89 | } 90 | 91 | public static void removeYyyyMMdd() { 92 | DATE_FORMATY_YYYMMDD.remove(); 93 | } 94 | 95 | /** 96 | * 格式化时间为时间戳:yyyyMM 97 | * @param date 98 | * @return 99 | */ 100 | public static String formatNormalDate(Date date) { 101 | return DATE_FORMAT_NORMAL_THREAD_LOCAL.get().format(date); 102 | } 103 | 104 | /** 105 | * 解析字符串形式的时间戳为java.util.Date 106 | * @param timeStampStr 107 | * @return 108 | */ 109 | public static Date parseNormalDateFromStr(String timeStampStr) { 110 | try { 111 | return DATE_FORMAT_NORMAL_THREAD_LOCAL.get().parse(timeStampStr); 112 | } catch (ParseException e) { 113 | LOGGER.error("解析'yyyy-MM-dd HH:mm:ss'形式时间戳为java.util.Date失败,e={}", e); 114 | } 115 | return null; 116 | } 117 | 118 | public static void removeNormal() { 119 | DATE_FORMAT_NORMAL_THREAD_LOCAL.remove(); 120 | } 121 | 122 | /** 123 | * 格式化时间为时间戳:yyyy-MM-dd HH:mm:ss 124 | * @param date 125 | * @return 126 | */ 127 | public static String formatDateYyyyMM(Date date) { 128 | return DATE_FORMAT_YYYYMM.get().format(date); 129 | } 130 | 131 | /** 132 | * 解析字符串形式的时间戳为java.util.Date yyyy-MM-dd HH:mm:ss 133 | * @param timeStampStr 134 | * @return 135 | */ 136 | public static Date parseDateFromStrYyyyMM(String timeStampStr) { 137 | try { 138 | return DATE_FORMAT_YYYYMM.get().parse(timeStampStr); 139 | } catch (ParseException e) { 140 | LOGGER.error("解析'yyyyMM'形式时间戳为java.util.Date失败,e={}", e); 141 | } 142 | return null; 143 | } 144 | 145 | public static void removeYyyyMM() { 146 | DATE_FORMAT_YYYYMM.remove(); 147 | } 148 | 149 | /** 150 | * 格式化时间为时间戳:yyyy-MM-dd 151 | * @param date 152 | * @return 153 | */ 154 | public static String formatDateYyyyMMdd2(Date date) { 155 | return DATE_FORMATY_YYYMMDD2.get().format(date); 156 | } 157 | 158 | /** 159 | * 解析字符串形式的时间戳为java.util.Date yyyy-MM-dd 160 | * @param timeStampStr 161 | * @return 162 | */ 163 | public static Date parseDateFromStrYyyyMMdd2(String timeStampStr) { 164 | try { 165 | return DATE_FORMATY_YYYMMDD2.get().parse(timeStampStr); 166 | } catch (ParseException e) { 167 | LOGGER.error("解析'yyyy-MM-dd'形式时间戳为java.util.Date失败,e={}", e); 168 | } 169 | return null; 170 | } 171 | 172 | public static void removeYyyyMMdd2() { 173 | DATE_FORMATY_YYYMMDD2.remove(); 174 | } 175 | 176 | /** 177 | * 获取本月第一天的00:00:00 178 | * @return 179 | */ 180 | public static Date getCurrMonthFirstDate() { 181 | Calendar c = Calendar.getInstance(); 182 | c.add(Calendar.MONTH, 0); 183 | c.set(Calendar.DAY_OF_MONTH, 1); 184 | String first = DateUtil.formatDateYyyyMMdd2(c.getTime()); 185 | first = first + " 00:00:00"; 186 | Date queryStartDate = DateUtil.parseNormalDateFromStr(first); 187 | return queryStartDate; 188 | } 189 | 190 | /** 191 | * 获取本月最后一天的23:59:59 192 | * @return 193 | */ 194 | public static Date getCurrMonthEndDate() { 195 | // 查询结束时间--本月最后一秒 196 | Calendar lastDay = Calendar.getInstance(); 197 | lastDay.set(Calendar.DAY_OF_MONTH, lastDay.getActualMaximum(Calendar.DAY_OF_MONTH)); 198 | String last = DateUtil.formatDateYyyyMMdd2(lastDay.getTime()); 199 | last = last + " 23:59:59"; 200 | Date queryEndDate = DateUtil.parseNormalDateFromStr(last); 201 | return queryEndDate; 202 | } 203 | /** 204 | * 获取当前月份整数值--用于表分区 205 | * @return 206 | */ 207 | public static Integer getCurrMonthNum() { 208 | Calendar calendar = Calendar.getInstance(); 209 | return calendar.get(Calendar.MONTH) + 1; 210 | } 211 | 212 | } 213 | 214 | 215 | --------------------------------------------------------------------------------