提交 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: ...@@ -66,7 +66,7 @@ Action:
Consider defining a bean of type 'cn.bugstack.domain.strategy.IRaffleStock' in your configuration. 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 java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:98) 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.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:124)
...@@ -150,7 +150,7 @@ Action: ...@@ -150,7 +150,7 @@ Action:
Consider defining a bean of type 'cn.bugstack.domain.strategy.IRaffleStock' in your configuration. 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 java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:98) 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.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 @@ ...@@ -82,7 +82,14 @@
<groupId>org.redisson</groupId> <groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId> <artifactId>redisson-spring-boot-starter</artifactId>
</dependency> </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--> <!-- 工程模块;启动依赖 trigger->domain, infrastructure-->
<dependency> <dependency>
<groupId>cn.bugstack</groupId> <groupId>cn.bugstack</groupId>
......
...@@ -13,7 +13,18 @@ app: ...@@ -13,7 +13,18 @@ app:
api-version: v1 api-version: v1
# 跨域,开发阶段可以设置为 * 不限制 # 跨域,开发阶段可以设置为 * 不限制
cross-origin: '*' 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: thread:
pool: pool:
...@@ -26,22 +37,63 @@ thread: ...@@ -26,22 +37,63 @@ thread:
policy: CallerRunsPolicy policy: CallerRunsPolicy
# 数据库配置;启动时配置数据库资源信息 # 数据库配置;启动时配置数据库资源信息
spring: # 多数据源路由配置,库数量 * 表数量 为2的次幂,如2库4表
datasource: # mysql 5.x 配置 driver-class-name: com.mysql.jdbc.Driver mysql-connector-java 5.1.34
username: root # mysql 8.x 配置 driver-class-name: com.mysql.cj.jdbc.Driver mysql-connector-java 8.0.22
password: 123456 mini-db-router:
url: jdbc:mysql://127.0.0.1:13306/big_market?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&serverTimezone=UTC&useSSL=true jdbc:
driver-class-name: com.mysql.cj.jdbc.Driver datasource:
hikari: dbCount: 2
pool-name: Retail_HikariCP tbCount: 4
minimum-idle: 15 #最小空闲连接数量 default: db00
idle-timeout: 180000 #空闲连接存活最大时间,默认600000(10分钟) routerKey: userId
maximum-pool-size: 25 #连接池最大连接数,默认是10 list: db01,db02
auto-commit: true #此属性控制从池返回的连接的默认自动提交行为,默认值:true db00:
max-lifetime: 1800000 #此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟 driver-class-name: com.mysql.cj.jdbc.Driver
connection-timeout: 30000 #数据库连接超时时间,默认30秒,即30000 url: jdbc:mysql://127.0.0.1:13306/big_market?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&serverTimezone=UTC&useSSL=true
connection-test-query: SELECT 1 username: root
type: com.zaxxer.hikari.HikariDataSource 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
db01:
driver-class-name: com.mysql.cj.jdbc.Driver
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分钟)
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
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配置
mybatis: mybatis:
mapper-locations: classpath:/mybatis/mapper/*.xml mapper-locations: classpath:/mybatis/mapper/*.xml
...@@ -52,7 +104,7 @@ redis: ...@@ -52,7 +104,7 @@ redis:
sdk: sdk:
config: config:
host: 127.0.0.1 host: 127.0.0.1
port: 6379 port: 16379
pool-size: 10 pool-size: 10
min-idle-size: 5 min-idle-size: 5
idle-timeout: 30000 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 @@ ...@@ -10,7 +10,6 @@
<result column="award_desc" property="awardDesc"/> <result column="award_desc" property="awardDesc"/>
<result column="create_time" property="createTime"/> <result column="create_time" property="createTime"/>
<result column="update_time" property="updateTime"/> <result column="update_time" property="updateTime"/>
</resultMap> </resultMap>
<select id="queryAwardList" resultMap="dataMap"> <select id="queryAwardList" resultMap="dataMap">
...@@ -19,5 +18,4 @@ ...@@ -19,5 +18,4 @@
limit 10 limit 10
</select> </select>
</mapper> </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,11 +17,10 @@ ...@@ -17,11 +17,10 @@
<result column="update_time" property="updateTime"/> <result column="update_time" property="updateTime"/>
</resultMap> </resultMap>
<select id="queryStrategyAwardList" resultMap="dataMap"> <select id="queryStrategyAwardList" resultMap="dataMap">
select strategy_id select strategy_id
from strategy_award from strategy_award
limit 10 limit 10
</select> </select>
<select id="queryStrategyAwardListByStrategyId" parameterType="java.lang.Long" resultMap="dataMap"> <select id="queryStrategyAwardListByStrategyId" parameterType="java.lang.Long" resultMap="dataMap">
...@@ -48,7 +47,4 @@ ...@@ -48,7 +47,4 @@
where strategy_id = #{strategyId} and award_id = #{awardId} where strategy_id = #{strategyId} and award_id = #{awardId}
</select> </select>
</mapper> </mapper>
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
<select id="queryStrategyList" resultMap="dataMap"> <select id="queryStrategyList" resultMap="dataMap">
select strategy_id select strategy_id
from strategy from strategy
limit 10 limit 10
</select> </select>
<select id="queryStrategyByStrategyId" parameterType="java.lang.Long" resultMap="dataMap"> <select id="queryStrategyByStrategyId" parameterType="java.lang.Long" resultMap="dataMap">
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
<select id="queryStrategyRuleList" resultMap="dataMap"> <select id="queryStrategyRuleList" resultMap="dataMap">
select strategy_id select strategy_id
from strategy_rule from strategy_rule
limit 10 limit 10
</select> </select>
<select id="queryStrategyRule" parameterType="cn.bugstack.infrastructure.persistent.po.StrategyRule" resultMap="dataMap"> <select id="queryStrategyRule" parameterType="cn.bugstack.infrastructure.persistent.po.StrategyRule" resultMap="dataMap">
......
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.armory.IStrategyArmory;
import cn.bugstack.domain.strategy.service.rule.chain.ILogicChain; 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.model.valobj.*;
import cn.bugstack.domain.strategy.service.rule.tree.factory.DefaultTreeFactory; 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.RaffleAwardEntity;
import cn.bugstack.domain.strategy.model.entity.RaffleFactorEntity; 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.model.valobj.StrategyAwardStockKeyVO;
import cn.bugstack.domain.strategy.IRaffleStock;
import cn.bugstack.domain.strategy.service.armory.IStrategyArmory; 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.chain.impl.RuleWeightLogicChain;
import cn.bugstack.domain.strategy.service.rule.tree.impl.RuleLockLogicTreeNode; 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 cn.bugstack.domain.strategy.service.armory.IStrategyArmory;
import lombok.extern.slf4j.Slf4j; 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.IStrategyArmory;
import cn.bugstack.domain.strategy.service.armory.IStrategyDispatch; 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; ...@@ -6,12 +6,10 @@ import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
/** /**
* @ClassName: AwardEntity * @author Fuzhengwei bugstack.cn @小傅哥
* @Description: * @description 策略结果实体
* @Author: zhaoyongfeng * @create 2023-12-23 09:13
* @Date: 2024/11/14 19:37
*/ */
@Data @Data
@Builder @Builder
@AllArgsConstructor @AllArgsConstructor
...@@ -24,4 +22,3 @@ public class AwardEntity { ...@@ -24,4 +22,3 @@ public class AwardEntity {
private Integer awardId; private Integer awardId;
} }
...@@ -6,16 +6,16 @@ import lombok.Data; ...@@ -6,16 +6,16 @@ import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
/** /**
* @ClassName: RaffleAwardEntity * @author Fuzhengwei bugstack.cn @小傅哥
* @Description: 抽奖奖品实体 * @description 抽奖奖品实体
* @Author: zhaoyongfeng * @create 2024-01-06 09:20
* @Date: 2024/11/17 16:25
*/ */
@Data @Data
@Builder
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
@Builder
public class RaffleAwardEntity { public class RaffleAwardEntity {
/** 奖品ID */ /** 奖品ID */
private Integer awardId; private Integer awardId;
/** 奖品配置信息 */ /** 奖品配置信息 */
......
...@@ -4,25 +4,21 @@ import lombok.AllArgsConstructor; ...@@ -4,25 +4,21 @@ import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.checkerframework.checker.units.qual.A;
/** /**
* @ClassName: RaffleFactorEntity * @author Fuzhengwei bugstack.cn @小傅哥
* @Description: 抽奖因子实体 * @description 抽奖因子实体
* @Author: zhaoyongfeng * @create 2024-01-06 09:20
* @Date: 2024/11/17 16:26
*/ */
@Data @Data
@Builder @Builder
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
public class RaffleFactorEntity { public class RaffleFactorEntity {
/** 用户ID */ /** 用户ID */
private String userId; private String userId;
/** 策略ID */ /** 策略ID */
private Long strategyId; private Long strategyId;
/** 奖品ID */
private Integer awardId;
} }
...@@ -8,16 +8,16 @@ import lombok.NoArgsConstructor; ...@@ -8,16 +8,16 @@ import lombok.NoArgsConstructor;
import java.math.BigDecimal; import java.math.BigDecimal;
/** /**
* @ClassName: StrategyAwardEntity * @author Fuzhengwei bugstack.cn @小傅哥
* @Description: * @description 策略奖品实体
* @Author: zhaoyongfeng * @create 2023-12-23 10:48
* @Date: 2024/11/14 19:32
*/ */
@Data @Data
@Builder @Builder
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
public class StrategyAwardEntity { public class StrategyAwardEntity {
/** 抽奖策略ID */ /** 抽奖策略ID */
private Long strategyId; private Long strategyId;
/** 抽奖奖品ID - 内部流转使用 */ /** 抽奖奖品ID - 内部流转使用 */
...@@ -35,5 +35,4 @@ public class StrategyAwardEntity { ...@@ -35,5 +35,4 @@ public class StrategyAwardEntity {
/** 排序 */ /** 排序 */
private Integer sort; private Integer sort;
} }
...@@ -6,16 +6,16 @@ import lombok.Data; ...@@ -6,16 +6,16 @@ import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
/** /**
* @ClassName: StrategyConditionEntity * @author Fuzhengwei bugstack.cn @小傅哥
* @Description: * @description 策略条件实体
* @Author: zhaoyongfeng * @create 2023-12-23 09:10
* @Date: 2024/11/14 19:37
*/ */
@Data @Data
@Builder @Builder
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
public class StrategyConditionEntity { public class StrategyConditionEntity {
/** 用户ID */ /** 用户ID */
private String userId; private String userId;
/** 策略ID */ /** 策略ID */
......
...@@ -8,10 +8,9 @@ import lombok.NoArgsConstructor; ...@@ -8,10 +8,9 @@ import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
/** /**
* @ClassName: StrategyEntity * @author Fuzhengwei bugstack.cn @小傅哥
* @Description: 策略实体 * @description 策略实体
* @Author: zhaoyongfeng * @create 2023-12-31 15:24
* @Date: 2024/11/15 23:35
*/ */
@Data @Data
@Builder @Builder
...@@ -19,11 +18,17 @@ import org.apache.commons.lang3.StringUtils; ...@@ -19,11 +18,17 @@ import org.apache.commons.lang3.StringUtils;
@NoArgsConstructor @NoArgsConstructor
public class StrategyEntity { public class StrategyEntity {
/** 抽奖策略ID */ /**
* 抽奖策略ID
*/
private Long strategyId; private Long strategyId;
/** 抽奖策略描述 */ /**
* 抽奖策略描述
*/
private String strategyDesc; private String strategyDesc;
/** 抽奖规则模型 rule_weight,rule_blacklist */ /**
* 抽奖规则模型 rule_weight,rule_blacklist
*/
private String ruleModels; private String ruleModels;
public String[] ruleModels() { public String[] ruleModels() {
...@@ -35,8 +40,9 @@ public class StrategyEntity { ...@@ -35,8 +40,9 @@ public class StrategyEntity {
String[] ruleModels = this.ruleModels(); String[] ruleModels = this.ruleModels();
if (null == ruleModels) return null; if (null == ruleModels) return null;
for (String ruleModel : ruleModels) { for (String ruleModel : ruleModels) {
if("rule_weight".equals(ruleModel)) return ruleModel; if ("rule_weight".equals(ruleModel)) return ruleModel;
} }
return null; return null;
} }
} }
...@@ -12,10 +12,9 @@ import java.util.List; ...@@ -12,10 +12,9 @@ import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* @ClassName: StrategyRuleEntity * @author Fuzhengwei bugstack.cn @小傅哥
* @Description: 策略规则实体 * @description 策略规则实体
* @Author: zhaoyongfeng * @create 2023-12-31 15:32
* @Date: 2024/11/15 23:36
*/ */
@Data @Data
@Builder @Builder
...@@ -66,4 +65,5 @@ public class StrategyRuleEntity { ...@@ -66,4 +65,5 @@ public class StrategyRuleEntity {
return resultMap; return resultMap;
} }
} }
...@@ -4,10 +4,9 @@ import lombok.AllArgsConstructor; ...@@ -4,10 +4,9 @@ import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
/** /**
* @ClassName: RuleLimitTypeVO * @author Fuzhengwei bugstack.cn @小傅哥
* @Description: 规则限定枚举值 * @description 规则限定枚举值
* @Author: zhaoyongfeng * @create 2024-01-27 12:27
* @Date: 2024/11/20 12:37
*/ */
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
...@@ -25,4 +24,3 @@ public enum RuleLimitTypeVO { ...@@ -25,4 +24,3 @@ public enum RuleLimitTypeVO {
private final String info; private final String info;
} }
package cn.bugstack.domain.strategy.model.valobj; package cn.bugstack.domain.strategy.model.valobj;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor;
/** /**
* @ClassName: RuleLogicCheckTypeVO * @author Fuzhengwei bugstack.cn @小傅哥
* @Description: 规则过滤校验类型值对象 * @description 规则过滤校验类型值对象
* @Author: zhaoyongfeng * @create 2024-01-06 11:10
* @Date: 2024/11/17 16:24
*/ */
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
...@@ -23,4 +20,3 @@ public enum RuleLogicCheckTypeVO { ...@@ -23,4 +20,3 @@ public enum RuleLogicCheckTypeVO {
private final String info; private final String info;
} }
package cn.bugstack.domain.strategy.model.valobj; package cn.bugstack.domain.strategy.model.valobj;
import lombok.*; import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/** /**
* @ClassName: RuleTreeNodeLineVO * @author Fuzhengwei bugstack.cn @小傅哥
* @Description: 规则树节点指向线对象。用于衔接 from->to 节点链路关系 * @description 规则树节点指向线对象。用于衔接 from->to 节点链路关系
* @Author: zhaoyongfeng * @create 2024-01-27 10:49
* @Date: 2024/11/20 12:37
*/ */
@Data @Data
@Builder @Builder
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
public class RuleTreeNodeLineVO { public class RuleTreeNodeLineVO {
/** 规则树ID */ /** 规则树ID */
private String treeId; private String treeId;
/** 规则Key节点 From */ /** 规则Key节点 From */
......
...@@ -8,10 +8,9 @@ import lombok.NoArgsConstructor; ...@@ -8,10 +8,9 @@ import lombok.NoArgsConstructor;
import java.util.List; import java.util.List;
/** /**
* @ClassName: RuleTreeNodeVO * @author Fuzhengwei bugstack.cn @小傅哥
* @Description: 规则树节点对象 * @description 规则树节点对象
* @Author: zhaoyongfeng * @create 2024-01-27 10:48
* @Date: 2024/11/20 12:37
*/ */
@Data @Data
@Builder @Builder
......
...@@ -8,16 +8,16 @@ import lombok.NoArgsConstructor; ...@@ -8,16 +8,16 @@ import lombok.NoArgsConstructor;
import java.util.Map; import java.util.Map;
/** /**
* @ClassName: RuleTreeVO * @author Fuzhengwei bugstack.cn @小傅哥
* @Description: 规则树对象【注意;不具有唯一ID,不需要改变数据库结果的对象,可以被定义为值对象】 * @description 规则树对象【注意;不具有唯一ID,不需要改变数据库结果的对象,可以被定义为值对象】
* @Author: zhaoyongfeng * @create 2024-01-27 10:45
* @Date: 2024/11/20 12:37
*/ */
@Data @Data
@Builder @Builder
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
public class RuleTreeVO { public class RuleTreeVO {
/** 规则树ID */ /** 规则树ID */
private String treeId; private String treeId;
/** 规则树名称 */ /** 规则树名称 */
......
package cn.bugstack.domain.strategy.model.valobj; package cn.bugstack.domain.strategy.model.valobj;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
/** /**
* @ClassName: StrategyAwardRuleModelVO * @author Fuzhengwei bugstack.cn @小傅哥
* @Description: 抽奖策略规则规则值对象;值对象,没有唯一ID,仅限于从数据库查询对象 * @description 抽奖策略规则规则值对象;值对象,没有唯一ID,仅限于从数据库查询对象
* @Author: zhaoyongfeng * @create 2024-01-13 09:30
* @Date: 2024/11/21 0:05
*/ */
@Getter @Getter
@Builder @Builder
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
public class StrategyAwardRuleModelVO { public class StrategyAwardRuleModelVO {
private String ruleModels; private String ruleModels;
}
}
...@@ -6,18 +6,19 @@ import lombok.Data; ...@@ -6,18 +6,19 @@ import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
/** /**
* @ClassName: StrategyAwardStockKeyVO * @author Fuzhengwei bugstack.cn @小傅哥
* @Description: * @description 策略奖品库存Key标识值对象
* @Author: zhaoyongfeng * @create 2024-02-09 11:24
* @Date: 2024/11/21 13:20
*/ */
@Data @Data
@Builder @Builder
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
public class StrategyAwardStockKeyVO { public class StrategyAwardStockKeyVO {
//策略ID
// 策略ID
private Long strategyId; private Long strategyId;
// 奖品ID // 奖品ID
private Integer awardId; private Integer awardId;
} }
...@@ -10,11 +10,16 @@ import cn.bugstack.domain.strategy.model.valobj.StrategyAwardStockKeyVO; ...@@ -10,11 +10,16 @@ import cn.bugstack.domain.strategy.model.valobj.StrategyAwardStockKeyVO;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
/**
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 策略服务仓储接口
* @create 2023-12-23 09:33
*/
public interface IStrategyRepository { public interface IStrategyRepository {
List<StrategyAwardEntity> queryStrategyAwardList(Long strategyId); 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); Integer getStrategyAwardAssemble(String key, Integer rateKey);
...@@ -26,35 +31,36 @@ public interface IStrategyRepository { ...@@ -26,35 +31,36 @@ public interface IStrategyRepository {
StrategyRuleEntity queryStrategyRule(Long strategyId, String ruleModel); StrategyRuleEntity queryStrategyRule(Long strategyId, String ruleModel);
String queryStrategyRuleValue(Long strategyId, Integer awardId, String ruleModel);
String queryStrategyRuleValue(Long strategyId, String ruleModel); String queryStrategyRuleValue(Long strategyId, String ruleModel);
String queryStrategyRuleValue(Long strategyId, Integer awardId, String ruleModel);
StrategyAwardRuleModelVO queryStrategyAwardRuleModelVO(Long strategyId, Integer awardId); StrategyAwardRuleModelVO queryStrategyAwardRuleModelVO(Long strategyId, Integer awardId);
/** /**
* 根据规则树ID,查询树结构信息 * 根据规则树ID,查询树结构信息
* *
* @param treeId 规则树ID * @param treeId 规则树ID
* @return 树结构信息 * @return 树结构信息
*/ */
RuleTreeVO queryRuleTreeVOByTreeId(String treeId); RuleTreeVO queryRuleTreeVOByTreeId(String treeId);
/** /**
* 缓存奖品库存 * 缓存奖品库存
* *
* @param cacheKey key * @param cacheKey key
* @param awardCount 库存值 * @param awardCount 库存值
*/ */
void cacheStrategyAwardCount(String cacheKey, Integer awardCount); void cacheStrategyAwardCount(String cacheKey, Integer awardCount);
/** /**
* 缓存key,decr 方式扣减库存 * 缓存key,decr 方式扣减库存
* *
* @param cacheKey 缓存Key * @param cacheKey 缓存Key
* @return 扣减结果 * @return 扣减结果
*/ */
Boolean subtractionAwardStock(String cacheKey); Boolean subtractionAwardStock(String cacheKey);
/** /**
* 写入奖品库存消费队列 * 写入奖品库存消费队列
* *
...@@ -71,9 +77,17 @@ public interface IStrategyRepository { ...@@ -71,9 +77,17 @@ public interface IStrategyRepository {
* 更新奖品库存消耗 * 更新奖品库存消耗
* *
* @param strategyId 策略ID * @param strategyId 策略ID
* @param awardId 奖品ID * @param awardId 奖品ID
*/ */
void updateStrategyAwardStock(Long strategyId, Integer awardId); void updateStrategyAwardStock(Long strategyId, Integer awardId);
/**
* 根据策略ID+奖品ID的唯一值组合,查询奖品信息
*
* @param strategyId 策略ID
* @param awardId 奖品ID
* @return 奖品信息
*/
StrategyAwardEntity queryStrategyAwardEntity(Long strategyId, Integer awardId); 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.RaffleAwardEntity;
import cn.bugstack.domain.strategy.model.entity.RaffleFactorEntity; import cn.bugstack.domain.strategy.model.entity.RaffleFactorEntity;
...@@ -13,10 +13,9 @@ import lombok.extern.slf4j.Slf4j; ...@@ -13,10 +13,9 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
/** /**
* @ClassName: AbstractRaffleStrategy * @author Fuzhengwei bugstack.cn @小傅哥
* @Description: 抽奖策略抽象类,定义抽奖的标准流程 * @description 抽奖策略抽象类,定义抽奖的标准流程
* @Author: zhaoyongfeng * @create 2024-01-06 09:26
* @Date: 2024/11/17 16:14
*/ */
@Slf4j @Slf4j
public abstract class AbstractRaffleStrategy implements IRaffleStrategy { public abstract class AbstractRaffleStrategy implements IRaffleStrategy {
...@@ -30,6 +29,7 @@ public abstract class AbstractRaffleStrategy implements IRaffleStrategy { ...@@ -30,6 +29,7 @@ public abstract class AbstractRaffleStrategy implements IRaffleStrategy {
// 抽奖的决策树 -> 负责抽奖中到抽奖后的规则过滤,如抽奖到A奖品ID,之后要做次数的判断和库存的扣减等。 // 抽奖的决策树 -> 负责抽奖中到抽奖后的规则过滤,如抽奖到A奖品ID,之后要做次数的判断和库存的扣减等。
protected final DefaultTreeFactory defaultTreeFactory; 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) { public AbstractRaffleStrategy(IStrategyRepository repository, IStrategyDispatch strategyDispatch, DefaultChainFactory defaultChainFactory, DefaultTreeFactory defaultTreeFactory) {
this.repository = repository; this.repository = repository;
this.strategyDispatch = strategyDispatch; this.strategyDispatch = strategyDispatch;
...@@ -91,4 +91,3 @@ public abstract class AbstractRaffleStrategy implements IRaffleStrategy { ...@@ -91,4 +91,3 @@ public abstract class AbstractRaffleStrategy implements IRaffleStrategy {
public abstract DefaultTreeFactory.StrategyAwardVO raffleLogicTree(String userId, Long strategyId, Integer awardId); 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 cn.bugstack.domain.strategy.model.entity.StrategyAwardEntity;
import java.util.List; import java.util.List;
/**
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 策略奖品接口
* @create 2024-02-14 16:44
*/
public interface IRaffleAward { public interface IRaffleAward {
/** /**
...@@ -13,5 +18,5 @@ public interface IRaffleAward { ...@@ -13,5 +18,5 @@ public interface IRaffleAward {
* @return 奖品列表 * @return 奖品列表
*/ */
List<StrategyAwardEntity> queryRaffleStrategyAwardList(Long strategyId); List<StrategyAwardEntity> queryRaffleStrategyAwardList(Long strategyId);
}
}
package cn.bugstack.domain.strategy; package cn.bugstack.domain.strategy.service;
import cn.bugstack.domain.strategy.model.valobj.StrategyAwardStockKeyVO; import cn.bugstack.domain.strategy.model.valobj.StrategyAwardStockKeyVO;
/**
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 抽奖库存相关服务,获取库存消耗队列
* @create 2024-02-09 12:17
*/
public interface IRaffleStock { public interface IRaffleStock {
/** /**
* 获取奖品库存消耗队列 * 获取奖品库存消耗队列
* *
...@@ -18,4 +24,5 @@ public interface IRaffleStock { ...@@ -18,4 +24,5 @@ public interface IRaffleStock {
* @param awardId 奖品ID * @param awardId 奖品ID
*/ */
void updateStrategyAwardStock(Long strategyId, Integer awardId); 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.RaffleAwardEntity;
import cn.bugstack.domain.strategy.model.entity.RaffleFactorEntity; import cn.bugstack.domain.strategy.model.entity.RaffleFactorEntity;
/**
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 抽奖策略接口
* @create 2024-01-06 09:19
*/
public interface IRaffleStrategy { public interface IRaffleStrategy {
/** /**
...@@ -15,4 +19,3 @@ public interface IRaffleStrategy { ...@@ -15,4 +19,3 @@ public interface IRaffleStrategy {
RaffleAwardEntity performRaffle(RaffleFactorEntity raffleFactorEntity); RaffleAwardEntity performRaffle(RaffleFactorEntity raffleFactorEntity);
} }
package cn.bugstack.domain.strategy.service.armory; package cn.bugstack.domain.strategy.service.armory;
/*
* @return /**
* @author zhaoyongfeng * @author Fuzhengwei bugstack.cn @小傅哥
* @description 策略装配库(兵工厂),负责初始化策略计算 * @description 策略装配库(兵工厂),负责初始化策略计算
* * @create 2023-12-23 09:44
* @param null
*/ */
public interface IStrategyArmory { public interface IStrategyArmory {
...@@ -16,5 +15,4 @@ public interface IStrategyArmory { ...@@ -16,5 +15,4 @@ public interface IStrategyArmory {
*/ */
boolean assembleLotteryStrategy(Long strategyId); boolean assembleLotteryStrategy(Long strategyId);
} }
package cn.bugstack.domain.strategy.service.armory; package cn.bugstack.domain.strategy.service.armory;
/*
* @return /**
* @author zhaoyongfeng * @author Fuzhengwei bugstack.cn @小傅哥
* @description 策略抽奖调度 * @description 策略抽奖调度
* * @create 2023-12-31 15:15
* @param null
*/ */
public interface IStrategyDispatch { public interface IStrategyDispatch {
/** /**
* 获取抽奖策略装配的随机结果 * 获取抽奖策略装配的随机结果
* *
......
...@@ -12,19 +12,18 @@ import org.springframework.stereotype.Service; ...@@ -12,19 +12,18 @@ import org.springframework.stereotype.Service;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.*; import java.util.*;
/** /**
* @ClassName: StrategyArmory * @author Fuzhengwei bugstack.cn @小傅哥
* @Description: 策略装配库(兵工厂),负责初始化策略计算 * @description 策略装配库(兵工厂),负责初始化策略计算
* @Author: zhaoyongfeng * @create 2023-12-23 10:02
* @Date: 2024/11/14 17:39
*/ */
@Slf4j @Slf4j
@Service @Service
public class StrategyArmoryDispatch implements IStrategyArmory,IStrategyDispatch{ public class StrategyArmoryDispatch implements IStrategyArmory, IStrategyDispatch {
@Resource @Resource
private IStrategyRepository repository; private IStrategyRepository repository;
...@@ -42,7 +41,6 @@ public class StrategyArmoryDispatch implements IStrategyArmory,IStrategyDispatch ...@@ -42,7 +41,6 @@ public class StrategyArmoryDispatch implements IStrategyArmory,IStrategyDispatch
cacheStrategyAwardCount(strategyId, awardId, awardCount); cacheStrategyAwardCount(strategyId, awardId, awardCount);
} }
// 3.1 默认装配配置【全量抽奖概率】 // 3.1 默认装配配置【全量抽奖概率】
assembleLotteryStrategy(String.valueOf(strategyId), strategyAwardEntities); assembleLotteryStrategy(String.valueOf(strategyId), strategyAwardEntities);
...@@ -67,6 +65,7 @@ public class StrategyArmoryDispatch implements IStrategyArmory,IStrategyDispatch ...@@ -67,6 +65,7 @@ public class StrategyArmoryDispatch implements IStrategyArmory,IStrategyDispatch
return true; return true;
} }
/** /**
* 计算公式; * 计算公式;
* 1. 找到范围内最小的概率值,比如 0.1、0.02、0.003,需要找到的值是 0.003 * 1. 找到范围内最小的概率值,比如 0.1、0.02、0.003,需要找到的值是 0.003
...@@ -74,7 +73,6 @@ public class StrategyArmoryDispatch implements IStrategyArmory,IStrategyDispatch ...@@ -74,7 +73,6 @@ public class StrategyArmoryDispatch implements IStrategyArmory,IStrategyDispatch
* 3. 那么「概率 * 1000」分别占比100个、20个、3个,总计是123个 * 3. 那么「概率 * 1000」分别占比100个、20个、3个,总计是123个
* 4. 后续的抽奖就用123作为随机数的范围值,生成的值100个都是0.1概率的奖品、20个是概率0.02的奖品、最后是3个是0.003的奖品。 * 4. 后续的抽奖就用123作为随机数的范围值,生成的值100个都是0.1概率的奖品、20个是概率0.02的奖品、最后是3个是0.003的奖品。
*/ */
private void assembleLotteryStrategy(String key, List<StrategyAwardEntity> strategyAwardEntities) { private void assembleLotteryStrategy(String key, List<StrategyAwardEntity> strategyAwardEntities) {
// 1. 获取最小概率值 // 1. 获取最小概率值
BigDecimal minAwardRate = strategyAwardEntities.stream() BigDecimal minAwardRate = strategyAwardEntities.stream()
...@@ -105,14 +103,16 @@ public class StrategyArmoryDispatch implements IStrategyArmory,IStrategyDispatch ...@@ -105,14 +103,16 @@ public class StrategyArmoryDispatch implements IStrategyArmory,IStrategyDispatch
shuffleStrategyAwardSearchRateTable.put(i, strategyAwardSearchRateTables.get(i)); shuffleStrategyAwardSearchRateTable.put(i, strategyAwardSearchRateTables.get(i));
} }
// 6. 存放到 Redis // 6. 存放到 Redis
repository.storeStrategyAwardSearchRateTable(key, shuffleStrategyAwardSearchRateTable.size(), shuffleStrategyAwardSearchRateTable); repository.storeStrategyAwardSearchRateTable(key, shuffleStrategyAwardSearchRateTable.size(), shuffleStrategyAwardSearchRateTable);
} }
/** /**
* 转换计算,只根据小数位来计算。如【0.01返回100】、【0.009返回1000】、【0.0018返回10000】 * 转换计算,只根据小数位来计算。如【0.01返回100】、【0.009返回1000】、【0.0018返回10000】
*/ */
private double convert(double min) { private double convert(double min) {
if(0 == min) return 1D;
double current = min; double current = min;
double max = 1; double max = 1;
while (current < 1) { while (current < 1) {
...@@ -139,7 +139,7 @@ public class StrategyArmoryDispatch implements IStrategyArmory,IStrategyDispatch ...@@ -139,7 +139,7 @@ public class StrategyArmoryDispatch implements IStrategyArmory,IStrategyDispatch
// 分布式部署下,不一定为当前应用做的策略装配。也就是值不一定会保存到本应用,而是分布式应用,所以需要从 Redis 中获取。 // 分布式部署下,不一定为当前应用做的策略装配。也就是值不一定会保存到本应用,而是分布式应用,所以需要从 Redis 中获取。
int rateRange = repository.getRateRange(strategyId); 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 @Override
...@@ -148,8 +148,6 @@ public class StrategyArmoryDispatch implements IStrategyArmory,IStrategyDispatch ...@@ -148,8 +148,6 @@ public class StrategyArmoryDispatch implements IStrategyArmory,IStrategyDispatch
return getRandomAwardId(key); return getRandomAwardId(key);
} }
@Override @Override
public Integer getRandomAwardId(String key) { public Integer getRandomAwardId(String key) {
// 分布式部署下,不一定为当前应用做的策略装配。也就是值不一定会保存到本应用,而是分布式应用,所以需要从 Redis 中获取。 // 分布式部署下,不一定为当前应用做的策略装配。也就是值不一定会保存到本应用,而是分布式应用,所以需要从 Redis 中获取。
...@@ -158,12 +156,10 @@ public class StrategyArmoryDispatch implements IStrategyArmory,IStrategyDispatch ...@@ -158,12 +156,10 @@ public class StrategyArmoryDispatch implements IStrategyArmory,IStrategyDispatch
return repository.getStrategyAwardAssemble(key, secureRandom.nextInt(rateRange)); return repository.getStrategyAwardAssemble(key, secureRandom.nextInt(rateRange));
} }
@Override @Override
public Boolean subtractionAwardStock(Long strategyId, Integer awardId) { public Boolean subtractionAwardStock(Long strategyId, Integer awardId) {
String cacheKey = Constants.RedisKey.STRATEGY_AWARD_COUNT_KEY + strategyId + Constants.UNDERLINE + awardId; String cacheKey = Constants.RedisKey.STRATEGY_AWARD_COUNT_KEY + strategyId + Constants.UNDERLINE + awardId;
return repository.subtractionAwardStock(cacheKey); return repository.subtractionAwardStock(cacheKey);
} }
} }
package cn.bugstack.domain.strategy.service.raffle; 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.entity.StrategyAwardEntity;
import cn.bugstack.domain.strategy.model.valobj.RuleTreeVO; import cn.bugstack.domain.strategy.model.valobj.RuleTreeVO;
import cn.bugstack.domain.strategy.model.valobj.StrategyAwardRuleModelVO; import cn.bugstack.domain.strategy.model.valobj.StrategyAwardRuleModelVO;
import cn.bugstack.domain.strategy.model.valobj.StrategyAwardStockKeyVO; import cn.bugstack.domain.strategy.model.valobj.StrategyAwardStockKeyVO;
import cn.bugstack.domain.strategy.repository.IStrategyRepository; 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.armory.IStrategyDispatch;
import cn.bugstack.domain.strategy.service.rule.chain.ILogicChain; import cn.bugstack.domain.strategy.service.rule.chain.ILogicChain;
import cn.bugstack.domain.strategy.service.rule.chain.factory.DefaultChainFactory; import cn.bugstack.domain.strategy.service.rule.chain.factory.DefaultChainFactory;
...@@ -19,10 +19,9 @@ import org.springframework.stereotype.Service; ...@@ -19,10 +19,9 @@ import org.springframework.stereotype.Service;
import java.util.List; import java.util.List;
/** /**
* @ClassName: DefaultRaffleStrategy * @author Fuzhengwei bugstack.cn @小傅哥
* @Description: 默认的抽奖策略实现 * @description 默认的抽奖策略实现
* @Author: zhaoyongfeng * @create 2024-01-06 11:46
* @Date: 2024/11/17 16:16
*/ */
@Slf4j @Slf4j
@Service @Service
...@@ -68,5 +67,3 @@ public class DefaultRaffleStrategy extends AbstractRaffleStrategy implements IRa ...@@ -68,5 +67,3 @@ public class DefaultRaffleStrategy extends AbstractRaffleStrategy implements IRa
} }
} }
...@@ -3,13 +3,12 @@ package cn.bugstack.domain.strategy.service.rule.chain; ...@@ -3,13 +3,12 @@ package cn.bugstack.domain.strategy.service.rule.chain;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
/** /**
* @ClassName: AbstractLogicChain * @author Fuzhengwei bugstack.cn @小傅哥
* @Description: 抽奖策略责任链,判断走那种抽奖策略。如;默认抽象、权重抽奖、黑名单抽奖 * @description 抽奖策略责任链,判断走那种抽奖策略。如;默认抽象、权重抽奖、黑名单抽奖
* @Author: zhaoyongfeng * @create 2024-01-20 09:37
* @Date: 2024/11/18 22:03
*/ */
@Slf4j @Slf4j
public abstract class AbstractLogicChain implements ILogicChain { public abstract class AbstractLogicChain implements ILogicChain{
private ILogicChain next; private ILogicChain next;
...@@ -27,4 +26,3 @@ public abstract class AbstractLogicChain implements ILogicChain { ...@@ -27,4 +26,3 @@ public abstract class AbstractLogicChain implements ILogicChain {
protected abstract String ruleModel(); protected abstract String ruleModel();
} }
...@@ -2,23 +2,20 @@ package cn.bugstack.domain.strategy.service.rule.chain; ...@@ -2,23 +2,20 @@ package cn.bugstack.domain.strategy.service.rule.chain;
import cn.bugstack.domain.strategy.service.rule.chain.factory.DefaultChainFactory; import cn.bugstack.domain.strategy.service.rule.chain.factory.DefaultChainFactory;
/* /**
* @return * @author Fuzhengwei bugstack.cn @小傅哥
* @author zhaoyongfeng
* @description 抽奖策略规则责任链接口 * @description 抽奖策略规则责任链接口
* * @create 2024-01-20 09:40
* @param null
*/ */
public interface ILogicChain extends ILogicChainArmory{ public interface ILogicChain extends ILogicChainArmory, Cloneable {
/** /**
* 责任链接口 * 责任链接口
* *
* @param userId 用户ID * @param userId 用户ID
* @param strategyId 策略ID * @param strategyId 策略ID
* @return 奖品ID * @return 奖品对象
*/ */
DefaultChainFactory.StrategyAwardVO logic(String userId, Long strategyId); DefaultChainFactory.StrategyAwardVO logic(String userId, Long strategyId);
} }
package cn.bugstack.domain.strategy.service.rule.chain; package cn.bugstack.domain.strategy.service.rule.chain;
/*
* @return /**
* @author zhaoyongfeng * @author Fuzhengwei bugstack.cn @小傅哥
* @description 责任链装配 * @description 责任链装配
* * @create 2024-01-20 11:53
* @param null
*/ */
public interface ILogicChainArmory { public interface ILogicChainArmory {
ILogicChain next(); ILogicChain next();
ILogicChain appendNext(ILogicChain next); ILogicChain appendNext(ILogicChain next);
} }
...@@ -4,25 +4,31 @@ import cn.bugstack.domain.strategy.model.entity.StrategyEntity; ...@@ -4,25 +4,31 @@ import cn.bugstack.domain.strategy.model.entity.StrategyEntity;
import cn.bugstack.domain.strategy.repository.IStrategyRepository; import cn.bugstack.domain.strategy.repository.IStrategyRepository;
import cn.bugstack.domain.strategy.service.rule.chain.ILogicChain; import cn.bugstack.domain.strategy.service.rule.chain.ILogicChain;
import lombok.*; import lombok.*;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* @ClassName: DefaultLogicFactory * @author Fuzhengwei bugstack.cn @小傅哥
* @Description: * @description 工厂
* @Author: zhaoyongfeng * @create 2024-01-20 10:54
* @Date: 2024/11/17 16:05
*/ */
@Service @Service
public class DefaultChainFactory { public class DefaultChainFactory {
// 原型模式获取对象
private final Map<String, ILogicChain> logicChainGroup; private final ApplicationContext applicationContext;
// 仓储信息
protected IStrategyRepository repository; protected IStrategyRepository repository;
// 存放策略链,策略ID -> 责任链
private final Map<Long, ILogicChain> strategyChainGroup;
public DefaultChainFactory(Map<String, ILogicChain> logicChainGroup, IStrategyRepository repository) { public DefaultChainFactory(ApplicationContext applicationContext, IStrategyRepository repository) {
this.logicChainGroup = logicChainGroup; this.applicationContext = applicationContext;
this.repository = repository; this.repository = repository;
this.strategyChainGroup = new ConcurrentHashMap<>();
} }
/** /**
...@@ -32,33 +38,48 @@ public class DefaultChainFactory { ...@@ -32,33 +38,48 @@ public class DefaultChainFactory {
* @return LogicChain * @return LogicChain
*/ */
public ILogicChain openLogicChain(Long strategyId) { public ILogicChain openLogicChain(Long strategyId) {
ILogicChain cacheLogicChain = strategyChainGroup.get(strategyId);
if (null != cacheLogicChain) return cacheLogicChain;
StrategyEntity strategy = repository.queryStrategyEntityByStrategyId(strategyId); StrategyEntity strategy = repository.queryStrategyEntityByStrategyId(strategyId);
String[] ruleModels = strategy.ruleModels(); 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缓存中获取,如果更新库表,记得在测试阶段手动处理缓存」 // 按照配置顺序装填用户配置的责任链;rule_blacklist、rule_weight 「注意此数据从Redis缓存中获取,如果更新库表,记得在测试阶段手动处理缓存」
ILogicChain logicChain = logicChainGroup.get(ruleModels[0]); ILogicChain logicChain = applicationContext.getBean(ruleModels[0], ILogicChain.class);
ILogicChain current = logicChain; ILogicChain current = logicChain;
for (int i = 1; i < ruleModels.length; i++) { 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 = current.appendNext(nextChain);
} }
// 责任链的最后装填默认责任链 // 责任链的最后装填默认责任链
current.appendNext(logicChainGroup.get("default")); current.appendNext(applicationContext.getBean(LogicModel.RULE_DEFAULT.getCode(), ILogicChain.class));
// 写入缓存
strategyChainGroup.put(strategyId, logicChain);
return logicChain; return logicChain;
} }
@Data @Data
@Builder @Builder
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
public static class StrategyAwardVO { public static class StrategyAwardVO {
/** 抽奖奖品ID - 内部流转使用 */ /**
* 抽奖奖品ID - 内部流转使用
*/
private Integer awardId; private Integer awardId;
/** */ /**
*
*/
private String logicModel; private String logicModel;
} }
...@@ -76,5 +97,4 @@ public class DefaultChainFactory { ...@@ -76,5 +97,4 @@ public class DefaultChainFactory {
} }
} }
...@@ -5,19 +5,22 @@ import cn.bugstack.domain.strategy.service.rule.chain.AbstractLogicChain; ...@@ -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.domain.strategy.service.rule.chain.factory.DefaultChainFactory;
import cn.bugstack.types.common.Constants; import cn.bugstack.types.common.Constants;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.Resource; import javax.annotation.Resource;
/** /**
* @ClassName: RuleBackListLogicFilter * @author Fuzhengwei bugstack.cn @小傅哥
* @Description: 【抽奖前规则】黑名单用户过滤规则 * @description 黑名单责任链
* @Author: zhaoyongfeng * @create 2024-01-20 10:23
* @Date: 2024/11/17 16:07
*/ */
@Slf4j @Slf4j
@Component("rule_blacklist") @Component("rule_blacklist")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class BackListLogicChain extends AbstractLogicChain { public class BackListLogicChain extends AbstractLogicChain {
@Resource @Resource
private IStrategyRepository repository; private IStrategyRepository repository;
...@@ -40,7 +43,6 @@ public class BackListLogicChain extends AbstractLogicChain { ...@@ -40,7 +43,6 @@ public class BackListLogicChain extends AbstractLogicChain {
.logicModel(ruleModel()) .logicModel(ruleModel())
.build(); .build();
} }
} }
// 过滤其他责任链 // 过滤其他责任链
...@@ -53,6 +55,4 @@ public class BackListLogicChain extends AbstractLogicChain { ...@@ -53,6 +55,4 @@ public class BackListLogicChain extends AbstractLogicChain {
return DefaultChainFactory.LogicModel.RULE_BLACKLIST.getCode(); return DefaultChainFactory.LogicModel.RULE_BLACKLIST.getCode();
} }
} }
...@@ -4,18 +4,20 @@ import cn.bugstack.domain.strategy.service.armory.IStrategyDispatch; ...@@ -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.AbstractLogicChain;
import cn.bugstack.domain.strategy.service.rule.chain.factory.DefaultChainFactory; import cn.bugstack.domain.strategy.service.rule.chain.factory.DefaultChainFactory;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.Resource; import javax.annotation.Resource;
/** /**
* @ClassName: RuleLockLogicFilter * @author Fuzhengwei bugstack.cn @小傅哥
* @Description: * @description 默认的责任链「作为最后一个链」
* @Author: zhaoyongfeng * @create 2024-01-20 10:06
* @Date: 2024/11/18 20:25
*/ */
@Slf4j @Slf4j
@Component("default") @Component("rule_default")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class DefaultLogicChain extends AbstractLogicChain { public class DefaultLogicChain extends AbstractLogicChain {
@Resource @Resource
...@@ -29,7 +31,6 @@ public class DefaultLogicChain extends AbstractLogicChain { ...@@ -29,7 +31,6 @@ public class DefaultLogicChain extends AbstractLogicChain {
.awardId(awardId) .awardId(awardId)
.logicModel(ruleModel()) .logicModel(ruleModel())
.build(); .build();
} }
@Override @Override
......
...@@ -6,18 +6,21 @@ import cn.bugstack.domain.strategy.service.rule.chain.AbstractLogicChain; ...@@ -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.domain.strategy.service.rule.chain.factory.DefaultChainFactory;
import cn.bugstack.types.common.Constants; import cn.bugstack.types.common.Constants;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.*; import java.util.*;
/** /**
* @ClassName: RuleWeightLogicFilter * @author Fuzhengwei bugstack.cn @小傅哥
* @Description: 【抽奖前规则】根据抽奖权重返回可抽奖范围KEY * @description 权重抽奖责任链
* @Author: zhaoyongfeng * @create 2024-01-20 10:38
* @Date: 2024/11/17 16:08
*/ */
@Slf4j @Slf4j
@Component("rule_weight") @Component("rule_weight")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class RuleWeightLogicChain extends AbstractLogicChain { public class RuleWeightLogicChain extends AbstractLogicChain {
@Resource @Resource
...@@ -29,10 +32,6 @@ public class RuleWeightLogicChain extends AbstractLogicChain { ...@@ -29,10 +32,6 @@ public class RuleWeightLogicChain extends AbstractLogicChain {
// 根据用户ID查询用户抽奖消耗的积分值,本章节我们先写死为固定的值。后续需要从数据库中查询。 // 根据用户ID查询用户抽奖消耗的积分值,本章节我们先写死为固定的值。后续需要从数据库中查询。
public Long userScore = 0L; 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 * 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 { ...@@ -44,9 +43,12 @@ public class RuleWeightLogicChain extends AbstractLogicChain {
String ruleValue = repository.queryStrategyRuleValue(strategyId, ruleModel()); 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); 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值,并默认排序 // 2. 转换Keys值,并默认排序
List<Long> analyticalSortedKeys = new ArrayList<>(analyticalValueGroup.keySet()); List<Long> analyticalSortedKeys = new ArrayList<>(analyticalValueGroup.keySet());
...@@ -60,7 +62,7 @@ public class RuleWeightLogicChain extends AbstractLogicChain { ...@@ -60,7 +62,7 @@ public class RuleWeightLogicChain extends AbstractLogicChain {
* nextValue = analyticalSortedKeyValue; * nextValue = analyticalSortedKeyValue;
* } * }
* } * }
* * 星球伙伴 @慢慢来 ID 6267 提供
* Long nextValue = analyticalSortedKeys.stream() * Long nextValue = analyticalSortedKeys.stream()
* .filter(key -> userScore >= key) * .filter(key -> userScore >= key)
* .max(Comparator.naturalOrder()) * .max(Comparator.naturalOrder())
...@@ -82,7 +84,6 @@ public class RuleWeightLogicChain extends AbstractLogicChain { ...@@ -82,7 +84,6 @@ public class RuleWeightLogicChain extends AbstractLogicChain {
.build(); .build();
} }
// 5. 过滤其他责任链 // 5. 过滤其他责任链
log.info("抽奖责任链-权重放行 userId: {} strategyId: {} ruleModel: {}", userId, strategyId, ruleModel()); log.info("抽奖责任链-权重放行 userId: {} strategyId: {} ruleModel: {}", userId, strategyId, ruleModel());
return next().logic(userId, strategyId); return next().logic(userId, strategyId);
...@@ -93,7 +94,6 @@ public class RuleWeightLogicChain extends AbstractLogicChain { ...@@ -93,7 +94,6 @@ public class RuleWeightLogicChain extends AbstractLogicChain {
return DefaultChainFactory.LogicModel.RULE_WEIGHT.getCode(); return DefaultChainFactory.LogicModel.RULE_WEIGHT.getCode();
} }
private Map<Long, String> getAnalyticalValue(String ruleValue) { private Map<Long, String> getAnalyticalValue(String ruleValue) {
String[] ruleValueGroups = ruleValue.split(Constants.SPACE); String[] ruleValueGroups = ruleValue.split(Constants.SPACE);
Map<Long, String> ruleValueMap = new HashMap<>(); Map<Long, String> ruleValueMap = new HashMap<>();
...@@ -113,5 +113,3 @@ public class RuleWeightLogicChain extends AbstractLogicChain { ...@@ -113,5 +113,3 @@ public class RuleWeightLogicChain extends AbstractLogicChain {
} }
} }
...@@ -2,13 +2,13 @@ package cn.bugstack.domain.strategy.service.rule.tree; ...@@ -2,13 +2,13 @@ package cn.bugstack.domain.strategy.service.rule.tree;
import cn.bugstack.domain.strategy.service.rule.tree.factory.DefaultTreeFactory; import cn.bugstack.domain.strategy.service.rule.tree.factory.DefaultTreeFactory;
/* /**
* @return * @author Fuzhengwei bugstack.cn @小傅哥
* @author zhaoyongfeng * @description 规则树接口
* @description 规则数接口 * @create 2024-01-27 11:14
*
* @param null
*/ */
public interface ILogicTreeNode { public interface ILogicTreeNode {
DefaultTreeFactory.TreeActionEntity logic(String userId, Long strategyId, Integer awardId, String ruleValue); DefaultTreeFactory.TreeActionEntity logic(String userId, Long strategyId, Integer awardId, String ruleValue);
} }
...@@ -5,19 +5,15 @@ import cn.bugstack.domain.strategy.model.valobj.RuleTreeVO; ...@@ -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.ILogicTreeNode;
import cn.bugstack.domain.strategy.service.rule.tree.factory.engine.IDecisionTreeEngine; import cn.bugstack.domain.strategy.service.rule.tree.factory.engine.IDecisionTreeEngine;
import cn.bugstack.domain.strategy.service.rule.tree.factory.engine.impl.DecisionTreeEngine; import cn.bugstack.domain.strategy.service.rule.tree.factory.engine.impl.DecisionTreeEngine;
import lombok.AllArgsConstructor; import lombok.*;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.Map; import java.util.Map;
/** /**
* @ClassName: DefaultTreeFactory * @author Fuzhengwei bugstack.cn @小傅哥
* @Description: 规则树工厂 * @description 规则树工厂
* @Author: zhaoyongfeng * @create 2024-01-27 11:28
* @Date: 2024/11/20 12:03
*/ */
@Service @Service
public class DefaultTreeFactory { public class DefaultTreeFactory {
...@@ -28,11 +24,12 @@ public class DefaultTreeFactory { ...@@ -28,11 +24,12 @@ public class DefaultTreeFactory {
this.logicTreeNodeGroup = logicTreeNodeGroup; this.logicTreeNodeGroup = logicTreeNodeGroup;
} }
public IDecisionTreeEngine openLogicTree(RuleTreeVO ruleTreeVO){ public IDecisionTreeEngine openLogicTree(RuleTreeVO ruleTreeVO) {
return new DecisionTreeEngine(logicTreeNodeGroup,ruleTreeVO); return new DecisionTreeEngine(logicTreeNodeGroup, ruleTreeVO);
} }
/** /**
* 决策树动作 * 决策树个动作实习
*/ */
@Data @Data
@Builder @Builder
...@@ -42,6 +39,7 @@ public class DefaultTreeFactory { ...@@ -42,6 +39,7 @@ public class DefaultTreeFactory {
private RuleLogicCheckTypeVO ruleLogicCheckType; private RuleLogicCheckTypeVO ruleLogicCheckType;
private StrategyAwardVO strategyAwardVO; private StrategyAwardVO strategyAwardVO;
} }
@Data @Data
@Builder @Builder
@AllArgsConstructor @AllArgsConstructor
...@@ -53,5 +51,4 @@ public class DefaultTreeFactory { ...@@ -53,5 +51,4 @@ public class DefaultTreeFactory {
private String awardRuleValue; private String awardRuleValue;
} }
} }
...@@ -2,7 +2,13 @@ package cn.bugstack.domain.strategy.service.rule.tree.factory.engine; ...@@ -2,7 +2,13 @@ package cn.bugstack.domain.strategy.service.rule.tree.factory.engine;
import cn.bugstack.domain.strategy.service.rule.tree.factory.DefaultTreeFactory; import cn.bugstack.domain.strategy.service.rule.tree.factory.DefaultTreeFactory;
/**
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 规则树组合接口
* @create 2024-01-27 11:33
*/
public interface IDecisionTreeEngine { public interface IDecisionTreeEngine {
DefaultTreeFactory.StrategyAwardVO process(String userId, Long strategyId, Integer awardId); DefaultTreeFactory.StrategyAwardVO process(String userId, Long strategyId, Integer awardId);
} }
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册