提交 42280b6e 编写于 作者: 鲸落和鲨掉's avatar 鲸落和鲨掉

引入MQ处理活动SKU库存一致性、以及修改活动表,添加SKU表(活动库表)

上级 9c727658
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SqlDialectMappings">
<file url="file://$PROJECT_DIR$/docs/dev-ops/mysql/sql/big_market.sql" dialect="MySQL" />
<file url="file://$PROJECT_DIR$/docs/dev-ops/mysql/sql/big_market_01.sql" dialect="MySQL" />
<file url="file://$PROJECT_DIR$/docs/dev-ops/mysql/sql/big_market_02.sql" dialect="MySQL" />
</component>
</project>
\ No newline at end of file
24-11-18.19:52:29.536 [HikariPool-1 housekeeper] WARN HikariPool - HikariPool-1 - Thread starvation or clock leap detected (housekeeper delta=46s731ms900µs600ns).
24-11-18.19:56:11.941 [HikariPool-1 housekeeper] WARN HikariPool - HikariPool-1 - Thread starvation or clock leap detected (housekeeper delta=46s929ms97µs900ns).
24-11-18.20:06:26.644 [HikariPool-1 housekeeper] WARN HikariPool - HikariPool-1 - Thread starvation or clock leap detected (housekeeper delta=1m54s620ms108µs700ns).
24-11-21.15:10:59.570 [main ] WARN GenericWebApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'updateAwardStockJob': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'cn.bugstack.domain.strategy.IRaffleStock' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@javax.annotation.Resource(shareable=true, lookup=, name=, description=, authenticationType=CONTAINER, type=class java.lang.Object, mappedName=)}
24-11-21.15:10:59.615 [main ] ERROR LoggingFailureAnalysisReporter -
***************************
APPLICATION FAILED TO START
***************************
Description:
A component required a bean of type 'cn.bugstack.domain.strategy.IRaffleStock' that could not be found.
Action:
Consider defining a bean of type 'cn.bugstack.domain.strategy.IRaffleStock' in your configuration.
24-11-21.15:10:59.618 [main ] ERROR TestContextManager - Caught exception while allowing TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener@63475ace] to prepare test instance [cn.bugstack.test.domain.RaffleStrategyTest@46383a78]
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:98)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:124)
at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:190)
at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:132)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:248)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:227)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:246)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'updateAwardStockJob': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'cn.bugstack.domain.strategy.IRaffleStock' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@javax.annotation.Resource(shareable=true, lookup=, name=, description=, authenticationType=CONTAINER, type=class java.lang.Object, mappedName=)}
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:332)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1431)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:920)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:731)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:307)
at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:136)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:141)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:90)
... 27 common frames omitted
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'cn.bugstack.domain.strategy.IRaffleStock' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@javax.annotation.Resource(shareable=true, lookup=, name=, description=, authenticationType=CONTAINER, type=class java.lang.Object, mappedName=)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1801)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1357)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1311)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:544)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:520)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:673)
at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:228)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:329)
... 43 common frames omitted
24-11-21.15:12:01.523 [main ] WARN GenericWebApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'updateAwardStockJob': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'cn.bugstack.domain.strategy.IRaffleStock' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@javax.annotation.Resource(shareable=true, lookup=, name=, description=, authenticationType=CONTAINER, type=class java.lang.Object, mappedName=)}
24-11-21.15:12:01.565 [main ] ERROR LoggingFailureAnalysisReporter -
***************************
APPLICATION FAILED TO START
***************************
Description:
A component required a bean of type 'cn.bugstack.domain.strategy.IRaffleStock' that could not be found.
Action:
Consider defining a bean of type 'cn.bugstack.domain.strategy.IRaffleStock' in your configuration.
24-11-21.15:12:01.568 [main ] ERROR TestContextManager - Caught exception while allowing TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener@bccb269] to prepare test instance [cn.bugstack.test.domain.RaffleStrategyTest@6af91cc8]
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:98)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:124)
at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:190)
at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:132)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:248)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:227)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:246)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'updateAwardStockJob': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'cn.bugstack.domain.strategy.IRaffleStock' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@javax.annotation.Resource(shareable=true, lookup=, name=, description=, authenticationType=CONTAINER, type=class java.lang.Object, mappedName=)}
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:332)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1431)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:920)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:731)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:307)
at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:136)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:141)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:90)
... 27 common frames omitted
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'cn.bugstack.domain.strategy.IRaffleStock' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@javax.annotation.Resource(shareable=true, lookup=, name=, description=, authenticationType=CONTAINER, type=class java.lang.Object, mappedName=)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1801)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1357)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1311)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:544)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:520)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:673)
at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:228)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:329)
... 43 common frames omitted
24-11-26.14:44:55.728 [main ] ERROR TestContextManager - Caught exception while allowing TestExecutionListener [org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener@75db5df9] to prepare test instance [cn.bugstack.test.infrastructure.RaffleActivityDaoTest@77f03916]
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cn.bugstack.test.infrastructure.RaffleActivityDaoTest': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'cn.bugstack.infrastructure.persistent.dao.IRaffleActivityDao' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@javax.annotation.Resource(shareable=true, lookup=, name=, description=, authenticationType=CONTAINER, type=class java.lang.Object, mappedName=)}
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:332)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1431)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireBeanProperties(AbstractAutowireCapableBeanFactory.java:417)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:119)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
at org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener.prepareTestInstance(SpringBootDependencyInjectionTestExecutionListener.java:43)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:248)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:227)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:246)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'cn.bugstack.infrastructure.persistent.dao.IRaffleActivityDao' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@javax.annotation.Resource(shareable=true, lookup=, name=, description=, authenticationType=CONTAINER, type=class java.lang.Object, mappedName=)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1801)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1357)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1311)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:544)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:520)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:673)
at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:228)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:329)
... 29 common frames omitted
......@@ -66,7 +66,7 @@ Action:
Consider defining a bean of type 'cn.bugstack.domain.strategy.IRaffleStock' in your configuration.
24-11-21.15:10:59.618 [main ] ERROR TestContextManager - Caught exception while allowing TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener@63475ace] to prepare test instance [cn.bugstack.test.domain.RaffleStrategyTest@46383a78]
24-11-21.15:10:59.618 [main ] ERROR TestContextManager - Caught exception while allowing TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener@63475ace] to prepare test instance [cn.bugstack.test.domain.strategy.RaffleStrategyTest@46383a78]
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:98)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:124)
......@@ -150,7 +150,7 @@ Action:
Consider defining a bean of type 'cn.bugstack.domain.strategy.IRaffleStock' in your configuration.
24-11-21.15:12:01.568 [main ] ERROR TestContextManager - Caught exception while allowing TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener@bccb269] to prepare test instance [cn.bugstack.test.domain.RaffleStrategyTest@6af91cc8]
24-11-21.15:12:01.568 [main ] ERROR TestContextManager - Caught exception while allowing TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener@bccb269] to prepare test instance [cn.bugstack.test.domain.strategy.RaffleStrategyTest@6af91cc8]
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:98)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:124)
......
24-11-26.14:44:46.454 [main ] INFO RaffleActivityDaoTest - Starting RaffleActivityDaoTest using Java 1.8.0_412 on zhaoyongfeng with PID 24288 (started by zhaoyongfeng in C:\Users\31126\Desktop\bigmarket-lite\bigmarket-app)
24-11-26.14:44:46.455 [main ] INFO RaffleActivityDaoTest - The following 1 profile is active: "dev"
24-11-26.14:44:47.728 [main ] INFO RepositoryConfigurationDelegate - Multiple Spring Data modules found, entering strict repository configuration mode
24-11-26.14:44:47.732 [main ] INFO RepositoryConfigurationDelegate - Bootstrapping Spring Data Redis repositories in DEFAULT mode.
24-11-26.14:44:47.813 [main ] INFO RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 63 ms. Found 0 Redis repository interfaces.
24-11-26.14:44:50.908 [main ] INFO Version - Redisson 3.23.4
24-11-26.14:44:52.489 [redisson-netty-2-4] INFO MasterPubSubConnectionPool - 1 connections initialized for 127.0.0.1/127.0.0.1:16379
24-11-26.14:44:52.502 [redisson-netty-2-13] INFO MasterConnectionPool - 5 connections initialized for 127.0.0.1/127.0.0.1:16379
24-11-26.14:44:55.529 [main ] INFO EndpointLinksResolver - Exposing 1 endpoint(s) beneath base path '/actuator'
24-11-26.14:44:55.628 [main ] INFO RaffleActivityDaoTest - Started RaffleActivityDaoTest in 9.786 seconds (JVM running for 11.079)
24-11-26.14:44:55.728 [main ] ERROR TestContextManager - Caught exception while allowing TestExecutionListener [org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener@75db5df9] to prepare test instance [cn.bugstack.test.infrastructure.RaffleActivityDaoTest@77f03916]
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cn.bugstack.test.infrastructure.RaffleActivityDaoTest': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'cn.bugstack.infrastructure.persistent.dao.IRaffleActivityDao' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@javax.annotation.Resource(shareable=true, lookup=, name=, description=, authenticationType=CONTAINER, type=class java.lang.Object, mappedName=)}
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:332)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1431)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireBeanProperties(AbstractAutowireCapableBeanFactory.java:417)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:119)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
at org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener.prepareTestInstance(SpringBootDependencyInjectionTestExecutionListener.java:43)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:248)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:227)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:246)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'cn.bugstack.infrastructure.persistent.dao.IRaffleActivityDao' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@javax.annotation.Resource(shareable=true, lookup=, name=, description=, authenticationType=CONTAINER, type=class java.lang.Object, mappedName=)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1801)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1357)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1311)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:544)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:520)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:673)
at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:228)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:329)
... 29 common frames omitted
24-11-26.14:58:05.651 [main ] INFO RaffleActivityDaoTest - Starting RaffleActivityDaoTest using Java 1.8.0_412 on zhaoyongfeng with PID 12612 (started by zhaoyongfeng in C:\Users\31126\Desktop\bigmarket-lite\bigmarket-app)
24-11-26.14:58:05.652 [main ] INFO RaffleActivityDaoTest - The following 1 profile is active: "dev"
24-11-26.14:58:07.208 [main ] INFO RepositoryConfigurationDelegate - Multiple Spring Data modules found, entering strict repository configuration mode
24-11-26.14:58:07.214 [main ] INFO RepositoryConfigurationDelegate - Bootstrapping Spring Data Redis repositories in DEFAULT mode.
24-11-26.14:58:07.327 [main ] INFO RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 92 ms. Found 0 Redis repository interfaces.
24-11-26.14:58:10.505 [main ] INFO Version - Redisson 3.23.4
24-11-26.14:58:12.087 [redisson-netty-2-4] INFO MasterPubSubConnectionPool - 1 connections initialized for 127.0.0.1/127.0.0.1:16379
24-11-26.14:58:12.101 [redisson-netty-2-13] INFO MasterConnectionPool - 5 connections initialized for 127.0.0.1/127.0.0.1:16379
24-11-26.14:58:16.275 [main ] INFO EndpointLinksResolver - Exposing 1 endpoint(s) beneath base path '/actuator'
24-11-26.14:58:16.442 [main ] INFO RaffleActivityDaoTest - Started RaffleActivityDaoTest in 11.508 seconds (JVM running for 13.113)
24-11-26.14:58:16.818 [main ] INFO HikariDataSource - Retail_HikariCP - Starting...
24-11-26.14:58:17.253 [main ] INFO HikariDataSource - Retail_HikariCP - Start completed.
24-11-26.14:58:17.534 [main ] INFO RaffleActivityDaoTest - 测试结果:{"activityCountId":1,"activityDesc":"测试活动","activityId":100301,"activityName":"测试活动","beginDateTime":1732619927000,"endDateTime":1735211934000,"state":"0","stockCount":1000,"stockCountSurplus":1000,"strategyId":100006}
24-11-26.14:59:18.748 [main ] INFO RaffleActivityOrderDaoTest - Starting RaffleActivityOrderDaoTest using Java 1.8.0_412 on zhaoyongfeng with PID 18800 (started by zhaoyongfeng in C:\Users\31126\Desktop\bigmarket-lite\bigmarket-app)
24-11-26.14:59:18.749 [main ] INFO RaffleActivityOrderDaoTest - The following 1 profile is active: "dev"
24-11-26.14:59:20.080 [main ] INFO RepositoryConfigurationDelegate - Multiple Spring Data modules found, entering strict repository configuration mode
24-11-26.14:59:20.084 [main ] INFO RepositoryConfigurationDelegate - Bootstrapping Spring Data Redis repositories in DEFAULT mode.
24-11-26.14:59:20.170 [main ] INFO RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 70 ms. Found 0 Redis repository interfaces.
24-11-26.14:59:23.289 [main ] INFO Version - Redisson 3.23.4
24-11-26.14:59:25.040 [redisson-netty-2-4] INFO MasterPubSubConnectionPool - 1 connections initialized for 127.0.0.1/127.0.0.1:16379
24-11-26.14:59:25.054 [redisson-netty-2-13] INFO MasterConnectionPool - 5 connections initialized for 127.0.0.1/127.0.0.1:16379
24-11-26.14:59:28.634 [main ] INFO EndpointLinksResolver - Exposing 1 endpoint(s) beneath base path '/actuator'
24-11-26.14:59:28.752 [main ] INFO RaffleActivityOrderDaoTest - Started RaffleActivityOrderDaoTest in 10.621 seconds (JVM running for 12.018)
24-11-26.14:59:29.097 [main ] INFO HikariDataSource - Retail_HikariCP - Starting...
24-11-26.14:59:29.443 [main ] INFO HikariDataSource - Retail_HikariCP - Start completed.
24-11-26.15:11:41.013 [main ] INFO RaffleActivityOrderDaoTest - Starting RaffleActivityOrderDaoTest using Java 1.8.0_412 on zhaoyongfeng with PID 13736 (started by zhaoyongfeng in C:\Users\31126\Desktop\bigmarket-lite\bigmarket-app)
24-11-26.15:11:41.015 [main ] INFO RaffleActivityOrderDaoTest - The following 1 profile is active: "dev"
24-11-26.15:11:42.629 [main ] INFO RepositoryConfigurationDelegate - Multiple Spring Data modules found, entering strict repository configuration mode
24-11-26.15:11:42.635 [main ] INFO RepositoryConfigurationDelegate - Bootstrapping Spring Data Redis repositories in DEFAULT mode.
24-11-26.15:11:42.734 [main ] INFO RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 77 ms. Found 0 Redis repository interfaces.
24-11-26.15:11:45.911 [main ] INFO Version - Redisson 3.23.4
24-11-26.15:11:47.552 [redisson-netty-2-4] INFO MasterPubSubConnectionPool - 1 connections initialized for 127.0.0.1/127.0.0.1:16379
24-11-26.15:11:47.565 [redisson-netty-2-13] INFO MasterConnectionPool - 5 connections initialized for 127.0.0.1/127.0.0.1:16379
24-11-26.15:11:50.740 [main ] INFO EndpointLinksResolver - Exposing 1 endpoint(s) beneath base path '/actuator'
24-11-26.15:11:50.835 [main ] INFO RaffleActivityOrderDaoTest - Started RaffleActivityOrderDaoTest in 10.43 seconds (JVM running for 11.685)
24-11-26.15:11:51.155 [main ] INFO HikariDataSource - Retail_HikariCP - Starting...
24-11-26.15:11:51.486 [main ] INFO HikariDataSource - Retail_HikariCP - Start completed.
24-11-26.15:12:43.811 [main ] INFO RaffleActivityOrderDaoTest - Starting RaffleActivityOrderDaoTest using Java 1.8.0_412 on zhaoyongfeng with PID 21760 (started by zhaoyongfeng in C:\Users\31126\Desktop\bigmarket-lite\bigmarket-app)
24-11-26.15:12:43.813 [main ] INFO RaffleActivityOrderDaoTest - The following 1 profile is active: "dev"
24-11-26.15:12:45.129 [main ] INFO RepositoryConfigurationDelegate - Multiple Spring Data modules found, entering strict repository configuration mode
24-11-26.15:12:45.134 [main ] INFO RepositoryConfigurationDelegate - Bootstrapping Spring Data Redis repositories in DEFAULT mode.
24-11-26.15:12:45.215 [main ] INFO RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 66 ms. Found 0 Redis repository interfaces.
24-11-26.15:12:48.483 [main ] INFO Version - Redisson 3.23.4
24-11-26.15:12:50.070 [redisson-netty-2-4] INFO MasterPubSubConnectionPool - 1 connections initialized for 127.0.0.1/127.0.0.1:16379
24-11-26.15:12:50.083 [redisson-netty-2-13] INFO MasterConnectionPool - 5 connections initialized for 127.0.0.1/127.0.0.1:16379
24-11-26.15:12:53.096 [main ] INFO EndpointLinksResolver - Exposing 1 endpoint(s) beneath base path '/actuator'
24-11-26.15:12:53.192 [main ] INFO RaffleActivityOrderDaoTest - Started RaffleActivityOrderDaoTest in 9.965 seconds (JVM running for 11.124)
24-11-26.15:12:53.489 [main ] INFO HikariDataSource - Retail_HikariCP - Starting...
24-11-26.15:12:53.809 [main ] INFO HikariDataSource - Retail_HikariCP - Start completed.
24-11-26.15:14:17.824 [main ] INFO RaffleActivityOrderDaoTest - Starting RaffleActivityOrderDaoTest using Java 1.8.0_412 on zhaoyongfeng with PID 13588 (started by zhaoyongfeng in C:\Users\31126\Desktop\bigmarket-lite\bigmarket-app)
24-11-26.15:14:17.825 [main ] INFO RaffleActivityOrderDaoTest - The following 1 profile is active: "dev"
24-11-26.15:14:19.064 [main ] INFO RepositoryConfigurationDelegate - Multiple Spring Data modules found, entering strict repository configuration mode
24-11-26.15:14:19.068 [main ] INFO RepositoryConfigurationDelegate - Bootstrapping Spring Data Redis repositories in DEFAULT mode.
24-11-26.15:14:19.151 [main ] INFO RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 65 ms. Found 0 Redis repository interfaces.
24-11-26.15:14:22.155 [main ] INFO Version - Redisson 3.23.4
24-11-26.15:14:23.698 [redisson-netty-2-4] INFO MasterPubSubConnectionPool - 1 connections initialized for 127.0.0.1/127.0.0.1:16379
24-11-26.15:14:23.711 [redisson-netty-2-13] INFO MasterConnectionPool - 5 connections initialized for 127.0.0.1/127.0.0.1:16379
24-11-26.15:14:26.961 [main ] INFO EndpointLinksResolver - Exposing 1 endpoint(s) beneath base path '/actuator'
24-11-26.15:14:27.061 [main ] INFO RaffleActivityOrderDaoTest - Started RaffleActivityOrderDaoTest in 9.806 seconds (JVM running for 11.021)
24-11-26.15:14:27.433 [main ] INFO HikariDataSource - Retail_HikariCP - Starting...
24-11-26.15:14:27.805 [main ] INFO HikariDataSource - Retail_HikariCP - Start completed.
24-11-26.15:14:28.009 [main ] INFO RaffleActivityOrderDaoTest - 测试结果:[]
24-11-26.15:14:41.387 [main ] INFO RaffleActivityOrderDaoTest - Starting RaffleActivityOrderDaoTest using Java 1.8.0_412 on zhaoyongfeng with PID 14992 (started by zhaoyongfeng in C:\Users\31126\Desktop\bigmarket-lite\bigmarket-app)
24-11-26.15:14:41.389 [main ] INFO RaffleActivityOrderDaoTest - The following 1 profile is active: "dev"
24-11-26.15:14:42.536 [main ] INFO RepositoryConfigurationDelegate - Multiple Spring Data modules found, entering strict repository configuration mode
24-11-26.15:14:42.540 [main ] INFO RepositoryConfigurationDelegate - Bootstrapping Spring Data Redis repositories in DEFAULT mode.
24-11-26.15:14:42.620 [main ] INFO RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 64 ms. Found 0 Redis repository interfaces.
24-11-26.15:14:45.528 [main ] INFO Version - Redisson 3.23.4
24-11-26.15:14:47.077 [redisson-netty-2-5] INFO MasterPubSubConnectionPool - 1 connections initialized for 127.0.0.1/127.0.0.1:16379
24-11-26.15:14:47.090 [redisson-netty-2-13] INFO MasterConnectionPool - 5 connections initialized for 127.0.0.1/127.0.0.1:16379
24-11-26.15:14:49.879 [main ] INFO EndpointLinksResolver - Exposing 1 endpoint(s) beneath base path '/actuator'
24-11-26.15:14:49.968 [main ] INFO RaffleActivityOrderDaoTest - Started RaffleActivityOrderDaoTest in 9.091 seconds (JVM running for 10.251)
24-11-26.15:14:50.276 [main ] INFO HikariDataSource - Retail_HikariCP - Starting...
24-11-26.15:14:50.591 [main ] INFO HikariDataSource - Retail_HikariCP - Start completed.
24-11-26.15:14:50.802 [main ] INFO RaffleActivityOrderDaoTest - 测试结果:[{"activityId":100301,"activityName":"测试活动","orderId":"88675199","orderTime":1709954149000,"state":"not_used","strategyId":100006,"userId":"xiaofuge"},{"activityId":100301,"activityName":"测试活动","orderId":"382877096957","orderTime":1709965454000,"state":"not_used","strategyId":100006,"userId":"xiaofuge"},{"activityId":100301,"activityName":"测试活动","orderId":"832133072786","orderTime":1732605173000,"state":"not_used","strategyId":100006,"userId":"xiaofuge"}]
此差异已折叠。
......@@ -82,7 +82,14 @@
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>cn.bugstack.middleware</groupId>
<artifactId>db-router-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!-- 工程模块;启动依赖 trigger->domain, infrastructure-->
<dependency>
<groupId>cn.bugstack</groupId>
......
......@@ -13,7 +13,18 @@ app:
api-version: v1
# 跨域,开发阶段可以设置为 * 不限制
cross-origin: '*'
# Spring 配置;rabbitmq
spring:
rabbitmq:
addresses: 127.0.0.1
port: 5672
username: admin
password: admin
listener:
simple:
prefetch: 1 # 每次投递n个消息,消费完在投递n个
topic:
activity_sku_stock_zero: activity_sku_stock_zero
# 线程池配置
thread:
pool:
......@@ -26,13 +37,39 @@ thread:
policy: CallerRunsPolicy
# 数据库配置;启动时配置数据库资源信息
spring:
# 多数据源路由配置,库数量 * 表数量 为2的次幂,如2库4表
# mysql 5.x 配置 driver-class-name: com.mysql.jdbc.Driver mysql-connector-java 5.1.34
# mysql 8.x 配置 driver-class-name: com.mysql.cj.jdbc.Driver mysql-connector-java 8.0.22
mini-db-router:
jdbc:
datasource:
dbCount: 2
tbCount: 4
default: db00
routerKey: userId
list: db01,db02
db00:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:13306/big_market?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&serverTimezone=UTC&useSSL=true
username: root
password: 123456
url: jdbc:mysql://127.0.0.1:13306/big_market?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&serverTimezone=UTC&useSSL=true
type-class-name: com.zaxxer.hikari.HikariDataSource
pool:
pool-name: Retail_HikariCP
minimum-idle: 15 #最小空闲连接数量
idle-timeout: 180000 #空闲连接存活最大时间,默认600000(10分钟)
maximum-pool-size: 25 #连接池最大连接数,默认是10
auto-commit: true #此属性控制从池返回的连接的默认自动提交行为,默认值:true
max-lifetime: 1800000 #此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟
connection-timeout: 30000 #数据库连接超时时间,默认30秒,即30000
connection-test-query: SELECT 1
db01:
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
url: jdbc:mysql://127.0.0.1:13306/big_market_01?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&serverTimezone=UTC&useSSL=true
username: root
password: 123456
type-class-name: com.zaxxer.hikari.HikariDataSource
pool:
pool-name: Retail_HikariCP
minimum-idle: 15 #最小空闲连接数量
idle-timeout: 180000 #空闲连接存活最大时间,默认600000(10分钟)
......@@ -41,7 +78,22 @@ spring:
max-lifetime: 1800000 #此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟
connection-timeout: 30000 #数据库连接超时时间,默认30秒,即30000
connection-test-query: SELECT 1
type: com.zaxxer.hikari.HikariDataSource
db02:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:13306/big_market_02?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&serverTimezone=UTC&useSSL=true
username: root
password: 123456
type-class-name: com.zaxxer.hikari.HikariDataSource
pool:
pool-name: Retail_HikariCP
minimum-idle: 15 #最小空闲连接数量
idle-timeout: 180000 #空闲连接存活最大时间,默认600000(10分钟)
maximum-pool-size: 25 #连接池最大连接数,默认是10
auto-commit: true #此属性控制从池返回的连接的默认自动提交行为,默认值:true
max-lifetime: 1800000 #此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟
connection-timeout: 30000 #数据库连接超时时间,默认30秒,即30000
connection-test-query: SELECT 1
#Mybatis配置
mybatis:
mapper-locations: classpath:/mybatis/mapper/*.xml
......@@ -52,7 +104,7 @@ redis:
sdk:
config:
host: 127.0.0.1
port: 6379
port: 16379
pool-size: 10
min-idle-size: 5
idle-timeout: 30000
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.bugstack.infrastructure.persistent.dao.IRaffleActivityAccountFlowDao">
<resultMap id="dataMap" type="cn.bugstack.infrastructure.persistent.po.RaffleActivityAccountFlow">
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="activity_id" property="activityId"/>
<result column="total_count" property="totalCount"/>
<result column="day_count" property="dayCount"/>
<result column="month_count" property="monthCount"/>
<result column="flow_id" property="flowId"/>
<result column="flow_channel" property="flowChannel"/>
<result column="biz_id" property="bizId"/>
<result column="create_time" property="createTime"/>
<result column="update_time" property="updateTime"/>
</resultMap>
</mapper>
\ No newline at end of file
......@@ -10,7 +10,6 @@
<result column="award_desc" property="awardDesc"/>
<result column="create_time" property="createTime"/>
<result column="update_time" property="updateTime"/>
</resultMap>
<select id="queryAwardList" resultMap="dataMap">
......@@ -19,5 +18,4 @@
limit 10
</select>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.bugstack.infrastructure.persistent.dao.IRaffleActivityAccountDao">
<resultMap id="dataMap" type="cn.bugstack.infrastructure.persistent.po.RaffleActivityAccount">
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="activity_id" property="activityId"/>
<result column="total_count" property="totalCount"/>
<result column="total_count_surplus" property="totalCountSurplus"/>
<result column="day_count" property="dayCount"/>
<result column="day_count_surplus" property="dayCountSurplus"/>
<result column="month_count" property="monthCount"/>
<result column="month_count_surplus" property="monthCountSurplus"/>
<result column="create_time" property="createTime"/>
<result column="update_time" property="updateTime"/>
</resultMap>
<insert id="insert" parameterType="cn.bugstack.infrastructure.persistent.po.RaffleActivityAccount">
insert into raffle_activity_account
(user_id, activity_id, total_count, total_count_surplus, day_count,
day_count_surplus, month_count, month_count_surplus, create_time, update_time)
values
(#{userId}, #{activityId}, #{totalCount}, #{totalCountSurplus}, #{dayCount},
#{dayCountSurplus}, #{monthCount}, #{monthCountSurplus}, now(), now())
</insert>
<update id="updateAccountQuota" parameterType="cn.bugstack.infrastructure.persistent.po.RaffleActivityAccount">
update raffle_activity_account
set
total_count = total_count + #{totalCount},
total_count_surplus = total_count_surplus + #{totalCountSurplus},
day_count_surplus = day_count_surplus + #{dayCountSurplus},
day_count = day_count + #{dayCount},
month_count_surplus = month_count_surplus + #{monthCountSurplus},
month_count = month_count + #{monthCount},
update_time = now()
where user_id = #{userId} and activity_id = #{activityId}
</update>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.bugstack.infrastructure.persistent.dao.IRaffleActivityCountDao">
<resultMap id="dataMap" type="cn.bugstack.infrastructure.persistent.po.RaffleActivityCount">
<id column="id" property="id"/>
<result column="activity_count_id" property="activityCountId"/>
<result column="total_count" property="totalCount"/>
<result column="day_count" property="dayCount"/>
<result column="month_count" property="monthCount"/>
<result column="create_time" property="createTime"/>
<result column="update_time" property="updateTime"/>
</resultMap>
<select id="queryRaffleActivityCountByActivityCountId" parameterType="java.lang.Long" resultMap="dataMap">
select activity_count_id, total_count, day_count, month_count
from raffle_activity_count
where activity_count_id = #{activityCountId}
</select>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.bugstack.infrastructure.persistent.dao.IRaffleActivityDao">
<resultMap id="dataMap" type="cn.bugstack.infrastructure.persistent.po.RaffleActivity">
<id column="id" property="id"/>
<result column="activity_id" property="activityId"/>
<result column="activity_name" property="activityName"/>
<result column="activity_desc" property="activityDesc"/>
<result column="begin_date_time" property="beginDateTime"/>
<result column="end_date_time" property="endDateTime"/>
<result column="strategy_id" property="strategyId"/>
<result column="state" property="state"/>
<result column="create_time" property="createTime"/>
<result column="update_time" property="updateTime"/>
</resultMap>
<select id="queryRaffleActivityByActivityId" parameterType="java.lang.Long" resultMap="dataMap">
select activity_id, activity_name, activity_desc, begin_date_time, end_date_time, strategy_id, state
from raffle_activity
where activity_id = #{activityId}
</select>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.bugstack.infrastructure.persistent.dao.IRaffleActivityOrderDao">
<resultMap id="dataMap" type="cn.bugstack.infrastructure.persistent.po.RaffleActivityOrder">
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="sku" property="sku"/>
<result column="activity_id" property="activityId"/>
<result column="activity_name" property="activityName"/>
<result column="strategy_id" property="strategyId"/>
<result column="order_id" property="orderId"/>
<result column="order_time" property="orderTime"/>
<result column="total_count" property="totalCount"/>
<result column="day_count" property="dayCount"/>
<result column="month_count" property="monthCount"/>
<result column="state" property="state"/>
<result column="out_business_no" property="outBusinessNo"/>
<result column="create_time" property="createTime"/>
<result column="update_time" property="updateTime"/>
</resultMap>
<insert id="insert" parameterType="cn.bugstack.infrastructure.persistent.po.RaffleActivityOrder">
insert into raffle_activity_order
(user_id, sku, activity_id, activity_name, strategy_id, order_id, order_time, state, out_business_no, total_count, day_count, month_count,
create_time, update_time)
values
(#{userId}, #{sku}, #{activityId}, #{activityName}, #{strategyId}, #{orderId}, #{orderTime}, #{state}, #{outBusinessNo}, #{totalCount}, #{dayCount}, #{monthCount},
now(), now())
</insert>
<select id="queryRaffleActivityOrderByUserId" parameterType="java.lang.String" resultMap="dataMap">
select user_id, sku, activity_id, activity_name, strategy_id, order_id, order_time, state
from raffle_activity_order
where user_id = #{userId}
</select>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.bugstack.infrastructure.persistent.dao.IRaffleActivitySkuDao">
<resultMap id="dataMap" type="cn.bugstack.infrastructure.persistent.po.RaffleActivitySku">
<result column="sku" property="sku"/>
<result column="activity_id" property="activityId"/>
<result column="activity_count_id" property="activityCountId"/>
<result column="stock_count" property="stockCount"/>
<result column="stock_count_surplus" property="stockCountSurplus"/>
<result column="create_time" property="createTime"/>
<result column="update_time" property="updateTime"/>
</resultMap>
<select id="queryActivitySku" parameterType="java.lang.Long" resultMap="dataMap">
select sku, activity_id, activity_count_id, stock_count, stock_count_surplus
from raffle_activity_sku
where sku = #{sku}
</select>
<update id="updateActivitySkuStock" parameterType="java.lang.Long">
update raffle_activity_sku
set stock_count_surplus = stock_count_surplus - 1, update_time = now()
where sku = #{sku} and stock_count_surplus > 0
</update>
<update id="clearActivitySkuStock" parameterType="java.lang.Long">
update raffle_activity_sku
set stock_count_surplus = 0, update_time = now()
where sku = #{sku}
</update>
</mapper>
......@@ -17,7 +17,6 @@
<result column="update_time" property="updateTime"/>
</resultMap>
<select id="queryStrategyAwardList" resultMap="dataMap">
select strategy_id
from strategy_award
......@@ -48,7 +47,4 @@
where strategy_id = #{strategyId} and award_id = #{awardId}
</select>
</mapper>
package cn.bugstack.test.domain.activity;
import cn.bugstack.domain.activity.model.entity.SkuRechargeEntity;
import cn.bugstack.domain.activity.service.IRaffleOrder;
import cn.bugstack.domain.activity.service.armory.IActivityArmory;
import cn.bugstack.types.exception.AppException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.util.concurrent.CountDownLatch;
/**
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 抽奖活动订单单测
* @create 2024-03-16 11:51
*/
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class RaffleOrderTest {
@Resource
private IRaffleOrder raffleOrder;
@Resource
private IActivityArmory activityArmory;
@Before
public void setUp() {
log.info("装配活动:{}", activityArmory.assembleActivitySku(9011L));
}
@Test
public void test_createSkuRechargeOrder_duplicate() {
SkuRechargeEntity skuRechargeEntity = new SkuRechargeEntity();
skuRechargeEntity.setUserId("xiaofuge");
skuRechargeEntity.setSku(9011L);
// outBusinessNo 作为幂等仿重使用,同一个业务单号2次使用会抛出索引冲突 Duplicate entry '700091009111' for key 'uq_out_business_no' 确保唯一性。
skuRechargeEntity.setOutBusinessNo("700091009121");
String orderId = raffleOrder.createSkuRechargeOrder(skuRechargeEntity);
log.info("测试结果:{}", orderId);
}
/**
* 测试库存消耗和最终一致更新
* 1. raffle_activity_sku 库表库存可以设置20个
* 2. 清空 redis 缓存 flushall
* 3. for 循环20次,消耗完库存,最终数据库剩余库存为0
*/
@Test
public void test_createSkuRechargeOrder() throws InterruptedException {
for (int i = 0; i < 20; i++) {
try {
SkuRechargeEntity skuRechargeEntity = new SkuRechargeEntity();
skuRechargeEntity.setUserId("xiaofuge");
skuRechargeEntity.setSku(9011L);
// outBusinessNo 作为幂等仿重使用,同一个业务单号2次使用会抛出索引冲突 Duplicate entry '700091009111' for key 'uq_out_business_no' 确保唯一性。
skuRechargeEntity.setOutBusinessNo(RandomStringUtils.randomNumeric(12));
String orderId = raffleOrder.createSkuRechargeOrder(skuRechargeEntity);
log.info("测试结果:{}", orderId);
} catch (AppException e) {
log.warn(e.getInfo());
}
}
new CountDownLatch(1).await();
}
}
package cn.bugstack.test.domain;
package cn.bugstack.test.domain.strategy;
import cn.bugstack.domain.strategy.service.armory.IStrategyArmory;
import cn.bugstack.domain.strategy.service.rule.chain.ILogicChain;
......
package cn.bugstack.test.domain;
package cn.bugstack.test.domain.strategy;
import cn.bugstack.domain.strategy.model.valobj.*;
import cn.bugstack.domain.strategy.service.rule.tree.factory.DefaultTreeFactory;
......
package cn.bugstack.test.domain;
package cn.bugstack.test.domain.strategy;
import cn.bugstack.domain.strategy.model.entity.RaffleAwardEntity;
import cn.bugstack.domain.strategy.model.entity.RaffleFactorEntity;
import cn.bugstack.domain.strategy.IRaffleStrategy;
import cn.bugstack.domain.strategy.service.IRaffleStock;
import cn.bugstack.domain.strategy.service.IRaffleStrategy;
import cn.bugstack.domain.strategy.model.valobj.StrategyAwardStockKeyVO;
import cn.bugstack.domain.strategy.IRaffleStock;
import cn.bugstack.domain.strategy.service.armory.IStrategyArmory;
import cn.bugstack.domain.strategy.service.rule.chain.impl.RuleWeightLogicChain;
import cn.bugstack.domain.strategy.service.rule.tree.impl.RuleLockLogicTreeNode;
......
package cn.bugstack.test.domain;
package cn.bugstack.test.domain.strategy;
import cn.bugstack.domain.strategy.service.armory.IStrategyArmory;
import lombok.extern.slf4j.Slf4j;
......
package cn.bugstack.test.domain;
package cn.bugstack.test.domain.strategy;
import cn.bugstack.domain.strategy.service.armory.IStrategyArmory;
import cn.bugstack.domain.strategy.service.armory.IStrategyDispatch;
......
package cn.bugstack.test.infrastructure;
import cn.bugstack.infrastructure.persistent.dao.IRaffleActivityDao;
import cn.bugstack.infrastructure.persistent.po.RaffleActivity;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
/**
* @ClassName: RaffleActivityDaoTest
* @Description:
* @Author: zhaoyongfeng
* @Date: 2024/11/26 14:41
*/
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class RaffleActivityDaoTest {
@Resource
private IRaffleActivityDao raffleActivityDao;
@Test
public void test_queryRaffleActivityByActivityId() {
RaffleActivity raffleActivity = raffleActivityDao.queryRaffleActivityByActivityId(100301L);
log.info("测试结果:{}", JSON.toJSONString(raffleActivity));
}
}
package cn.bugstack.test.infrastructure;
import cn.bugstack.infrastructure.persistent.dao.IRaffleActivityOrderDao;
import cn.bugstack.infrastructure.persistent.po.RaffleActivityOrder;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.RandomStringUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
/**
* @ClassName: RaffleActivityOrderDaoTest
* @Description:
* @Author: zhaoyongfeng
* @Date: 2024/11/26 14:46
*/
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class RaffleActivityOrderDaoTest {
@Resource
private IRaffleActivityOrderDao raffleActivityOrderDao;
@Test
public void test_insert() {
RaffleActivityOrder raffleActivityOrder = new RaffleActivityOrder();
raffleActivityOrder.setUserId("xiaofuge");
raffleActivityOrder.setActivityId(100301L);
raffleActivityOrder.setActivityName("测试活动");
raffleActivityOrder.setStrategyId(100006L);
raffleActivityOrder.setOrderId(RandomStringUtils.randomNumeric(12));
raffleActivityOrder.setOrderTime(new Date());
raffleActivityOrder.setState("not_used");
// 插入数据
raffleActivityOrderDao.insert(raffleActivityOrder);
}
@Test
public void test_queryRaffleActivityOrderByUserId() {
String userId = "xiaofuge";
List<RaffleActivityOrder> raffleActivityOrders = raffleActivityOrderDao.queryRaffleActivityOrderByUserId(userId);
log.info("测试结果:{}", JSON.toJSONString(raffleActivityOrders));
}
}
package cn.bugstack.domain.activity.event;
import cn.bugstack.types.event.BaseEvent;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.util.Date;
/**
* @author zhaoyongfeng
* @description 活动sku库存清空消息
* @create 2024-03-30 12:43
*/
@Component
public class ActivitySkuStockZeroMessageEvent extends BaseEvent<Long> {
@Value("${spring.rabbitmq.topic.activity_sku_stock_zero}")
private String topic;
@Override
public EventMessage<Long> buildEventMessage(Long sku) {
return EventMessage.<Long>builder()
.id(RandomStringUtils.randomNumeric(11))
.timestamp(new Date())
.data(sku)
.build();
}
@Override
public String topic() {
return topic;
}
}
package cn.bugstack.domain.activity.model.aggregate;
import cn.bugstack.domain.activity.model.entity.ActivityAccountEntity;
import cn.bugstack.domain.activity.model.entity.ActivityOrderEntity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author zhaoyongfeng
* @description 下单聚合对象
* @create 2024-03-16 10:32
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class CreateOrderAggregate {
/**
* 用户ID
*/
private String userId;
/**
* 活动ID
*/
private Long activityId;
/**
* 增加;总次数
*/
private Integer totalCount;
/**
* 增加;日次数
*/
private Integer dayCount;
/**
* 增加;月次数
*/
private Integer monthCount;
/**
* 活动订单实体
*/
private ActivityOrderEntity activityOrderEntity;
}
package cn.bugstack.domain.activity.model.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author zhaoyongfeng
* @description 活动账户实体对象
* @create 2024-03-16 10:43
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ActivityAccountEntity {
/**
* 用户ID
*/
private String userId;
/**
* 活动ID
*/
private Long activityId;
/**
* 总次数
*/
private Integer totalCount;
/**
* 总次数-剩余
*/
private Integer totalCountSurplus;
/**
* 日次数
*/
private Integer dayCount;
/**
* 日次数-剩余
*/
private Integer dayCountSurplus;
/**
* 月次数
*/
private Integer monthCount;
/**
* 月次数-剩余
*/
private Integer monthCountSurplus;
}
package cn.bugstack.domain.activity.model.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author zhaoyongfeng
* @description 活动次数实体对象
* @create 2024-03-16 11:19
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ActivityCountEntity {
/**
* 活动次数编号
*/
private Long activityCountId;
/**
* 总次数
*/
private Integer totalCount;
/**
* 日次数
*/
private Integer dayCount;
/**
* 月次数
*/
private Integer monthCount;
}
package cn.bugstack.domain.activity.model.entity;
import cn.bugstack.domain.activity.model.valobj.ActivityStateVO;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* @author zhaoyongfeng
* @description 活动实体对象
* @create 2024-03-16 11:15
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ActivityEntity {
/**
* 活动ID
*/
private Long activityId;
/**
* 活动名称
*/
private String activityName;
/**
* 活动描述
*/
private String activityDesc;
/**
* 开始时间
*/
private Date beginDateTime;
/**
* 结束时间
*/
private Date endDateTime;
/**
* 活动参与次数配置
*/
private Long activityCountId;
/**
* 抽奖策略ID
*/
private Long strategyId;
/**
* 活动状态
*/
private ActivityStateVO state;
}
package cn.bugstack.domain.activity.model.entity;
import cn.bugstack.domain.activity.model.valobj.OrderStateVO;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* @author zhaoyongfeng
* @description 活动参与实体对象
* @create 2024-03-16 09:02
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ActivityOrderEntity {
/**
* 用户ID
*/
private String userId;
/**
* sku
*/
private Long sku;
/**
* 活动ID
*/
private Long activityId;
/**
* 活动名称
*/
private String activityName;
/**
* 抽奖策略ID
*/
private Long strategyId;
/**
* 订单ID
*/
private String orderId;
/**
* 下单时间
*/
private Date orderTime;
/**
* 总次数
*/
private Integer totalCount;
/**
* 日次数
*/
private Integer dayCount;
/**
* 月次数
*/
private Integer monthCount;
/**
* 订单状态
*/
private OrderStateVO state;
/**
* 业务仿重ID - 外部透传的,确保幂等
*/
private String outBusinessNo;
}
package cn.bugstack.domain.activity.model.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author zhaoyongfeng
* @description 活动sku实体对象
* @create 2024-03-16 10:29
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ActivitySkuEntity {
/** 商品sku */
private Long sku;
/** 活动ID */
private Long activityId;
/** 活动个人参数ID;在这个活动上,一个人可参与多少次活动(总、日、月) */
private Long activityCountId;
/** 库存总量 */
private Integer stockCount;
/** 剩余库存 */
private Integer stockCountSurplus;
}
package cn.bugstack.domain.activity.model.entity;
import lombok.Data;
/**
* @author zhaoyongfeng
* @description 活动商品充值实体对象
* @create 2024-03-23 09:11
*/
@Data
public class SkuRechargeEntity {
/** 用户ID */
private String userId;
/** 商品SKU - activity + activity count */
private Long sku;
/** 幂等业务单号,外部谁充值谁透传,这样来保证幂等(多次调用也能确保结果唯一,不会多次充值)。 */
private String outBusinessNo;
}
package cn.bugstack.domain.activity.model.valobj;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author zhaoyongfeng
* @description 活动sku库存 key 值对象
* @create 2024-03-30 09:44
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ActivitySkuStockKeyVO {
/** 商品sku */
private Long sku;
/** 活动ID */
private Long activityId;
}
package cn.bugstack.domain.activity.model.valobj;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author zhaoyongfeng
* @description 活动状态值对象
* @create 2024-03-16 11:16
*/
@Getter
@AllArgsConstructor
public enum ActivityStateVO {
create("create", "创建"),
open("open", "开启"),
close("close", "关闭"),
;
private final String code;
private final String desc;
}
package cn.bugstack.domain.activity.model.valobj;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
/**
* @author zhaoyongfeng
* @description 订单状态枚举值对象(用于描述对象属性的值,如枚举,不影响数据库操作的对象,无生命周期)
* @create 2024-03-16 10:34
*/
@Getter
@AllArgsConstructor
public enum OrderStateVO {
completed("completed", "完成");
private final String code;
private final String desc;
}
package cn.bugstack.domain.activity.repository;
import cn.bugstack.domain.activity.model.aggregate.CreateOrderAggregate;
import cn.bugstack.domain.activity.model.entity.ActivityCountEntity;
import cn.bugstack.domain.activity.model.entity.ActivityEntity;
import cn.bugstack.domain.activity.model.entity.ActivitySkuEntity;
import cn.bugstack.domain.activity.model.valobj.ActivitySkuStockKeyVO;
import java.util.Date;
/*
* @return
* @author zhaoyongfeng
* @description 活动仓储接口
*
* @param null
*/
public interface IActivityRepository {
ActivitySkuEntity queryActivitySku(Long sku);
ActivityEntity queryRaffleActivityByActivityId(Long activityId);
ActivityCountEntity queryRaffleActivityCountByActivityCountId(Long activityCountId);
void doSaveOrder(CreateOrderAggregate createOrderAggregate);
void cacheActivitySkuStockCount(String cacheKey, Integer stockCount);
boolean subtractionActivitySkuStock(Long sku, String cacheKey, Date endDateTime);
void activitySkuStockConsumeSendQueue(ActivitySkuStockKeyVO activitySkuStockKeyVO);
ActivitySkuStockKeyVO takeQueueValue();
void clearQueueValue();
void updateActivitySkuStock(Long sku);
void clearActivitySkuStock(Long sku);
}
package cn.bugstack.domain.activity.service;
import cn.bugstack.domain.activity.model.aggregate.CreateOrderAggregate;
import cn.bugstack.domain.activity.model.entity.*;
import cn.bugstack.domain.activity.repository.IActivityRepository;
import cn.bugstack.domain.activity.service.rule.IActionChain;
import cn.bugstack.domain.activity.service.rule.factory.DefaultActivityChainFactory;
import cn.bugstack.types.enums.ResponseCode;
import cn.bugstack.types.exception.AppException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
/*
* @return
* @author zhaoyongfeng
* @description 抽奖活动抽象类,定义标准的流程
*
* @param null
*/
@Slf4j
public abstract class AbstractRaffleActivity extends RaffleActivitySupport implements IRaffleOrder{
public AbstractRaffleActivity(IActivityRepository activityRepository, DefaultActivityChainFactory defaultActivityChainFactory) {
super(activityRepository, defaultActivityChainFactory);
}
@Override
public String createSkuRechargeOrder(SkuRechargeEntity skuRechargeEntity) {
// 1. 参数校验
String userId = skuRechargeEntity.getUserId();
Long sku = skuRechargeEntity.getSku();
String outBusinessNo = skuRechargeEntity.getOutBusinessNo();
if (null == sku || StringUtils.isBlank(userId) || StringUtils.isBlank(outBusinessNo)) {
throw new AppException(ResponseCode.ILLEGAL_PARAMETER.getCode(), ResponseCode.ILLEGAL_PARAMETER.getInfo());
}
// 2. 查询基础信息
// 2.1 通过sku查询活动信息
ActivitySkuEntity activitySkuEntity = queryActivitySku(sku);
// 2.2 查询活动信息
ActivityEntity activityEntity = queryRaffleActivityByActivityId(activitySkuEntity.getActivityId());
// 2.3 查询次数信息(用户在活动上可参与的次数)
ActivityCountEntity activityCountEntity = queryRaffleActivityCountByActivityCountId(activitySkuEntity.getActivityCountId());
// 3. 活动动作规则校验 「过滤失败则直接抛异常」
IActionChain actionChain = defaultActivityChainFactory.openActionChain();
actionChain.action(activitySkuEntity, activityEntity, activityCountEntity);
// 4. 构建订单聚合对象
CreateOrderAggregate createOrderAggregate = buildOrderAggregate(skuRechargeEntity, activitySkuEntity, activityEntity, activityCountEntity);
// 5. 保存订单
doSaveOrder(createOrderAggregate);
// 6. 返回单号
return createOrderAggregate.getActivityOrderEntity().getOrderId();
}
protected abstract CreateOrderAggregate buildOrderAggregate(SkuRechargeEntity skuRechargeEntity, ActivitySkuEntity activitySkuEntity, ActivityEntity activityEntity, ActivityCountEntity activityCountEntity);
protected abstract void doSaveOrder(CreateOrderAggregate createOrderAggregate);
}
package cn.bugstack.domain.activity.service;
import cn.bugstack.domain.activity.model.entity.SkuRechargeEntity;
/*
* @return
* @author zhaoyongfeng
* @description 抽奖活动订单接口
*
* @param null
*/
public interface IRaffleOrder {
/**
* 创建 sku 账户充值订单,给用户增加抽奖次数
* <p>
* 1. 在【打卡、签到、分享、对话、积分兑换】等行为动作下,创建出活动订单,给用户的活动账户【日、月】充值可用的抽奖次数。
* 2. 对于用户可获得的抽奖次数,比如首次进来就有一次,则是依赖于运营配置的动作,在前端页面上。用户点击后,可以获得一次抽奖次数。
*
* @param skuRechargeEntity 活动商品充值实体对象
* @return 活动ID
*/
String createSkuRechargeOrder(SkuRechargeEntity skuRechargeEntity);
}
package cn.bugstack.domain.activity.service;
import cn.bugstack.domain.activity.model.valobj.ActivitySkuStockKeyVO;
/*
* @return
* @author zhaoyongfeng
* @description 活动sku库存处理接口
*
* @param null
*/
public interface ISkuStock {
/**
* 获取活动sku库存消耗队列
*
* @return 奖品库存Key信息
* @throws InterruptedException 异常
*/
ActivitySkuStockKeyVO takeQueueValue() throws InterruptedException;
/**
* 清空队列
*/
void clearQueueValue();
/**
* 延迟队列 + 任务趋势更新活动sku库存
*
* @param sku 活动商品
*/
void updateActivitySkuStock(Long sku);
/**
* 缓存库存以消耗完毕,清空数据库库存
*
* @param sku 活动商品
*/
void clearActivitySkuStock(Long sku);
}
package cn.bugstack.domain.activity.service;
import cn.bugstack.domain.activity.model.aggregate.CreateOrderAggregate;
import cn.bugstack.domain.activity.model.entity.*;
import cn.bugstack.domain.activity.model.valobj.ActivitySkuStockKeyVO;
import cn.bugstack.domain.activity.model.valobj.OrderStateVO;
import cn.bugstack.domain.activity.repository.IActivityRepository;
import cn.bugstack.domain.activity.service.rule.factory.DefaultActivityChainFactory;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.stereotype.Service;
import java.util.Date;
/*
* @return
* @author zhaoyongfeng
* @description 抽奖活动服务
*
* @param null
*/
@Service
public class RaffleActivityService extends AbstractRaffleActivity implements ISkuStock{
public RaffleActivityService(IActivityRepository activityRepository, DefaultActivityChainFactory defaultActivityChainFactory) {
super(activityRepository, defaultActivityChainFactory);
}
@Override
protected CreateOrderAggregate buildOrderAggregate(SkuRechargeEntity skuRechargeEntity, ActivitySkuEntity activitySkuEntity, ActivityEntity activityEntity, ActivityCountEntity activityCountEntity) {
// 订单实体对象
ActivityOrderEntity activityOrderEntity = new ActivityOrderEntity();
activityOrderEntity.setUserId(skuRechargeEntity.getUserId());
activityOrderEntity.setSku(skuRechargeEntity.getSku());
activityOrderEntity.setActivityId(activityEntity.getActivityId());
activityOrderEntity.setActivityName(activityEntity.getActivityName());
activityOrderEntity.setStrategyId(activityEntity.getStrategyId());
// 公司里一般会有专门的雪花算法UUID服务,我们这里直接生成个12位就可以了。
activityOrderEntity.setOrderId(RandomStringUtils.randomNumeric(12));
activityOrderEntity.setOrderTime(new Date());
activityOrderEntity.setTotalCount(activityCountEntity.getTotalCount());
activityOrderEntity.setDayCount(activityCountEntity.getDayCount());
activityOrderEntity.setMonthCount(activityCountEntity.getMonthCount());
activityOrderEntity.setState(OrderStateVO.completed);
activityOrderEntity.setOutBusinessNo(skuRechargeEntity.getOutBusinessNo());
// 构建聚合对象
return CreateOrderAggregate.builder()
.userId(skuRechargeEntity.getUserId())
.activityId(activitySkuEntity.getActivityId())
.totalCount(activityCountEntity.getTotalCount())
.dayCount(activityCountEntity.getDayCount())
.monthCount(activityCountEntity.getMonthCount())
.activityOrderEntity(activityOrderEntity)
.build();
}
@Override
protected void doSaveOrder(CreateOrderAggregate createOrderAggregate) {
activityRepository.doSaveOrder(createOrderAggregate);
}
@Override
public ActivitySkuStockKeyVO takeQueueValue() throws InterruptedException {
return activityRepository.takeQueueValue();
}
@Override
public void clearQueueValue() {
activityRepository.clearQueueValue();
}
@Override
public void updateActivitySkuStock(Long sku) {
activityRepository.updateActivitySkuStock(sku);
}
@Override
public void clearActivitySkuStock(Long sku) {
activityRepository.clearActivitySkuStock(sku);
}
}
package cn.bugstack.domain.activity.service;
import cn.bugstack.domain.activity.model.entity.ActivityCountEntity;
import cn.bugstack.domain.activity.model.entity.ActivityEntity;
import cn.bugstack.domain.activity.model.entity.ActivitySkuEntity;
import cn.bugstack.domain.activity.repository.IActivityRepository;
import cn.bugstack.domain.activity.service.rule.factory.DefaultActivityChainFactory;
/*
* @return
* @author zhaoyongfeng
* @description 抽奖活动的支撑类
*
* @param null
*/
public class RaffleActivitySupport {
protected DefaultActivityChainFactory defaultActivityChainFactory;
protected IActivityRepository activityRepository;
public RaffleActivitySupport(IActivityRepository activityRepository, DefaultActivityChainFactory defaultActivityChainFactory) {
this.activityRepository = activityRepository;
this.defaultActivityChainFactory = defaultActivityChainFactory;
}
public ActivitySkuEntity queryActivitySku(Long sku) {
return activityRepository.queryActivitySku(sku);
}
public ActivityEntity queryRaffleActivityByActivityId(Long activityId) {
return activityRepository.queryRaffleActivityByActivityId(activityId);
}
public ActivityCountEntity queryRaffleActivityCountByActivityCountId(Long activityCountId) {
return activityRepository.queryRaffleActivityCountByActivityCountId(activityCountId);
}
}
package cn.bugstack.domain.activity.service.armory;
import cn.bugstack.domain.activity.model.entity.ActivitySkuEntity;
import cn.bugstack.domain.activity.model.valobj.ActivitySkuStockKeyVO;
import cn.bugstack.domain.activity.repository.IActivityRepository;
import cn.bugstack.domain.strategy.model.valobj.StrategyAwardStockKeyVO;
import cn.bugstack.types.common.Constants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 活动sku预热
* @create 2024-03-30 09:12
*/
@Slf4j
@Service
public class ActivityArmory implements IActivityArmory, IActivityDispatch {
@Resource
private IActivityRepository activityRepository;
@Override
public boolean assembleActivitySku(Long sku) {
// 预热活动sku库存
ActivitySkuEntity activitySkuEntity = activityRepository.queryActivitySku(sku);
cacheActivitySkuStockCount(sku, activitySkuEntity.getStockCount());
// 预热活动【查询时预热到缓存】
activityRepository.queryRaffleActivityByActivityId(activitySkuEntity.getActivityId());
// 预热活动次数【查询时预热到缓存】
activityRepository.queryRaffleActivityCountByActivityCountId(activitySkuEntity.getActivityCountId());
return true;
}
private void cacheActivitySkuStockCount(Long sku, Integer stockCount) {
String cacheKey = Constants.RedisKey.ACTIVITY_SKU_STOCK_COUNT_KEY + sku;
activityRepository.cacheActivitySkuStockCount(cacheKey, stockCount);
}
@Override
public boolean subtractionActivitySkuStock(Long sku, Date endDateTime) {
String cacheKey = Constants.RedisKey.ACTIVITY_SKU_STOCK_COUNT_KEY + sku;
return activityRepository.subtractionActivitySkuStock(sku, cacheKey, endDateTime);
}
}
package cn.bugstack.domain.activity.service.armory;
/**
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 活动装配预热
* @create 2024-03-30 09:09
*/
public interface IActivityArmory {
boolean assembleActivitySku(Long sku);
}
package cn.bugstack.domain.activity.service.armory;
import cn.bugstack.domain.activity.model.valobj.ActivitySkuStockKeyVO;
import java.util.Date;
/**
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 活动调度【扣减库存】
* @create 2024-03-30 09:28
*/
public interface IActivityDispatch {
/**
* 根据策略ID和奖品ID,扣减奖品缓存库存
*
* @param sku 互动SKU
* @param endDateTime 活动结束时间,根据结束时间设置加锁的key为结束时间
* @return 扣减结果
*/
boolean subtractionActivitySkuStock(Long sku, Date endDateTime);
}
package cn.bugstack.domain.activity.service.rule;
/**
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 下单规则责任链抽象类
* @create 2024-03-23 10:16
*/
public abstract class AbstractActionChain implements IActionChain {
private IActionChain next;
@Override
public IActionChain next() {
return next;
}
@Override
public IActionChain appendNext(IActionChain next) {
this.next = next;
return next;
}
}
package cn.bugstack.domain.activity.service.rule;
import cn.bugstack.domain.activity.model.entity.ActivityCountEntity;
import cn.bugstack.domain.activity.model.entity.ActivityEntity;
import cn.bugstack.domain.activity.model.entity.ActivitySkuEntity;
/**
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 下单规则过滤接口
* @create 2024-03-23 09:40
*/
public interface IActionChain extends IActionChainArmory {
boolean action(ActivitySkuEntity activitySkuEntity, ActivityEntity activityEntity, ActivityCountEntity activityCountEntity);
}
package cn.bugstack.domain.activity.service.rule;
/**
* @author Fuzhengwei bugstack.cn @小傅哥
* @description
* @create 2024-03-23 10:15
*/
public interface IActionChainArmory {
IActionChain next();
IActionChain appendNext(IActionChain next);
}
package cn.bugstack.domain.activity.service.rule.factory;
import cn.bugstack.domain.activity.service.rule.IActionChain;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.stereotype.Service;
import java.util.Map;
/**
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 责任链工厂
* @create 2024-03-23 10:30
*/
@Service
public class DefaultActivityChainFactory {
private final IActionChain actionChain;
/**
* 1. 通过构造函数注入。
* 2. Spring 可以自动注入 IActionChain 接口实现类到 map 对象中,key 就是 bean 的名字。
* 3. 活动下单动作的责任链是固定的,所以直接在构造函数中组装即可。
*/
public DefaultActivityChainFactory(Map<String, IActionChain> actionChainGroup) {
actionChain = actionChainGroup.get(ActionModel.activity_base_action.code);
actionChain.appendNext(actionChainGroup.get(ActionModel.activity_sku_stock_action.getCode()));
}
public IActionChain openActionChain() {
return this.actionChain;
}
@Getter
@AllArgsConstructor
public enum ActionModel {
activity_base_action("activity_base_action", "活动的库存、时间校验"),
activity_sku_stock_action("activity_sku_stock_action", "活动sku库存"),
;
private final String code;
private final String info;
}
}
package cn.bugstack.domain.activity.service.rule.impl;
import cn.bugstack.domain.activity.model.entity.ActivityCountEntity;
import cn.bugstack.domain.activity.model.entity.ActivityEntity;
import cn.bugstack.domain.activity.model.entity.ActivitySkuEntity;
import cn.bugstack.domain.activity.model.valobj.ActivityStateVO;
import cn.bugstack.domain.activity.service.rule.AbstractActionChain;
import cn.bugstack.types.enums.ResponseCode;
import cn.bugstack.types.exception.AppException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 活动规则过滤【日期、状态】
* @create 2024-03-23 10:23
*/
@Slf4j
@Component("activity_base_action")
public class ActivityBaseActionChain extends AbstractActionChain {
@Override
public boolean action(ActivitySkuEntity activitySkuEntity, ActivityEntity activityEntity, ActivityCountEntity activityCountEntity) {
log.info("活动责任链-基础信息【有效期、状态、库存(sku)】校验开始。sku:{} activityId:{}", activitySkuEntity.getSku(), activityEntity.getActivityId());
// 校验;活动状态
if (!ActivityStateVO.open.equals(activityEntity.getState())) {
throw new AppException(ResponseCode.ACTIVITY_STATE_ERROR.getCode(), ResponseCode.ACTIVITY_STATE_ERROR.getInfo());
}
// 校验;活动日期「开始时间 <- 当前时间 -> 结束时间」
Date currentDate = new Date();
if (activityEntity.getBeginDateTime().after(currentDate) || activityEntity.getEndDateTime().before(currentDate)) {
throw new AppException(ResponseCode.ACTIVITY_DATE_ERROR.getCode(), ResponseCode.ACTIVITY_DATE_ERROR.getInfo());
}
// 校验;活动sku库存 「剩余库存从缓存获取的」
if (activitySkuEntity.getStockCountSurplus() <= 0) {
throw new AppException(ResponseCode.ACTIVITY_SKU_STOCK_ERROR.getCode(), ResponseCode.ACTIVITY_SKU_STOCK_ERROR.getInfo());
}
return next().action(activitySkuEntity, activityEntity, activityCountEntity);
}
}
package cn.bugstack.domain.activity.service.rule.impl;
import cn.bugstack.domain.activity.model.entity.ActivityCountEntity;
import cn.bugstack.domain.activity.model.entity.ActivityEntity;
import cn.bugstack.domain.activity.model.entity.ActivitySkuEntity;
import cn.bugstack.domain.activity.model.valobj.ActivitySkuStockKeyVO;
import cn.bugstack.domain.activity.repository.IActivityRepository;
import cn.bugstack.domain.activity.service.armory.IActivityDispatch;
import cn.bugstack.domain.activity.service.rule.AbstractActionChain;
import cn.bugstack.types.enums.ResponseCode;
import cn.bugstack.types.exception.AppException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 商品库存规则节点
* @create 2024-03-23 10:25
*/
@Slf4j
@Component("activity_sku_stock_action")
public class ActivitySkuStockActionChain extends AbstractActionChain {
@Resource
private IActivityDispatch activityDispatch;
@Resource
private IActivityRepository activityRepository;
@Override
public boolean action(ActivitySkuEntity activitySkuEntity, ActivityEntity activityEntity, ActivityCountEntity activityCountEntity) {
log.info("活动责任链-商品库存处理【有效期、状态、库存(sku)】开始。sku:{} activityId:{}", activitySkuEntity.getSku(), activityEntity.getActivityId());
// 扣减库存
boolean status = activityDispatch.subtractionActivitySkuStock(activitySkuEntity.getSku(), activityEntity.getEndDateTime());
// true;库存扣减成功
if (status) {
log.info("活动责任链-商品库存处理【有效期、状态、库存(sku)】成功。sku:{} activityId:{}", activitySkuEntity.getSku(), activityEntity.getActivityId());
// 写入延迟队列,延迟消费更新库存记录
activityRepository.activitySkuStockConsumeSendQueue(ActivitySkuStockKeyVO.builder()
.sku(activitySkuEntity.getSku())
.activityId(activityEntity.getActivityId())
.build());
return true;
}
throw new AppException(ResponseCode.ACTIVITY_SKU_STOCK_ERROR.getCode(), ResponseCode.ACTIVITY_SKU_STOCK_ERROR.getInfo());
}
}
......@@ -6,12 +6,10 @@ import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @ClassName: AwardEntity
* @Description:
* @Author: zhaoyongfeng
* @Date: 2024/11/14 19:37
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 策略结果实体
* @create 2023-12-23 09:13
*/
@Data
@Builder
@AllArgsConstructor
......@@ -24,4 +22,3 @@ public class AwardEntity {
private Integer awardId;
}
......@@ -6,16 +6,16 @@ import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @ClassName: RaffleAwardEntity
* @Description: 抽奖奖品实体
* @Author: zhaoyongfeng
* @Date: 2024/11/17 16:25
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 抽奖奖品实体
* @create 2024-01-06 09:20
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class RaffleAwardEntity {
/** 奖品ID */
private Integer awardId;
/** 奖品配置信息 */
......
......@@ -4,25 +4,21 @@ import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.checkerframework.checker.units.qual.A;
/**
* @ClassName: RaffleFactorEntity
* @Description: 抽奖因子实体
* @Author: zhaoyongfeng
* @Date: 2024/11/17 16:26
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 抽奖因子实体
* @create 2024-01-06 09:20
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class RaffleFactorEntity {
/** 用户ID */
private String userId;
/** 策略ID */
private Long strategyId;
/** 奖品ID */
private Integer awardId;
}
......@@ -8,16 +8,16 @@ import lombok.NoArgsConstructor;
import java.math.BigDecimal;
/**
* @ClassName: StrategyAwardEntity
* @Description:
* @Author: zhaoyongfeng
* @Date: 2024/11/14 19:32
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 策略奖品实体
* @create 2023-12-23 10:48
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class StrategyAwardEntity {
/** 抽奖策略ID */
private Long strategyId;
/** 抽奖奖品ID - 内部流转使用 */
......@@ -35,5 +35,4 @@ public class StrategyAwardEntity {
/** 排序 */
private Integer sort;
}
......@@ -6,16 +6,16 @@ import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @ClassName: StrategyConditionEntity
* @Description:
* @Author: zhaoyongfeng
* @Date: 2024/11/14 19:37
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 策略条件实体
* @create 2023-12-23 09:10
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class StrategyConditionEntity {
/** 用户ID */
private String userId;
/** 策略ID */
......
......@@ -8,10 +8,9 @@ import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;
/**
* @ClassName: StrategyEntity
* @Description: 策略实体
* @Author: zhaoyongfeng
* @Date: 2024/11/15 23:35
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 策略实体
* @create 2023-12-31 15:24
*/
@Data
@Builder
......@@ -19,11 +18,17 @@ import org.apache.commons.lang3.StringUtils;
@NoArgsConstructor
public class StrategyEntity {
/** 抽奖策略ID */
/**
* 抽奖策略ID
*/
private Long strategyId;
/** 抽奖策略描述 */
/**
* 抽奖策略描述
*/
private String strategyDesc;
/** 抽奖规则模型 rule_weight,rule_blacklist */
/**
* 抽奖规则模型 rule_weight,rule_blacklist
*/
private String ruleModels;
public String[] ruleModels() {
......@@ -35,8 +40,9 @@ public class StrategyEntity {
String[] ruleModels = this.ruleModels();
if (null == ruleModels) return null;
for (String ruleModel : ruleModels) {
if("rule_weight".equals(ruleModel)) return ruleModel;
if ("rule_weight".equals(ruleModel)) return ruleModel;
}
return null;
}
}
......@@ -12,10 +12,9 @@ import java.util.List;
import java.util.Map;
/**
* @ClassName: StrategyRuleEntity
* @Description: 策略规则实体
* @Author: zhaoyongfeng
* @Date: 2024/11/15 23:36
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 策略规则实体
* @create 2023-12-31 15:32
*/
@Data
@Builder
......@@ -66,4 +65,5 @@ public class StrategyRuleEntity {
return resultMap;
}
}
......@@ -4,10 +4,9 @@ import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @ClassName: RuleLimitTypeVO
* @Description: 规则限定枚举值
* @Author: zhaoyongfeng
* @Date: 2024/11/20 12:37
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 规则限定枚举值
* @create 2024-01-27 12:27
*/
@Getter
@AllArgsConstructor
......@@ -25,4 +24,3 @@ public enum RuleLimitTypeVO {
private final String info;
}
package cn.bugstack.domain.strategy.model.valobj;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
/**
* @ClassName: RuleLogicCheckTypeVO
* @Description: 规则过滤校验类型值对象
* @Author: zhaoyongfeng
* @Date: 2024/11/17 16:24
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 规则过滤校验类型值对象
* @create 2024-01-06 11:10
*/
@Getter
@AllArgsConstructor
......@@ -23,4 +20,3 @@ public enum RuleLogicCheckTypeVO {
private final String info;
}
package cn.bugstack.domain.strategy.model.valobj;
import lombok.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @ClassName: RuleTreeNodeLineVO
* @Description: 规则树节点指向线对象。用于衔接 from->to 节点链路关系
* @Author: zhaoyongfeng
* @Date: 2024/11/20 12:37
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 规则树节点指向线对象。用于衔接 from->to 节点链路关系
* @create 2024-01-27 10:49
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class RuleTreeNodeLineVO {
/** 规则树ID */
private String treeId;
/** 规则Key节点 From */
......
......@@ -8,10 +8,9 @@ import lombok.NoArgsConstructor;
import java.util.List;
/**
* @ClassName: RuleTreeNodeVO
* @Description: 规则树节点对象
* @Author: zhaoyongfeng
* @Date: 2024/11/20 12:37
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 规则树节点对象
* @create 2024-01-27 10:48
*/
@Data
@Builder
......
......@@ -8,16 +8,16 @@ import lombok.NoArgsConstructor;
import java.util.Map;
/**
* @ClassName: RuleTreeVO
* @Description: 规则树对象【注意;不具有唯一ID,不需要改变数据库结果的对象,可以被定义为值对象】
* @Author: zhaoyongfeng
* @Date: 2024/11/20 12:37
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 规则树对象【注意;不具有唯一ID,不需要改变数据库结果的对象,可以被定义为值对象】
* @create 2024-01-27 10:45
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class RuleTreeVO {
/** 规则树ID */
private String treeId;
/** 规则树名称 */
......
package cn.bugstack.domain.strategy.model.valobj;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
/**
* @ClassName: StrategyAwardRuleModelVO
* @Description: 抽奖策略规则规则值对象;值对象,没有唯一ID,仅限于从数据库查询对象
* @Author: zhaoyongfeng
* @Date: 2024/11/21 0:05
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 抽奖策略规则规则值对象;值对象,没有唯一ID,仅限于从数据库查询对象
* @create 2024-01-13 09:30
*/
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class StrategyAwardRuleModelVO {
private String ruleModels;
}
}
......@@ -6,18 +6,19 @@ import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @ClassName: StrategyAwardStockKeyVO
* @Description:
* @Author: zhaoyongfeng
* @Date: 2024/11/21 13:20
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 策略奖品库存Key标识值对象
* @create 2024-02-09 11:24
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class StrategyAwardStockKeyVO {
//策略ID
// 策略ID
private Long strategyId;
// 奖品ID
private Integer awardId;
}
......@@ -10,11 +10,16 @@ import cn.bugstack.domain.strategy.model.valobj.StrategyAwardStockKeyVO;
import java.util.List;
import java.util.Map;
/**
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 策略服务仓储接口
* @create 2023-12-23 09:33
*/
public interface IStrategyRepository {
List<StrategyAwardEntity> queryStrategyAwardList(Long strategyId);
void storeStrategyAwardSearchRateTable(String key, int rateRange, Map<Integer, Integer> strategyAwardSearchRateTable);
void storeStrategyAwardSearchRateTable(String key, Integer rateRange, Map<Integer, Integer> strategyAwardSearchRateTable);
Integer getStrategyAwardAssemble(String key, Integer rateKey);
......@@ -26,35 +31,36 @@ public interface IStrategyRepository {
StrategyRuleEntity queryStrategyRule(Long strategyId, String ruleModel);
String queryStrategyRuleValue(Long strategyId, Integer awardId, String ruleModel);
String queryStrategyRuleValue(Long strategyId, String ruleModel);
String queryStrategyRuleValue(Long strategyId, Integer awardId, String ruleModel);
StrategyAwardRuleModelVO queryStrategyAwardRuleModelVO(Long strategyId, Integer awardId);
/**
* 根据规则树ID,查询树结构信息
*
* @param treeId 规则树ID
* @return 树结构信息
*/
RuleTreeVO queryRuleTreeVOByTreeId(String treeId);
/**
* 缓存奖品库存
*
* @param cacheKey key
* @param awardCount 库存值
*/
void cacheStrategyAwardCount(String cacheKey, Integer awardCount);
/**
* 缓存key,decr 方式扣减库存
*
* @param cacheKey 缓存Key
* @return 扣减结果
*/
Boolean subtractionAwardStock(String cacheKey);
/**
* 写入奖品库存消费队列
*
......@@ -75,5 +81,13 @@ public interface IStrategyRepository {
*/
void updateStrategyAwardStock(Long strategyId, Integer awardId);
/**
* 根据策略ID+奖品ID的唯一值组合,查询奖品信息
*
* @param strategyId 策略ID
* @param awardId 奖品ID
* @return 奖品信息
*/
StrategyAwardEntity queryStrategyAwardEntity(Long strategyId, Integer awardId);
}
package cn.bugstack.domain.strategy;
package cn.bugstack.domain.strategy.service;
import cn.bugstack.domain.strategy.model.entity.RaffleAwardEntity;
import cn.bugstack.domain.strategy.model.entity.RaffleFactorEntity;
......@@ -13,10 +13,9 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
/**
* @ClassName: AbstractRaffleStrategy
* @Description: 抽奖策略抽象类,定义抽奖的标准流程
* @Author: zhaoyongfeng
* @Date: 2024/11/17 16:14
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 抽奖策略抽象类,定义抽奖的标准流程
* @create 2024-01-06 09:26
*/
@Slf4j
public abstract class AbstractRaffleStrategy implements IRaffleStrategy {
......@@ -30,6 +29,7 @@ public abstract class AbstractRaffleStrategy implements IRaffleStrategy {
// 抽奖的决策树 -> 负责抽奖中到抽奖后的规则过滤,如抽奖到A奖品ID,之后要做次数的判断和库存的扣减等。
protected final DefaultTreeFactory defaultTreeFactory;
// 为什么 Spring 推荐使用构造注入;https://docs.spring.io/spring-framework/reference/core/beans/dependencies/factory-collaborators.html
public AbstractRaffleStrategy(IStrategyRepository repository, IStrategyDispatch strategyDispatch, DefaultChainFactory defaultChainFactory, DefaultTreeFactory defaultTreeFactory) {
this.repository = repository;
this.strategyDispatch = strategyDispatch;
......@@ -91,4 +91,3 @@ public abstract class AbstractRaffleStrategy implements IRaffleStrategy {
public abstract DefaultTreeFactory.StrategyAwardVO raffleLogicTree(String userId, Long strategyId, Integer awardId);
}
package cn.bugstack.domain.strategy;
package cn.bugstack.domain.strategy.service;
import cn.bugstack.domain.strategy.model.entity.StrategyAwardEntity;
import java.util.List;
/**
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 策略奖品接口
* @create 2024-02-14 16:44
*/
public interface IRaffleAward {
/**
......@@ -13,5 +18,5 @@ public interface IRaffleAward {
* @return 奖品列表
*/
List<StrategyAwardEntity> queryRaffleStrategyAwardList(Long strategyId);
}
}
package cn.bugstack.domain.strategy;
package cn.bugstack.domain.strategy.service;
import cn.bugstack.domain.strategy.model.valobj.StrategyAwardStockKeyVO;
/**
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 抽奖库存相关服务,获取库存消耗队列
* @create 2024-02-09 12:17
*/
public interface IRaffleStock {
/**
* 获取奖品库存消耗队列
*
......@@ -18,4 +24,5 @@ public interface IRaffleStock {
* @param awardId 奖品ID
*/
void updateStrategyAwardStock(Long strategyId, Integer awardId);
}
package cn.bugstack.domain.strategy;
package cn.bugstack.domain.strategy.service;
import cn.bugstack.domain.strategy.model.entity.RaffleAwardEntity;
import cn.bugstack.domain.strategy.model.entity.RaffleFactorEntity;
/**
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 抽奖策略接口
* @create 2024-01-06 09:19
*/
public interface IRaffleStrategy {
/**
......@@ -15,4 +19,3 @@ public interface IRaffleStrategy {
RaffleAwardEntity performRaffle(RaffleFactorEntity raffleFactorEntity);
}
package cn.bugstack.domain.strategy.service.armory;
/*
* @return
* @author zhaoyongfeng
/**
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 策略装配库(兵工厂),负责初始化策略计算
*
* @param null
* @create 2023-12-23 09:44
*/
public interface IStrategyArmory {
......@@ -16,5 +15,4 @@ public interface IStrategyArmory {
*/
boolean assembleLotteryStrategy(Long strategyId);
}
package cn.bugstack.domain.strategy.service.armory;
/*
* @return
* @author zhaoyongfeng
/**
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 策略抽奖调度
*
* @param null
* @create 2023-12-31 15:15
*/
public interface IStrategyDispatch {
/**
* 获取抽奖策略装配的随机结果
*
......
......@@ -12,19 +12,18 @@ import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.security.SecureRandom;
import java.util.*;
/**
* @ClassName: StrategyArmory
* @Description: 策略装配库(兵工厂),负责初始化策略计算
* @Author: zhaoyongfeng
* @Date: 2024/11/14 17:39
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 策略装配库(兵工厂),负责初始化策略计算
* @create 2023-12-23 10:02
*/
@Slf4j
@Service
public class StrategyArmoryDispatch implements IStrategyArmory,IStrategyDispatch{
public class StrategyArmoryDispatch implements IStrategyArmory, IStrategyDispatch {
@Resource
private IStrategyRepository repository;
......@@ -42,7 +41,6 @@ public class StrategyArmoryDispatch implements IStrategyArmory,IStrategyDispatch
cacheStrategyAwardCount(strategyId, awardId, awardCount);
}
// 3.1 默认装配配置【全量抽奖概率】
assembleLotteryStrategy(String.valueOf(strategyId), strategyAwardEntities);
......@@ -67,6 +65,7 @@ public class StrategyArmoryDispatch implements IStrategyArmory,IStrategyDispatch
return true;
}
/**
* 计算公式;
* 1. 找到范围内最小的概率值,比如 0.1、0.02、0.003,需要找到的值是 0.003
......@@ -74,7 +73,6 @@ public class StrategyArmoryDispatch implements IStrategyArmory,IStrategyDispatch
* 3. 那么「概率 * 1000」分别占比100个、20个、3个,总计是123个
* 4. 后续的抽奖就用123作为随机数的范围值,生成的值100个都是0.1概率的奖品、20个是概率0.02的奖品、最后是3个是0.003的奖品。
*/
private void assembleLotteryStrategy(String key, List<StrategyAwardEntity> strategyAwardEntities) {
// 1. 获取最小概率值
BigDecimal minAwardRate = strategyAwardEntities.stream()
......@@ -105,14 +103,16 @@ public class StrategyArmoryDispatch implements IStrategyArmory,IStrategyDispatch
shuffleStrategyAwardSearchRateTable.put(i, strategyAwardSearchRateTables.get(i));
}
// 6. 存放到 Redis
repository.storeStrategyAwardSearchRateTable(key, shuffleStrategyAwardSearchRateTable.size(), shuffleStrategyAwardSearchRateTable);
}
/**
* 转换计算,只根据小数位来计算。如【0.01返回100】、【0.009返回1000】、【0.0018返回10000】
*/
private double convert(double min) {
if(0 == min) return 1D;
double current = min;
double max = 1;
while (current < 1) {
......@@ -139,7 +139,7 @@ public class StrategyArmoryDispatch implements IStrategyArmory,IStrategyDispatch
// 分布式部署下,不一定为当前应用做的策略装配。也就是值不一定会保存到本应用,而是分布式应用,所以需要从 Redis 中获取。
int rateRange = repository.getRateRange(strategyId);
// 通过生成的随机值,获取概率值奖品查找表的结果
return repository.getStrategyAwardAssemble(String.valueOf(strategyId), new SecureRandom().nextInt(rateRange));
return repository.getStrategyAwardAssemble(String.valueOf(strategyId), secureRandom.nextInt(rateRange));
}
@Override
......@@ -148,8 +148,6 @@ public class StrategyArmoryDispatch implements IStrategyArmory,IStrategyDispatch
return getRandomAwardId(key);
}
@Override
public Integer getRandomAwardId(String key) {
// 分布式部署下,不一定为当前应用做的策略装配。也就是值不一定会保存到本应用,而是分布式应用,所以需要从 Redis 中获取。
......@@ -158,12 +156,10 @@ public class StrategyArmoryDispatch implements IStrategyArmory,IStrategyDispatch
return repository.getStrategyAwardAssemble(key, secureRandom.nextInt(rateRange));
}
@Override
public Boolean subtractionAwardStock(Long strategyId, Integer awardId) {
String cacheKey = Constants.RedisKey.STRATEGY_AWARD_COUNT_KEY + strategyId + Constants.UNDERLINE + awardId;
return repository.subtractionAwardStock(cacheKey);
}
}
package cn.bugstack.domain.strategy.service.raffle;
import cn.bugstack.domain.strategy.AbstractRaffleStrategy;
import cn.bugstack.domain.strategy.IRaffleAward;
import cn.bugstack.domain.strategy.IRaffleStock;
import cn.bugstack.domain.strategy.model.entity.StrategyAwardEntity;
import cn.bugstack.domain.strategy.model.valobj.RuleTreeVO;
import cn.bugstack.domain.strategy.model.valobj.StrategyAwardRuleModelVO;
import cn.bugstack.domain.strategy.model.valobj.StrategyAwardStockKeyVO;
import cn.bugstack.domain.strategy.repository.IStrategyRepository;
import cn.bugstack.domain.strategy.service.AbstractRaffleStrategy;
import cn.bugstack.domain.strategy.service.IRaffleAward;
import cn.bugstack.domain.strategy.service.IRaffleStock;
import cn.bugstack.domain.strategy.service.armory.IStrategyDispatch;
import cn.bugstack.domain.strategy.service.rule.chain.ILogicChain;
import cn.bugstack.domain.strategy.service.rule.chain.factory.DefaultChainFactory;
......@@ -19,10 +19,9 @@ import org.springframework.stereotype.Service;
import java.util.List;
/**
* @ClassName: DefaultRaffleStrategy
* @Description: 默认的抽奖策略实现
* @Author: zhaoyongfeng
* @Date: 2024/11/17 16:16
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 默认的抽奖策略实现
* @create 2024-01-06 11:46
*/
@Slf4j
@Service
......@@ -68,5 +67,3 @@ public class DefaultRaffleStrategy extends AbstractRaffleStrategy implements IRa
}
}
......@@ -3,13 +3,12 @@ package cn.bugstack.domain.strategy.service.rule.chain;
import lombok.extern.slf4j.Slf4j;
/**
* @ClassName: AbstractLogicChain
* @Description: 抽奖策略责任链,判断走那种抽奖策略。如;默认抽象、权重抽奖、黑名单抽奖
* @Author: zhaoyongfeng
* @Date: 2024/11/18 22:03
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 抽奖策略责任链,判断走那种抽奖策略。如;默认抽象、权重抽奖、黑名单抽奖
* @create 2024-01-20 09:37
*/
@Slf4j
public abstract class AbstractLogicChain implements ILogicChain {
public abstract class AbstractLogicChain implements ILogicChain{
private ILogicChain next;
......@@ -27,4 +26,3 @@ public abstract class AbstractLogicChain implements ILogicChain {
protected abstract String ruleModel();
}
......@@ -2,23 +2,20 @@ package cn.bugstack.domain.strategy.service.rule.chain;
import cn.bugstack.domain.strategy.service.rule.chain.factory.DefaultChainFactory;
/*
* @return
* @author zhaoyongfeng
/**
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 抽奖策略规则责任链接口
*
* @param null
* @create 2024-01-20 09:40
*/
public interface ILogicChain extends ILogicChainArmory{
public interface ILogicChain extends ILogicChainArmory, Cloneable {
/**
* 责任链接口
*
* @param userId 用户ID
* @param strategyId 策略ID
* @return 奖品ID
* @return 奖品对象
*/
DefaultChainFactory.StrategyAwardVO logic(String userId, Long strategyId);
}
package cn.bugstack.domain.strategy.service.rule.chain;
/*
* @return
* @author zhaoyongfeng
/**
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 责任链装配
*
* @param null
* @create 2024-01-20 11:53
*/
public interface ILogicChainArmory {
ILogicChain next();
ILogicChain appendNext(ILogicChain next);
}
......@@ -4,25 +4,31 @@ import cn.bugstack.domain.strategy.model.entity.StrategyEntity;
import cn.bugstack.domain.strategy.repository.IStrategyRepository;
import cn.bugstack.domain.strategy.service.rule.chain.ILogicChain;
import lombok.*;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @ClassName: DefaultLogicFactory
* @Description:
* @Author: zhaoyongfeng
* @Date: 2024/11/17 16:05
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 工厂
* @create 2024-01-20 10:54
*/
@Service
public class DefaultChainFactory {
private final Map<String, ILogicChain> logicChainGroup;
// 原型模式获取对象
private final ApplicationContext applicationContext;
// 仓储信息
protected IStrategyRepository repository;
// 存放策略链,策略ID -> 责任链
private final Map<Long, ILogicChain> strategyChainGroup;
public DefaultChainFactory(Map<String, ILogicChain> logicChainGroup, IStrategyRepository repository) {
this.logicChainGroup = logicChainGroup;
public DefaultChainFactory(ApplicationContext applicationContext, IStrategyRepository repository) {
this.applicationContext = applicationContext;
this.repository = repository;
this.strategyChainGroup = new ConcurrentHashMap<>();
}
/**
......@@ -32,33 +38,48 @@ public class DefaultChainFactory {
* @return LogicChain
*/
public ILogicChain openLogicChain(Long strategyId) {
ILogicChain cacheLogicChain = strategyChainGroup.get(strategyId);
if (null != cacheLogicChain) return cacheLogicChain;
StrategyEntity strategy = repository.queryStrategyEntityByStrategyId(strategyId);
String[] ruleModels = strategy.ruleModels();
// 如果未配置策略规则,则只装填一个默认责任链
if (null == ruleModels || 0 == ruleModels.length) return logicChainGroup.get("default");
if (null == ruleModels || 0 == ruleModels.length) {
ILogicChain ruleDefaultLogicChain = applicationContext.getBean(LogicModel.RULE_DEFAULT.getCode(), ILogicChain.class);
// 写入缓存
strategyChainGroup.put(strategyId, ruleDefaultLogicChain);
return ruleDefaultLogicChain;
}
// 按照配置顺序装填用户配置的责任链;rule_blacklist、rule_weight 「注意此数据从Redis缓存中获取,如果更新库表,记得在测试阶段手动处理缓存」
ILogicChain logicChain = logicChainGroup.get(ruleModels[0]);
ILogicChain logicChain = applicationContext.getBean(ruleModels[0], ILogicChain.class);
ILogicChain current = logicChain;
for (int i = 1; i < ruleModels.length; i++) {
ILogicChain nextChain = logicChainGroup.get(ruleModels[i]);
ILogicChain nextChain = applicationContext.getBean(ruleModels[i], ILogicChain.class);
current = current.appendNext(nextChain);
}
// 责任链的最后装填默认责任链
current.appendNext(logicChainGroup.get("default"));
current.appendNext(applicationContext.getBean(LogicModel.RULE_DEFAULT.getCode(), ILogicChain.class));
// 写入缓存
strategyChainGroup.put(strategyId, logicChain);
return logicChain;
}
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class StrategyAwardVO {
/** 抽奖奖品ID - 内部流转使用 */
/**
* 抽奖奖品ID - 内部流转使用
*/
private Integer awardId;
/** */
/**
*
*/
private String logicModel;
}
......@@ -76,5 +97,4 @@ public class DefaultChainFactory {
}
}
......@@ -5,19 +5,22 @@ import cn.bugstack.domain.strategy.service.rule.chain.AbstractLogicChain;
import cn.bugstack.domain.strategy.service.rule.chain.factory.DefaultChainFactory;
import cn.bugstack.types.common.Constants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @ClassName: RuleBackListLogicFilter
* @Description: 【抽奖前规则】黑名单用户过滤规则
* @Author: zhaoyongfeng
* @Date: 2024/11/17 16:07
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 黑名单责任链
* @create 2024-01-20 10:23
*/
@Slf4j
@Component("rule_blacklist")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class BackListLogicChain extends AbstractLogicChain {
@Resource
private IStrategyRepository repository;
......@@ -40,7 +43,6 @@ public class BackListLogicChain extends AbstractLogicChain {
.logicModel(ruleModel())
.build();
}
}
// 过滤其他责任链
......@@ -53,6 +55,4 @@ public class BackListLogicChain extends AbstractLogicChain {
return DefaultChainFactory.LogicModel.RULE_BLACKLIST.getCode();
}
}
......@@ -4,18 +4,20 @@ import cn.bugstack.domain.strategy.service.armory.IStrategyDispatch;
import cn.bugstack.domain.strategy.service.rule.chain.AbstractLogicChain;
import cn.bugstack.domain.strategy.service.rule.chain.factory.DefaultChainFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @ClassName: RuleLockLogicFilter
* @Description:
* @Author: zhaoyongfeng
* @Date: 2024/11/18 20:25
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 默认的责任链「作为最后一个链」
* @create 2024-01-20 10:06
*/
@Slf4j
@Component("default")
@Component("rule_default")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class DefaultLogicChain extends AbstractLogicChain {
@Resource
......@@ -29,7 +31,6 @@ public class DefaultLogicChain extends AbstractLogicChain {
.awardId(awardId)
.logicModel(ruleModel())
.build();
}
@Override
......
......@@ -6,18 +6,21 @@ import cn.bugstack.domain.strategy.service.rule.chain.AbstractLogicChain;
import cn.bugstack.domain.strategy.service.rule.chain.factory.DefaultChainFactory;
import cn.bugstack.types.common.Constants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.*;
/**
* @ClassName: RuleWeightLogicFilter
* @Description: 【抽奖前规则】根据抽奖权重返回可抽奖范围KEY
* @Author: zhaoyongfeng
* @Date: 2024/11/17 16:08
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 权重抽奖责任链
* @create 2024-01-20 10:38
*/
@Slf4j
@Component("rule_weight")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class RuleWeightLogicChain extends AbstractLogicChain {
@Resource
......@@ -29,10 +32,6 @@ public class RuleWeightLogicChain extends AbstractLogicChain {
// 根据用户ID查询用户抽奖消耗的积分值,本章节我们先写死为固定的值。后续需要从数据库中查询。
public Long userScore = 0L;
// 根据用户ID查询用户抽奖消耗的积分值,本章节我们先写死为固定的值。后续需要从数据库中查询。
/**
* 权重责任链过滤;
* 1. 权重规则格式;4000:102,103,104,105 5000:102,103,104,105,106,107 6000:102,103,104,105,106,107,108,109
......@@ -44,9 +43,12 @@ public class RuleWeightLogicChain extends AbstractLogicChain {
String ruleValue = repository.queryStrategyRuleValue(strategyId, ruleModel());
// 1. 根据用户ID查询用户抽奖消耗的积分值,本章节我们先写死为固定的值。后续需要从数据库中查询。
// 1. 解析权重规则值 4000:102,103,104,105 拆解为;4000 -> 4000:102,103,104,105 便于比对判断
Map<Long, String> analyticalValueGroup = getAnalyticalValue(ruleValue);
if (null == analyticalValueGroup || analyticalValueGroup.isEmpty()) return null;
if (null == analyticalValueGroup || analyticalValueGroup.isEmpty()) {
log.warn("抽奖责任链-权重告警【策略配置权重,但ruleValue未配置相应值】 userId: {} strategyId: {} ruleModel: {}", userId, strategyId, ruleModel());
return next().logic(userId, strategyId);
}
// 2. 转换Keys值,并默认排序
List<Long> analyticalSortedKeys = new ArrayList<>(analyticalValueGroup.keySet());
......@@ -60,7 +62,7 @@ public class RuleWeightLogicChain extends AbstractLogicChain {
* nextValue = analyticalSortedKeyValue;
* }
* }
*
* 星球伙伴 @慢慢来 ID 6267 提供
* Long nextValue = analyticalSortedKeys.stream()
* .filter(key -> userScore >= key)
* .max(Comparator.naturalOrder())
......@@ -82,7 +84,6 @@ public class RuleWeightLogicChain extends AbstractLogicChain {
.build();
}
// 5. 过滤其他责任链
log.info("抽奖责任链-权重放行 userId: {} strategyId: {} ruleModel: {}", userId, strategyId, ruleModel());
return next().logic(userId, strategyId);
......@@ -93,7 +94,6 @@ public class RuleWeightLogicChain extends AbstractLogicChain {
return DefaultChainFactory.LogicModel.RULE_WEIGHT.getCode();
}
private Map<Long, String> getAnalyticalValue(String ruleValue) {
String[] ruleValueGroups = ruleValue.split(Constants.SPACE);
Map<Long, String> ruleValueMap = new HashMap<>();
......@@ -113,5 +113,3 @@ public class RuleWeightLogicChain extends AbstractLogicChain {
}
}
......@@ -2,13 +2,13 @@ package cn.bugstack.domain.strategy.service.rule.tree;
import cn.bugstack.domain.strategy.service.rule.tree.factory.DefaultTreeFactory;
/*
* @return
* @author zhaoyongfeng
* @description 规则数接口
*
* @param null
/**
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 规则树接口
* @create 2024-01-27 11:14
*/
public interface ILogicTreeNode {
DefaultTreeFactory.TreeActionEntity logic(String userId, Long strategyId, Integer awardId, String ruleValue);
}
......@@ -5,19 +5,15 @@ import cn.bugstack.domain.strategy.model.valobj.RuleTreeVO;
import cn.bugstack.domain.strategy.service.rule.tree.ILogicTreeNode;
import cn.bugstack.domain.strategy.service.rule.tree.factory.engine.IDecisionTreeEngine;
import cn.bugstack.domain.strategy.service.rule.tree.factory.engine.impl.DecisionTreeEngine;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.*;
import org.springframework.stereotype.Service;
import java.util.Map;
/**
* @ClassName: DefaultTreeFactory
* @Description: 规则树工厂
* @Author: zhaoyongfeng
* @Date: 2024/11/20 12:03
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 规则树工厂
* @create 2024-01-27 11:28
*/
@Service
public class DefaultTreeFactory {
......@@ -28,11 +24,12 @@ public class DefaultTreeFactory {
this.logicTreeNodeGroup = logicTreeNodeGroup;
}
public IDecisionTreeEngine openLogicTree(RuleTreeVO ruleTreeVO){
return new DecisionTreeEngine(logicTreeNodeGroup,ruleTreeVO);
public IDecisionTreeEngine openLogicTree(RuleTreeVO ruleTreeVO) {
return new DecisionTreeEngine(logicTreeNodeGroup, ruleTreeVO);
}
/**
* 决策树动作
* 决策树个动作实习
*/
@Data
@Builder
......@@ -42,6 +39,7 @@ public class DefaultTreeFactory {
private RuleLogicCheckTypeVO ruleLogicCheckType;
private StrategyAwardVO strategyAwardVO;
}
@Data
@Builder
@AllArgsConstructor
......@@ -53,5 +51,4 @@ public class DefaultTreeFactory {
private String awardRuleValue;
}
}
......@@ -2,7 +2,13 @@ package cn.bugstack.domain.strategy.service.rule.tree.factory.engine;
import cn.bugstack.domain.strategy.service.rule.tree.factory.DefaultTreeFactory;
/**
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 规则树组合接口
* @create 2024-01-27 11:33
*/
public interface IDecisionTreeEngine {
DefaultTreeFactory.StrategyAwardVO process(String userId, Long strategyId, Integer awardId);
}
package cn.bugstack.domain.strategy.service.rule.tree.factory.engine.impl;
import cn.bugstack.domain.strategy.model.valobj.RuleLogicCheckTypeVO;
import cn.bugstack.domain.strategy.model.valobj.RuleTreeNodeLineVO;
import cn.bugstack.domain.strategy.model.valobj.RuleTreeNodeVO;
import cn.bugstack.domain.strategy.model.valobj.RuleTreeVO;
import cn.bugstack.domain.strategy.model.valobj.*;
import cn.bugstack.domain.strategy.service.rule.tree.ILogicTreeNode;
import cn.bugstack.domain.strategy.service.rule.tree.factory.DefaultTreeFactory;
import cn.bugstack.domain.strategy.service.rule.tree.factory.engine.IDecisionTreeEngine;
......@@ -13,10 +10,9 @@ import java.util.List;
import java.util.Map;
/**
* @ClassName: DecisionTreeEngine
* @Description: 决策树引擎
* @Author: zhaoyongfeng
* @Date: 2024/11/20 12:22
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 决策树引擎
* @create 2024-01-27 11:34
*/
@Slf4j
public class DecisionTreeEngine implements IDecisionTreeEngine {
......@@ -44,6 +40,7 @@ public class DecisionTreeEngine implements IDecisionTreeEngine {
// 获取决策节点
ILogicTreeNode logicTreeNode = logicTreeNodeGroup.get(ruleTreeNode.getRuleKey());
String ruleValue = ruleTreeNode.getRuleValue();
// 决策节点计算
DefaultTreeFactory.TreeActionEntity logicEntity = logicTreeNode.logic(userId, strategyId, awardId, ruleValue);
RuleLogicCheckTypeVO ruleLogicCheckTypeVO = logicEntity.getRuleLogicCheckType();
......
/**
* 聚合对象;
* 1. 聚合实体和值对象
* 2. 聚合是聚合的对象,和提供基础处理对象的方法。但不建议在聚合中引入仓储和接口来做过大的逻辑。而这些复杂的操作应该放到service中处理
* 3. 对象名称 XxxAggregate
*/
package cn.bugstack.domain.xxx.model.aggregate;
\ No newline at end of file
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册