├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── scs-main ├── src │ └── main │ │ ├── resources │ │ ├── application.properties │ │ └── logback-spring.xml │ │ └── java │ │ └── com │ │ └── smart │ │ └── classroom │ │ └── subscription │ │ ├── config │ │ └── BootstrapConfiguration.java │ │ └── SubscriptionApplication.java └── pom.xml ├── scs-facade ├── src │ └── main │ │ └── java │ │ └── com │ │ └── smart │ │ └── classroom │ │ └── subscription │ │ └── facade │ │ ├── healthy │ │ └── HealthyFacade.java │ │ ├── biz │ │ ├── subscription │ │ │ ├── request │ │ │ │ ├── ReaderColumnQueryRequest.java │ │ │ │ ├── PrepareSubscribeRequest.java │ │ │ │ └── SubscriptionPageRequest.java │ │ │ ├── SubscriptionWriteFacade.java │ │ │ ├── response │ │ │ │ ├── PrepareSubscribeDTO.java │ │ │ │ └── SubscriptionDTO.java │ │ │ └── SubscriptionReadFacade.java │ │ └── order │ │ │ ├── OrderReadFacade.java │ │ │ └── response │ │ │ └── OrderDTO.java │ │ └── common │ │ └── resposne │ │ └── PagerResponse.java └── pom.xml ├── docs └── start │ ├── nacos.md │ ├── zookeeper.md │ └── rocketmq.md ├── scs-repository ├── src │ └── main │ │ ├── resources │ │ ├── application-repository.properties │ │ └── mybatis │ │ │ ├── mapper │ │ │ ├── subscription │ │ │ │ ├── SubscriptionMapper.xml │ │ │ │ └── SubscriptionBaseMapper.xml │ │ │ └── order │ │ │ │ └── OrderMapper.xml │ │ │ └── mybatis-config.xml │ │ └── java │ │ └── com │ │ └── smart │ │ └── classroom │ │ └── subscription │ │ └── repository │ │ ├── orm │ │ ├── base │ │ │ └── BaseEntityDO.java │ │ ├── subscription │ │ │ └── SubscriptionDO.java │ │ └── order │ │ │ └── OrderDO.java │ │ ├── mapper │ │ ├── subscription │ │ │ ├── SubscriptionMapper.java │ │ │ └── SubscriptionBaseMapper.java │ │ ├── base │ │ │ └── BaseMapper.java │ │ └── order │ │ │ ├── OrderMapper.java │ │ │ └── OrderBaseMapper.java │ │ ├── impl │ │ ├── subscription │ │ │ ├── converter │ │ │ │ ├── SubscriptionDO2ModelConverter.java │ │ │ │ └── SubscriptionModel2DOConverter.java │ │ │ └── SubscriptionRepositoryImpl.java │ │ └── order │ │ │ ├── converter │ │ │ ├── OrderDO2ModelConverter.java │ │ │ └── OrderModel2DOConverter.java │ │ │ └── OrderRepositoryImpl.java │ │ ├── transaction │ │ └── TransactionService.java │ │ └── config │ │ └── DataSourceConfiguration.java └── pom.xml ├── scs-domain ├── src │ └── main │ │ └── java │ │ └── com │ │ └── smart │ │ └── classroom │ │ └── subscription │ │ └── domain │ │ ├── rpc │ │ ├── reader │ │ │ ├── ReaderFacadeClient.java │ │ │ └── vo │ │ │ │ └── ReaderVO.java │ │ ├── column │ │ │ ├── ColumnFacadeClient.java │ │ │ └── vo │ │ │ │ └── ColumnVO.java │ │ ├── quote │ │ │ ├── ColumnQuoteFacadeClient.java │ │ │ └── vo │ │ │ │ └── ColumnQuoteVO.java │ │ └── payment │ │ │ ├── info │ │ │ ├── PaymentCreateInfo.java │ │ │ └── PreparePaymentInfo.java │ │ │ ├── PaymentFacadeClient.java │ │ │ └── vo │ │ │ └── PaymentVO.java │ │ ├── middleware │ │ └── mq │ │ │ ├── MqConsumer.java │ │ │ ├── info │ │ │ ├── MqMessagePayloadInfo.java │ │ │ ├── MqSendResultInfo.java │ │ │ └── MqConsumeResultInfo.java │ │ │ ├── MqConsumerListener.java │ │ │ └── MqProducer.java │ │ ├── base │ │ └── member │ │ │ └── Member.java │ │ └── biz │ │ ├── order │ │ ├── info │ │ │ └── PrepareSubscribeInfo.java │ │ ├── enums │ │ │ └── OrderStatus.java │ │ ├── repository │ │ │ ├── query │ │ │ │ └── OrderPageQuery.java │ │ │ └── OrderRepository.java │ │ ├── model │ │ │ └── OrderModel.java │ │ └── service │ │ │ ├── OrderPaidDomainService.java │ │ │ └── OrderPrepareDomainService.java │ │ └── subscription │ │ ├── repository │ │ ├── query │ │ │ └── SubscriptionPageQuery.java │ │ └── SubscriptionRepository.java │ │ ├── enums │ │ └── SubscriptionStatus.java │ │ ├── event │ │ └── SubscriptionDomainEvent.java │ │ ├── info │ │ └── SubscriptionEventPayload.java │ │ ├── model │ │ └── SubscriptionModel.java │ │ └── service │ │ └── SubscriptionCreateDomainService.java └── pom.xml ├── scs-testsuit ├── src │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── ddd │ │ │ └── practise │ │ │ └── testsuit │ │ │ └── DddPractiseApplication.java │ └── main │ │ ├── resources │ │ └── saber │ │ │ ├── TemplateMapperXml.vm │ │ │ ├── TemplateMapperJava.vm │ │ │ ├── TemplateDOJava.vm │ │ │ ├── TemplateBaseMapperJava.vm │ │ │ └── TemplateBaseMapperXml.vm │ │ └── java │ │ └── com │ │ └── smart │ │ └── classroom │ │ └── subscription │ │ └── tool │ │ └── saber │ │ ├── config │ │ └── SaberConfig.java │ │ ├── artwork │ │ ├── base │ │ │ └── Artwork.java │ │ ├── MapperJavaArtwork.java │ │ ├── MapperXmlArtwork.java │ │ └── DOArtwork.java │ │ ├── x │ │ ├── order │ │ │ └── OrderCommand.java │ │ └── subscription │ │ │ └── SubscriptionCommand.java │ │ ├── meta │ │ └── SaberTable.java │ │ ├── command │ │ └── SaberCommand.java │ │ └── contrast │ │ └── TypeContrast.java └── pom.xml ├── scs-utility ├── src │ └── main │ │ └── java │ │ └── com │ │ └── smart │ │ └── classroom │ │ └── subscription │ │ └── utility │ │ ├── exception │ │ ├── ExceptionCode.java │ │ ├── UnauthorizedException.java │ │ ├── SystemException.java │ │ └── UtilException.java │ │ ├── enums │ │ └── SortDirection.java │ │ ├── model │ │ ├── Triplet.java │ │ └── Pair.java │ │ ├── util │ │ ├── TemplateUtil.java │ │ ├── NumberUtil.java │ │ ├── PathUtil.java │ │ └── JsonUtil.java │ │ └── result │ │ └── Pager.java └── pom.xml ├── .gitignore ├── scs-infrastructure ├── src │ └── main │ │ ├── resources │ │ └── application-infrastructure.properties │ │ └── java │ │ └── com │ │ └── smart │ │ └── classroom │ │ └── subscription │ │ └── infrastructure │ │ ├── rpc │ │ ├── reader │ │ │ ├── converter │ │ │ │ └── ReaderDTO2VOConverter.java │ │ │ └── ReaderFacadeClientImpl.java │ │ ├── column │ │ │ ├── converter │ │ │ │ └── ColumnDTO2VOConverter.java │ │ │ └── ColumnFacadeClientImpl.java │ │ ├── payment │ │ │ ├── converter │ │ │ │ ├── PreparePaymentDTO2InfoConverter.java │ │ │ │ └── PaymentDTO2VOConverter.java │ │ │ └── PaymentFacadeClientImpl.java │ │ └── quote │ │ │ ├── converter │ │ │ └── ColumnQuoteDTO2VOConverter.java │ │ │ └── ColumnQuoteFacadeClientImpl.java │ │ └── middleware │ │ └── rocketmq │ │ ├── RocketMqProducerImpl.java │ │ └── RocketMqConsumerImpl.java └── pom.xml ├── scs-facade-impl ├── src │ └── main │ │ └── java │ │ └── com │ │ └── smart │ │ └── classroom │ │ └── subscription │ │ └── facade │ │ └── impl │ │ ├── healthy │ │ └── HealthyFacadeImpl.java │ │ └── biz │ │ ├── order │ │ ├── OrderReadFacadeImpl.java │ │ └── converter │ │ │ └── OrderModel2DTOConverter.java │ │ └── subscription │ │ ├── converter │ │ └── SubscriptionModel2DTOConverter.java │ │ ├── SubscriptionWriteFacadeImpl.java │ │ └── SubscriptionReadFacadeImpl.java └── pom.xml ├── scs-application ├── src │ └── main │ │ └── java │ │ └── com │ │ └── smart │ │ └── classroom │ │ └── subscription │ │ └── application │ │ └── rpc │ │ └── payment │ │ ├── event │ │ └── PaymentDomainEvent.java │ │ └── info │ │ └── PaymentEventPayload.java └── pom.xml └── README.md /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eyebluecn/smart-classroom-subscription/HEAD/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /scs-main/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=6500 2 | 3 | # include repository profiles 4 | spring.profiles.include=repository,infrastructure 5 | -------------------------------------------------------------------------------- /scs-facade/src/main/java/com/smart/classroom/subscription/facade/healthy/HealthyFacade.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.facade.healthy; 2 | 3 | /** 4 | * @author lishuang 5 | * @date 2023-05-11 6 | */ 7 | public interface HealthyFacade { 8 | String check(); 9 | } 10 | -------------------------------------------------------------------------------- /docs/start/nacos.md: -------------------------------------------------------------------------------- 1 | # Nacos使用方法 2 | ## 下载 3 | 从 [官网](https://nacos.io/zh-cn/index.html) 下载,比如下载nacos-server-2.1.2.zip 4 | 5 | ## 解压 6 | unzip nacos-server-2.1.2.zip 7 | cd nacos/bin 8 | 9 | ## 启动 10 | ```shell 11 | sh startup.sh -m standalone 12 | ``` 13 | 14 | window下使用 15 | ```shell 16 | startup.cmd -m standalone 17 | ``` 18 | 19 | # 控制台 20 | 访问 http://localhost:8848/nacos/ 即可看到自己注册的服务。 -------------------------------------------------------------------------------- /scs-repository/src/main/resources/application-repository.properties: -------------------------------------------------------------------------------- 1 | spring.datasource.primary.jdbc-url=jdbc:mysql://127.0.0.1:3306/smart_classroom?useUnicode=true&useSSL=false&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useInformationSchema=true 2 | spring.datasource.primary.username=smart_classroom 3 | spring.datasource.primary.password=Smart_classroom123 4 | 5 | logging.level.com.zaxxer.hikari=INFO 6 | 7 | -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/rpc/reader/ReaderFacadeClient.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.rpc.reader; 2 | 3 | import com.smart.classroom.subscription.domain.rpc.reader.vo.ReaderVO; 4 | 5 | /** 6 | * @author lishuang 7 | * @date 2023-05-15 8 | */ 9 | public interface ReaderFacadeClient { 10 | 11 | ReaderVO queryById(long readerId); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/rpc/column/ColumnFacadeClient.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.rpc.column; 2 | 3 | import com.smart.classroom.subscription.domain.rpc.column.vo.ColumnVO; 4 | import com.smart.classroom.subscription.domain.rpc.reader.vo.ReaderVO; 5 | 6 | /** 7 | * @author lishuang 8 | * @date 2023-05-15 9 | */ 10 | public interface ColumnFacadeClient { 11 | 12 | ColumnVO queryById(long columnId); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/middleware/mq/MqConsumer.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.middleware.mq; 2 | 3 | /** 4 | * @author lishuang 5 | * @date 2023-05-17 6 | * MQ消息订阅 7 | */ 8 | public interface MqConsumer { 9 | 10 | /** 11 | * 监听某个"领域"下的某个"事件"消息。 12 | * 13 | * @param eventName 事件由 领域名前缀+事件构成. 需要上下游约定产出。 14 | */ 15 | void listen(String eventName, MqConsumerListener mqConsumerListener); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/rpc/quote/ColumnQuoteFacadeClient.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.rpc.quote; 2 | 3 | import com.smart.classroom.subscription.domain.rpc.quote.vo.ColumnQuoteVO; 4 | 5 | /** 6 | * @author lishuang 7 | * @date 2023-05-15 8 | */ 9 | public interface ColumnQuoteFacadeClient { 10 | 11 | ColumnQuoteVO queryById(long columnQuoteId); 12 | 13 | 14 | ColumnQuoteVO queryByColumnId(long columnId); 15 | 16 | 17 | } 18 | -------------------------------------------------------------------------------- /scs-testsuit/src/test/java/com/ddd/practise/testsuit/DddPractiseApplication.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.testsuit; 2 | import org.springframework.boot.SpringApplication; 3 | import org.springframework.boot.autoconfigure.SpringBootApplication; 4 | 5 | /** 6 | * @author fusu 7 | * @date 2023-04-14 8 | */ 9 | @SpringBootApplication 10 | public class DddPractiseApplication { 11 | 12 | public static void main(String[] args) { 13 | SpringApplication.run(DddPractiseApplication.class, args); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /scs-utility/src/main/java/com/smart/classroom/subscription/utility/exception/ExceptionCode.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.utility.exception; 2 | 3 | import lombok.Getter; 4 | 5 | 6 | /** 7 | * 所有定义的错误码 8 | */ 9 | public enum ExceptionCode { 10 | 11 | OK("成功"), 12 | UNAUTHORIZED("没有权限"), 13 | SYSTEM_ERROR("系统异常"), 14 | UNKNOWN("服务器未知错误"), 15 | ; 16 | 17 | @Getter 18 | private String message; 19 | 20 | ExceptionCode(String message) { 21 | this.message = message; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | 35 | update.sh 36 | production/ -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/base/member/Member.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.base.member; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * 员工成员 9 | * @author fusu 10 | * @date 2023-04-14 11 | */ 12 | @Data 13 | @NoArgsConstructor 14 | @AllArgsConstructor 15 | public class Member { 16 | 17 | /** 18 | * 工号 19 | */ 20 | private String workNo; 21 | 22 | /** 23 | * 花名 24 | */ 25 | private String nickname; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /scs-testsuit/src/main/resources/saber/TemplateMapperXml.vm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /scs-infrastructure/src/main/resources/application-infrastructure.properties: -------------------------------------------------------------------------------- 1 | dubbo.application.name=smart-classroom-subscription 2 | dubbo.application.qos-port=22222 3 | 4 | # Enable token verification for each invocation 5 | dubbo.provider.token=false 6 | # do not check provider status when start. 7 | dubbo.consumer.check=false 8 | 9 | # Specify the registry address 10 | # dubbo.registry.address=nacos://localhost:8848?username=nacos&password=nacos 11 | dubbo.registry.address=nacos://${nacos.address:localhost}:8848?username=nacos&password=nacos 12 | 13 | dubbo.protocol.name=dubbo 14 | dubbo.protocol.port=20882 -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/middleware/mq/info/MqMessagePayloadInfo.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.middleware.mq.info; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * @author lishuang 10 | * @date 2023-05-17 11 | */ 12 | @Data 13 | @Builder 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class MqMessagePayloadInfo { 17 | 18 | String messageId; 19 | 20 | String content; 21 | 22 | String tag; 23 | 24 | String keys; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /scs-testsuit/src/main/resources/saber/TemplateMapperJava.vm: -------------------------------------------------------------------------------- 1 | /** 2 | * smart-classroom.com Inc. Copyright (c) 2015-present All Rights Reserved. 3 | * generated by SaberGenerator 4 | */ 5 | package ${artwork.mapperPackage}; 6 | 7 | #foreach($item in $artwork.mapperImportNones) 8 | import ${item}; 9 | #if(!$foreach.hasNext) 10 | 11 | #end 12 | #end 13 | #foreach($item in $artwork.mapperImportJavas) 14 | import ${item}; 15 | #if(!$foreach.hasNext) 16 | 17 | #end 18 | #end 19 | @Mapper 20 | @Repository 21 | public interface ${artwork.entityUpperBodyName}Mapper extends ${artwork.entityUpperBodyName}BaseMapper { 22 | 23 | } -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/middleware/mq/MqConsumerListener.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.middleware.mq; 2 | 3 | import com.smart.classroom.subscription.domain.middleware.mq.info.MqMessagePayloadInfo; 4 | 5 | /** 6 | * @author lishuang 7 | * @date 2023-05-17 8 | * MQ消息订阅 9 | */ 10 | public interface MqConsumerListener { 11 | 12 | /** 13 | * 订阅这。 14 | * 15 | * @param mqMessagePayloadInfo Mq投递过来的内容 16 | * @return true:表示消费成功,消息不再重新投递。 false:表示消费失败,消息会继续投递。 17 | */ 18 | boolean consume(MqMessagePayloadInfo mqMessagePayloadInfo); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /scs-utility/src/main/java/com/smart/classroom/subscription/utility/exception/UnauthorizedException.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.utility.exception; 2 | 3 | 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | 7 | 8 | /** 9 | * 用户没有权限,不能访问 10 | */ 11 | @EqualsAndHashCode(callSuper = false) 12 | @Data 13 | public class UnauthorizedException extends UtilException { 14 | 15 | public UnauthorizedException() { 16 | super(ExceptionCode.UNAUTHORIZED); 17 | } 18 | 19 | public UnauthorizedException(String messagePattern, Object... arguments) { 20 | super(ExceptionCode.UNAUTHORIZED, messagePattern, arguments); 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /scs-main/src/main/java/com/smart/classroom/subscription/config/BootstrapConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.config; 2 | 3 | 4 | import com.smart.classroom.subscription.utility.util.StringUtil; 5 | 6 | /** 7 | * @author lishuang 8 | * @date 2023-05-16 9 | *

10 | * 启动配置 11 | */ 12 | public class BootstrapConfiguration { 13 | 14 | public static void start() { 15 | 16 | //配置日志路径。 17 | String logPath = StringUtil.format("{}/logs/smart-classroom-subscription", 18 | System.getProperty("user.home")); 19 | System.setProperty("log.path", logPath); 20 | System.out.println("日志地址:" + logPath); 21 | 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /docs/start/zookeeper.md: -------------------------------------------------------------------------------- 1 | # zookeeper使用方法 2 | ## 下载 3 | 从 [官网](https://zookeeper.apache.org/) 下载,比如下载apache-zookeeper-3.8.1-bin.tar.gz 4 | 5 | ## 修改配置 6 | 解压后,将 apache-zookeeper-3.8.1-bin/conf/zoo_sample.cfg 重命名为 zoo.cfg,修改日志和数据存放位置: 7 | ```shell 8 | # dataDir表示保存数据的目录,修改成你希望的位置。 9 | dataDir=/tmp/zookeeper 10 | ``` 11 | 12 | ## 运行 13 | ```shell 14 | cd apache-zookeeper-3.8.1-bin/bin/ 15 | ./zkServer.sh start 16 | ``` 17 | 18 | 看到以下提示表示启动成功 19 | ```text 20 | Using config: .../conf/zoo.cfg 21 | Starting zookeeper ... STARTED 22 | ``` 23 | 24 | windows平台下,双击运行 apache-zookeeper-3.8.1-bin/zkServer.cmd 25 | 26 | ## 验证 27 | 28 | 方法1: 29 | 使用jvm命令jps 30 | ```shell 31 | jps 32 | ``` 33 | 看到"QuorumPeerMain"表示启动成功 34 | 35 | -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/middleware/mq/MqProducer.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.middleware.mq; 2 | 3 | import com.smart.classroom.subscription.domain.middleware.mq.info.MqSendResultInfo; 4 | 5 | /** 6 | * @author lishuang 7 | * @date 2023-05-17 8 | * MQ消息生产 9 | */ 10 | public interface MqProducer { 11 | 12 | /** 13 | * 发送MQ消息 14 | *

15 | * 消息的topic统一采用"SmartClassroomTopic" 16 | * 17 | * @param tags 消息的tags 18 | * @param keys 消息的Key字段是为了唯一标识消息的,方便运维排查问题。如果不设置Key,则无法定位消息丢失原因。 19 | * @param body 发送的消息体,在智慧课堂中只发送文本消息。 20 | */ 21 | MqSendResultInfo send(String tags, String keys, String body); 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/middleware/mq/info/MqSendResultInfo.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.middleware.mq.info; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * @author lishuang 10 | * @date 2023-05-17 11 | */ 12 | @Data 13 | @Builder 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class MqSendResultInfo { 17 | /** 18 | * mq发送是否成功 19 | */ 20 | private boolean success; 21 | 22 | /** 23 | * 发送的消息id 24 | */ 25 | private String messageId; 26 | 27 | /** 28 | * 如果发送失败,错误消息是什么 29 | */ 30 | private String msg; 31 | } 32 | -------------------------------------------------------------------------------- /scs-repository/src/main/java/com/smart/classroom/subscription/repository/orm/base/BaseEntityDO.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.repository.orm.base; 2 | 3 | 4 | import lombok.Data; 5 | 6 | import java.util.Date; 7 | 8 | /** 9 | * @author fusu 10 | * @date 2023-04-14 11 | */ 12 | @Data 13 | public class BaseEntityDO { 14 | 15 | public final static String EMPTY_JSON_ARRAY = "[]"; 16 | public final static String EMPTY_JSON_OBJECT = "{}"; 17 | 18 | /** 19 | * 主键 20 | */ 21 | private Long id; 22 | 23 | /** 24 | * 创建时间 25 | */ 26 | private Date createTime = new Date(); 27 | 28 | /** 29 | * 修改时间 30 | */ 31 | private Date updateTime = new Date(); 32 | 33 | } 34 | -------------------------------------------------------------------------------- /scs-facade-impl/src/main/java/com/smart/classroom/subscription/facade/impl/healthy/HealthyFacadeImpl.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.facade.impl.healthy; 2 | 3 | import com.smart.classroom.subscription.facade.healthy.HealthyFacade; 4 | import com.smart.classroom.subscription.utility.util.DateUtil; 5 | import org.apache.dubbo.config.annotation.DubboService; 6 | 7 | import java.util.Date; 8 | 9 | /** 10 | * @author lishuang 11 | * @date 2023-05-11 12 | */ 13 | @DubboService 14 | public class HealthyFacadeImpl implements HealthyFacade { 15 | 16 | @Override 17 | public String check() { 18 | return "Hello,你好. 我是smart-classroom-subscription,状态正常 现在时间:" + DateUtil.convertDateToString(new Date()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/middleware/mq/info/MqConsumeResultInfo.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.middleware.mq.info; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * @author lishuang 10 | * @date 2023-05-17 11 | */ 12 | @Data 13 | @Builder 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class MqConsumeResultInfo { 17 | /** 18 | * mq发送是否成功 19 | */ 20 | private boolean success; 21 | 22 | /** 23 | * 发送的消息id 24 | */ 25 | private String messageId; 26 | 27 | /** 28 | * 如果发送失败,错误消息是什么 29 | */ 30 | private String msg; 31 | } 32 | -------------------------------------------------------------------------------- /scs-facade/src/main/java/com/smart/classroom/subscription/facade/biz/subscription/request/ReaderColumnQueryRequest.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.facade.biz.subscription.request; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.io.Serializable; 9 | 10 | /** 11 | * @author lishuang 12 | * @date 2023-05-15 13 | */ 14 | @Data 15 | @Builder 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | public class ReaderColumnQueryRequest implements Serializable { 19 | 20 | /** 21 | * 当前读者id 22 | */ 23 | private long readerId; 24 | 25 | /** 26 | * 希望订阅的专栏 27 | */ 28 | private long columnId; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /scs-main/src/main/java/com/smart/classroom/subscription/SubscriptionApplication.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription; 2 | 3 | import com.smart.classroom.subscription.config.BootstrapConfiguration; 4 | import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | 8 | @EnableDubbo 9 | @SpringBootApplication 10 | public class SubscriptionApplication { 11 | 12 | public static void main(String[] args) { 13 | //应用定义的启动配置 14 | BootstrapConfiguration.start(); 15 | 16 | //spring-boot的启动入口 17 | SpringApplication.run(SubscriptionApplication.class, args); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/rpc/payment/info/PaymentCreateInfo.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.rpc.payment.info; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * @author lishuang 10 | * @date 2023-05-17 11 | */ 12 | @Data 13 | @Builder 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class PaymentCreateInfo { 17 | 18 | /** 19 | * 订单编号 20 | */ 21 | private String orderNo = null; 22 | 23 | /** 24 | * 支付方式 ALIPAY/WEIXIN 支付宝/微信 25 | */ 26 | private String method = null; 27 | 28 | /** 29 | * 金额(单位:分) 30 | */ 31 | private Long amount = null; 32 | 33 | } 34 | -------------------------------------------------------------------------------- /scs-repository/src/main/java/com/smart/classroom/subscription/repository/mapper/subscription/SubscriptionMapper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * smart-classroom.com Inc. Copyright (c) 2015-present All Rights Reserved. 3 | * generated by SaberGenerator 4 | */ 5 | package com.smart.classroom.subscription.repository.mapper.subscription; 6 | 7 | import com.smart.classroom.subscription.repository.orm.subscription.SubscriptionDO; 8 | import org.apache.ibatis.annotations.Mapper; 9 | import org.apache.ibatis.annotations.Param; 10 | import org.springframework.stereotype.Repository; 11 | 12 | @Mapper 13 | @Repository 14 | public interface SubscriptionMapper extends SubscriptionBaseMapper { 15 | SubscriptionDO queryByReaderIdAndColumnId(@Param("readerId") Long readerId, @Param("columnId") Long columnId); 16 | } -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/biz/order/info/PrepareSubscribeInfo.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.biz.order.info; 2 | 3 | import com.smart.classroom.subscription.domain.biz.order.model.OrderModel; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | /** 10 | * @author lishuang 11 | * @date 2023-05-15 12 | */ 13 | @Data 14 | @Builder 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | public class PrepareSubscribeInfo { 18 | 19 | /** 20 | * 相关的订单。 21 | */ 22 | OrderModel orderModel; 23 | 24 | /** 25 | * 支付的一些token及信息 26 | */ 27 | String thirdTransactionNo; 28 | 29 | /** 30 | * 支付时候的验证信息等。 31 | */ 32 | String nonceStr; 33 | 34 | } 35 | -------------------------------------------------------------------------------- /scs-facade/src/main/java/com/smart/classroom/subscription/facade/biz/order/OrderReadFacade.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.facade.biz.order; 2 | 3 | import com.smart.classroom.subscription.facade.biz.order.response.OrderDTO; 4 | import com.smart.classroom.subscription.facade.biz.subscription.request.ReaderColumnQueryRequest; 5 | import com.smart.classroom.subscription.facade.biz.subscription.request.SubscriptionPageRequest; 6 | import com.smart.classroom.subscription.facade.biz.subscription.response.SubscriptionDTO; 7 | import com.smart.classroom.subscription.facade.common.resposne.PagerResponse; 8 | 9 | /** 10 | * @author lishuang 11 | * @date 2023-05-15 12 | */ 13 | public interface OrderReadFacade { 14 | 15 | /** 16 | * 按照id查询 17 | */ 18 | OrderDTO queryById(Long orderId); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/rpc/payment/PaymentFacadeClient.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.rpc.payment; 2 | 3 | import com.smart.classroom.subscription.domain.rpc.payment.info.PaymentCreateInfo; 4 | import com.smart.classroom.subscription.domain.rpc.payment.info.PreparePaymentInfo; 5 | import com.smart.classroom.subscription.domain.rpc.payment.vo.PaymentVO; 6 | 7 | /** 8 | * @author lishuang 9 | * @date 2023-05-15 10 | */ 11 | public interface PaymentFacadeClient { 12 | 13 | PaymentVO queryById(long paymentId); 14 | 15 | /** 16 | * 创建支付单 17 | * 同时返回可用于支付的物流 18 | */ 19 | PreparePaymentInfo create(PaymentCreateInfo paymentCreateInfo); 20 | 21 | /** 22 | * 获取一个支付单对应的支付准备物料等信息。 23 | */ 24 | PreparePaymentInfo prepare(long paymentId); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/rpc/reader/vo/ReaderVO.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.rpc.reader.vo; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.util.Date; 9 | 10 | /** 11 | * @author lishuang 12 | * @date 2023-05-15 13 | */ 14 | @Data 15 | @Builder 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | public class ReaderVO { 19 | 20 | 21 | /** 22 | * 主键 23 | */ 24 | private Long id = null; 25 | 26 | /** 27 | * 创建时间 28 | */ 29 | private Date createTime = null; 30 | 31 | /** 32 | * 修改时间 33 | */ 34 | private Date updateTime = null; 35 | 36 | /** 37 | * 用户名 38 | */ 39 | private String username = null; 40 | 41 | } 42 | -------------------------------------------------------------------------------- /scs-facade/src/main/java/com/smart/classroom/subscription/facade/biz/subscription/request/PrepareSubscribeRequest.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.facade.biz.subscription.request; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.io.Serializable; 9 | 10 | /** 11 | * @author lishuang 12 | * @date 2023-05-15 13 | */ 14 | @Data 15 | @Builder 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | public class PrepareSubscribeRequest implements Serializable { 19 | 20 | /** 21 | * 当前读者id 22 | */ 23 | private long readerId; 24 | 25 | /** 26 | * 希望订阅的专栏 27 | */ 28 | private long columnId; 29 | 30 | /** 31 | * 支付方式 支持 ALIPAY/WEIXIN 32 | */ 33 | private String payMethod; 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /scs-facade/src/main/java/com/smart/classroom/subscription/facade/biz/subscription/SubscriptionWriteFacade.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.facade.biz.subscription; 2 | 3 | import com.smart.classroom.subscription.facade.biz.subscription.request.PrepareSubscribeRequest; 4 | import com.smart.classroom.subscription.facade.biz.subscription.response.PrepareSubscribeDTO; 5 | import com.smart.classroom.subscription.facade.biz.subscription.response.SubscriptionDTO; 6 | 7 | /** 8 | * @author lishuang 9 | * @date 2023-05-15 10 | */ 11 | public interface SubscriptionWriteFacade { 12 | 13 | 14 | /** 15 | * 发起订阅请求 16 | */ 17 | PrepareSubscribeDTO prepareSubscribe(PrepareSubscribeRequest request); 18 | 19 | 20 | /** 21 | * 支付成功消息补偿。 22 | */ 23 | SubscriptionDTO compensatePaymentPaid(long paymentId); 24 | 25 | 26 | } 27 | -------------------------------------------------------------------------------- /scs-facade/src/main/java/com/smart/classroom/subscription/facade/biz/subscription/request/SubscriptionPageRequest.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.facade.biz.subscription.request; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.io.Serializable; 9 | 10 | /** 11 | * @author lishuang 12 | * @date 2023-05-12 13 | */ 14 | @Data 15 | @Builder 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | public class SubscriptionPageRequest implements Serializable { 19 | int pageNum = 1; 20 | int pageSize = 20; 21 | //参考 SortDirection枚举 22 | String orderCreateTime = null; 23 | //参考 SortDirection枚举 24 | String orderUpdateTime = null; 25 | 26 | Long readerId = null; 27 | Long columnId = null; 28 | Long orderId = null; 29 | String status = null; 30 | } 31 | -------------------------------------------------------------------------------- /scs-testsuit/src/main/java/com/smart/classroom/subscription/tool/saber/config/SaberConfig.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.tool.saber.config; 2 | 3 | /** 4 | * Saber工具中用到的一些配置项 5 | * 6 | * @author lishuang 7 | * @date 2023-05-11 8 | */ 9 | public interface SaberConfig { 10 | 11 | //仓储层的模块名 12 | String REPOSITORY_MODULE_NAME = "scs-repository"; 13 | 14 | //基础包名,即所有类共同的包名前缀。 15 | String BASE_PACKAGE_NAME = "com.smart.classroom.subscription"; 16 | 17 | //数据库表前缀 18 | String DATABASE_TABLE_PREFIX = "scs_"; 19 | 20 | //数据库连接串 useInformationSchema=true应该加上,否则获取不到表名注释。 21 | String JDBC_URL = "jdbc:mysql://127.0.0.1:3306/smart_classroom?useUnicode=true&useSSL=false&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useInformationSchema=true"; 22 | String JDBC_USERNAME = "smart_classroom"; 23 | String JDBC_PASSWORD = "Smart_classroom123"; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /scs-utility/src/main/java/com/smart/classroom/subscription/utility/enums/SortDirection.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.utility.enums; 2 | 3 | /** 4 | * @author fusu 5 | * @date 2023-04-14 6 | */ 7 | 8 | import lombok.Getter; 9 | 10 | public enum SortDirection { 11 | ASC("升序"), 12 | DESC("降序");; 13 | 14 | @Getter 15 | private final String description; 16 | 17 | SortDirection(String description) { 18 | this.description = description; 19 | } 20 | 21 | 22 | public static String toString(SortDirection orderStatus) { 23 | if (orderStatus == null) { 24 | return null; 25 | } 26 | return orderStatus.name(); 27 | } 28 | 29 | public static SortDirection toEnum(String s) { 30 | if (s == null) { 31 | return null; 32 | } 33 | return SortDirection.valueOf(s); 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /scs-utility/src/main/java/com/smart/classroom/subscription/utility/model/Triplet.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.utility.model; 2 | 3 | /** 4 | * 将三个值封装在一个类中 5 | * 从而方便多值返回使用 6 | * 7 | * @author lishuang 8 | * @version 2023-05-11 9 | */ 10 | public class Triplet { 11 | 12 | private X x; 13 | private Y y; 14 | private Z z; 15 | 16 | public X getX() { 17 | return x; 18 | } 19 | 20 | public void setX(X x) { 21 | this.x = x; 22 | } 23 | 24 | public Y getY() { 25 | return y; 26 | } 27 | 28 | public void setY(Y y) { 29 | this.y = y; 30 | } 31 | 32 | public Z getZ() { 33 | return z; 34 | } 35 | 36 | public void setZ(Z z) { 37 | this.z = z; 38 | } 39 | 40 | public Triplet(X x, Y y, Z z) { 41 | this.x = x; 42 | this.y = y; 43 | this.z = z; 44 | } 45 | } -------------------------------------------------------------------------------- /scs-facade/src/main/java/com/smart/classroom/subscription/facade/biz/subscription/response/PrepareSubscribeDTO.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.facade.biz.subscription.response; 2 | 3 | import com.smart.classroom.subscription.facade.biz.order.response.OrderDTO; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.io.Serializable; 10 | 11 | /** 12 | * @author lishuang 13 | * @date 2023-05-15 14 | */ 15 | @Data 16 | @Builder 17 | @NoArgsConstructor 18 | @AllArgsConstructor 19 | public class PrepareSubscribeDTO implements Serializable { 20 | 21 | /** 22 | * 相关的订单。 23 | */ 24 | OrderDTO orderDTO; 25 | 26 | /** 27 | * 支付的一些token及信息 28 | */ 29 | String thirdTransactionNo; 30 | 31 | /** 32 | * 支付时候的验证信息等。 33 | */ 34 | String nonceStr; 35 | 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /scs-testsuit/src/main/resources/saber/TemplateDOJava.vm: -------------------------------------------------------------------------------- 1 | /** 2 | * smart-classroom.com Inc. Copyright (c) 2015-present All Rights Reserved. 3 | * generated by SaberGenerator 4 | */ 5 | package ${artwork.entityPackage}; 6 | 7 | #foreach($item in $artwork.entityImportNones) 8 | import ${item}; 9 | #if(!$foreach.hasNext) 10 | 11 | #end 12 | #end 13 | #foreach($item in $artwork.entityImportJavas) 14 | import ${item}; 15 | #if(!$foreach.hasNext) 16 | 17 | #end 18 | #end 19 | /** 20 | * ${artwork.tableRemark} 21 | */ 22 | @Data 23 | @Builder 24 | @NoArgsConstructor 25 | @AllArgsConstructor 26 | @EqualsAndHashCode(callSuper = true) 27 | public class ${artwork.entityUpperClassName} extends BaseEntityDO { 28 | 29 | #foreach($column in $artwork.selfColumns) 30 | /** 31 | * ${column.remark} 32 | */ 33 | private ${column.javaSimpleName} ${column.lowerCamelName} = ${column.defaultValue}; 34 | 35 | #end 36 | 37 | } -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/biz/order/enums/OrderStatus.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.biz.order.enums; 2 | 3 | import lombok.Getter; 4 | 5 | public enum OrderStatus { 6 | 7 | CREATED("已创建"), 8 | PAID("已支付"), 9 | SUBSCRIBED("已订阅"), 10 | CLOSED("已关闭"), 11 | CANCELED("已取消"); 12 | 13 | @Getter 14 | private final String description; 15 | 16 | OrderStatus(String description) { 17 | this.description = description; 18 | } 19 | 20 | 21 | public static String toString(OrderStatus orderStatus) { 22 | if (orderStatus == null) { 23 | return null; 24 | } 25 | return orderStatus.name(); 26 | } 27 | 28 | public static OrderStatus toEnum(String s) { 29 | if (s == null) { 30 | return null; 31 | } 32 | return OrderStatus.valueOf(s); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /scs-application/src/main/java/com/smart/classroom/subscription/application/rpc/payment/event/PaymentDomainEvent.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.application.rpc.payment.event; 2 | 3 | import lombok.Getter; 4 | 5 | /** 6 | * 定义领域事件类型。 7 | */ 8 | public enum PaymentDomainEvent { 9 | 10 | PAYMENT_DOMAIN_EVENT_PAID("已支付"); 11 | 12 | @Getter 13 | private final String description; 14 | 15 | PaymentDomainEvent(String description) { 16 | this.description = description; 17 | } 18 | 19 | 20 | public static String toString(PaymentDomainEvent status) { 21 | if (status == null) { 22 | return null; 23 | } 24 | return status.name(); 25 | } 26 | 27 | public static PaymentDomainEvent toEnum(String s) { 28 | if (s == null) { 29 | return null; 30 | } 31 | return PaymentDomainEvent.valueOf(s); 32 | } 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/biz/subscription/repository/query/SubscriptionPageQuery.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.biz.subscription.repository.query; 2 | 3 | import com.smart.classroom.subscription.domain.biz.subscription.enums.SubscriptionStatus; 4 | import com.smart.classroom.subscription.utility.enums.SortDirection; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | /** 11 | * @author lishuang 12 | * @date 2023-05-15 13 | */ 14 | @Data 15 | @Builder 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | public class SubscriptionPageQuery { 19 | 20 | int pageNum = 1; 21 | int pageSize = 20; 22 | 23 | SortDirection orderCreateTime; 24 | SortDirection orderUpdateTime; 25 | Long readerId; 26 | Long columnId; 27 | Long orderId; 28 | SubscriptionStatus status; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/biz/subscription/enums/SubscriptionStatus.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.biz.subscription.enums; 2 | 3 | import lombok.Getter; 4 | 5 | public enum SubscriptionStatus { 6 | 7 | CREATED("已创建"), 8 | OK("已生效"), 9 | DISABLED("已失效"); 10 | 11 | @Getter 12 | private final String description; 13 | 14 | SubscriptionStatus(String description) { 15 | this.description = description; 16 | } 17 | 18 | 19 | public static String toString(SubscriptionStatus subscriptionStatus) { 20 | if (subscriptionStatus == null) { 21 | return null; 22 | } 23 | return subscriptionStatus.name(); 24 | } 25 | 26 | public static SubscriptionStatus toEnum(String s) { 27 | if (s == null) { 28 | return null; 29 | } 30 | return SubscriptionStatus.valueOf(s); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/biz/order/repository/query/OrderPageQuery.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.biz.order.repository.query; 2 | 3 | import com.smart.classroom.subscription.domain.biz.order.enums.OrderStatus; 4 | import com.smart.classroom.subscription.utility.enums.SortDirection; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | /** 11 | * @author lishuang 12 | * @date 2023-05-15 13 | */ 14 | @Data 15 | @Builder 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | public class OrderPageQuery { 19 | 20 | int pageNum = 1; 21 | int pageSize = 20; 22 | SortDirection orderCreateTime; 23 | SortDirection orderUpdateTime; 24 | Long readerId = null; 25 | Long columnId = null; 26 | Long columnQuoteId = null; 27 | Long paymentId = null; 28 | OrderStatus status = null; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/biz/subscription/event/SubscriptionDomainEvent.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.biz.subscription.event; 2 | 3 | import lombok.Getter; 4 | 5 | /** 6 | * 定义领域事件类型。 7 | */ 8 | public enum SubscriptionDomainEvent { 9 | 10 | SUBSCRIPTION_DOMAIN_EVENT_CREATED("订阅成功"); 11 | 12 | @Getter 13 | private final String description; 14 | 15 | SubscriptionDomainEvent(String description) { 16 | this.description = description; 17 | } 18 | 19 | 20 | public static String toString(SubscriptionDomainEvent status) { 21 | if (status == null) { 22 | return null; 23 | } 24 | return status.name(); 25 | } 26 | 27 | public static SubscriptionDomainEvent toEnum(String s) { 28 | if (s == null) { 29 | return null; 30 | } 31 | return SubscriptionDomainEvent.valueOf(s); 32 | } 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/rpc/payment/info/PreparePaymentInfo.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.rpc.payment.info; 2 | 3 | import com.smart.classroom.subscription.domain.biz.order.model.OrderModel; 4 | import com.smart.classroom.subscription.domain.rpc.payment.vo.PaymentVO; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | /** 11 | * @author lishuang 12 | * @date 2023-05-16 13 | */ 14 | @Data 15 | @Builder 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | public class PreparePaymentInfo { 19 | 20 | /** 21 | * 支付单 22 | */ 23 | PaymentVO paymentVO; 24 | 25 | /** 26 | * 订单号 27 | */ 28 | String orderNo; 29 | 30 | /** 31 | * 支付的一些token及信息 32 | */ 33 | String thirdTransactionNo; 34 | 35 | /** 36 | * 支付时候的验证信息等。 37 | */ 38 | String nonceStr; 39 | 40 | } 41 | -------------------------------------------------------------------------------- /scs-infrastructure/src/main/java/com/smart/classroom/subscription/infrastructure/rpc/reader/converter/ReaderDTO2VOConverter.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.infrastructure.rpc.reader.converter; 2 | 3 | 4 | import com.smart.classroom.misc.facade.biz.reader.response.ReaderDTO; 5 | import com.smart.classroom.subscription.domain.rpc.reader.vo.ReaderVO; 6 | 7 | /** 8 | * @author lishuang 9 | * @date 2023-05-12 10 | * 转换器 11 | */ 12 | public class ReaderDTO2VOConverter { 13 | 14 | /** 15 | * 将DO转换成模型 16 | */ 17 | public static ReaderVO convert(ReaderDTO readerDTO) { 18 | if (readerDTO == null) { 19 | return null; 20 | } 21 | 22 | return ReaderVO.builder() 23 | .id(readerDTO.getId()) 24 | .createTime(readerDTO.getCreateTime()) 25 | .updateTime(readerDTO.getUpdateTime()) 26 | .username(readerDTO.getUsername()) 27 | .build(); 28 | } 29 | 30 | 31 | } 32 | -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/biz/subscription/repository/SubscriptionRepository.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.biz.subscription.repository; 2 | 3 | import com.smart.classroom.subscription.domain.biz.subscription.model.SubscriptionModel; 4 | import com.smart.classroom.subscription.domain.biz.subscription.repository.query.SubscriptionPageQuery; 5 | import com.smart.classroom.subscription.domain.rpc.column.vo.ColumnVO; 6 | import com.smart.classroom.subscription.domain.rpc.reader.vo.ReaderVO; 7 | import com.smart.classroom.subscription.utility.result.Pager; 8 | 9 | /** 10 | * @author lishuang 11 | * @date 2023-05-12 12 | */ 13 | public interface SubscriptionRepository { 14 | 15 | SubscriptionModel queryByReaderAndColumn(ReaderVO readerVO, ColumnVO columnVO); 16 | 17 | //分页查询 18 | Pager page(SubscriptionPageQuery subscriptionPageQuery); 19 | 20 | SubscriptionModel insert(SubscriptionModel order); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /scs-repository/src/main/java/com/smart/classroom/subscription/repository/mapper/base/BaseMapper.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.repository.mapper.base; 2 | 3 | import org.apache.ibatis.annotations.Param; 4 | 5 | import java.util.List; 6 | 7 | public interface BaseMapper { 8 | 9 | /** 10 | * 插入一个对象 返回影响的条数。要获取id使用 fsEvent.getId() 11 | */ 12 | int insert(T t); 13 | 14 | /** 15 | * 批量插入对象 16 | */ 17 | int insertBatch(@Param("list") List list); 18 | 19 | /** 20 | * 按id删除 21 | */ 22 | int deleteById(Long id); 23 | 24 | /** 25 | * 根据一系列id来删除对应的条目 26 | */ 27 | int deleteByIds(@Param("list") List list); 28 | 29 | /** 30 | * 更新 返回影响的条数 31 | */ 32 | int update(T fsEvent); 33 | 34 | /** 35 | * 按id查询 36 | */ 37 | T queryById(Long id); 38 | 39 | /** 40 | * 根据一系列id来获取到对应的条目 41 | */ 42 | List queryByIds(@Param("list") List list); 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /scs-facade/src/main/java/com/smart/classroom/subscription/facade/biz/subscription/SubscriptionReadFacade.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.facade.biz.subscription; 2 | 3 | import com.smart.classroom.subscription.facade.biz.subscription.request.ReaderColumnQueryRequest; 4 | import com.smart.classroom.subscription.facade.biz.subscription.request.SubscriptionPageRequest; 5 | import com.smart.classroom.subscription.facade.biz.subscription.response.SubscriptionDTO; 6 | import com.smart.classroom.subscription.facade.common.resposne.PagerResponse; 7 | 8 | /** 9 | * @author lishuang 10 | * @date 2023-05-15 11 | */ 12 | public interface SubscriptionReadFacade { 13 | 14 | 15 | /** 16 | * 查找某个读者关于某个专栏的订阅情况 17 | */ 18 | SubscriptionDTO queryByColumnIdAndReaderId(ReaderColumnQueryRequest readerColumnQueryRequest); 19 | 20 | 21 | /** 22 | * 查找订阅情况分页 23 | */ 24 | PagerResponse page(SubscriptionPageRequest subscriptionPageRequest); 25 | 26 | 27 | } 28 | -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/rpc/column/vo/ColumnVO.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.rpc.column.vo; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.util.Date; 9 | 10 | /** 11 | * @author lishuang 12 | * @date 2023-05-15 13 | */ 14 | @Data 15 | @Builder 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | public class ColumnVO { 19 | 20 | /** 21 | * 主键 22 | */ 23 | private Long id = null; 24 | 25 | /** 26 | * 创建时间 27 | */ 28 | private Date createTime = null; 29 | 30 | /** 31 | * 修改时间 32 | */ 33 | private Date updateTime = null; 34 | 35 | /** 36 | * 专栏名称 37 | */ 38 | private String name = null; 39 | 40 | /** 41 | * 作者id 42 | */ 43 | private Long authorId = null; 44 | 45 | /** 46 | * 参考:ColumnStatus 47 | * 状态 NEW/OK/DISABLED 未发布/已生效/已禁用 48 | */ 49 | private String status = null; 50 | } 51 | -------------------------------------------------------------------------------- /scs-utility/src/main/java/com/smart/classroom/subscription/utility/util/TemplateUtil.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.utility.util; 2 | 3 | 4 | import lombok.NonNull; 5 | import org.apache.velocity.VelocityContext; 6 | import org.apache.velocity.app.Velocity; 7 | 8 | import java.io.StringWriter; 9 | import java.util.Map; 10 | 11 | 12 | /** 13 | * Velocity语法 14 | * 专门用来渲染模板的。 15 | */ 16 | public class TemplateUtil { 17 | 18 | 19 | //传入模板字符串,和参数,自动填写完成! 20 | public static String render(@NonNull String template, @NonNull Map map) { 21 | 22 | VelocityContext context = new VelocityContext(); 23 | 24 | for (Map.Entry entry : map.entrySet()) { 25 | if (entry.getValue() != null) { 26 | context.put(entry.getKey(), entry.getValue()); 27 | } 28 | } 29 | 30 | StringWriter writer = new StringWriter(); 31 | 32 | Velocity.evaluate(context, writer, "", template); 33 | 34 | return writer.toString(); 35 | } 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/biz/subscription/info/SubscriptionEventPayload.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.biz.subscription.info; 2 | 3 | import com.smart.classroom.subscription.domain.biz.subscription.enums.SubscriptionStatus; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.util.Date; 10 | 11 | /** 12 | * @author lishuang 13 | * @date 2023-05-16 14 | */ 15 | @Data 16 | @Builder 17 | @NoArgsConstructor 18 | @AllArgsConstructor 19 | public class SubscriptionEventPayload { 20 | 21 | /** 22 | * 读者id 23 | */ 24 | private Long readerId = null; 25 | 26 | /** 27 | * 专栏id 28 | */ 29 | private Long columnId = null; 30 | 31 | /** 32 | * 订单id 33 | */ 34 | private Long orderId = null; 35 | 36 | /** 37 | * 状态 CREATED/OK/DISABLED 38 | */ 39 | private String status = null; 40 | 41 | /** 42 | * 时间 43 | */ 44 | private Date occurTime = null; 45 | 46 | } 47 | -------------------------------------------------------------------------------- /scs-utility/src/main/java/com/smart/classroom/subscription/utility/exception/SystemException.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.utility.exception; 2 | 3 | import lombok.Data; 4 | import lombok.EqualsAndHashCode; 5 | 6 | /** 7 | * 系统异常 8 | *

9 | * 应用中所有的非业务异常——也就是系统异常,都抛这个异常 10 | * 11 | * @author lishuang 12 | * @date 2023/2/16 13 | */ 14 | @EqualsAndHashCode(callSuper = true) 15 | @Data 16 | public class SystemException extends UtilException { 17 | 18 | public SystemException() { 19 | super(ExceptionCode.SYSTEM_ERROR); 20 | } 21 | 22 | public SystemException(String messagePattern, Object... arguments) { 23 | super(messagePattern, arguments); 24 | } 25 | 26 | public SystemException(ExceptionCode resultCode) { 27 | super(resultCode); 28 | } 29 | 30 | public SystemException(Throwable throwable) { 31 | super(throwable); 32 | } 33 | 34 | public SystemException(ExceptionCode resultCode, String messagePattern, Object... arguments) { 35 | super(resultCode, messagePattern, arguments); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /scs-infrastructure/src/main/java/com/smart/classroom/subscription/infrastructure/rpc/column/converter/ColumnDTO2VOConverter.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.infrastructure.rpc.column.converter; 2 | 3 | 4 | import com.smart.classroom.misc.facade.biz.column.response.ColumnDTO; 5 | import com.smart.classroom.subscription.domain.rpc.column.vo.ColumnVO; 6 | 7 | /** 8 | * @author lishuang 9 | * @date 2023-05-12 10 | * 转换器 11 | */ 12 | public class ColumnDTO2VOConverter { 13 | 14 | /** 15 | * 将DO转换成模型 16 | */ 17 | public static ColumnVO convert(ColumnDTO columnDTO) { 18 | if (columnDTO == null) { 19 | return null; 20 | } 21 | 22 | return ColumnVO.builder() 23 | .id(columnDTO.getId()) 24 | .createTime(columnDTO.getCreateTime()) 25 | .updateTime(columnDTO.getUpdateTime()) 26 | .name(columnDTO.getName()) 27 | .authorId(columnDTO.getAuthorId()) 28 | .status(columnDTO.getStatus()) 29 | 30 | .build(); 31 | } 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /scs-application/src/main/java/com/smart/classroom/subscription/application/rpc/payment/info/PaymentEventPayload.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.application.rpc.payment.info; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.util.Date; 9 | 10 | /** 11 | * @author lishuang 12 | * @date 2023-05-16 13 | */ 14 | @Data 15 | @Builder 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | public class PaymentEventPayload { 19 | 20 | /** 21 | * 支付单 22 | */ 23 | private Long paymentId; 24 | 25 | /** 26 | * 订单编号 27 | */ 28 | private String orderNo = null; 29 | 30 | /** 31 | * 支付方式 ALIPAY/WEIXIN 支付宝/微信 32 | */ 33 | private String method = null; 34 | 35 | /** 36 | * 金额(单位:分) 37 | */ 38 | private Long amount = null; 39 | 40 | /** 41 | * 支付状态 UNPAID/PAID/CLOSED 未支付/已支付/已关闭 42 | */ 43 | private String status = null; 44 | 45 | /** 46 | * 时间 47 | */ 48 | private Date occurTime = null; 49 | 50 | } 51 | -------------------------------------------------------------------------------- /scs-infrastructure/src/main/java/com/smart/classroom/subscription/infrastructure/rpc/payment/converter/PreparePaymentDTO2InfoConverter.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.infrastructure.rpc.payment.converter; 2 | 3 | 4 | import com.smart.classroom.misc.facade.biz.payment.response.PreparePaymentDTO; 5 | import com.smart.classroom.subscription.domain.rpc.payment.info.PreparePaymentInfo; 6 | 7 | /** 8 | * @author lishuang 9 | * @date 2023-05-12 10 | * 转换器 11 | */ 12 | public class PreparePaymentDTO2InfoConverter { 13 | 14 | /** 15 | * 将DO转换成模型 16 | */ 17 | public static PreparePaymentInfo convert(PreparePaymentDTO preparePaymentDTO) { 18 | if (preparePaymentDTO == null) { 19 | return null; 20 | } 21 | 22 | return PreparePaymentInfo.builder() 23 | .paymentVO(PaymentDTO2VOConverter.convert(preparePaymentDTO.getPaymentDTO())) 24 | .thirdTransactionNo(preparePaymentDTO.getThirdTransactionNo()) 25 | .nonceStr(preparePaymentDTO.getNonceStr()) 26 | .build(); 27 | } 28 | 29 | 30 | } 31 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip 18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar 19 | -------------------------------------------------------------------------------- /scs-repository/src/main/resources/mybatis/mapper/subscription/SubscriptionMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 22 | 23 | -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/rpc/quote/vo/ColumnQuoteVO.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.rpc.quote.vo; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.util.Date; 9 | 10 | /** 11 | * @author lishuang 12 | * @date 2023-05-15 13 | */ 14 | @Data 15 | @Builder 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | public class ColumnQuoteVO { 19 | 20 | /** 21 | * 主键 22 | */ 23 | private Long id = null; 24 | 25 | /** 26 | * 创建时间 27 | */ 28 | private Date createTime = null; 29 | 30 | /** 31 | * 修改时间 32 | */ 33 | private Date updateTime = null; 34 | 35 | /** 36 | * 专栏id 37 | */ 38 | private Long columnId = null; 39 | 40 | /** 41 | * 编辑id 42 | */ 43 | private Long editorId = null; 44 | 45 | /** 46 | * 价格(单位:分) 47 | */ 48 | private Long price = null; 49 | 50 | /** 51 | * 报价状态 DISABLED/OK 未生效/已生效 52 | */ 53 | private String status = null; 54 | } 55 | -------------------------------------------------------------------------------- /scs-testsuit/src/main/java/com/smart/classroom/subscription/tool/saber/artwork/base/Artwork.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.tool.saber.artwork.base; 2 | 3 | import com.smart.classroom.subscription.tool.saber.command.SaberCommand; 4 | import com.smart.classroom.subscription.tool.saber.meta.SaberTable; 5 | import lombok.Data; 6 | import lombok.NonNull; 7 | 8 | /** 9 | * 一个产出物,一件艺术品。 10 | * 比如 TemplateMapperJava.vm 是模板,对应的产出物是 XXMapper.java 11 | */ 12 | @Data 13 | public abstract class Artwork { 14 | 15 | //模板的文件名称。 16 | private String templateFileName; 17 | 18 | //该作品是 可覆盖型的 19 | //true: 表示直接用最新的去覆盖。 false: 表示文件不存在就生成,存在了就不改动了。 20 | private boolean overwrite = false; 21 | 22 | public Artwork() { 23 | 24 | } 25 | 26 | public Artwork(String templateFileName, boolean overwrite) { 27 | this.templateFileName = templateFileName; 28 | this.overwrite = overwrite; 29 | } 30 | 31 | //目标文件的路径。 32 | public abstract String destAbsolutePath(); 33 | 34 | public abstract void init(@NonNull SaberTable saberTable, @NonNull SaberCommand saberCommand); 35 | } 36 | -------------------------------------------------------------------------------- /scs-facade/src/main/java/com/smart/classroom/subscription/facade/common/resposne/PagerResponse.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.facade.common.resposne; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | 6 | import java.io.Serializable; 7 | import java.util.List; 8 | 9 | 10 | @Data 11 | @NoArgsConstructor 12 | public class PagerResponse implements Serializable { 13 | 14 | //当前页码。 1基 15 | private long pageNum; 16 | 17 | //每页的大小。 18 | private long pageSize; 19 | 20 | //总条目数 21 | private long totalItems; 22 | 23 | //总页数 24 | private long totalPages; 25 | 26 | private List data; 27 | 28 | 29 | public PagerResponse(long pageNum, long pageSize, long totalItems, List data) { 30 | 31 | if (pageSize <= 0) { 32 | pageSize = 10; 33 | } 34 | 35 | this.pageNum = pageNum; 36 | 37 | this.pageSize = pageSize; 38 | 39 | this.totalItems = totalItems; 40 | 41 | this.totalPages = (long) Math.ceil(this.totalItems * 1.0 / this.pageSize); 42 | 43 | this.data = data; 44 | 45 | } 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/biz/order/repository/OrderRepository.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.biz.order.repository; 2 | 3 | import com.smart.classroom.subscription.domain.biz.order.model.OrderModel; 4 | import com.smart.classroom.subscription.domain.biz.order.repository.query.OrderPageQuery; 5 | import com.smart.classroom.subscription.domain.rpc.column.vo.ColumnVO; 6 | import com.smart.classroom.subscription.domain.rpc.reader.vo.ReaderVO; 7 | import com.smart.classroom.subscription.utility.result.Pager; 8 | 9 | /** 10 | * @author lishuang 11 | * @date 2023-05-12 12 | */ 13 | public interface OrderRepository { 14 | 15 | 16 | OrderModel queryById(long orderId); 17 | 18 | OrderModel queryByNo(String orderNo); 19 | 20 | //分页查询 21 | Pager page(OrderPageQuery orderPageQuery); 22 | 23 | OrderModel insert(OrderModel order); 24 | 25 | OrderModel updatePaymentId(OrderModel order); 26 | 27 | //查找某人关于某个专栏的非终态订单。 28 | OrderModel queryNonFinalState(ReaderVO readerVO, ColumnVO columnVO); 29 | 30 | OrderModel updateStatus(OrderModel orderModel); 31 | } 32 | -------------------------------------------------------------------------------- /scs-infrastructure/src/main/java/com/smart/classroom/subscription/infrastructure/rpc/column/ColumnFacadeClientImpl.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.infrastructure.rpc.column; 2 | 3 | import com.smart.classroom.misc.facade.biz.column.ColumnReadFacade; 4 | import com.smart.classroom.misc.facade.biz.column.response.ColumnDTO; 5 | import com.smart.classroom.subscription.domain.rpc.column.ColumnFacadeClient; 6 | import com.smart.classroom.subscription.domain.rpc.column.vo.ColumnVO; 7 | import com.smart.classroom.subscription.infrastructure.rpc.column.converter.ColumnDTO2VOConverter; 8 | import org.apache.dubbo.config.annotation.DubboReference; 9 | import org.springframework.stereotype.Service; 10 | 11 | /** 12 | * @author lishuang 13 | * @date 2023-05-15 14 | */ 15 | @Service 16 | public class ColumnFacadeClientImpl implements ColumnFacadeClient { 17 | 18 | @DubboReference 19 | ColumnReadFacade columnReadFacade; 20 | 21 | @Override 22 | public ColumnVO queryById(long columnId) { 23 | 24 | ColumnDTO columnDTO = columnReadFacade.queryById(columnId); 25 | 26 | return ColumnDTO2VOConverter.convert(columnDTO); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /scs-infrastructure/src/main/java/com/smart/classroom/subscription/infrastructure/rpc/reader/ReaderFacadeClientImpl.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.infrastructure.rpc.reader; 2 | 3 | import com.smart.classroom.misc.facade.biz.reader.ReaderReadFacade; 4 | import com.smart.classroom.misc.facade.biz.reader.response.ReaderDTO; 5 | import com.smart.classroom.subscription.domain.rpc.reader.ReaderFacadeClient; 6 | import com.smart.classroom.subscription.domain.rpc.reader.vo.ReaderVO; 7 | import com.smart.classroom.subscription.infrastructure.rpc.reader.converter.ReaderDTO2VOConverter; 8 | import org.apache.dubbo.config.annotation.DubboReference; 9 | import org.springframework.stereotype.Service; 10 | 11 | /** 12 | * @author lishuang 13 | * @date 2023-05-15 14 | */ 15 | @Service 16 | public class ReaderFacadeClientImpl implements ReaderFacadeClient { 17 | 18 | @DubboReference 19 | ReaderReadFacade readerReadFacade; 20 | 21 | @Override 22 | public ReaderVO queryById(long readerId) { 23 | 24 | ReaderDTO readerDTO = readerReadFacade.queryById(readerId); 25 | 26 | return ReaderDTO2VOConverter.convert(readerDTO); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /scs-utility/src/main/java/com/smart/classroom/subscription/utility/result/Pager.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.utility.result; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | 6 | import java.util.List; 7 | 8 | 9 | @Data 10 | @NoArgsConstructor 11 | public class Pager { 12 | 13 | //允许的最大页数 14 | public static int MAX_PAGE_SIZE = 1000; 15 | 16 | //批处理时每次页数 17 | public static int HANDLE_PAGE_SIZE = 500; 18 | 19 | //当前页码。 1基 20 | private long pageNum; 21 | 22 | //每页的大小。 23 | private long pageSize; 24 | 25 | //总条目数 26 | private long totalItems; 27 | 28 | //总页数 29 | private long totalPages; 30 | 31 | private List data; 32 | 33 | 34 | public Pager(long pageNum, long pageSize, long totalItems, List data) { 35 | 36 | if (pageSize <= 0) { 37 | pageSize = 10; 38 | } 39 | 40 | this.pageNum = pageNum; 41 | 42 | this.pageSize = pageSize; 43 | 44 | this.totalItems = totalItems; 45 | 46 | this.totalPages = (long) Math.ceil(this.totalItems * 1.0 / this.pageSize); 47 | 48 | this.data = data; 49 | 50 | } 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /scs-facade-impl/src/main/java/com/smart/classroom/subscription/facade/impl/biz/order/OrderReadFacadeImpl.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.facade.impl.biz.order; 2 | 3 | import com.smart.classroom.subscription.domain.biz.order.model.OrderModel; 4 | import com.smart.classroom.subscription.domain.biz.order.repository.OrderRepository; 5 | import com.smart.classroom.subscription.facade.biz.order.OrderReadFacade; 6 | import com.smart.classroom.subscription.facade.biz.order.response.OrderDTO; 7 | import com.smart.classroom.subscription.facade.impl.biz.order.converter.OrderModel2DTOConverter; 8 | import org.apache.dubbo.config.annotation.DubboService; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | 11 | import javax.annotation.Resource; 12 | 13 | /** 14 | * @author lishuang 15 | * @date 2023-05-19 16 | */ 17 | @DubboService 18 | public class OrderReadFacadeImpl implements OrderReadFacade { 19 | 20 | @Resource 21 | OrderRepository orderRepository; 22 | 23 | @Override 24 | public OrderDTO queryById(Long orderId) { 25 | OrderModel orderModel = orderRepository.queryById(orderId); 26 | return OrderModel2DTOConverter.convert(orderModel); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /scs-facade/src/main/java/com/smart/classroom/subscription/facade/biz/subscription/response/SubscriptionDTO.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.facade.biz.subscription.response; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.io.Serializable; 9 | import java.util.Date; 10 | 11 | /** 12 | * @author lishuang 13 | * @date 2023-05-15 14 | */ 15 | @Data 16 | @Builder 17 | @NoArgsConstructor 18 | @AllArgsConstructor 19 | public class SubscriptionDTO implements Serializable { 20 | 21 | 22 | /** 23 | * 主键 24 | */ 25 | private Long id; 26 | 27 | /** 28 | * 创建时间 29 | */ 30 | private Date createTime = null; 31 | 32 | /** 33 | * 修改时间 34 | */ 35 | private Date updateTime = null; 36 | 37 | /** 38 | * 读者id 39 | */ 40 | private Long readerId = null; 41 | 42 | /** 43 | * 专栏id 44 | */ 45 | private Long columnId = null; 46 | 47 | /** 48 | * 订单id 49 | */ 50 | private Long orderId = null; 51 | 52 | /** 53 | * 状态 CREATED/OK/DISABLED 54 | */ 55 | private String status = null; 56 | 57 | 58 | 59 | 60 | } 61 | -------------------------------------------------------------------------------- /scs-repository/src/main/java/com/smart/classroom/subscription/repository/mapper/order/OrderMapper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * smart-classroom.com Inc. Copyright (c) 2015-present All Rights Reserved. 3 | * generated by SaberGenerator 4 | */ 5 | package com.smart.classroom.subscription.repository.mapper.order; 6 | 7 | import com.smart.classroom.subscription.domain.biz.order.enums.OrderStatus; 8 | import com.smart.classroom.subscription.repository.orm.order.OrderDO; 9 | import com.smart.classroom.subscription.repository.orm.subscription.SubscriptionDO; 10 | import org.apache.ibatis.annotations.Mapper; 11 | import org.apache.ibatis.annotations.Param; 12 | import org.springframework.stereotype.Repository; 13 | 14 | import java.util.List; 15 | 16 | @Mapper 17 | @Repository 18 | public interface OrderMapper extends OrderBaseMapper { 19 | 20 | List queryByReaderIdAndColumnIdAndStatuses( 21 | @Param("readerId") Long readerId, 22 | @Param("columnId") Long columnId, 23 | @Param("statuses") List statuses 24 | ); 25 | 26 | OrderDO queryByNo(@Param("orderNo") String orderNo); 27 | 28 | int updateStatus(@Param("orderId") Long orderId, @Param("status") OrderStatus status); 29 | } -------------------------------------------------------------------------------- /scs-testsuit/src/main/resources/saber/TemplateBaseMapperJava.vm: -------------------------------------------------------------------------------- 1 | /** 2 | * smart-classroom.com Inc. Copyright (c) 2015-present All Rights Reserved. 3 | * generated by SaberGenerator 4 | */ 5 | package ${artwork.mapperPackage}; 6 | 7 | #foreach($item in $artwork.mapperImportNones) 8 | import ${item}; 9 | #if(!$foreach.hasNext) 10 | 11 | #end 12 | #end 13 | #foreach($item in $artwork.mapperImportJavas) 14 | import ${item}; 15 | #if(!$foreach.hasNext) 16 | 17 | #end 18 | #end 19 | @Mapper 20 | @Repository 21 | public interface ${artwork.entityUpperBodyName}BaseMapper extends BaseMapper<${artwork.entityUpperBodyName}DO> { 22 | 23 | /** 24 | * 当入参中含有PageNum和pageSize时,会自动分页。 25 | */ 26 | Page<${artwork.entityUpperBodyName}DO> page( 27 | @Param("pageNum") int pageNum, 28 | @Param("pageSize") int pageSize, 29 | #foreach($item in $artwork.filterSummaryColumns) 30 | #if($item.canSort) 31 | @Param("order${item.upperCamelName}") SortDirection order${item.upperCamelName}#if($foreach.hasNext), 32 | #end 33 | #else 34 | @Param("${item.lowerCamelName}") ${item.javaSimpleName} ${item.lowerCamelName}#if($foreach.hasNext), 35 | #end 36 | #end 37 | #end 38 | 39 | ); 40 | 41 | } -------------------------------------------------------------------------------- /docs/start/rocketmq.md: -------------------------------------------------------------------------------- 1 | # Nacos使用方法 2 | ## 下载 3 | 从 [官网](https://rocketmq.apache.org/zh/docs/quickStart/01quickstart/) 下载,比如下载rocketmq-all-5.1.0-bin-release.zip 4 | 5 | ## 启动NameServer 6 | ```shell 7 | $ nohup sh bin/mqnamesrv & 8 | 9 | ### 验证namesrv是否启动成功 10 | $ tail -f ~/logs/rocketmqlogs/namesrv.log 11 | The Name Server boot success... 12 | ``` 13 | 14 | 15 | ## 启动Broker+Proxy 16 | ```shell 17 | ### 先启动broker 18 | $ nohup sh bin/mqbroker -n localhost:9876 --enable-proxy & 19 | 20 | ### 验证broker是否启动成功, 比如, broker的ip是192.168.1.2 然后名字是broker-a 21 | $ tail -f ~/logs/rocketmqlogs/proxy.log 22 | The broker[broker-a,192.169.1.2:10911] boot success... 23 | ``` 24 | 25 | 首次启动的时候需要创建一个Topic. 26 | ```shell 27 | sh bin/mqadmin updatetopic -n localhost:9876 -t SmartClassroomTopic -c DefaultCluster 28 | ``` 29 | 30 | ## 关闭 31 | ```shell 32 | $ sh bin/mqshutdown broker 33 | $ sh bin/mqshutdown namesrv 34 | ``` 35 | 36 | 37 | ## 常见问题 38 | ### 有可能rocket-mq会因为磁盘不足而报错: 39 | ```text 40 | the broker's disk is full 41 | ``` 42 | 这个时候将磁盘阈值调大: 43 | ```text 44 | vi bin/runbroker.sh 45 | ``` 46 | 增加一行java启动参数 47 | ```shell 48 | JAVA_OPT="${JAVA_OPT} -Drocketmq.broker.diskSpaceWarningLevelRatio=0.99" 49 | ``` 50 | 然后重启rocketmq即可。 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /scs-repository/src/main/java/com/smart/classroom/subscription/repository/orm/subscription/SubscriptionDO.java: -------------------------------------------------------------------------------- 1 | /** 2 | * smart-classroom.com Inc. Copyright (c) 2015-present All Rights Reserved. 3 | * generated by SaberGenerator 4 | */ 5 | package com.smart.classroom.subscription.repository.orm.subscription; 6 | 7 | import com.smart.classroom.subscription.domain.biz.subscription.enums.SubscriptionStatus; 8 | import com.smart.classroom.subscription.repository.orm.base.BaseEntityDO; 9 | import lombok.AllArgsConstructor; 10 | import lombok.Builder; 11 | import lombok.Data; 12 | import lombok.EqualsAndHashCode; 13 | import lombok.NoArgsConstructor; 14 | 15 | /** 16 | * 订阅表 17 | */ 18 | @Data 19 | @Builder 20 | @NoArgsConstructor 21 | @AllArgsConstructor 22 | @EqualsAndHashCode(callSuper = true) 23 | public class SubscriptionDO extends BaseEntityDO { 24 | 25 | /** 26 | * 读者id 27 | */ 28 | private Long readerId = null; 29 | 30 | /** 31 | * 专栏id 32 | */ 33 | private Long columnId = null; 34 | 35 | /** 36 | * 订单id 37 | */ 38 | private Long orderId = null; 39 | 40 | /** 41 | * 状态 CREATED/OK/DISABLED 42 | */ 43 | private SubscriptionStatus status = SubscriptionStatus.CREATED; 44 | 45 | 46 | } -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/rpc/payment/vo/PaymentVO.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.rpc.payment.vo; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.util.Date; 9 | 10 | /** 11 | * @author lishuang 12 | * @date 2023-05-15 13 | */ 14 | @Data 15 | @Builder 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | public class PaymentVO { 19 | 20 | /** 21 | * 主键 22 | */ 23 | private Long id = null; 24 | 25 | /** 26 | * 创建时间 27 | */ 28 | private Date createTime = null; 29 | 30 | /** 31 | * 修改时间 32 | */ 33 | private Date updateTime = null; 34 | 35 | 36 | /** 37 | * 订单编号 38 | */ 39 | private String orderNo = null; 40 | 41 | /** 42 | * 支付方式 ALIPAY/WEIXIN 支付宝/微信 43 | */ 44 | private String method = null; 45 | 46 | /** 47 | * 支付平台订单号 48 | */ 49 | private String thirdTransactionNo = null; 50 | 51 | /** 52 | * 金额(单位:分) 53 | */ 54 | private Long amount = null; 55 | 56 | /** 57 | * 支付状态 UNPAID/PAID/CLOSED 未支付/已支付/已关闭 58 | */ 59 | private String status = null; 60 | } 61 | -------------------------------------------------------------------------------- /scs-infrastructure/src/main/java/com/smart/classroom/subscription/infrastructure/rpc/payment/converter/PaymentDTO2VOConverter.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.infrastructure.rpc.payment.converter; 2 | 3 | 4 | import com.smart.classroom.misc.facade.biz.payment.response.PaymentDTO; 5 | import com.smart.classroom.subscription.domain.rpc.payment.vo.PaymentVO; 6 | 7 | /** 8 | * @author lishuang 9 | * @date 2023-05-12 10 | * 转换器 11 | */ 12 | public class PaymentDTO2VOConverter { 13 | 14 | /** 15 | * 将DO转换成模型 16 | */ 17 | public static PaymentVO convert(PaymentDTO paymentDTO) { 18 | if (paymentDTO == null) { 19 | return null; 20 | } 21 | 22 | return PaymentVO.builder() 23 | .id(paymentDTO.getId()) 24 | .createTime(paymentDTO.getCreateTime()) 25 | .updateTime(paymentDTO.getUpdateTime()) 26 | 27 | .orderNo(paymentDTO.getOrderNo()) 28 | .method(paymentDTO.getMethod()) 29 | .thirdTransactionNo(paymentDTO.getThirdTransactionNo()) 30 | .amount(paymentDTO.getAmount()) 31 | 32 | .status(paymentDTO.getStatus()) 33 | 34 | .build(); 35 | } 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /scs-repository/src/main/java/com/smart/classroom/subscription/repository/impl/subscription/converter/SubscriptionDO2ModelConverter.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.repository.impl.subscription.converter; 2 | 3 | 4 | import com.smart.classroom.subscription.domain.biz.subscription.model.SubscriptionModel; 5 | import com.smart.classroom.subscription.repository.orm.subscription.SubscriptionDO; 6 | 7 | /** 8 | * @author lishuang 9 | * @date 2023-05-12 10 | * 转换器 11 | */ 12 | public class SubscriptionDO2ModelConverter { 13 | 14 | /** 15 | * 将DO转换成模型 16 | */ 17 | public static SubscriptionModel convert(SubscriptionDO subscriptionDO) { 18 | if (subscriptionDO == null) { 19 | return null; 20 | } 21 | 22 | return SubscriptionModel.builder() 23 | .id(subscriptionDO.getId()) 24 | .createTime(subscriptionDO.getCreateTime()) 25 | .updateTime(subscriptionDO.getUpdateTime()) 26 | .readerId(subscriptionDO.getReaderId()) 27 | .columnId(subscriptionDO.getColumnId()) 28 | .orderId(subscriptionDO.getOrderId()) 29 | .status(subscriptionDO.getStatus()) 30 | .build(); 31 | } 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/biz/subscription/model/SubscriptionModel.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.biz.subscription.model; 2 | 3 | import com.smart.classroom.subscription.domain.biz.subscription.enums.SubscriptionStatus; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.util.Date; 10 | 11 | /** 12 | * @author lishuang 13 | * @date 2023-05-12 14 | */ 15 | @Data 16 | @Builder 17 | @NoArgsConstructor 18 | @AllArgsConstructor 19 | public class SubscriptionModel { 20 | 21 | /** 22 | * 主键 23 | */ 24 | private Long id; 25 | 26 | /** 27 | * 创建时间 28 | */ 29 | private Date createTime = null; 30 | 31 | /** 32 | * 修改时间 33 | */ 34 | private Date updateTime = null; 35 | 36 | /** 37 | * 读者id 38 | */ 39 | private Long readerId = null; 40 | 41 | /** 42 | * 专栏id 43 | */ 44 | private Long columnId = null; 45 | 46 | /** 47 | * 订单id 48 | */ 49 | private Long orderId = null; 50 | 51 | /** 52 | * 状态 CREATED/OK/DISABLED 53 | */ 54 | private SubscriptionStatus status = SubscriptionStatus.CREATED; 55 | 56 | 57 | } 58 | -------------------------------------------------------------------------------- /scs-repository/src/main/java/com/smart/classroom/subscription/repository/impl/order/converter/OrderDO2ModelConverter.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.repository.impl.order.converter; 2 | 3 | 4 | import com.smart.classroom.subscription.domain.biz.order.model.OrderModel; 5 | import com.smart.classroom.subscription.repository.orm.order.OrderDO; 6 | 7 | /** 8 | * @author lishuang 9 | * @date 2023-05-12 10 | * 转换器 11 | */ 12 | public class OrderDO2ModelConverter { 13 | 14 | /** 15 | * 将DO转换成模型 16 | */ 17 | public static OrderModel convert(OrderDO orderDO) { 18 | if (orderDO == null) { 19 | return null; 20 | } 21 | 22 | return OrderModel.builder() 23 | .id(orderDO.getId()) 24 | .createTime(orderDO.getCreateTime()) 25 | .updateTime(orderDO.getUpdateTime()) 26 | .no(orderDO.getNo()) 27 | .readerId(orderDO.getReaderId()) 28 | .columnId(orderDO.getColumnId()) 29 | .columnQuoteId(orderDO.getColumnQuoteId()) 30 | .paymentId(orderDO.getPaymentId()) 31 | .price(orderDO.getPrice()) 32 | .status(orderDO.getStatus()) 33 | .build(); 34 | } 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /scs-facade-impl/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | smart-classroom-subscription 7 | com.smart.classroom 8 | 1.0.0 9 | 10 | 4.0.0 11 | 12 | scs-facade-impl 13 | 14 | 15 | 8 16 | 8 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | com.smart.classroom 26 | scs-application 27 | 28 | 29 | 30 | com.smart.classroom 31 | scs-facade 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /scs-domain/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | smart-classroom-subscription 7 | com.smart.classroom 8 | 1.0.0 9 | 10 | 4.0.0 11 | 12 | scs-domain 13 | 14 | 15 | 8 16 | 8 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | com.smart.classroom 26 | scs-utility 27 | 28 | 29 | 30 | 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /scs-testsuit/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | smart-classroom-subscription 7 | com.smart.classroom 8 | 1.0.0 9 | 10 | 4.0.0 11 | 12 | scs-testsuit 13 | 14 | 15 | 8 16 | 8 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | com.smart.classroom 25 | scs-main 26 | 27 | 28 | 29 | 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-test 34 | test 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /scs-infrastructure/src/main/java/com/smart/classroom/subscription/infrastructure/rpc/quote/converter/ColumnQuoteDTO2VOConverter.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.infrastructure.rpc.quote.converter; 2 | 3 | 4 | import com.smart.classroom.misc.facade.biz.column.response.ColumnDTO; 5 | import com.smart.classroom.misc.facade.biz.quote.response.ColumnQuoteDTO; 6 | import com.smart.classroom.subscription.domain.rpc.column.vo.ColumnVO; 7 | import com.smart.classroom.subscription.domain.rpc.quote.vo.ColumnQuoteVO; 8 | 9 | /** 10 | * @author lishuang 11 | * @date 2023-05-12 12 | * 转换器 13 | */ 14 | public class ColumnQuoteDTO2VOConverter { 15 | 16 | /** 17 | * 将DO转换成模型 18 | */ 19 | public static ColumnQuoteVO convert(ColumnQuoteDTO columnQuoteDTO) { 20 | if (columnQuoteDTO == null) { 21 | return null; 22 | } 23 | 24 | return ColumnQuoteVO.builder() 25 | .id(columnQuoteDTO.getId()) 26 | .createTime(columnQuoteDTO.getCreateTime()) 27 | .updateTime(columnQuoteDTO.getUpdateTime()) 28 | .columnId(columnQuoteDTO.getColumnId()) 29 | .editorId(columnQuoteDTO.getEditorId()) 30 | .price(columnQuoteDTO.getPrice()) 31 | .status(columnQuoteDTO.getStatus()) 32 | 33 | .build(); 34 | } 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /scs-repository/src/main/java/com/smart/classroom/subscription/repository/impl/order/converter/OrderModel2DOConverter.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.repository.impl.order.converter; 2 | 3 | import com.smart.classroom.subscription.domain.biz.order.model.OrderModel; 4 | import com.smart.classroom.subscription.repository.orm.order.OrderDO; 5 | 6 | /** 7 | * @author lishuang 8 | * @date 2023-05-12 9 | * 转换器 10 | */ 11 | public class OrderModel2DOConverter { 12 | 13 | /** 14 | * 将模型转换成DO 15 | */ 16 | public static OrderDO convert(OrderModel orderModel) { 17 | if (orderModel == null) { 18 | return null; 19 | } 20 | 21 | OrderDO orderDO = OrderDO.builder() 22 | .no(orderModel.getNo()) 23 | .readerId(orderModel.getReaderId()) 24 | .columnId(orderModel.getColumnId()) 25 | .columnQuoteId(orderModel.getColumnQuoteId()) 26 | .paymentId(orderModel.getPaymentId()) 27 | .price(orderModel.getPrice()) 28 | .status(orderModel.getStatus()) 29 | .build(); 30 | 31 | orderDO.setId(orderModel.getId()); 32 | orderDO.setCreateTime(orderModel.getCreateTime()); 33 | orderDO.setUpdateTime(orderModel.getUpdateTime()); 34 | 35 | return orderDO; 36 | 37 | } 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /scs-repository/src/main/java/com/smart/classroom/subscription/repository/impl/subscription/converter/SubscriptionModel2DOConverter.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.repository.impl.subscription.converter; 2 | 3 | import com.smart.classroom.subscription.domain.biz.subscription.model.SubscriptionModel; 4 | import com.smart.classroom.subscription.repository.orm.subscription.SubscriptionDO; 5 | 6 | /** 7 | * @author lishuang 8 | * @date 2023-05-12 9 | * 转换器 10 | */ 11 | public class SubscriptionModel2DOConverter { 12 | 13 | /** 14 | * 将模型转换成DO 15 | */ 16 | public static SubscriptionDO convert(SubscriptionModel subscriptionModel) { 17 | if (subscriptionModel == null) { 18 | return null; 19 | } 20 | 21 | SubscriptionDO subscriptionDO = SubscriptionDO.builder() 22 | .readerId(subscriptionModel.getReaderId()) 23 | .columnId(subscriptionModel.getColumnId()) 24 | .orderId(subscriptionModel.getOrderId()) 25 | .status(subscriptionModel.getStatus()) 26 | .build(); 27 | 28 | subscriptionDO.setId(subscriptionModel.getId()); 29 | subscriptionDO.setCreateTime(subscriptionModel.getCreateTime()); 30 | subscriptionDO.setUpdateTime(subscriptionModel.getUpdateTime()); 31 | 32 | return subscriptionDO; 33 | 34 | } 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /scs-facade-impl/src/main/java/com/smart/classroom/subscription/facade/impl/biz/order/converter/OrderModel2DTOConverter.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.facade.impl.biz.order.converter; 2 | 3 | import com.smart.classroom.subscription.domain.biz.order.enums.OrderStatus; 4 | import com.smart.classroom.subscription.domain.biz.order.model.OrderModel; 5 | import com.smart.classroom.subscription.facade.biz.order.response.OrderDTO; 6 | 7 | /** 8 | * @author lishuang 9 | * @date 2023-05-12 10 | * 转换器 11 | */ 12 | public class OrderModel2DTOConverter { 13 | 14 | /** 15 | * 将DO转换成模型 16 | */ 17 | public static OrderDTO convert(OrderModel orderModel) { 18 | if (orderModel == null) { 19 | return null; 20 | } 21 | 22 | OrderDTO orderDTO = OrderDTO.builder() 23 | .id(orderModel.getId()) 24 | .createTime(orderModel.getCreateTime()) 25 | .updateTime(orderModel.getUpdateTime()) 26 | .no(orderModel.getNo()) 27 | .readerId(orderModel.getReaderId()) 28 | .columnId(orderModel.getColumnId()) 29 | .columnQuoteId(orderModel.getColumnQuoteId()) 30 | .paymentId(orderModel.getPaymentId()) 31 | .price(orderModel.getPrice()) 32 | .status(OrderStatus.toString(orderModel.getStatus())) 33 | .build(); 34 | 35 | return orderDTO; 36 | } 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /scs-application/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | smart-classroom-subscription 7 | com.smart.classroom 8 | 1.0.0 9 | 10 | 4.0.0 11 | 12 | scs-application 13 | 14 | 15 | 8 16 | 8 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | com.smart.classroom 25 | scs-domain 26 | 27 | 28 | 29 | com.smart.classroom 30 | scs-infrastructure 31 | 32 | 33 | 34 | com.smart.classroom 35 | scs-repository 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /scs-facade-impl/src/main/java/com/smart/classroom/subscription/facade/impl/biz/subscription/converter/SubscriptionModel2DTOConverter.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.facade.impl.biz.subscription.converter; 2 | 3 | import com.smart.classroom.subscription.domain.biz.subscription.enums.SubscriptionStatus; 4 | import com.smart.classroom.subscription.domain.biz.subscription.model.SubscriptionModel; 5 | import com.smart.classroom.subscription.facade.biz.subscription.response.SubscriptionDTO; 6 | 7 | /** 8 | * @author lishuang 9 | * @date 2023-05-12 10 | * 转换器 11 | */ 12 | public class SubscriptionModel2DTOConverter { 13 | 14 | /** 15 | * 将DO转换成模型 16 | */ 17 | public static SubscriptionDTO convert(SubscriptionModel subscriptionModel) { 18 | if (subscriptionModel == null) { 19 | return null; 20 | } 21 | 22 | SubscriptionDTO subscriptionDTO = SubscriptionDTO.builder() 23 | .id(subscriptionModel.getId()) 24 | .createTime(subscriptionModel.getCreateTime()) 25 | .updateTime(subscriptionModel.getUpdateTime()) 26 | 27 | .readerId(subscriptionModel.getReaderId()) 28 | .columnId(subscriptionModel.getColumnId()) 29 | .orderId(subscriptionModel.getOrderId()) 30 | .status(SubscriptionStatus.toString(subscriptionModel.getStatus())) 31 | .build(); 32 | 33 | return subscriptionDTO; 34 | } 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /scs-facade/src/main/java/com/smart/classroom/subscription/facade/biz/order/response/OrderDTO.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.facade.biz.order.response; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.io.Serializable; 9 | import java.util.Date; 10 | 11 | /** 12 | * @author lishuang 13 | * @date 2023-05-15 14 | */ 15 | @Data 16 | @Builder 17 | @NoArgsConstructor 18 | @AllArgsConstructor 19 | public class OrderDTO implements Serializable { 20 | 21 | 22 | /** 23 | * 主键 24 | */ 25 | private Long id; 26 | 27 | /** 28 | * 创建时间 29 | */ 30 | private Date createTime = null; 31 | 32 | /** 33 | * 修改时间 34 | */ 35 | private Date updateTime = null; 36 | 37 | /** 38 | * 订单唯一编号,整个系统唯一,带有前缀 39 | */ 40 | private String no = null; 41 | 42 | /** 43 | * 读者id 44 | */ 45 | private Long readerId = null; 46 | 47 | /** 48 | * 专栏id 49 | */ 50 | private Long columnId = null; 51 | 52 | /** 53 | * 专栏报价id 54 | */ 55 | private Long columnQuoteId = null; 56 | 57 | /** 58 | * 支付单id 59 | */ 60 | private Long paymentId = null; 61 | 62 | /** 63 | * 价格(单位:分) 64 | */ 65 | private Long price = null; 66 | 67 | /** 68 | * 状态 CREATED/PAID/SUBSCRIBED/CLOSED/CANCELED 69 | */ 70 | private String status = null; 71 | 72 | 73 | 74 | } 75 | -------------------------------------------------------------------------------- /scs-repository/src/main/java/com/smart/classroom/subscription/repository/orm/order/OrderDO.java: -------------------------------------------------------------------------------- 1 | /** 2 | * smart-classroom.com Inc. Copyright (c) 2015-present All Rights Reserved. 3 | * generated by SaberGenerator 4 | */ 5 | package com.smart.classroom.subscription.repository.orm.order; 6 | 7 | import com.smart.classroom.subscription.domain.biz.order.enums.OrderStatus; 8 | import com.smart.classroom.subscription.repository.orm.base.BaseEntityDO; 9 | import lombok.AllArgsConstructor; 10 | import lombok.Builder; 11 | import lombok.Data; 12 | import lombok.EqualsAndHashCode; 13 | import lombok.NoArgsConstructor; 14 | 15 | /** 16 | * 订单表 17 | */ 18 | @Data 19 | @Builder 20 | @NoArgsConstructor 21 | @AllArgsConstructor 22 | @EqualsAndHashCode(callSuper = true) 23 | public class OrderDO extends BaseEntityDO { 24 | 25 | /** 26 | * 订单唯一编号,整个系统唯一,带有前缀 27 | */ 28 | private String no = null; 29 | 30 | /** 31 | * 读者id 32 | */ 33 | private Long readerId = null; 34 | 35 | /** 36 | * 专栏id 37 | */ 38 | private Long columnId = null; 39 | 40 | /** 41 | * 专栏报价id 42 | */ 43 | private Long columnQuoteId = null; 44 | 45 | /** 46 | * 支付单id 47 | */ 48 | private Long paymentId = null; 49 | 50 | /** 51 | * 价格(单位:分) 52 | */ 53 | private Long price = null; 54 | 55 | /** 56 | * 状态 CREATED/PAID/SUBSCRIBED/CLOSED/CANCELED 57 | */ 58 | private OrderStatus status = OrderStatus.CREATED; 59 | 60 | 61 | } -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/biz/order/model/OrderModel.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.biz.order.model; 2 | 3 | import com.smart.classroom.subscription.domain.biz.order.enums.OrderStatus; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.util.Date; 10 | 11 | /** 12 | * @author lishuang 13 | * @date 2023-05-12 14 | */ 15 | @Data 16 | @Builder 17 | @NoArgsConstructor 18 | @AllArgsConstructor 19 | public class OrderModel { 20 | 21 | /** 22 | * 主键 23 | */ 24 | private Long id; 25 | 26 | /** 27 | * 创建时间 28 | */ 29 | private Date createTime = null; 30 | 31 | /** 32 | * 修改时间 33 | */ 34 | private Date updateTime = null; 35 | 36 | /** 37 | * 订单唯一编号,整个系统唯一,带有前缀 38 | */ 39 | private String no = null; 40 | 41 | /** 42 | * 读者id 43 | */ 44 | private Long readerId = null; 45 | 46 | /** 47 | * 专栏id 48 | */ 49 | private Long columnId = null; 50 | 51 | /** 52 | * 专栏报价id 53 | */ 54 | private Long columnQuoteId = null; 55 | 56 | /** 57 | * 支付单id 58 | */ 59 | private Long paymentId = null; 60 | 61 | /** 62 | * 价格(单位:分) 63 | */ 64 | private Long price = null; 65 | 66 | /** 67 | * 状态 CREATED/PAID/SUBSCRIBED/CLOSED/CANCELED 68 | */ 69 | private OrderStatus status = OrderStatus.CREATED; 70 | 71 | 72 | } 73 | -------------------------------------------------------------------------------- /scs-repository/src/main/java/com/smart/classroom/subscription/repository/mapper/order/OrderBaseMapper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * smart-classroom.com Inc. Copyright (c) 2015-present All Rights Reserved. 3 | * generated by SaberGenerator 4 | */ 5 | package com.smart.classroom.subscription.repository.mapper.order; 6 | 7 | import com.github.pagehelper.Page; 8 | import com.smart.classroom.subscription.domain.biz.order.enums.OrderStatus; 9 | import com.smart.classroom.subscription.repository.mapper.base.BaseMapper; 10 | import com.smart.classroom.subscription.repository.orm.order.OrderDO; 11 | import com.smart.classroom.subscription.utility.enums.SortDirection; 12 | import org.apache.ibatis.annotations.Mapper; 13 | import org.apache.ibatis.annotations.Param; 14 | import org.springframework.stereotype.Repository; 15 | 16 | @Mapper 17 | @Repository 18 | public interface OrderBaseMapper extends BaseMapper { 19 | 20 | /** 21 | * 当入参中含有PageNum和pageSize时,会自动分页。 22 | */ 23 | Page page( 24 | @Param("pageNum") int pageNum, 25 | @Param("pageSize") int pageSize, 26 | @Param("orderCreateTime") SortDirection orderCreateTime, 27 | @Param("orderUpdateTime") SortDirection orderUpdateTime, 28 | @Param("readerId") Long readerId, 29 | @Param("columnId") Long columnId, 30 | @Param("columnQuoteId") Long columnQuoteId, 31 | @Param("paymentId") Long paymentId, 32 | @Param("status") OrderStatus status 33 | ); 34 | 35 | 36 | } -------------------------------------------------------------------------------- /scs-repository/src/main/java/com/smart/classroom/subscription/repository/mapper/subscription/SubscriptionBaseMapper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * smart-classroom.com Inc. Copyright (c) 2015-present All Rights Reserved. 3 | * generated by SaberGenerator 4 | */ 5 | package com.smart.classroom.subscription.repository.mapper.subscription; 6 | 7 | import com.github.pagehelper.Page; 8 | import com.smart.classroom.subscription.domain.biz.subscription.enums.SubscriptionStatus; 9 | import com.smart.classroom.subscription.repository.mapper.base.BaseMapper; 10 | import com.smart.classroom.subscription.repository.orm.subscription.SubscriptionDO; 11 | import com.smart.classroom.subscription.utility.enums.SortDirection; 12 | import org.apache.ibatis.annotations.Mapper; 13 | import org.apache.ibatis.annotations.Param; 14 | import org.springframework.stereotype.Repository; 15 | 16 | @Mapper 17 | @Repository 18 | public interface SubscriptionBaseMapper extends BaseMapper { 19 | 20 | /** 21 | * 当入参中含有PageNum和pageSize时,会自动分页。 22 | */ 23 | Page page( 24 | @Param("pageNum") int pageNum, 25 | @Param("pageSize") int pageSize, 26 | @Param("orderCreateTime") SortDirection orderCreateTime, 27 | @Param("orderUpdateTime") SortDirection orderUpdateTime, 28 | @Param("readerId") Long readerId, 29 | @Param("columnId") Long columnId, 30 | @Param("orderId") Long orderId, 31 | @Param("status") SubscriptionStatus status 32 | ); 33 | 34 | } -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/biz/order/service/OrderPaidDomainService.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.biz.order.service; 2 | 3 | import com.smart.classroom.subscription.domain.biz.order.enums.OrderStatus; 4 | import com.smart.classroom.subscription.domain.biz.order.model.OrderModel; 5 | import com.smart.classroom.subscription.domain.biz.order.repository.OrderRepository; 6 | import com.smart.classroom.subscription.domain.rpc.payment.PaymentFacadeClient; 7 | import com.smart.classroom.subscription.domain.rpc.quote.ColumnQuoteFacadeClient; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.stereotype.Service; 11 | 12 | /** 13 | * 准备订单 领域服务 14 | * 15 | * @author lishuang 16 | * @date 2023-05-12 17 | */ 18 | @Slf4j 19 | @Service 20 | public class OrderPaidDomainService { 21 | 22 | 23 | @Autowired 24 | OrderRepository orderRepository; 25 | 26 | @Autowired 27 | ColumnQuoteFacadeClient columnQuoteFacadeClient; 28 | 29 | @Autowired 30 | PaymentFacadeClient paymentFacadeClient; 31 | 32 | 33 | /** 34 | * 更新订单状态至已支付。 35 | */ 36 | public OrderModel orderPaid(OrderModel orderModel) { 37 | 38 | //更新状态 39 | orderModel.setStatus(OrderStatus.PAID); 40 | orderModel = orderRepository.updateStatus(orderModel); 41 | 42 | log.info("订单id={} no={}状态已更新成PAID", orderModel.getId(), orderModel.getNo()); 43 | 44 | 45 | return orderModel; 46 | 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /scs-utility/src/main/java/com/smart/classroom/subscription/utility/model/Pair.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.utility.model; 2 | 3 | import java.util.Objects; 4 | 5 | /** 6 | * 将两个值封装在一个类中 7 | * 从而方便多值返回使用 8 | * 9 | * @author lishuang 10 | * @version 2023-05-11 11 | */ 12 | public class Pair { 13 | 14 | /** 15 | * 键 16 | */ 17 | private K key; 18 | 19 | public K getKey() { 20 | return key; 21 | } 22 | 23 | /** 24 | * 值 25 | */ 26 | private V value; 27 | 28 | public V getValue() { 29 | return value; 30 | } 31 | 32 | public Pair(K key, V value) { 33 | this.key = key; 34 | this.value = value; 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | return key + "=" + value; 40 | } 41 | 42 | @Override 43 | public int hashCode() { 44 | // name's hashCode is multiplied by an arbitrary prime number (13) 45 | // in order to make sure there is a difference in the hashCode between 46 | // these two parameters: 47 | // name: a value: aa 48 | // name: aa value: a 49 | return key.hashCode() * 13 + (value == null ? 0 : value.hashCode()); 50 | } 51 | 52 | @Override 53 | public boolean equals(Object o) { 54 | if (this == o) { 55 | return true; 56 | } 57 | if (o instanceof Pair) { 58 | Pair pair = (Pair) o; 59 | if (!Objects.equals(key, pair.key)) { 60 | return false; 61 | } 62 | return Objects.equals(value, pair.value); 63 | } 64 | return false; 65 | } 66 | } -------------------------------------------------------------------------------- /scs-testsuit/src/main/java/com/smart/classroom/subscription/tool/saber/x/order/OrderCommand.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.tool.saber.x.order; 2 | 3 | 4 | import com.smart.classroom.subscription.domain.biz.order.enums.OrderStatus; 5 | import com.smart.classroom.subscription.tool.saber.command.SaberCommand; 6 | import com.smart.classroom.subscription.tool.saber.support.SaberHelper; 7 | import com.smart.classroom.subscription.utility.model.Pair; 8 | 9 | import java.util.ArrayList; 10 | import java.util.HashMap; 11 | 12 | /** 13 | * 直接运行该文件,会生成对应的DO,Mapper文件。 14 | */ 15 | public class OrderCommand { 16 | public static void main(String[] args) { 17 | 18 | /* 19 | * scs_order 20 | */ 21 | SaberCommand saberCommand = new SaberCommand("scs_order"); 22 | 23 | //模糊筛选 24 | saberCommand.setFilterLikeFields(new ArrayList() {{ 25 | //下划线风格,驼峰均可以 26 | 27 | 28 | }}); 29 | 30 | //等号筛选 31 | saberCommand.setFilterEqualFields(new ArrayList() {{ 32 | //下划线风格,驼峰均可以 33 | 34 | add("reader_id"); 35 | add("column_id"); 36 | add("column_quote_id"); 37 | add("payment_id"); 38 | add("status"); 39 | }}); 40 | 41 | //排序 42 | saberCommand.setFilterOrderFields(new ArrayList() {{ 43 | //下划线风格,驼峰均可以 44 | 45 | }}); 46 | 47 | //枚举定义 48 | saberCommand.setEnumFieldMap(new HashMap>, String>>() {{ 49 | put("status", new Pair<>(OrderStatus.class, OrderStatus.CREATED.name())); 50 | }}); 51 | 52 | SaberHelper.run(saberCommand); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /scs-testsuit/src/main/java/com/smart/classroom/subscription/tool/saber/x/subscription/SubscriptionCommand.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.tool.saber.x.subscription; 2 | 3 | 4 | import com.smart.classroom.subscription.domain.biz.subscription.enums.SubscriptionStatus; 5 | import com.smart.classroom.subscription.tool.saber.command.SaberCommand; 6 | import com.smart.classroom.subscription.tool.saber.support.SaberHelper; 7 | import com.smart.classroom.subscription.utility.model.Pair; 8 | 9 | import java.util.ArrayList; 10 | import java.util.HashMap; 11 | 12 | /** 13 | * 直接运行该文件,会生成对应的DO,Mapper文件。 14 | */ 15 | public class SubscriptionCommand { 16 | public static void main(String[] args) { 17 | 18 | /* 19 | * scs_subscription 20 | */ 21 | SaberCommand saberCommand = new SaberCommand("scs_subscription"); 22 | 23 | //模糊筛选 24 | saberCommand.setFilterLikeFields(new ArrayList() {{ 25 | //下划线风格,驼峰均可以 26 | 27 | 28 | }}); 29 | 30 | //等号筛选 31 | saberCommand.setFilterEqualFields(new ArrayList() {{ 32 | //下划线风格,驼峰均可以 33 | 34 | add("reader_id"); 35 | add("column_id"); 36 | add("order_id"); 37 | add("status"); 38 | }}); 39 | 40 | //排序 41 | saberCommand.setFilterOrderFields(new ArrayList() {{ 42 | //下划线风格,驼峰均可以 43 | 44 | }}); 45 | 46 | //枚举定义 47 | saberCommand.setEnumFieldMap(new HashMap>, String>>() {{ 48 | put("status", new Pair<>(SubscriptionStatus.class, SubscriptionStatus.CREATED.name())); 49 | }}); 50 | 51 | SaberHelper.run(saberCommand); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /scs-repository/src/main/resources/mybatis/mapper/order/OrderMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 29 | 30 | 37 | 38 | 39 | 40 | UPDATE scs_order 41 | SET 42 | update_time = current_timestamp() , 43 | status = #{status} 44 | WHERE id = #{orderId} 45 | 46 | 47 | -------------------------------------------------------------------------------- /scs-repository/src/main/resources/mybatis/mybatis-config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /scs-utility/src/main/java/com/smart/classroom/subscription/utility/exception/UtilException.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.utility.exception; 2 | 3 | 4 | import com.smart.classroom.subscription.utility.util.StringUtil; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | 8 | /** 9 | * 作为我们自定义异常的父类 10 | */ 11 | @EqualsAndHashCode(callSuper = false) 12 | @Data 13 | public class UtilException extends RuntimeException { 14 | 15 | protected ExceptionCode code; 16 | protected String message; 17 | 18 | public UtilException() { 19 | this(ExceptionCode.UNKNOWN); 20 | } 21 | 22 | public UtilException(String messagePattern, Object... arguments) { 23 | 24 | super(StringUtil.format(messagePattern, arguments)); 25 | 26 | this.code = ExceptionCode.UNKNOWN; 27 | this.message = StringUtil.format(messagePattern, arguments); 28 | } 29 | 30 | public UtilException(ExceptionCode resultCode) { 31 | super(resultCode.getMessage()); 32 | 33 | this.code = resultCode; 34 | this.message = resultCode.getMessage(); 35 | } 36 | 37 | public UtilException(Throwable throwable) { 38 | super(throwable); 39 | 40 | this.code = ExceptionCode.UNKNOWN; 41 | this.message = throwable.getMessage(); 42 | } 43 | 44 | 45 | public UtilException(ExceptionCode resultCode, String messagePattern, Object... arguments) { 46 | super(StringUtil.format(messagePattern, arguments)); 47 | 48 | this.code = resultCode; 49 | this.message = StringUtil.format(messagePattern, arguments); 50 | } 51 | 52 | @Override 53 | public String getMessage() { 54 | return this.message; 55 | } 56 | 57 | @Override 58 | public String toString() { 59 | return StringUtil.format("{} code={} message={}", this.getClass().getSimpleName(), this.code, this.message); 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /scs-utility/src/main/java/com/smart/classroom/subscription/utility/util/NumberUtil.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.utility.util; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.Objects; 5 | import java.util.Random; 6 | 7 | public class NumberUtil { 8 | 9 | 10 | /** 11 | * 判断一个字符串是否为整数 12 | * 13 | * @param value 待检测的字符串 14 | * @return true=>是整数 false=>不是整数 15 | */ 16 | public static boolean isInteger(String value) { 17 | try { 18 | Integer.parseInt(value); 19 | return true; 20 | } catch (Throwable throwable) { 21 | return false; 22 | } 23 | } 24 | 25 | 26 | /** 27 | * 判断一个BigDecimal是否为整数 28 | * 29 | * @param bigDecimal 待检测的字符串 30 | * @return true=>是整数 false=>不是整数 31 | */ 32 | public static boolean isInteger(BigDecimal bigDecimal) { 33 | return bigDecimal.stripTrailingZeros().scale() <= 0; 34 | } 35 | 36 | 37 | /** 38 | * [a,b)的一个随机整数 39 | */ 40 | public static int random(int start, int end) { 41 | 42 | if (start > end) { 43 | int temp = start; 44 | start = end; 45 | end = temp; 46 | } 47 | 48 | Random r = new Random(); 49 | 50 | return r.nextInt(end - start) + start; 51 | } 52 | 53 | 54 | /** 55 | * 比较两个泛型Number的大小 56 | */ 57 | public static int numberCompare(Number number1, Number number2) { 58 | 59 | return new BigDecimal(number1.toString()).compareTo(new BigDecimal(number2.toString())); 60 | 61 | } 62 | 63 | public static int min(int a, int b) { 64 | return Math.min(a, b); 65 | 66 | } 67 | 68 | 69 | /** 70 | * 都为null 为true ,相等为true 71 | */ 72 | public static Boolean equals(Number number1,Number number2) { 73 | 74 | if (Objects.isNull(number1) && Objects.isNull(number2)) { 75 | return true; 76 | } else if (Objects.nonNull(number1) && number1.equals(number2)) { 77 | return true; 78 | } else{ 79 | return false; 80 | } 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /scs-infrastructure/src/main/java/com/smart/classroom/subscription/infrastructure/rpc/quote/ColumnQuoteFacadeClientImpl.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.infrastructure.rpc.quote; 2 | 3 | import com.smart.classroom.misc.facade.biz.payment.PaymentReadFacade; 4 | import com.smart.classroom.misc.facade.biz.payment.response.PaymentDTO; 5 | import com.smart.classroom.misc.facade.biz.payment.response.PreparePaymentDTO; 6 | import com.smart.classroom.misc.facade.biz.quote.ColumnQuoteReadFacade; 7 | import com.smart.classroom.misc.facade.biz.quote.response.ColumnQuoteDTO; 8 | import com.smart.classroom.subscription.domain.rpc.payment.PaymentFacadeClient; 9 | import com.smart.classroom.subscription.domain.rpc.payment.info.PreparePaymentInfo; 10 | import com.smart.classroom.subscription.domain.rpc.payment.vo.PaymentVO; 11 | import com.smart.classroom.subscription.domain.rpc.quote.ColumnQuoteFacadeClient; 12 | import com.smart.classroom.subscription.domain.rpc.quote.vo.ColumnQuoteVO; 13 | import com.smart.classroom.subscription.infrastructure.rpc.payment.converter.PaymentDTO2VOConverter; 14 | import com.smart.classroom.subscription.infrastructure.rpc.payment.converter.PreparePaymentDTO2InfoConverter; 15 | import com.smart.classroom.subscription.infrastructure.rpc.quote.converter.ColumnQuoteDTO2VOConverter; 16 | import org.apache.dubbo.config.annotation.DubboReference; 17 | import org.springframework.stereotype.Service; 18 | 19 | /** 20 | * @author lishuang 21 | * @date 2023-05-15 22 | */ 23 | @Service 24 | public class ColumnQuoteFacadeClientImpl implements ColumnQuoteFacadeClient { 25 | 26 | @DubboReference 27 | ColumnQuoteReadFacade columnQuoteReadFacade; 28 | 29 | @Override 30 | public ColumnQuoteVO queryById(long columnQuoteId) { 31 | 32 | ColumnQuoteDTO columnQuoteDTO = columnQuoteReadFacade.queryById(columnQuoteId); 33 | 34 | return ColumnQuoteDTO2VOConverter.convert(columnQuoteDTO); 35 | } 36 | 37 | @Override 38 | public ColumnQuoteVO queryByColumnId(long columnId) { 39 | 40 | ColumnQuoteDTO columnQuoteDTO = columnQuoteReadFacade.queryByColumnId(columnId); 41 | 42 | return ColumnQuoteDTO2VOConverter.convert(columnQuoteDTO); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /scs-facade/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | smart-classroom-subscription 7 | com.smart.classroom 8 | 1.0.0 9 | 10 | 4.0.0 11 | 12 | scs-facade 13 | 14 | 15 | 8 16 | 8 17 | 18 | 19 | 20 | 21 | 22 | org.projectlombok 23 | lombok 24 | provided 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | org.apache.maven.plugins 35 | maven-compiler-plugin 36 | 3.5.1 37 | 38 | 1.8 39 | 1.8 40 | UTF-8 41 | 42 | 43 | 44 | 45 | 46 | org.apache.maven.plugins 47 | maven-source-plugin 48 | 3.0.1 49 | 50 | 51 | package 52 | 53 | jar-no-fork 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /scs-main/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 1000 7 | 8 | 9 | 10 | 11 | 12 | 13 | 15 | 16 | 17 | 18 | 19 | 20 | ${APPLICATION_FILE} 21 | 22 | 23 | ${APPLICATION_FILE}.%d{yyyy-MM-dd}.%i 24 | 25 | 7 26 | 50MB 27 | 2GB 28 | 29 | 30 | 31 | ${PATTERN} 32 | 33 | 34 | 35 | 36 | INFO 37 | 38 | 39 | 40 | 41 | 42 | 43 | ${PATTERN} 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /scs-main/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | smart-classroom-subscription 7 | com.smart.classroom 8 | 1.0.0 9 | 10 | 4.0.0 11 | 12 | scs-main 13 | 14 | 15 | 8 16 | 8 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | com.smart.classroom 25 | scs-facade-impl 26 | 27 | 28 | 29 | 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-devtools 34 | runtime 35 | true 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | org.springframework.boot 49 | spring-boot-maven-plugin 50 | 51 | com.smart.classroom.subscription.SubscriptionApplication 52 | 53 | true 54 | 55 | 56 | 57 | 58 | repackage 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /scs-repository/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | smart-classroom-subscription 7 | com.smart.classroom 8 | 1.0.0 9 | 10 | 4.0.0 11 | 12 | scs-repository 13 | 14 | 15 | 8 16 | 8 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | com.smart.classroom 26 | scs-domain 27 | 28 | 29 | 30 | com.smart.classroom 31 | scs-utility 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | org.mybatis.spring.boot 43 | mybatis-spring-boot-starter 44 | 45 | 46 | 47 | 48 | com.zaxxer 49 | HikariCP 50 | 51 | 52 | 53 | 54 | org.mybatis 55 | mybatis 56 | 57 | 58 | 59 | 60 | com.github.pagehelper 61 | pagehelper 62 | 63 | 64 | 65 | mysql 66 | mysql-connector-java 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /scs-infrastructure/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | smart-classroom-subscription 7 | com.smart.classroom 8 | 1.0.0 9 | 10 | 4.0.0 11 | 12 | scs-infrastructure 13 | 14 | 15 | 8 16 | 8 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | com.smart.classroom 25 | scs-domain 26 | 27 | 28 | 29 | com.smart.classroom 30 | scs-utility 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | com.alibaba.nacos 42 | nacos-client 43 | 44 | 45 | 46 | 47 | org.apache.dubbo 48 | dubbo-spring-boot-starter 49 | 50 | 51 | 52 | 53 | 54 | org.apache.rocketmq 55 | rocketmq-client-java 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | com.smart.classroom 66 | scm-facade 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /scs-repository/src/main/java/com/smart/classroom/subscription/repository/transaction/TransactionService.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.repository.transaction; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.jdbc.datasource.DataSourceTransactionManager; 5 | import org.springframework.lang.Nullable; 6 | import org.springframework.stereotype.Component; 7 | import org.springframework.transaction.TransactionDefinition; 8 | import org.springframework.transaction.TransactionException; 9 | import org.springframework.transaction.TransactionStatus; 10 | import org.springframework.transaction.support.DefaultTransactionDefinition; 11 | import org.springframework.transaction.support.TransactionCallback; 12 | import org.springframework.transaction.support.TransactionTemplate; 13 | 14 | /** 15 | * 事务,手动挡。 16 | * 17 | * @author lishuang 18 | * @date 2023-05-12 19 | */ 20 | @Component 21 | public class TransactionService { 22 | 23 | @Autowired 24 | private DataSourceTransactionManager transactionManager; 25 | 26 | @Autowired 27 | private TransactionTemplate transactionTemplate; 28 | 29 | //*************************** 精细手动控制 开始 ***************************// 30 | 31 | //开启事务, 默认使用RR隔离级别,REQUIRED传播级别 32 | public TransactionStatus begin() { 33 | DefaultTransactionDefinition def = new DefaultTransactionDefinition(); 34 | // 事物隔离级别,开启新事务 35 | def.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ); 36 | // 事务传播行为 37 | def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); 38 | //将拿到的事务返回进去,才能提交。 39 | return transactionManager.getTransaction(def); 40 | } 41 | 42 | //提交事务 43 | public void commit(TransactionStatus transaction) { 44 | //提交事务 45 | transactionManager.commit(transaction); 46 | } 47 | 48 | //回滚事务 49 | public void rollback(TransactionStatus transaction) { 50 | transactionManager.rollback(transaction); 51 | } 52 | 53 | //*************************** 精细手动控制 结束 ***************************// 54 | 55 | 56 | 57 | //*************************** 粗粒度手动控制 开始 ***************************// 58 | //直接执行方法方法体。 59 | @Nullable 60 | public T execute(TransactionCallback action) throws TransactionException { 61 | return transactionTemplate.execute(action); 62 | } 63 | //*************************** 粗粒度手动控制 结束 ***************************// 64 | 65 | } -------------------------------------------------------------------------------- /scs-utility/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | smart-classroom-subscription 7 | com.smart.classroom 8 | 1.0.0 9 | 10 | 4.0.0 11 | 12 | scs-utility 13 | 14 | 15 | 8 16 | 8 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | org.projectlombok 25 | lombok 26 | 27 | 28 | 29 | 30 | org.apache.commons 31 | commons-lang3 32 | 33 | 34 | 35 | commons-io 36 | commons-io 37 | 38 | 39 | 40 | commons-codec 41 | commons-codec 42 | 43 | 44 | 45 | commons-collections 46 | commons-collections 47 | 48 | 49 | 50 | 51 | com.google.guava 52 | guava 53 | 54 | 55 | 56 | org.slf4j 57 | slf4j-api 58 | 59 | 60 | 61 | 62 | 63 | org.apache.velocity 64 | velocity 65 | 66 | 67 | 68 | 69 | org.springframework.boot 70 | spring-boot-starter-json 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /scs-facade-impl/src/main/java/com/smart/classroom/subscription/facade/impl/biz/subscription/SubscriptionWriteFacadeImpl.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.facade.impl.biz.subscription; 2 | 3 | import com.smart.classroom.subscription.application.biz.subscription.SubscriptionCommandAppService; 4 | import com.smart.classroom.subscription.domain.biz.order.info.PrepareSubscribeInfo; 5 | import com.smart.classroom.subscription.domain.biz.subscription.model.SubscriptionModel; 6 | import com.smart.classroom.subscription.facade.biz.order.response.OrderDTO; 7 | import com.smart.classroom.subscription.facade.biz.subscription.SubscriptionWriteFacade; 8 | import com.smart.classroom.subscription.facade.biz.subscription.request.PrepareSubscribeRequest; 9 | import com.smart.classroom.subscription.facade.biz.subscription.response.PrepareSubscribeDTO; 10 | import com.smart.classroom.subscription.facade.biz.subscription.response.SubscriptionDTO; 11 | import com.smart.classroom.subscription.facade.impl.biz.order.converter.OrderModel2DTOConverter; 12 | import com.smart.classroom.subscription.facade.impl.biz.subscription.converter.SubscriptionModel2DTOConverter; 13 | import org.apache.dubbo.config.annotation.DubboService; 14 | 15 | import javax.annotation.Resource; 16 | 17 | /** 18 | * @author lishuang 19 | * @date 2023-05-15 20 | */ 21 | @DubboService 22 | public class SubscriptionWriteFacadeImpl implements SubscriptionWriteFacade { 23 | 24 | @Resource 25 | SubscriptionCommandAppService subscriptionCommandAppService; 26 | 27 | /** 28 | * 发起订阅请求 29 | */ 30 | public PrepareSubscribeDTO prepareSubscribe(PrepareSubscribeRequest request) { 31 | 32 | PrepareSubscribeInfo prepareSubscribeInfo = subscriptionCommandAppService.prepareSubscribe(request.getReaderId(), request.getColumnId(), request.getPayMethod()); 33 | 34 | OrderDTO orderDTO = OrderModel2DTOConverter.convert(prepareSubscribeInfo.getOrderModel()); 35 | 36 | PrepareSubscribeDTO prepareSubscribeDTO = new PrepareSubscribeDTO( 37 | orderDTO, 38 | prepareSubscribeInfo.getThirdTransactionNo(), 39 | prepareSubscribeInfo.getNonceStr() 40 | ); 41 | 42 | return prepareSubscribeDTO; 43 | 44 | 45 | } 46 | 47 | 48 | /** 49 | * 支付成功消息补偿。 50 | */ 51 | @Override 52 | public SubscriptionDTO compensatePaymentPaid(long paymentId) { 53 | 54 | SubscriptionModel subscriptionModel = subscriptionCommandAppService.paymentPaid(paymentId); 55 | 56 | return SubscriptionModel2DTOConverter.convert(subscriptionModel); 57 | } 58 | 59 | 60 | } 61 | -------------------------------------------------------------------------------- /scs-infrastructure/src/main/java/com/smart/classroom/subscription/infrastructure/rpc/payment/PaymentFacadeClientImpl.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.infrastructure.rpc.payment; 2 | 3 | import com.smart.classroom.misc.facade.biz.payment.PaymentReadFacade; 4 | import com.smart.classroom.misc.facade.biz.payment.PaymentWriteFacade; 5 | import com.smart.classroom.misc.facade.biz.payment.request.PaymentCreateRequest; 6 | import com.smart.classroom.misc.facade.biz.payment.response.PaymentDTO; 7 | import com.smart.classroom.misc.facade.biz.payment.response.PreparePaymentDTO; 8 | import com.smart.classroom.subscription.domain.rpc.payment.PaymentFacadeClient; 9 | import com.smart.classroom.subscription.domain.rpc.payment.info.PaymentCreateInfo; 10 | import com.smart.classroom.subscription.domain.rpc.payment.info.PreparePaymentInfo; 11 | import com.smart.classroom.subscription.domain.rpc.payment.vo.PaymentVO; 12 | import com.smart.classroom.subscription.infrastructure.rpc.payment.converter.PaymentDTO2VOConverter; 13 | import com.smart.classroom.subscription.infrastructure.rpc.payment.converter.PreparePaymentDTO2InfoConverter; 14 | import org.apache.dubbo.config.annotation.DubboReference; 15 | import org.springframework.stereotype.Service; 16 | 17 | /** 18 | * @author lishuang 19 | * @date 2023-05-15 20 | */ 21 | @Service 22 | public class PaymentFacadeClientImpl implements PaymentFacadeClient { 23 | 24 | @DubboReference 25 | PaymentReadFacade paymentReadFacade; 26 | 27 | @DubboReference 28 | PaymentWriteFacade paymentWriteFacade; 29 | 30 | @Override 31 | public PaymentVO queryById(long paymentId) { 32 | 33 | PaymentDTO paymentDTO = paymentReadFacade.queryById(paymentId); 34 | 35 | return PaymentDTO2VOConverter.convert(paymentDTO); 36 | } 37 | 38 | @Override 39 | public PreparePaymentInfo create(PaymentCreateInfo paymentCreateInfo) { 40 | PaymentCreateRequest paymentCreateRequest = 41 | PaymentCreateRequest.builder() 42 | .orderNo(paymentCreateInfo.getOrderNo()) 43 | .amount(paymentCreateInfo.getAmount()) 44 | .method(paymentCreateInfo.getMethod()) 45 | .build(); 46 | PreparePaymentDTO preparePaymentDTO = paymentWriteFacade.create(paymentCreateRequest); 47 | 48 | return PreparePaymentDTO2InfoConverter.convert(preparePaymentDTO); 49 | } 50 | 51 | @Override 52 | public PreparePaymentInfo prepare(long paymentId) { 53 | PreparePaymentDTO preparePaymentDTO = paymentReadFacade.prepare(paymentId); 54 | 55 | return PreparePaymentDTO2InfoConverter.convert(preparePaymentDTO); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # smart-classroom-subscription 2 | 3 | 本项目对应文章[《领域驱动设计DDD|从入门到代码实践》](https://mp.weixin.qq.com/s/HMLpjcE0UENUTfMK0Z9n8A) 4 | 5 | ## 首次启动 6 | 7 | ### 安装并启动Nacos 8 | 项目使用Nacos作为注册中心,安装和启动方法见 [这里](./docs/start/nacos.md) 9 | ```shell 10 | sh startup.sh -m standalone 11 | ``` 12 | 13 | ### 安装并启动RocketMQ 14 | 项目的消息队列使用RocketMQ,安装和启动方法见 [这里](./docs/start/rocketmq.md) 15 | ```shell 16 | nohup sh bin/mqnamesrv & 17 | nohup sh bin/mqbroker -n localhost:9876 --enable-proxy & 18 | ``` 19 | 20 | 首次启动的时候需要创建一个Topic. 21 | ```shell 22 | sh bin/mqadmin updatetopic -n localhost:9876 -t SmartClassroomTopic -c DefaultCluster 23 | ``` 24 | 25 | ### 导入数据库 26 | 数据库文件位于 docs/db 完全导入到自己的数据库中。 27 | 28 | 修改配置文件中的jdbc配置 29 | src/main/resources/application-repository.properties 30 | ```properties 31 | spring.datasource.primary.jdbc-url= 32 | spring.datasource.primary.username= 33 | spring.datasource.primary.password= 34 | ``` 35 | 36 | 如果需要做二次开发,也需要修改saber工具中的jdbc配置: 37 | src/main/java/com/smart/classroom/misc/tool/saber/config/SaberConfig.java 38 | 39 | ### 打包facade. 40 | 因为smart-classroom-misc和smart-classroom-subscription两个项目的facade包相互依赖,而我们又没有将其上传至中央仓库。 41 | 42 | 因此在启动前,需要先到这两个项目下各自打包facade,安装到本地仓库中。 43 | 44 | ```shell 45 | cd smart-classroom-misc 46 | ./mvnw install -pl scm-facade -DskipTests 47 | 48 | cd smart-classroom-subscription 49 | ./mvnw install -pl scs-facade -DskipTests 50 | ``` 51 | 52 | 53 | ### 启动应用. 54 | 分别运行两个项目的main文件即可: 55 | ```shell 56 | cd smart-classroom-misc 57 | src/main/java/com/smart/classroom/misc/MiscApplication.java 58 | 59 | cd smart-classroom-subscription 60 | src/main/java/com/smart/classroom/subscription/SubscriptionApplication.java 61 | ``` 62 | 63 | 64 | ### 验证 65 | ```shell 66 | # 健康检查接口有内容返回表示成功 67 | curl http://localhost:6500/api/index/healthy 68 | ``` 69 | ```text 70 | {"code":"OK","data":"服务健康, 现在时间:2023-05-22 16:36:00 远程调用结果:Hello,你好. 我是smart-classroom-subscription,状态正常 现在时间:2023-05-22 16:36:00","msg":"成功"} 71 | ``` 72 | 73 | 如果你也将前端项目 smart-classroom-front 启动了,那么就能够看到对应的页面了。 74 | 75 | ## 日常启动 76 | 77 | ### 启动Nacos 78 | 项目使用Nacos作为注册中心,安装和启动方法见 [这里](./docs/start/nacos.md) 79 | ```shell 80 | sh startup.sh -m standalone 81 | ``` 82 | 83 | ### 启动RocketMQ 84 | 项目的消息队列使用RocketMQ,安装和启动方法见 [这里](./docs/start/rocketmq.md) 85 | ```shell 86 | nohup sh bin/mqnamesrv & 87 | nohup sh bin/mqbroker -n localhost:9876 --enable-proxy & 88 | ``` 89 | 90 | ### 启动应用. 91 | 分别运行两个项目的main文件即可: 92 | ```shell 93 | cd smart-classroom-misc 94 | src/main/java/com/smart/classroom/misc/MiscApplication.java 95 | 96 | cd smart-classroom-subscription 97 | src/main/java/com/smart/classroom/subscription/SubscriptionApplication.java 98 | ``` 99 | 100 | 如果你也将前端项目 smart-classroom-front 启动了,那么就能够看到对应的页面了。 101 | -------------------------------------------------------------------------------- /scs-testsuit/src/main/java/com/smart/classroom/subscription/tool/saber/artwork/MapperJavaArtwork.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.tool.saber.artwork; 2 | 3 | import com.smart.classroom.subscription.tool.saber.artwork.base.Artwork; 4 | import com.smart.classroom.subscription.tool.saber.command.SaberCommand; 5 | import com.smart.classroom.subscription.tool.saber.config.SaberConfig; 6 | import com.smart.classroom.subscription.tool.saber.meta.SaberTable; 7 | import com.smart.classroom.subscription.utility.util.PathUtil; 8 | import com.smart.classroom.subscription.utility.util.StringUtil; 9 | import lombok.Data; 10 | import lombok.EqualsAndHashCode; 11 | import lombok.NonNull; 12 | import org.apache.ibatis.annotations.Mapper; 13 | import org.springframework.stereotype.Repository; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | /** 19 | * XXMapper.java 20 | * 负责生产模型Mapper的 21 | */ 22 | @EqualsAndHashCode(callSuper = true) 23 | @Data 24 | public class MapperJavaArtwork extends Artwork { 25 | /** 26 | * 表的主体名称,例如 scm_application_member 主体为application,主体会作为一层包名。 27 | * 默认:scm_后的第一个单词 28 | */ 29 | private String bodyName; 30 | 31 | //表注释。 32 | private String tableRemark; 33 | 34 | //包名路径 35 | private String mapperPackage; 36 | 37 | //类名大写。 38 | private String entityUpperBodyName; 39 | 40 | /** 41 | * Mapper类名 42 | * 默认会使用表名的首字母大写驼峰写法+Mapper 43 | */ 44 | private String mapperUpperClassName; 45 | 46 | 47 | 48 | //依赖的包。 49 | private List mapperImportNones; 50 | 51 | //依赖的包。 52 | private List mapperImportJavas; 53 | 54 | 55 | public MapperJavaArtwork() { 56 | super("TemplateMapperJava.vm", false); 57 | } 58 | 59 | @Override 60 | public String destAbsolutePath() { 61 | 62 | String directoryPath = PathUtil.getAppHomePath() + "/"+ SaberConfig.REPOSITORY_MODULE_NAME +"/src/main/java/" + this.mapperPackage.replaceAll("\\.", "/"); 63 | 64 | //获取到所在文件夹的路径,如果没有自动创建。 65 | directoryPath = PathUtil.getSafeDirectoryPath(directoryPath); 66 | 67 | return directoryPath + "/" + this.entityUpperBodyName + "Mapper.java"; 68 | } 69 | 70 | @Override 71 | public void init(@NonNull SaberTable saberTable, @NonNull SaberCommand saberCommand) { 72 | this.bodyName = saberCommand.getDirectFolderName(); 73 | this.tableRemark = saberTable.getRemark(); 74 | this.mapperPackage = SaberConfig.BASE_PACKAGE_NAME+".repository.mapper." + this.bodyName; 75 | this.entityUpperBodyName = saberCommand.getEntityUpperBodyName(); 76 | 77 | //处理Mapper类需要引入的包 78 | this.mapperImportNones = new ArrayList<>(); 79 | this.mapperImportNones.add(Mapper.class.getName()); 80 | this.mapperImportNones.add(Repository.class.getName()); 81 | 82 | this.mapperImportJavas = new ArrayList() {{ 83 | 84 | }}; 85 | 86 | 87 | 88 | //进行排序 89 | this.mapperImportJavas.sort(StringUtil::compare); 90 | this.mapperImportNones.sort(StringUtil::compare); 91 | 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /scs-repository/src/main/java/com/smart/classroom/subscription/repository/impl/subscription/SubscriptionRepositoryImpl.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.repository.impl.subscription; 2 | 3 | 4 | import com.github.pagehelper.Page; 5 | import com.smart.classroom.subscription.domain.biz.subscription.model.SubscriptionModel; 6 | import com.smart.classroom.subscription.domain.biz.subscription.repository.SubscriptionRepository; 7 | import com.smart.classroom.subscription.domain.biz.subscription.repository.query.SubscriptionPageQuery; 8 | import com.smart.classroom.subscription.domain.rpc.column.vo.ColumnVO; 9 | import com.smart.classroom.subscription.domain.rpc.reader.vo.ReaderVO; 10 | import com.smart.classroom.subscription.repository.impl.subscription.converter.SubscriptionDO2ModelConverter; 11 | import com.smart.classroom.subscription.repository.impl.subscription.converter.SubscriptionModel2DOConverter; 12 | import com.smart.classroom.subscription.repository.mapper.subscription.SubscriptionMapper; 13 | import com.smart.classroom.subscription.repository.orm.subscription.SubscriptionDO; 14 | import com.smart.classroom.subscription.utility.result.Pager; 15 | import org.springframework.beans.factory.annotation.Autowired; 16 | import org.springframework.stereotype.Service; 17 | 18 | import java.util.List; 19 | import java.util.stream.Collectors; 20 | 21 | /** 22 | * @author lishuang 23 | * @date 2023-05-12 24 | */ 25 | @Service 26 | public class SubscriptionRepositoryImpl implements SubscriptionRepository { 27 | 28 | 29 | @Autowired 30 | SubscriptionMapper subscriptionMapper; 31 | 32 | 33 | @Override 34 | public SubscriptionModel insert(SubscriptionModel subscriptionModel) { 35 | 36 | SubscriptionDO subscriptionDO = SubscriptionModel2DOConverter.convert(subscriptionModel); 37 | 38 | subscriptionMapper.insert(subscriptionDO); 39 | 40 | //查询一次。 41 | subscriptionDO = subscriptionMapper.queryById(subscriptionDO.getId()); 42 | 43 | return SubscriptionDO2ModelConverter.convert(subscriptionDO); 44 | } 45 | 46 | @Override 47 | public SubscriptionModel queryByReaderAndColumn(ReaderVO readerVO, ColumnVO columnVO) { 48 | 49 | SubscriptionDO subscriptionDO = subscriptionMapper.queryByReaderIdAndColumnId(readerVO.getId(), columnVO.getId()); 50 | 51 | return SubscriptionDO2ModelConverter.convert(subscriptionDO); 52 | } 53 | 54 | @Override 55 | public Pager page(SubscriptionPageQuery subscriptionPageQuery) { 56 | Page page = subscriptionMapper.page( 57 | subscriptionPageQuery.getPageNum(), 58 | subscriptionPageQuery.getPageSize(), 59 | subscriptionPageQuery.getOrderCreateTime(), 60 | subscriptionPageQuery.getOrderUpdateTime(), 61 | subscriptionPageQuery.getReaderId(), 62 | subscriptionPageQuery.getColumnId(), 63 | subscriptionPageQuery.getOrderId(), 64 | subscriptionPageQuery.getStatus() 65 | ); 66 | 67 | List subscriptionModelList = page.stream().map(SubscriptionDO2ModelConverter::convert).collect(Collectors.toList()); 68 | 69 | return new Pager<>(page.getPageNum(), page.getPageSize(), page.getTotal(), subscriptionModelList); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /scs-testsuit/src/main/java/com/smart/classroom/subscription/tool/saber/artwork/MapperXmlArtwork.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.tool.saber.artwork; 2 | 3 | import com.smart.classroom.subscription.tool.saber.artwork.base.Artwork; 4 | import com.smart.classroom.subscription.tool.saber.command.SaberCommand; 5 | import com.smart.classroom.subscription.tool.saber.config.SaberConfig; 6 | import com.smart.classroom.subscription.tool.saber.meta.SaberTable; 7 | import com.smart.classroom.subscription.utility.util.PathUtil; 8 | import lombok.Data; 9 | import lombok.EqualsAndHashCode; 10 | import lombok.NonNull; 11 | 12 | /** 13 | * XXMapper.xml 14 | * 负责生产模型Mapper的 15 | */ 16 | @EqualsAndHashCode(callSuper = true) 17 | @Data 18 | public class MapperXmlArtwork extends Artwork { 19 | /** 20 | * 表的主体名称,例如 scm_application_member 主体为application,主体会作为一层包名。 21 | * 默认:scm_后的第一个单词 22 | */ 23 | private String bodyName; 24 | 25 | 26 | /** 27 | * 数据库表名 28 | */ 29 | private String tableName; 30 | 31 | 32 | //表注释。 33 | private String tableRemark; 34 | 35 | //包名路径 36 | private String mapperPackage; 37 | 38 | //类名大写。 39 | private String entityUpperBodyName; 40 | 41 | /** 42 | * 类所在的包 43 | * 默认 SaberConfig.BASE_PACKAGE_NAME 44 | */ 45 | private String entityPackage; 46 | 47 | /** 48 | * 实体全名 49 | * 包名+类名 50 | */ 51 | private String entityFullName; 52 | 53 | /** 54 | * baseMapper全名 55 | */ 56 | private String baseMapperClassFullName; 57 | 58 | 59 | /** 60 | * Mapper类名 61 | * 默认会使用表名的首字母大写驼峰写法+Mapper 62 | */ 63 | private String mapperUpperClassName; 64 | /** 65 | * 实体全名 66 | * 包名+类名 67 | */ 68 | private String mapperFullName; 69 | 70 | 71 | public MapperXmlArtwork() { 72 | super("TemplateMapperXml.vm", false); 73 | } 74 | 75 | @Override 76 | public String destAbsolutePath() { 77 | 78 | String directoryPath = PathUtil.getAppHomePath() + "/"+ SaberConfig.REPOSITORY_MODULE_NAME +"/src/main/resources/mybatis/mapper/" + this.bodyName; 79 | 80 | //获取到所在文件夹的路径,如果没有自动创建。 81 | directoryPath = PathUtil.getSafeDirectoryPath(directoryPath); 82 | 83 | return directoryPath + "/" + this.entityUpperBodyName + "Mapper.xml"; 84 | } 85 | 86 | @Override 87 | public void init(@NonNull SaberTable saberTable, @NonNull SaberCommand saberCommand) { 88 | this.bodyName = saberCommand.getDirectFolderName(); 89 | this.tableName = saberCommand.getTableName(); 90 | this.tableRemark = saberTable.getRemark(); 91 | this.mapperPackage = SaberConfig.BASE_PACKAGE_NAME+".repository.mapper." + this.bodyName; 92 | this.entityPackage = SaberConfig.BASE_PACKAGE_NAME + ".repository.orm."+ this.bodyName; 93 | this.entityUpperBodyName = saberCommand.getEntityUpperBodyName(); 94 | this.entityFullName = this.entityPackage + "." + this.entityUpperBodyName+"DO"; 95 | this.baseMapperClassFullName = this.mapperPackage + "." + this.entityUpperBodyName+"BaseMapper"; 96 | 97 | this.mapperUpperClassName = this.entityUpperBodyName + "Mapper"; 98 | this.mapperFullName = this.mapperPackage + "." + this.mapperUpperClassName; 99 | 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /scs-utility/src/main/java/com/smart/classroom/subscription/utility/util/PathUtil.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.utility.util; 2 | 3 | 4 | import com.smart.classroom.subscription.utility.exception.UtilException; 5 | import lombok.SneakyThrows; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.apache.commons.io.IOUtils; 8 | 9 | import java.io.InputStream; 10 | import java.net.URL; 11 | import java.nio.charset.StandardCharsets; 12 | import java.nio.file.Files; 13 | import java.nio.file.Path; 14 | import java.nio.file.Paths; 15 | 16 | /** 17 | * 路径通用类 18 | */ 19 | @Slf4j 20 | public class PathUtil { 21 | 22 | /** 23 | * 获取应用的home目录。 24 | * 例如:/d/smart-classroom-misc 25 | */ 26 | public static String getAppHomePath() { 27 | return Paths.get("").toAbsolutePath().toString(); 28 | 29 | } 30 | 31 | 32 | /** 33 | * 获取当前模块名,需要传入一个resources下面的文件作为指南针文件 34 | * 例如:传入 doc.md 35 | * 获取到:/d/smart-classroom-misc/scm-utility 36 | */ 37 | public static String getModuleHomePath(String compassFile) { 38 | 39 | 40 | URL url = PathUtil.class.getClassLoader().getResource(compassFile); 41 | 42 | if (url == null) { 43 | throw new UtilException("No " + compassFile + ", please fix it!"); 44 | } 45 | 46 | 47 | String inputFilePath = url.getPath(); 48 | 49 | //windows环境和其他环境路径有差异 50 | String osName = System.getProperties().getProperty("os.name"); 51 | if (osName.toLowerCase().startsWith("win")) { 52 | //win环境下去掉file:/ 53 | inputFilePath = inputFilePath.substring(inputFilePath.indexOf("/") + 1); 54 | } 55 | 56 | //如果是jar包,那么要求Jar包旁边有个jar直接解压的文件夹。 57 | int index = inputFilePath.indexOf(".jar!"); 58 | if (index == -1) { 59 | index = inputFilePath.indexOf("/target/classes"); 60 | if (index == -1) { 61 | index = inputFilePath.indexOf("/target/test-classes"); 62 | 63 | } 64 | } 65 | 66 | return inputFilePath.substring(0, index); 67 | 68 | 69 | } 70 | 71 | /** 72 | * 获取一个安全的文件夹路径 73 | * 如果中间某个文件夹不存在,那么就创建文件夹 74 | */ 75 | @SneakyThrows 76 | public static String getSafeDirectoryPath(String directoryPath) { 77 | 78 | Path path = Paths.get(directoryPath); 79 | 80 | if (!Files.exists(path)) { 81 | 82 | Files.createDirectories(path); 83 | } 84 | 85 | return directoryPath; 86 | 87 | } 88 | 89 | 90 | /** 91 | * 判断一个文件是否存在 92 | */ 93 | public static boolean isExist(String filePath) { 94 | 95 | Path path = Paths.get(filePath); 96 | 97 | return Files.exists(path); 98 | 99 | } 100 | 101 | /** 102 | * 获取resources下面的某个文件内容 103 | * 104 | * @param resourceRelativeName 资源相对路径,相对于resources文件夹,eg: scm-utility/script/XX.java 105 | * @return 返回资源文件的内容,如果没有找到或者出错,那么就返回 null 106 | */ 107 | public static String getResourceContent(String resourceRelativeName) { 108 | 109 | ClassLoader classLoader = PathUtil.class.getClassLoader(); 110 | InputStream inputStream = classLoader.getResourceAsStream(resourceRelativeName); 111 | 112 | if (inputStream == null) { 113 | log.error("{} 对应的文件内容为空", resourceRelativeName); 114 | return null; 115 | } 116 | 117 | //Read File Content 118 | try { 119 | return IOUtils.toString(inputStream, StandardCharsets.UTF_8); 120 | } catch (Throwable throwable) { 121 | log.error("对应的文件内容为空" + resourceRelativeName, throwable); 122 | return null; 123 | } 124 | } 125 | 126 | 127 | } 128 | -------------------------------------------------------------------------------- /scs-testsuit/src/main/java/com/smart/classroom/subscription/tool/saber/meta/SaberTable.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.tool.saber.meta; 2 | 3 | 4 | import com.smart.classroom.subscription.tool.saber.command.SaberCommand; 5 | import com.smart.classroom.subscription.tool.saber.contrast.TypeContrast; 6 | import com.smart.classroom.subscription.utility.exception.UtilException; 7 | import com.smart.classroom.subscription.utility.model.Pair; 8 | import lombok.AllArgsConstructor; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | import lombok.NonNull; 12 | import lombok.extern.slf4j.Slf4j; 13 | 14 | import java.util.ArrayList; 15 | import java.util.Iterator; 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | /** 20 | * 数据库表,元信息 21 | * 22 | * @author lishuang 2023-05-11 23 | */ 24 | @Data 25 | @AllArgsConstructor 26 | @NoArgsConstructor 27 | @Slf4j 28 | public class SaberTable { 29 | 30 | private String name; 31 | private String remark; 32 | 33 | private List columns = new ArrayList<>(); 34 | 35 | 36 | //根据给定的数据库字段,获取到对应的SaberColumn. 37 | public List fetchSaberColumns(@NonNull List fields) { 38 | 39 | List columnList = new ArrayList<>(); 40 | 41 | fields.forEach(field -> { 42 | 43 | SaberColumn targetColumn = null; 44 | for (SaberColumn column : columns) { 45 | //用户输入create_time 或者 createTime 都可以 46 | if (column.sameField(field)) { 47 | 48 | targetColumn = column; 49 | 50 | } 51 | } 52 | 53 | if (targetColumn == null) { 54 | throw new UtilException("{} 中不存在字段 {}", name, field); 55 | 56 | } else { 57 | 58 | columnList.add(targetColumn); 59 | } 60 | 61 | }); 62 | 63 | return columnList; 64 | 65 | } 66 | 67 | 68 | //根据saberCommand中的自定义设置,来调整一下saberTable中的内容 69 | public void prepare(SaberCommand saberCommand) { 70 | 71 | 72 | for (Map.Entry entry : saberCommand.getCustomFieldTypes().entrySet()) { 73 | 74 | //调整自定义的字段 75 | for (SaberColumn column : this.columns) { 76 | 77 | //用户输入create_time 或者 createTime 都可以 78 | if (column.sameField(entry.getKey())) { 79 | 80 | column.updateTypeContrast(entry.getValue(), column.getDefaultValue()); 81 | 82 | } 83 | 84 | 85 | } 86 | 87 | 88 | } 89 | 90 | 91 | //一些枚举字段,我们希望直接使用枚举。。 92 | for (Map.Entry>, String>> entry : saberCommand.getEnumFieldMap().entrySet()) { 93 | 94 | //调整自定义的字段 95 | for (SaberColumn column : this.columns) { 96 | 97 | //用户输入gmt_create 或者 gmtCreate 都可以 98 | if (column.sameField(entry.getKey())) { 99 | 100 | column.updateEnumField(entry.getValue().getKey(), entry.getValue().getValue()); 101 | 102 | } 103 | 104 | 105 | } 106 | 107 | 108 | } 109 | 110 | 111 | //有个别废弃掉的字段不予考虑。(小心删除陷阱,必须使用迭代器删除) 112 | Iterator it = columns.iterator(); 113 | while (it.hasNext()) { 114 | SaberColumn column = it.next(); 115 | //调整自定义的字段 116 | for (String ignoreField : saberCommand.getIgnoreFields()) { 117 | 118 | //用户输入create_time 或者 createTime 都可以 119 | if (column.sameField(ignoreField)) { 120 | it.remove(); 121 | break; 122 | } 123 | 124 | 125 | } 126 | 127 | } 128 | 129 | 130 | } 131 | 132 | 133 | } 134 | -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/biz/subscription/service/SubscriptionCreateDomainService.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.biz.subscription.service; 2 | 3 | import com.smart.classroom.subscription.domain.biz.order.enums.OrderStatus; 4 | import com.smart.classroom.subscription.domain.biz.order.model.OrderModel; 5 | import com.smart.classroom.subscription.domain.biz.order.repository.OrderRepository; 6 | import com.smart.classroom.subscription.domain.biz.subscription.enums.SubscriptionStatus; 7 | import com.smart.classroom.subscription.domain.biz.subscription.event.SubscriptionDomainEvent; 8 | import com.smart.classroom.subscription.domain.biz.subscription.info.SubscriptionEventPayload; 9 | import com.smart.classroom.subscription.domain.biz.subscription.model.SubscriptionModel; 10 | import com.smart.classroom.subscription.domain.biz.subscription.repository.SubscriptionRepository; 11 | import com.smart.classroom.subscription.domain.middleware.mq.MqProducer; 12 | import com.smart.classroom.subscription.domain.middleware.mq.info.MqSendResultInfo; 13 | import com.smart.classroom.subscription.domain.rpc.column.ColumnFacadeClient; 14 | import com.smart.classroom.subscription.domain.rpc.column.vo.ColumnVO; 15 | import com.smart.classroom.subscription.domain.rpc.payment.vo.PaymentVO; 16 | import com.smart.classroom.subscription.domain.rpc.reader.ReaderFacadeClient; 17 | import com.smart.classroom.subscription.domain.rpc.reader.vo.ReaderVO; 18 | import com.smart.classroom.subscription.utility.exception.UtilException; 19 | import com.smart.classroom.subscription.utility.util.JsonUtil; 20 | import com.smart.classroom.subscription.utility.util.StringUtil; 21 | import lombok.extern.slf4j.Slf4j; 22 | import org.springframework.beans.factory.annotation.Autowired; 23 | import org.springframework.stereotype.Service; 24 | 25 | import java.util.Date; 26 | 27 | /** 28 | * 创建专栏领域服务 29 | * 30 | * @author lishuang 31 | * @date 2023-05-12 32 | */ 33 | @Slf4j 34 | @Service 35 | public class SubscriptionCreateDomainService { 36 | 37 | @Autowired 38 | OrderRepository orderRepository; 39 | 40 | @Autowired 41 | ReaderFacadeClient readerFacadeClient; 42 | 43 | @Autowired 44 | ColumnFacadeClient columnFacadeClient; 45 | 46 | @Autowired 47 | SubscriptionRepository subscriptionRepository; 48 | 49 | @Autowired 50 | MqProducer mqProducer; 51 | 52 | 53 | /** 54 | * 创建专栏 55 | */ 56 | public SubscriptionModel create(OrderModel orderModel) { 57 | 58 | //创建一个新的订阅关系。 59 | SubscriptionModel subscriptionModel = new SubscriptionModel( 60 | null, 61 | new Date(), 62 | new Date(), 63 | orderModel.getReaderId(), 64 | orderModel.getColumnId(), 65 | orderModel.getId(), 66 | SubscriptionStatus.CREATED 67 | ); 68 | 69 | //持久化 数据库做了唯一联合索引。 70 | subscriptionModel = subscriptionRepository.insert(subscriptionModel); 71 | 72 | log.info("专栏订阅成功,readerId={} columnId={}", subscriptionModel.getReaderId(), subscriptionModel.getColumnId()); 73 | 74 | //发送专栏创建成功的领域事件。 75 | String uuid = StringUtil.uuid(); 76 | SubscriptionEventPayload subscriptionEventPayload = new SubscriptionEventPayload( 77 | subscriptionModel.getReaderId(), 78 | subscriptionModel.getColumnId(), 79 | subscriptionModel.getOrderId(), 80 | subscriptionModel.getStatus().name(), 81 | subscriptionModel.getCreateTime() 82 | ); 83 | String content = JsonUtil.toJson(subscriptionEventPayload); 84 | MqSendResultInfo mqSendResultInfo = mqProducer.send(SubscriptionDomainEvent.SUBSCRIPTION_DOMAIN_EVENT_CREATED.name(), uuid, content); 85 | if (mqSendResultInfo.isSuccess()) { 86 | log.info("领域事件{}发布成功", SubscriptionDomainEvent.SUBSCRIPTION_DOMAIN_EVENT_CREATED); 87 | } else { 88 | log.error("领域事件{}发布失败", SubscriptionDomainEvent.SUBSCRIPTION_DOMAIN_EVENT_CREATED); 89 | } 90 | 91 | return subscriptionModel; 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /scs-domain/src/main/java/com/smart/classroom/subscription/domain/biz/order/service/OrderPrepareDomainService.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.domain.biz.order.service; 2 | 3 | import com.smart.classroom.subscription.domain.biz.order.enums.OrderStatus; 4 | import com.smart.classroom.subscription.domain.biz.order.info.PrepareSubscribeInfo; 5 | import com.smart.classroom.subscription.domain.biz.order.model.OrderModel; 6 | import com.smart.classroom.subscription.domain.biz.order.repository.OrderRepository; 7 | import com.smart.classroom.subscription.domain.rpc.column.vo.ColumnVO; 8 | import com.smart.classroom.subscription.domain.rpc.payment.PaymentFacadeClient; 9 | import com.smart.classroom.subscription.domain.rpc.payment.info.PaymentCreateInfo; 10 | import com.smart.classroom.subscription.domain.rpc.payment.info.PreparePaymentInfo; 11 | import com.smart.classroom.subscription.domain.rpc.payment.vo.PaymentVO; 12 | import com.smart.classroom.subscription.domain.rpc.quote.ColumnQuoteFacadeClient; 13 | import com.smart.classroom.subscription.domain.rpc.quote.vo.ColumnQuoteVO; 14 | import com.smart.classroom.subscription.domain.rpc.reader.vo.ReaderVO; 15 | import com.smart.classroom.subscription.utility.exception.UtilException; 16 | import com.smart.classroom.subscription.utility.util.NumberUtil; 17 | import org.springframework.beans.factory.annotation.Autowired; 18 | import org.springframework.stereotype.Service; 19 | 20 | import java.util.Date; 21 | 22 | /** 23 | * 准备订单 领域服务 24 | * 25 | * @author lishuang 26 | * @date 2023-05-12 27 | */ 28 | @Service 29 | public class OrderPrepareDomainService { 30 | 31 | 32 | @Autowired 33 | OrderRepository orderRepository; 34 | 35 | @Autowired 36 | ColumnQuoteFacadeClient columnQuoteFacadeClient; 37 | 38 | @Autowired 39 | PaymentFacadeClient paymentFacadeClient; 40 | 41 | 42 | /** 43 | * 订单编号生成规则 44 | */ 45 | private String generateOrderNo() { 46 | //订单编号简单使用时间戳 47 | return "SCO" + System.currentTimeMillis() + NumberUtil.random(1000, 10000); 48 | } 49 | 50 | /** 51 | * 创建专栏 52 | */ 53 | public PrepareSubscribeInfo createAndPrepare(ColumnVO columnVO, ReaderVO readerVO, String payMethod) { 54 | 55 | PreparePaymentInfo preparePaymentInfo = null; 56 | //2. 查找当前订单是否已经存在了。 57 | OrderModel orderModel = orderRepository.queryNonFinalState(readerVO, columnVO); 58 | if (orderModel != null) { 59 | 60 | //根据订单,获取支付的信息。 61 | preparePaymentInfo = paymentFacadeClient.prepare(orderModel.getPaymentId()); 62 | 63 | } else { 64 | 65 | //获取专栏报价。 66 | ColumnQuoteVO columnQuoteVO = columnQuoteFacadeClient.queryByColumnId(columnVO.getId()); 67 | if (columnQuoteVO == null) { 68 | throw new UtilException("专栏{}(id={})尚无报价,无法订阅", columnVO.getName(), columnVO.getId()); 69 | } 70 | 71 | String orderNo = this.generateOrderNo(); 72 | Long price = columnQuoteVO.getPrice(); 73 | 74 | PaymentCreateInfo paymentCreateInfo = new PaymentCreateInfo( 75 | orderNo, 76 | payMethod, 77 | price 78 | ); 79 | 80 | //创建对应的支付单。 81 | preparePaymentInfo = paymentFacadeClient.create(paymentCreateInfo); 82 | 83 | PaymentVO paymentVO = preparePaymentInfo.getPaymentVO(); 84 | 85 | //创建订单。 86 | orderModel = new OrderModel( 87 | null, 88 | new Date(), 89 | new Date(), 90 | orderNo, 91 | readerVO.getId(), 92 | columnVO.getId(), 93 | columnQuoteVO.getId(), 94 | paymentVO.getId(), 95 | price, 96 | OrderStatus.CREATED 97 | ); 98 | 99 | //持久化订单。 100 | orderRepository.insert(orderModel); 101 | } 102 | 103 | return new PrepareSubscribeInfo( 104 | orderModel, 105 | preparePaymentInfo.getThirdTransactionNo(), 106 | preparePaymentInfo.getNonceStr() 107 | ); 108 | 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /scs-repository/src/main/java/com/smart/classroom/subscription/repository/impl/order/OrderRepositoryImpl.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.repository.impl.order; 2 | 3 | 4 | import com.github.pagehelper.Page; 5 | import com.smart.classroom.subscription.domain.biz.order.enums.OrderStatus; 6 | import com.smart.classroom.subscription.domain.biz.order.model.OrderModel; 7 | import com.smart.classroom.subscription.domain.biz.order.repository.OrderRepository; 8 | import com.smart.classroom.subscription.domain.biz.order.repository.query.OrderPageQuery; 9 | import com.smart.classroom.subscription.domain.rpc.column.vo.ColumnVO; 10 | import com.smart.classroom.subscription.domain.rpc.reader.vo.ReaderVO; 11 | import com.smart.classroom.subscription.repository.impl.order.converter.OrderDO2ModelConverter; 12 | import com.smart.classroom.subscription.repository.impl.order.converter.OrderModel2DOConverter; 13 | import com.smart.classroom.subscription.repository.mapper.order.OrderMapper; 14 | import com.smart.classroom.subscription.repository.orm.order.OrderDO; 15 | import com.smart.classroom.subscription.utility.result.Pager; 16 | import org.apache.commons.collections.CollectionUtils; 17 | import org.springframework.beans.factory.annotation.Autowired; 18 | import org.springframework.stereotype.Service; 19 | 20 | import java.util.Collections; 21 | import java.util.List; 22 | import java.util.stream.Collectors; 23 | 24 | /** 25 | * @author lishuang 26 | * @date 2023-05-12 27 | */ 28 | @Service 29 | public class OrderRepositoryImpl implements OrderRepository { 30 | 31 | 32 | @Autowired 33 | OrderMapper orderMapper; 34 | 35 | 36 | @Override 37 | public OrderModel insert(OrderModel orderModel) { 38 | 39 | OrderDO orderDO = OrderModel2DOConverter.convert(orderModel); 40 | 41 | orderMapper.insert(orderDO); 42 | 43 | //查询一次。 44 | orderDO = orderMapper.queryById(orderDO.getId()); 45 | 46 | return OrderDO2ModelConverter.convert(orderDO); 47 | } 48 | 49 | @Override 50 | public OrderModel updatePaymentId(OrderModel orderModel) { 51 | 52 | 53 | 54 | 55 | return null; 56 | } 57 | 58 | @Override 59 | public OrderModel queryNonFinalState(ReaderVO readerVO, ColumnVO columnVO) { 60 | 61 | List list = Collections.singletonList(OrderStatus.CREATED); 62 | List orderDOList = orderMapper.queryByReaderIdAndColumnIdAndStatuses(readerVO.getId(), columnVO.getId(), list); 63 | if (CollectionUtils.isEmpty(orderDOList)) { 64 | return null; 65 | } else { 66 | return OrderDO2ModelConverter.convert(orderDOList.get(0)); 67 | } 68 | } 69 | 70 | @Override 71 | public OrderModel updateStatus(OrderModel orderModel) { 72 | 73 | orderMapper.updateStatus(orderModel.getId(),orderModel.getStatus()); 74 | OrderDO orderDO = orderMapper.queryById(orderModel.getId()); 75 | 76 | return OrderDO2ModelConverter.convert(orderDO); 77 | } 78 | 79 | @Override 80 | public OrderModel queryById(long orderId) { 81 | OrderDO orderDO = orderMapper.queryById(orderId); 82 | 83 | return OrderDO2ModelConverter.convert(orderDO); 84 | } 85 | 86 | @Override 87 | public OrderModel queryByNo(String orderNo) { 88 | 89 | OrderDO orderDO = orderMapper.queryByNo(orderNo); 90 | 91 | return OrderDO2ModelConverter.convert(orderDO); 92 | } 93 | 94 | @Override 95 | public Pager page(OrderPageQuery orderPageQuery) { 96 | Page page = orderMapper.page( 97 | orderPageQuery.getPageNum(), 98 | orderPageQuery.getPageSize(), 99 | orderPageQuery.getOrderCreateTime(), 100 | orderPageQuery.getOrderUpdateTime(), 101 | orderPageQuery.getReaderId(), 102 | orderPageQuery.getColumnId(), 103 | orderPageQuery.getColumnQuoteId(), 104 | orderPageQuery.getPaymentId(), 105 | orderPageQuery.getStatus() 106 | ); 107 | 108 | List orderModelList = page.stream().map(OrderDO2ModelConverter::convert).collect(Collectors.toList()); 109 | 110 | return new Pager<>(page.getPageNum(), page.getPageSize(), page.getTotal(), orderModelList); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /scs-infrastructure/src/main/java/com/smart/classroom/subscription/infrastructure/middleware/rocketmq/RocketMqProducerImpl.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.infrastructure.middleware.rocketmq; 2 | 3 | import com.smart.classroom.subscription.domain.middleware.mq.MqProducer; 4 | import com.smart.classroom.subscription.domain.middleware.mq.info.MqSendResultInfo; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.apache.rocketmq.client.apis.ClientConfiguration; 7 | import org.apache.rocketmq.client.apis.ClientConfigurationBuilder; 8 | import org.apache.rocketmq.client.apis.ClientServiceProvider; 9 | import org.apache.rocketmq.client.apis.message.Message; 10 | import org.apache.rocketmq.client.apis.producer.Producer; 11 | import org.apache.rocketmq.client.apis.producer.SendReceipt; 12 | import org.springframework.context.event.ContextRefreshedEvent; 13 | import org.springframework.context.event.ContextStoppedEvent; 14 | import org.springframework.context.event.EventListener; 15 | import org.springframework.stereotype.Component; 16 | 17 | /** 18 | * @author lishuang 19 | * @date 2023-05-17 20 | *

21 | * 实现代码参考:https://rocketmq.apache.org/zh/docs/quickStart/01quickstart 22 | */ 23 | @Slf4j 24 | @Component 25 | public class RocketMqProducerImpl implements MqProducer { 26 | // 接入点地址,需要设置成Proxy的地址和端口列表,一般是xxx:8081;xxx:8081。 27 | public static String ENDPOINT = "localhost:8081"; 28 | 29 | // 消息发送的目标Topic名称,需要提前在RocketMQ中创建。 30 | // 一般一个应用对应一个TOPIC. 31 | public static String TOPIC = "SmartClassroomTopic"; 32 | 33 | //标识是否已完成启动,防止多次启动 34 | private boolean initialized = false; 35 | 36 | private ClientServiceProvider provider; 37 | 38 | //消费者,发送消息的物件。 39 | private Producer producer = null; 40 | 41 | /** 42 | * 这个方法只在系统启动的时候调用,系统可能调多次。 43 | */ 44 | @EventListener(ContextRefreshedEvent.class) 45 | public void applicationRefreshed() { 46 | 47 | log.info("准备启动RocketMQProducer"); 48 | 49 | if (initialized) { 50 | return; 51 | } else { 52 | initialized = true; 53 | } 54 | 55 | /* 56 | * Producer对象在使用之前必须要调用start初始化,初始化一次即可 57 | * 注意:切记不可以在每次发送消息时,都调用start方法 58 | */ 59 | try { 60 | this.provider = ClientServiceProvider.loadService(); 61 | ClientConfigurationBuilder builder = ClientConfiguration.newBuilder().setEndpoints(ENDPOINT); 62 | ClientConfiguration configuration = builder.build(); 63 | // 初始化Producer时需要设置通信配置以及预绑定的Topic。 64 | this.producer = provider.newProducerBuilder() 65 | .setTopics(TOPIC) 66 | .setClientConfiguration(configuration) 67 | .build(); 68 | 69 | } catch (Throwable throwable) { 70 | log.error("RocketMQ启动失败,请检查RocketMQ是否已经启动", throwable); 71 | } 72 | } 73 | 74 | 75 | /** 76 | * 这个方法只在系统停止的时候调用 77 | */ 78 | @EventListener(ContextStoppedEvent.class) 79 | public void applicationStop() { 80 | log.info("RocketMQProducer 系统停止钩子函数"); 81 | 82 | //应用退出时,要调用shutdown来清理资源,关闭网络连接,从RocketMQ服务器上注销自己 83 | if (producer == null) { 84 | log.warn("在尝试关闭RocketMQProducer时发现producer已经不存在了"); 85 | } else { 86 | try { 87 | producer.close(); 88 | } catch (Throwable e) { 89 | log.error("在关闭RocketMQProducer的过程中遭遇错误", e); 90 | } 91 | } 92 | } 93 | 94 | 95 | @Override 96 | public MqSendResultInfo send(String tags, String keys, String body) { 97 | 98 | // 普通消息发送。 99 | Message message = this.provider.newMessageBuilder() 100 | .setTopic(TOPIC) 101 | // 设置消息索引键,可根据关键字精确查找某条消息。 102 | .setKeys(keys) 103 | // 设置消息Tag,用于消费端根据指定Tag过滤消息。 104 | .setTag(tags) 105 | // 消息体。 106 | .setBody(body.getBytes()) 107 | .build(); 108 | 109 | try { 110 | // 发送消息,需要关注发送结果,并捕获失败等异常。 111 | SendReceipt sendReceipt = producer.send(message); 112 | String messageId = sendReceipt.getMessageId().toString(); 113 | log.info("消息发送成功 keys={} tags={} messageId={} version={}", keys, tags, messageId, sendReceipt.getMessageId().getVersion()); 114 | return new MqSendResultInfo(true, messageId, null); 115 | } catch (Throwable e) { 116 | String errorMessage = e.getMessage(); 117 | log.error("消息发送失败 keys={} tags={} error={}", keys, tags, errorMessage); 118 | return new MqSendResultInfo(false, null, errorMessage); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /scs-utility/src/main/java/com/smart/classroom/subscription/utility/util/JsonUtil.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.utility.util; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.core.type.TypeReference; 5 | import com.fasterxml.jackson.databind.DeserializationFeature; 6 | import com.fasterxml.jackson.databind.JavaType; 7 | import com.fasterxml.jackson.databind.ObjectMapper; 8 | import com.smart.classroom.subscription.utility.exception.UtilException; 9 | import org.apache.commons.lang3.StringUtils; 10 | import org.apache.commons.lang3.exception.ExceptionUtils; 11 | 12 | import java.io.IOException; 13 | import java.text.SimpleDateFormat; 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | public class JsonUtil { 19 | 20 | public final static String EMPTY_JSON_ARRAY = "[]"; 21 | public final static String EMPTY_JSON_OBJECT = "{}"; 22 | 23 | public final static ObjectMapper objectMapper; 24 | 25 | static { 26 | objectMapper = new ObjectMapper(); 27 | //遇到没有定义的字段,反序列化不要报错。 28 | objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 29 | 30 | // 时间格式化 31 | objectMapper.setDateFormat(new SimpleDateFormat(DateUtil.DEFAULT_FORMAT)); 32 | } 33 | 34 | public static T toObject(String content, Class clazz) { 35 | 36 | try { 37 | return objectMapper.readValue(content, clazz); 38 | } catch (IOException e) { 39 | throw new UtilException("JSON信息有误:{} {}", content, ExceptionUtils.getRootCauseMessage(e)); 40 | } 41 | } 42 | 43 | 44 | //从一个jsonString中去获取对应类型的对象 45 | public static T toGenericObject(TypeReference typeReference, String jsonString) { 46 | 47 | if (jsonString == null) { 48 | throw new UtilException("jsonString必须指定"); 49 | } 50 | 51 | 52 | try { 53 | return objectMapper.readValue(jsonString, typeReference); 54 | } catch (IOException e) { 55 | throw new UtilException("JSON信息有误:{} {}", jsonString, ExceptionUtils.getRootCauseMessage(e)); 56 | } 57 | 58 | } 59 | 60 | /** 61 | * 从一个jsonString中去获取对应含有泛型的对象。 62 | * eg. 63 | * final TxyResponse o1 = JsonUtil.toGenericObject(TxyResponse.class, AesKeyData.class, content); 64 | */ 65 | public static T toGenericObject(Class baseClass, Class parameterClass, String jsonString) { 66 | 67 | if (jsonString == null) { 68 | throw new UtilException("jsonString必须指定"); 69 | } 70 | 71 | JavaType concreteType = objectMapper.getTypeFactory().constructParametricType( 72 | baseClass, 73 | parameterClass 74 | ); 75 | 76 | try { 77 | return objectMapper.readValue(jsonString, concreteType); 78 | } catch (IOException e) { 79 | throw new UtilException("JSON信息有误:{} {}", jsonString, ExceptionUtils.getRootCauseMessage(e)); 80 | } 81 | 82 | } 83 | 84 | //从一个jsonString中去获取List 85 | public static List toLongList(String jsonString) { 86 | 87 | 88 | if (jsonString == null) { 89 | return new ArrayList<>(); 90 | } 91 | 92 | return toGenericObject(new TypeReference>() { 93 | }, jsonString); 94 | } 95 | 96 | //从一个jsonString中去获取List 97 | public static List toStringList(String jsonString) { 98 | if (StringUtils.isEmpty(jsonString)) { 99 | return new ArrayList<>(); 100 | } 101 | 102 | return toGenericObject(new TypeReference>() { 103 | }, jsonString); 104 | } 105 | 106 | 107 | //将对象转换成json字符串。 108 | public static String toJson(Object obj) { 109 | 110 | 111 | try { 112 | 113 | return objectMapper.writeValueAsString(obj); 114 | 115 | } catch (JsonProcessingException e) { 116 | throw new UtilException("转成JSON时出错:{}", ExceptionUtils.getRootCauseMessage(e)); 117 | } 118 | } 119 | 120 | //将一个对象转换成 Map 121 | public static Map toMap(String jsonString, Class clazz) { 122 | 123 | TypeReference> typeReference = new TypeReference>() { 124 | }; 125 | 126 | return objectMapper.convertValue(jsonString, typeReference); 127 | } 128 | 129 | //从jsonString中解析出一个数组。 130 | public static List parseArray(String jsonString, Class clazz) { 131 | 132 | if (jsonString == null) { 133 | return new ArrayList<>(); 134 | } 135 | 136 | return toGenericObject(new TypeReference>() { 137 | }, jsonString); 138 | 139 | } 140 | 141 | } 142 | -------------------------------------------------------------------------------- /scs-repository/src/main/java/com/smart/classroom/subscription/repository/config/DataSourceConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.repository.config; 2 | 3 | import com.github.pagehelper.PageInterceptor; 4 | import com.zaxxer.hikari.HikariConfig; 5 | import com.zaxxer.hikari.HikariDataSource; 6 | import org.apache.ibatis.plugin.Interceptor; 7 | import org.apache.ibatis.session.SqlSessionFactory; 8 | import org.mybatis.spring.SqlSessionFactoryBean; 9 | import org.mybatis.spring.SqlSessionTemplate; 10 | import org.mybatis.spring.annotation.MapperScan; 11 | import org.springframework.beans.factory.annotation.Qualifier; 12 | import org.springframework.beans.factory.annotation.Value; 13 | import org.springframework.context.annotation.Bean; 14 | import org.springframework.context.annotation.Configuration; 15 | import org.springframework.context.annotation.Primary; 16 | import org.springframework.core.io.support.PathMatchingResourcePatternResolver; 17 | import org.springframework.jdbc.datasource.DataSourceTransactionManager; 18 | import org.springframework.transaction.support.TransactionTemplate; 19 | 20 | import javax.sql.DataSource; 21 | import java.util.Properties; 22 | 23 | /** 24 | * 数据源配置 参考文档: https://www.cnblogs.com/SweetCode/p/15591792.html 25 | * @author fusu 26 | * @date 2023-04-14 27 | */ 28 | @Configuration 29 | @MapperScan(basePackages = {"com.smart.classroom.subscription.repository"}, sqlSessionFactoryRef = "sqlSessionFactory") 30 | public class DataSourceConfiguration { 31 | 32 | 33 | @Value("${spring.datasource.primary.jdbc-url}") 34 | private String primaryJdbcUrl; 35 | 36 | @Value("${spring.datasource.primary.username}") 37 | private String primaryUsername; 38 | 39 | @Value("${spring.datasource.primary.password}") 40 | private String primaryPassword; 41 | 42 | 43 | @Bean(name = "dataSource") 44 | @Primary 45 | public DataSource dataSource() { 46 | 47 | //手动创建数据源 48 | HikariConfig config = new HikariConfig(); 49 | config.setJdbcUrl(primaryJdbcUrl); 50 | config.setUsername(primaryUsername); 51 | config.setPassword(primaryPassword); 52 | 53 | config.setConnectionTimeout(60000); 54 | config.setValidationTimeout(3000); 55 | config.setIdleTimeout(60000); 56 | config.setMaxLifetime(500000); 57 | config.setMaximumPoolSize(100); 58 | config.setMinimumIdle(4); 59 | config.setReadOnly(false); 60 | config.setLeakDetectionThreshold(2000); 61 | config.setPoolName("HikariPool"); 62 | 63 | 64 | return new HikariDataSource(config); 65 | } 66 | 67 | @Bean(name = "sqlSessionFactory") 68 | @Primary 69 | public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource") DataSource dataSource) throws Exception { 70 | SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); 71 | bean.setDataSource(dataSource); 72 | bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:/mybatis/mapper/**/*.xml")); 73 | 74 | 75 | //添加Mybatis插件 76 | Interceptor pageInterceptor = new PageInterceptor(); 77 | Properties properties = new Properties(); 78 | properties.setProperty("helperDialect", "mysql"); 79 | properties.setProperty("offsetAsPageNum", "false"); 80 | properties.setProperty("rowBoundsWithCount", "false"); 81 | properties.setProperty("pageSizeZero", "false"); 82 | properties.setProperty("reasonable", "false"); 83 | properties.setProperty("returnPageInfo", "none"); 84 | properties.setProperty("supportMethodsArguments", "true"); 85 | properties.setProperty("params", "pageNum=pageNum;pageSize=pageSize;"); 86 | pageInterceptor.setProperties(properties); 87 | 88 | //开启日志的写法 89 | //bean.setPlugins(pageInterceptor, new MybatisInterceptor()); 90 | 91 | //关闭日志的写法 92 | bean.setPlugins(pageInterceptor); 93 | 94 | 95 | return bean.getObject(); 96 | } 97 | 98 | @Bean(name = "transactionManager") 99 | @Primary 100 | public DataSourceTransactionManager transactionManager(@Qualifier("dataSource") DataSource dataSource) { 101 | return new DataSourceTransactionManager(dataSource); 102 | } 103 | 104 | @Bean(name = "sqlSessionTemplate") 105 | @Primary 106 | public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) { 107 | return new SqlSessionTemplate(sqlSessionFactory); 108 | } 109 | 110 | 111 | /** 112 | * 方便手动控制的事务模板 113 | */ 114 | @Bean(name = "transactionTemplate") 115 | @Primary 116 | public TransactionTemplate transactionManager(@Qualifier("transactionManager") DataSourceTransactionManager transactionManager) { 117 | return new TransactionTemplate(transactionManager); 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /scs-testsuit/src/main/java/com/smart/classroom/subscription/tool/saber/artwork/DOArtwork.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.tool.saber.artwork; 2 | 3 | import com.smart.classroom.subscription.repository.orm.base.BaseEntityDO; 4 | import com.smart.classroom.subscription.tool.saber.artwork.base.Artwork; 5 | import com.smart.classroom.subscription.tool.saber.command.SaberCommand; 6 | import com.smart.classroom.subscription.tool.saber.config.SaberConfig; 7 | import com.smart.classroom.subscription.tool.saber.meta.SaberColumn; 8 | import com.smart.classroom.subscription.tool.saber.meta.SaberTable; 9 | import com.smart.classroom.subscription.utility.util.PathUtil; 10 | import com.smart.classroom.subscription.utility.util.StringUtil; 11 | import lombok.*; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | /** 17 | * XX.java 18 | * 负责生产模型DO的 19 | */ 20 | @EqualsAndHashCode(callSuper = true) 21 | @Data 22 | public class DOArtwork extends Artwork { 23 | /** 24 | * 表的主体名称,例如 scm_application_member 主体为application,主体会作为一层包名。 25 | * 默认:scm_后的第一个单词 26 | */ 27 | private String bodyName; 28 | 29 | //包名路径 30 | private String entityPackage; 31 | 32 | //依赖的包。 33 | private List entityImportNones; 34 | 35 | //依赖的包。 36 | private List entityImportJavas; 37 | 38 | //表注释。 39 | private String tableRemark; 40 | 41 | //主体大写。 42 | private String entityUpperBodyName; 43 | 44 | //类名大写。 45 | private String entityUpperClassName; 46 | 47 | //所有的字段。 48 | private List selfColumns; 49 | 50 | public DOArtwork() { 51 | super("TemplateDOJava.vm", true); 52 | } 53 | 54 | @Override 55 | public String destAbsolutePath() { 56 | 57 | String directoryPath = PathUtil.getAppHomePath() + "/"+ SaberConfig.REPOSITORY_MODULE_NAME +"/src/main/java/" + this.entityPackage.replaceAll("\\.", "/"); 58 | 59 | //获取到所在文件夹的路径,如果没有自动创建。 60 | directoryPath = PathUtil.getSafeDirectoryPath(directoryPath); 61 | 62 | return directoryPath + "/" + this.entityUpperBodyName + "DO.java"; 63 | } 64 | 65 | @Override 66 | public void init(@NonNull SaberTable saberTable, @NonNull SaberCommand saberCommand) { 67 | this.bodyName = saberCommand.getDirectFolderName(); 68 | this.tableRemark = saberTable.getRemark(); 69 | this.entityPackage = SaberConfig.BASE_PACKAGE_NAME+".repository.orm." + this.bodyName; 70 | this.entityUpperBodyName = saberCommand.getEntityUpperBodyName(); 71 | this.entityUpperClassName = saberCommand.getEntityUpperBodyName()+"DO"; 72 | 73 | this.selfColumns = new ArrayList<>(); 74 | for (SaberColumn saberColumn : saberTable.getColumns()) { 75 | if (StringUtil.equals("id", saberColumn.getLowerCamelName()) || 76 | StringUtil.equals("createTime", saberColumn.getLowerCamelName()) || 77 | StringUtil.equals("updateTime", saberColumn.getLowerCamelName()) 78 | ) { 79 | continue; 80 | } 81 | selfColumns.add(saberColumn); 82 | } 83 | 84 | //处理DO类需要引入的包 85 | this.entityImportNones = new ArrayList<>(); 86 | this.entityImportNones.add(BaseEntityDO.class.getName()); 87 | this.entityImportNones.add(Data.class.getName()); 88 | this.entityImportNones.add(Builder.class.getName()); 89 | this.entityImportNones.add(NoArgsConstructor.class.getName()); 90 | this.entityImportNones.add(AllArgsConstructor.class.getName()); 91 | this.entityImportNones.add(EqualsAndHashCode.class.getName()); 92 | 93 | this.entityImportJavas = new ArrayList<>(); 94 | 95 | this.selfColumns.forEach(saberColumn -> { 96 | 97 | String javaClassName = saberColumn.getJavaClassName(); 98 | //如果实体和字段类型在同一个包中,那么就不需要添加到import中去。 99 | if (!(this.entityPackage + "." + saberColumn.getJavaSimpleName()).equals(javaClassName)) { 100 | 101 | //java.lang包下的默认不需要引入 102 | if (!StringUtil.startsWith(javaClassName, "java.lang")) { 103 | 104 | if (StringUtil.startsWith(javaClassName, "java")) { 105 | 106 | //不重复添加 107 | if (!this.entityImportJavas.contains(javaClassName)) { 108 | this.entityImportJavas.add(javaClassName); 109 | } 110 | 111 | } else { 112 | 113 | //不重复添加 114 | if (!this.entityImportNones.contains(javaClassName)) { 115 | this.entityImportNones.add(javaClassName); 116 | } 117 | 118 | } 119 | } 120 | } 121 | 122 | }); 123 | 124 | //进行排序 125 | this.entityImportJavas.sort(StringUtil::compare); 126 | this.entityImportNones.sort(StringUtil::compare); 127 | 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /scs-testsuit/src/main/java/com/smart/classroom/subscription/tool/saber/command/SaberCommand.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.tool.saber.command; 2 | 3 | import com.smart.classroom.subscription.tool.saber.config.SaberConfig; 4 | import com.smart.classroom.subscription.tool.saber.contrast.TypeContrast; 5 | import com.smart.classroom.subscription.utility.exception.UtilException; 6 | import com.smart.classroom.subscription.utility.model.Pair; 7 | import com.smart.classroom.subscription.utility.util.StringUtil; 8 | import lombok.Data; 9 | import lombok.NonNull; 10 | import lombok.extern.slf4j.Slf4j; 11 | 12 | import java.util.ArrayList; 13 | import java.util.HashMap; 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | 18 | /** 19 | * 用户的一条指令,最终生成的文件会严格按照用户的指令来执行。 20 | * 21 | * @author lishuang 2023-05-11 22 | */ 23 | @Data 24 | @Slf4j 25 | public class SaberCommand { 26 | 27 | /** 28 | * 数据库表名. 29 | * 必须以 SaberConfig.DATABASE_TABLE_PREFIX 开头。 30 | */ 31 | private String tableName; 32 | 33 | /** 34 | * 直接文件夹名称,例如 scm_application_member 主体为application,主体会作为一层包名。 35 | * 默认:scm_后的第一个单词 36 | */ 37 | private String directFolderName; 38 | 39 | 40 | /** 41 | * 类名 42 | * 默认会使用表名的驼峰写法 43 | */ 44 | private String entityUpperBodyName; 45 | 46 | /** 47 | * 定义用来排序的字段 48 | * 默认会添加 49 | * create_time(按创建时间排序) 50 | * update_time(按修改时间排序) 51 | */ 52 | private List filterOrderFields; 53 | 54 | /** 55 | * 定义用来做模糊筛选的字段 56 | */ 57 | private List filterLikeFields; 58 | 59 | 60 | /** 61 | * 定义用来做等号判断的字段 62 | */ 63 | private List filterEqualFields; 64 | 65 | 66 | /** 67 | * 对于一些字段,我们需要手动指定其JdbcType,典型的例子:数据库tinyint(4) 希望是 Boolean类型 68 | */ 69 | private Map customFieldTypes; 70 | 71 | /** 72 | * 一些枚举字段的对照关系。优先级高于 customFieldTypes。 73 | * 字段名 : (枚举类型, 初始值) 74 | */ 75 | private Map>, String>> enumFieldMap; 76 | 77 | 78 | /** 79 | * 对于一些字段,数据库中虽然还留着,但是我们已经废弃了,不希望出现了。 80 | */ 81 | private List ignoreFields; 82 | 83 | 84 | public SaberCommand(@NonNull String tableName) { 85 | 86 | this.tableName = tableName; 87 | 88 | 89 | } 90 | 91 | 92 | /** 93 | * 对每个指令进行预处理 94 | */ 95 | public void prepare() { 96 | 97 | if (StringUtil.isBlank(this.tableName)) { 98 | throw new UtilException("表名不能为空!"); 99 | } 100 | 101 | if (!this.tableName.startsWith(SaberConfig.DATABASE_TABLE_PREFIX)) { 102 | throw new UtilException("表名{}不符合规范,必须以{}开头", tableName, SaberConfig.DATABASE_TABLE_PREFIX); 103 | } 104 | 105 | //获取表的主体名称。 106 | if (StringUtil.isBlank(this.directFolderName)) { 107 | String[] split = this.tableName.split("_"); 108 | if (split.length == 0) { 109 | throw new UtilException("表名应该带有前缀"); 110 | } 111 | this.directFolderName = split[1]; 112 | if (StringUtil.isBlank(this.directFolderName)) { 113 | throw new UtilException("表名格式错误,无法提取到表名主体bodyName"); 114 | } 115 | } 116 | 117 | 118 | //类名默认使用表名的驼峰形式。 119 | if (StringUtil.isBlank(this.entityUpperBodyName)) { 120 | String trimTableName = this.tableName.substring(SaberConfig.DATABASE_TABLE_PREFIX.length()); 121 | this.entityUpperBodyName = StringUtil.underscoreToUpperCamel(trimTableName); 122 | } 123 | 124 | //筛选条件的处理。 125 | if (filterOrderFields == null) { 126 | filterOrderFields = new ArrayList<>(); 127 | } 128 | 129 | //添加create_time,update_time排序 130 | List tempFilterOrderFields = new ArrayList<>(); 131 | if (!filterOrderFields.contains("create_time") && !filterOrderFields.contains("createTime")) { 132 | tempFilterOrderFields.add("create_time"); 133 | } 134 | if (!filterOrderFields.contains("update_time") && !filterOrderFields.contains("updateTime")) { 135 | tempFilterOrderFields.add("update_time"); 136 | } 137 | tempFilterOrderFields.addAll(filterOrderFields); 138 | this.filterOrderFields = tempFilterOrderFields; 139 | 140 | 141 | if (filterLikeFields == null) { 142 | filterLikeFields = new ArrayList<>(); 143 | } 144 | 145 | 146 | if (filterEqualFields == null) { 147 | filterEqualFields = new ArrayList<>(); 148 | } 149 | 150 | //对于一些字段,我们希望手动指定其类型 151 | if (customFieldTypes == null) { 152 | customFieldTypes = new HashMap<>(); 153 | } 154 | 155 | //枚举类型的字段 156 | if (enumFieldMap == null) { 157 | enumFieldMap = new HashMap<>(); 158 | } 159 | 160 | 161 | //一些数据库中虽然还有,但是我们已经废弃掉的字段 162 | if (ignoreFields == null) { 163 | ignoreFields = new ArrayList<>(); 164 | } 165 | 166 | 167 | } 168 | 169 | } 170 | -------------------------------------------------------------------------------- /scs-testsuit/src/main/resources/saber/TemplateBaseMapperXml.vm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #foreach($column in $artwork.columns) 7 | #if($column.primaryKey) 8 | 9 | #else 10 | 11 | #end 12 | #end 13 | 14 | 15 | 16 | #foreach($column in $artwork.columns)${column.safeName()}#if($foreach.hasNext), #end#end 17 | 18 | 19 | 20 | 21 | INSERT INTO ${artwork.tableName} ( 22 | #foreach($column in $artwork.columns)#if(!$column.primaryKey)${column.safeName()}#if($foreach.hasNext), #end#end#end 23 | ) 24 | VALUES ( 25 | 26 | #foreach($column in $artwork.columns) 27 | #if(!$column.primaryKey)#{${column.lowerCamelName},jdbcType=${column.jdbcType}}#if($foreach.hasNext) ,#end#end 28 | 29 | #end 30 | ) 31 | 32 | 33 | 34 | 35 | 36 | INSERT INTO ${artwork.tableName} ( 37 | #foreach($column in $artwork.columns)#if(!$column.primaryKey)${column.safeName()}#if($foreach.hasNext), #end#end#end 38 | ) 39 | VALUES 40 | 41 | ( 42 | #foreach($column in $artwork.columns) 43 | #if(!$column.primaryKey) 44 | #{item.${column.lowerCamelName},jdbcType=${column.jdbcType}}#if($foreach.hasNext) , 45 | #end 46 | #end 47 | #end 48 | ) 49 | 50 | 51 | 52 | 53 | 54 | DELETE FROM ${artwork.tableName} 55 | WHERE id = #{id,jdbcType=BIGINT} 56 | 57 | 58 | 59 | 60 | 61 | DELETE FROM ${artwork.tableName} 62 | WHERE 63 | 64 | 65 | 1 = 0 66 | 67 | 68 | id IN 69 | 70 | #{id} 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | UPDATE ${artwork.tableName} 79 | SET 80 | 81 | #foreach($column in $artwork.columns) 82 | #if(!$column.primaryKey) 83 | ${column.safeName()} = #if($column.name=='update_time')current_timestamp()#else#{${column.lowerCamelName},jdbcType=${column.jdbcType}}#end#if($foreach.hasNext) ,#end 84 | 85 | #end 86 | #end 87 | WHERE id = #{id} 88 | 89 | 90 | 91 | 97 | 98 | 99 | 116 | 117 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /scs-facade-impl/src/main/java/com/smart/classroom/subscription/facade/impl/biz/subscription/SubscriptionReadFacadeImpl.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.facade.impl.biz.subscription; 2 | 3 | import com.smart.classroom.subscription.application.biz.subscription.SubscriptionCommandAppService; 4 | import com.smart.classroom.subscription.domain.biz.order.info.PrepareSubscribeInfo; 5 | import com.smart.classroom.subscription.domain.biz.subscription.enums.SubscriptionStatus; 6 | import com.smart.classroom.subscription.domain.biz.subscription.model.SubscriptionModel; 7 | import com.smart.classroom.subscription.domain.biz.subscription.repository.SubscriptionRepository; 8 | import com.smart.classroom.subscription.domain.biz.subscription.repository.query.SubscriptionPageQuery; 9 | import com.smart.classroom.subscription.domain.rpc.column.ColumnFacadeClient; 10 | import com.smart.classroom.subscription.domain.rpc.column.vo.ColumnVO; 11 | import com.smart.classroom.subscription.domain.rpc.reader.ReaderFacadeClient; 12 | import com.smart.classroom.subscription.domain.rpc.reader.vo.ReaderVO; 13 | import com.smart.classroom.subscription.facade.biz.order.response.OrderDTO; 14 | import com.smart.classroom.subscription.facade.biz.subscription.SubscriptionReadFacade; 15 | import com.smart.classroom.subscription.facade.biz.subscription.request.PrepareSubscribeRequest; 16 | import com.smart.classroom.subscription.facade.biz.subscription.request.ReaderColumnQueryRequest; 17 | import com.smart.classroom.subscription.facade.biz.subscription.request.SubscriptionPageRequest; 18 | import com.smart.classroom.subscription.facade.biz.subscription.response.PrepareSubscribeDTO; 19 | import com.smart.classroom.subscription.facade.biz.subscription.response.SubscriptionDTO; 20 | import com.smart.classroom.subscription.facade.common.resposne.PagerResponse; 21 | import com.smart.classroom.subscription.facade.impl.biz.order.converter.OrderModel2DTOConverter; 22 | import com.smart.classroom.subscription.facade.impl.biz.subscription.converter.SubscriptionModel2DTOConverter; 23 | import com.smart.classroom.subscription.utility.enums.SortDirection; 24 | import com.smart.classroom.subscription.utility.exception.UtilException; 25 | import com.smart.classroom.subscription.utility.result.Pager; 26 | import org.apache.dubbo.config.annotation.DubboService; 27 | 28 | import javax.annotation.Resource; 29 | import java.util.List; 30 | import java.util.stream.Collectors; 31 | 32 | /** 33 | * @author lishuang 34 | * @date 2023-05-15 35 | */ 36 | @DubboService 37 | public class SubscriptionReadFacadeImpl implements SubscriptionReadFacade { 38 | 39 | @Resource 40 | SubscriptionCommandAppService subscriptionCommandAppService; 41 | 42 | @Resource 43 | ReaderFacadeClient readerFacadeClient; 44 | 45 | @Resource 46 | ColumnFacadeClient columnFacadeClient; 47 | 48 | @Resource 49 | SubscriptionRepository subscriptionRepository; 50 | 51 | /** 52 | * 发起订阅请求 53 | */ 54 | public PrepareSubscribeDTO prepareSubscribe(PrepareSubscribeRequest request) { 55 | 56 | PrepareSubscribeInfo prepareSubscribeInfo = subscriptionCommandAppService.prepareSubscribe(request.getReaderId(), request.getColumnId(), request.getPayMethod()); 57 | 58 | OrderDTO orderDTO = OrderModel2DTOConverter.convert(prepareSubscribeInfo.getOrderModel()); 59 | 60 | PrepareSubscribeDTO prepareSubscribeDTO = new PrepareSubscribeDTO( 61 | orderDTO, 62 | prepareSubscribeInfo.getThirdTransactionNo(), 63 | prepareSubscribeInfo.getNonceStr() 64 | ); 65 | 66 | return prepareSubscribeDTO; 67 | 68 | 69 | } 70 | 71 | 72 | @Override 73 | public SubscriptionDTO queryByColumnIdAndReaderId(ReaderColumnQueryRequest readerColumnQueryRequest) { 74 | 75 | //1. 查询读者信息。 76 | ReaderVO readerVO = readerFacadeClient.queryById(readerColumnQueryRequest.getReaderId()); 77 | if (readerVO == null) { 78 | throw new UtilException("id={}对应的读者不存在", readerColumnQueryRequest.getReaderId()); 79 | } 80 | 81 | 82 | //2. 查询专栏信息。 83 | ColumnVO columnVO = columnFacadeClient.queryById(readerColumnQueryRequest.getColumnId()); 84 | if (columnVO == null) { 85 | throw new UtilException("id={}对应的专栏不存在", readerColumnQueryRequest.getColumnId()); 86 | } 87 | 88 | //3. 查找订阅关系是否已经创建了。 89 | SubscriptionModel subscriptionModel = subscriptionRepository.queryByReaderAndColumn(readerVO, columnVO); 90 | 91 | return SubscriptionModel2DTOConverter.convert(subscriptionModel); 92 | } 93 | 94 | @Override 95 | public PagerResponse page(SubscriptionPageRequest subscriptionPageRequest) { 96 | 97 | Pager subscriptionModelPager = subscriptionRepository.page(new SubscriptionPageQuery( 98 | subscriptionPageRequest.getPageNum(), 99 | subscriptionPageRequest.getPageSize(), 100 | SortDirection.toEnum(subscriptionPageRequest.getOrderCreateTime()), 101 | SortDirection.toEnum(subscriptionPageRequest.getOrderUpdateTime()), 102 | subscriptionPageRequest.getReaderId(), 103 | subscriptionPageRequest.getColumnId(), 104 | subscriptionPageRequest.getOrderId(), 105 | SubscriptionStatus.toEnum(subscriptionPageRequest.getStatus()) 106 | )); 107 | 108 | List list = subscriptionModelPager.getData().stream().map(SubscriptionModel2DTOConverter::convert).collect(Collectors.toList()); 109 | 110 | 111 | return new PagerResponse<>(subscriptionModelPager.getPageNum(), subscriptionModelPager.getPageSize(), subscriptionModelPager.getTotalItems(), list); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /scs-testsuit/src/main/java/com/smart/classroom/subscription/tool/saber/contrast/TypeContrast.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.tool.saber.contrast; 2 | 3 | 4 | import com.smart.classroom.subscription.tool.saber.config.SaberConfig; 5 | import com.smart.classroom.subscription.utility.exception.UtilException; 6 | import com.smart.classroom.subscription.utility.util.StringUtil; 7 | import lombok.Getter; 8 | 9 | import java.math.BigDecimal; 10 | import java.sql.Time; 11 | import java.util.Date; 12 | 13 | /** 14 | * 类型对照 15 | * 主要参考 @java.sql.Types 16 | * 这里定义了常见的类型 17 | * 18 | * @author lishuang 2023-05-11 19 | */ 20 | public enum TypeContrast { 21 | 22 | VARCHAR("VARCHAR", "VARCHAR", String.class), 23 | LONGVARCHAR("LONGVARCHAR", "LONGVARCHAR", String.class), 24 | CHAR("CHAR", "CHAR", String.class), 25 | BLOB("BLOB", "BLOB", byte[].class), 26 | TEXT("TEXT", "LONGVARCHAR", String.class), 27 | TINYTEXT("TINYTEXT", "LONGVARCHAR", String.class), 28 | MEDIUMTEXT("MEDIUMTEXT", "LONGVARCHAR", String.class), 29 | LONGTEXT("LONGTEXT", "LONGVARCHAR", String.class), 30 | INT("INT", "INTEGER", Integer.class), 31 | INT_UNSIGNED("INT UNSIGNED", "INTEGER", Integer.class), 32 | INTEGER("INTEGER", "INTEGER", Integer.class), 33 | INTEGER_UNSIGNED("INTEGER", "INTEGER UNSIGNED", Integer.class), 34 | TINYINT("TINYINT", "INTEGER", Boolean.class), 35 | TINYINT_UNSIGNED("TINYINT UNSIGNED", "INTEGER", Boolean.class), 36 | SMALLINT("SMALLINT", "INTEGER", Integer.class), 37 | SMALLINT_UNSIGNED("SMALLINT UNSIGNED", "INTEGER", Integer.class), 38 | MEDIUMINT("MEDIUMINT", "INTEGER", Integer.class), 39 | MEDIUMINT_UNSIGNED("MEDIUMINT UNSIGNED", "INTEGER", Integer.class), 40 | BIT("BIT", "BOOLEAN", Boolean.class), 41 | BIGINT("BIGINT", "BIGINT", Long.class), 42 | BIGINT_UNSIGNED("BIGINT UNSIGNED", "BIGINT", Long.class), 43 | FLOAT("FLOAT", "FLOAT", Float.class), 44 | DOUBLE("DOUBLE", "DOUBLE", Double.class), 45 | DECIMAL("DECIMAL", "DECIMAL", BigDecimal.class), 46 | BOOLEAN("BOOLEAN", "BOOLEAN", Boolean.class), 47 | ID("PK (INTEGER UNSIGNED)", "INTEGER", Long.class), 48 | DATE("DATE", "DATE", Date.class), 49 | TIME("TIME", "TIME", Time.class), 50 | DATETIME("DATETIME", "TIMESTAMP", Date.class), 51 | TIMESTAMP("TIMESTAMP", "TIMESTAMP", Date.class), 52 | YEAR("YEAR", "DATE", java.sql.Date.class); 53 | 54 | @Getter 55 | private String sqlType; 56 | 57 | //提供给mybatis使用的类型 58 | @Getter 59 | private String jdbcType; 60 | 61 | @Getter 62 | private Class javaClass; 63 | 64 | 65 | TypeContrast(String sqlType, String jdbcType, Class javaClass) { 66 | 67 | this.sqlType = sqlType; 68 | this.jdbcType = jdbcType; 69 | this.javaClass = javaClass; 70 | } 71 | 72 | public static TypeContrast getBySqlType(String sqlType) { 73 | TypeContrast[] values = TypeContrast.values(); 74 | for (TypeContrast value : values) { 75 | if (StringUtil.equalsIgnoreCase(value.getSqlType(), sqlType)) { 76 | return value; 77 | } 78 | } 79 | throw new UtilException("不支持类型{},请在"+ SaberConfig.BASE_PACKAGE_NAME+".tool.saber.contrast.TypeContrast中进行补充", sqlType); 80 | } 81 | 82 | //和前端的对照关系 83 | public String getTsType() { 84 | if (this.getJavaClass() == Integer.class || 85 | this.getJavaClass() == Long.class || 86 | this.getJavaClass() == Float.class || 87 | this.getJavaClass() == Double.class || 88 | this.getJavaClass() == BigDecimal.class 89 | ) { 90 | return "number"; 91 | } else if (this.getJavaClass() == Boolean.class) { 92 | return "boolean"; 93 | } else if (this.getJavaClass() == Date.class || 94 | this.getJavaClass() == Time.class || 95 | this.getJavaClass() == java.sql.Date.class) { 96 | return "Date"; 97 | } else { 98 | return "string"; 99 | } 100 | } 101 | 102 | //和前端的对照关系 103 | public String getOdpsType() { 104 | if (this.getJavaClass() == Integer.class) { 105 | return "INT"; 106 | } else if (this.getJavaClass() == Long.class) { 107 | return "BIGINT"; 108 | } else if (this.getJavaClass() == Float.class) { 109 | return "FLOAT"; 110 | } else if (this.getJavaClass() == Double.class) { 111 | return "DOUBLE"; 112 | } else if (this.getJavaClass() == Boolean.class) { 113 | return "INT"; 114 | } else if (this.getJavaClass() == java.sql.Date.class) { 115 | return "DATE"; 116 | } else if (this.getJavaClass() == Date.class) { 117 | return "DATETIME"; 118 | } else { 119 | return "STRING"; 120 | } 121 | } 122 | 123 | //和前端的对照关系 124 | public String getSwiftType() { 125 | if (this.getJavaClass() == Integer.class || this.getJavaClass() == Long.class) { 126 | return "Int"; 127 | } else if (this.getJavaClass() == Float.class) { 128 | return "Float"; 129 | } else if (this.getJavaClass() == Double.class || this.getJavaClass() == BigDecimal.class) { 130 | return "Double"; 131 | } else if (this.getJavaClass() == Boolean.class) { 132 | return "Bool"; 133 | } else if (this.getJavaClass() == Date.class || 134 | this.getJavaClass() == Time.class || 135 | this.getJavaClass() == java.sql.Date.class) { 136 | return "Date"; 137 | } else { 138 | return "String"; 139 | } 140 | } 141 | 142 | //对于前端而言,是否为日期字段 143 | public boolean isTsDate() { 144 | return this.getJavaClass() == Date.class || 145 | this.getJavaClass() == Time.class || 146 | this.getJavaClass() == java.sql.Date.class; 147 | } 148 | 149 | 150 | 151 | 152 | } 153 | -------------------------------------------------------------------------------- /scs-repository/src/main/resources/mybatis/mapper/subscription/SubscriptionBaseMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | id, create_time, update_time, reader_id, column_id, order_id, status 17 | 18 | 19 | 20 | INSERT INTO scs_subscription ( 21 | create_time, update_time, reader_id, column_id, order_id, status ) 22 | VALUES ( 23 | 24 | 25 | #{createTime,jdbcType=TIMESTAMP} , 26 | #{updateTime,jdbcType=TIMESTAMP} , 27 | #{readerId,jdbcType=BIGINT} , 28 | #{columnId,jdbcType=BIGINT} , 29 | #{orderId,jdbcType=BIGINT} , 30 | #{status,jdbcType=VARCHAR} 31 | ) 32 | 33 | 34 | 35 | 36 | 37 | INSERT INTO scs_subscription ( 38 | create_time, update_time, reader_id, column_id, order_id, status ) 39 | VALUES 40 | 41 | ( 42 | #{item.createTime,jdbcType=TIMESTAMP} , 43 | #{item.updateTime,jdbcType=TIMESTAMP} , 44 | #{item.readerId,jdbcType=BIGINT} , 45 | #{item.columnId,jdbcType=BIGINT} , 46 | #{item.orderId,jdbcType=BIGINT} , 47 | #{item.status,jdbcType=VARCHAR} ) 48 | 49 | 50 | 51 | 52 | 53 | DELETE FROM scs_subscription 54 | WHERE id = #{id,jdbcType=BIGINT} 55 | 56 | 57 | 58 | 59 | 60 | DELETE FROM scs_subscription 61 | WHERE 62 | 63 | 64 | 1 = 0 65 | 66 | 67 | id IN 68 | 69 | #{id} 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | UPDATE scs_subscription 78 | SET 79 | 80 | create_time = #{createTime,jdbcType=TIMESTAMP} , 81 | update_time = current_timestamp() , 82 | reader_id = #{readerId,jdbcType=BIGINT} , 83 | column_id = #{columnId,jdbcType=BIGINT} , 84 | order_id = #{orderId,jdbcType=BIGINT} , 85 | status = #{status,jdbcType=VARCHAR} 86 | WHERE id = #{id} 87 | 88 | 89 | 90 | 96 | 97 | 98 | 115 | 116 | 162 | 163 | 164 | -------------------------------------------------------------------------------- /scs-infrastructure/src/main/java/com/smart/classroom/subscription/infrastructure/middleware/rocketmq/RocketMqConsumerImpl.java: -------------------------------------------------------------------------------- 1 | package com.smart.classroom.subscription.infrastructure.middleware.rocketmq; 2 | 3 | import com.smart.classroom.subscription.domain.middleware.mq.MqConsumer; 4 | import com.smart.classroom.subscription.domain.middleware.mq.MqConsumerListener; 5 | import com.smart.classroom.subscription.domain.middleware.mq.info.MqMessagePayloadInfo; 6 | import com.smart.classroom.subscription.utility.exception.UtilException; 7 | import com.smart.classroom.subscription.utility.util.StringUtil; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.apache.rocketmq.client.apis.ClientConfiguration; 10 | import org.apache.rocketmq.client.apis.ClientServiceProvider; 11 | import org.apache.rocketmq.client.apis.consumer.ConsumeResult; 12 | import org.apache.rocketmq.client.apis.consumer.FilterExpression; 13 | import org.apache.rocketmq.client.apis.consumer.FilterExpressionType; 14 | import org.apache.rocketmq.client.apis.consumer.PushConsumer; 15 | import org.springframework.context.event.ContextRefreshedEvent; 16 | import org.springframework.context.event.ContextStoppedEvent; 17 | import org.springframework.context.event.EventListener; 18 | import org.springframework.stereotype.Component; 19 | 20 | import java.nio.charset.StandardCharsets; 21 | import java.util.Collection; 22 | import java.util.Collections; 23 | import java.util.HashMap; 24 | import java.util.Map; 25 | 26 | /** 27 | * @author lishuang 28 | * @date 2023-05-17 29 | *

30 | * 实现代码参考:https://rocketmq.apache.org/zh/docs/quickStart/01quickstart 31 | */ 32 | @Slf4j 33 | @Component 34 | public class RocketMqConsumerImpl implements MqConsumer { 35 | // 接入点地址,需要设置成Proxy的地址和端口列表,一般是xxx:8081;xxx:8081。 36 | public static String ENDPOINT = "localhost:8081"; 37 | 38 | // 消息发送的目标Topic名称,需要提前在RocketMQ中创建。 39 | public static String TOPIC = "SmartClassroomTopic"; 40 | 41 | // 为消费者指定所属的消费者分组,Group需要提前创建。 42 | public static String CONSUMER_GROUP = "SmartClassroomConsumerGroup"; 43 | 44 | //标识是否已完成启动,防止多次启动 45 | private boolean initialized = false; 46 | 47 | //所有的消费者。 48 | private final Map pushConsumers = new HashMap<>(); 49 | 50 | /** 51 | * 这个方法只在系统启动的时候调用,系统可能调多次。 52 | */ 53 | @EventListener(ContextRefreshedEvent.class) 54 | public void applicationRefreshed() { 55 | 56 | log.info("准备启动RocketMqConsumer"); 57 | 58 | if (initialized) { 59 | return; 60 | } else { 61 | initialized = true; 62 | } 63 | 64 | } 65 | 66 | /** 67 | * 这个方法只在系统停止的时候调用 68 | */ 69 | @EventListener(ContextStoppedEvent.class) 70 | public void applicationStop() { 71 | log.info("RocketMqConsumer 系统停止钩子函数"); 72 | 73 | try { 74 | 75 | for (Map.Entry entry : pushConsumers.entrySet()) { 76 | 77 | PushConsumer pushConsumer = entry.getValue(); 78 | 79 | pushConsumer.close(); 80 | } 81 | 82 | } catch (Throwable e) { 83 | log.error("在关闭RocketMqConsumer的过程中遭遇错误", e); 84 | } 85 | } 86 | 87 | 88 | @Override 89 | public synchronized void listen(String eventName, MqConsumerListener mqConsumerListener) { 90 | 91 | 92 | if (StringUtil.isBlank(eventName)) { 93 | throw new UtilException("事件名不能为空"); 94 | } 95 | 96 | if (this.pushConsumers.containsKey(eventName)) { 97 | throw new UtilException("{}已经完成了注册,请勿重复注册", eventName); 98 | } 99 | 100 | final ClientServiceProvider provider = ClientServiceProvider.loadService(); 101 | 102 | ClientConfiguration clientConfiguration = ClientConfiguration.newBuilder() 103 | .setEndpoints(ENDPOINT) 104 | .build(); 105 | // 订阅消息的过滤规则,表示订阅所有Tag的消息。 我们使用eventName来充当tag. 106 | FilterExpression filterExpression = new FilterExpression(eventName, FilterExpressionType.TAG); 107 | 108 | try { 109 | 110 | // 初始化PushConsumer,需要绑定消费者分组ConsumerGroup、通信参数以及订阅关系。 111 | PushConsumer pushConsumer = provider.newPushConsumerBuilder() 112 | .setClientConfiguration(clientConfiguration) 113 | // 设置消费者分组。 114 | .setConsumerGroup(CONSUMER_GROUP) 115 | // 设置预绑定的订阅关系。 116 | .setSubscriptionExpressions(Collections.singletonMap(TOPIC, filterExpression)) 117 | // 设置消费监听器。 118 | .setMessageListener(messageView -> { 119 | 120 | String messageId = messageView.getMessageId().toString(); 121 | String content = StandardCharsets.UTF_8.decode(messageView.getBody()).toString(); 122 | Collection keys = messageView.getKeys(); 123 | String keysStr = String.join(",", keys); 124 | 125 | log.info("收到消息 messageId={},keys={},content={}", messageId, keysStr, content); 126 | 127 | 128 | try { 129 | 130 | MqMessagePayloadInfo mqMessagePayloadInfo = new MqMessagePayloadInfo(); 131 | mqMessagePayloadInfo.setMessageId(messageId); 132 | mqMessagePayloadInfo.setContent(content); 133 | mqMessagePayloadInfo.setKeys(keysStr); 134 | 135 | boolean success = mqConsumerListener.consume(mqMessagePayloadInfo); 136 | 137 | log.info("消息消费成功 messageId={},keys={}", messageId, keysStr); 138 | 139 | // 处理消息并返回消费结果。 140 | return success ? ConsumeResult.SUCCESS : ConsumeResult.FAILURE; 141 | 142 | } catch (Throwable throwable) { 143 | 144 | log.info("在消费消息过程中遇到了异常 messageId={},keys={}", messageId, keysStr, throwable); 145 | 146 | return ConsumeResult.FAILURE; 147 | } 148 | 149 | }).build(); 150 | 151 | this.pushConsumers.put(eventName, pushConsumer); 152 | 153 | log.info("注册Mq消息监听成功 eventName = {}", eventName); 154 | 155 | } catch (Throwable throwable) { 156 | 157 | log.error("在注册Mq消息监听时出错了 eventName = {}", eventName, throwable); 158 | } 159 | 160 | } 161 | } 162 | --------------------------------------------------------------------------------