提交 cdc1171b 编写于 作者: C caathead

完成了概率抽奖的配置

上级 a5de638a
# xfg-frame-archetype - DDD 脚手架
## 重要信息
- 2核4G 可部署整套环境开发学习 [京东云服务器 2核4G 132元/1年或者3年的](https://3.cn/1K-cfT7D)
- 工程中没有提供 application/case 层,如果你的工程较大,可以使用脚手架构建完工程后,自己新增加此模块
- 框架使用文档:[https://bugstack.cn/md/road-map/ddd-archetype.html](https://bugstack.cn/md/road-map/ddd-archetype.html) - 此文章下还有系列的 DDD 知识
## Maven - 阿里云镜像
```java
<!-- mirrors
| This is a list of mirrors to be used in downloading artifacts from remote repositories.
|
| It works like this: a POM may declare a repository to use in resolving certain artifacts.
| However, this repository may have problems with heavy traffic at times, so people have mirrored
| it to several places.
|
| That repository definition will have a unique id, so we can create a mirror reference for that
| repository, to be used as an alternate download site. The mirror site will be the preferred
| server for that repository.
|-->
<mirrors>
<!-- mirror
| Specifies a repository mirror site to use instead of a given repository. The repository that
| this mirror serves has an ID that matches the mirrorOf element of this mirror. IDs are used
| for inheritance and direct lookup purposes, and must be unique across the set of mirrors.
-->
<mirror>
<id>alimaven</id>
<mirrorOf>central</mirrorOf>
<name>aliyun maven</name>
<url>https://maven.aliyun.com/nexus/content/repositories/central/</url>
</mirror>
</mirrors>
```
- 没有镜像你可能会出现拉取很慢的问题,以及错误。
......@@ -24,15 +24,6 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<developers>
<developer>
<name>xiaofuge</name>
<email>184172133@qq.com</email>
<organization>fuzhengwei</organization>
<organizationUrl>https://github.com/fuzhengwei</organizationUrl>
</developer>
</developers>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
......@@ -48,6 +39,13 @@
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.23.4</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
......@@ -123,6 +121,7 @@
<artifactId>xfg-frame-archetype-lite-trigger</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</dependencyManagement>
......
25-03-02.22:16:34.489 [main ] INFO AwardDaoTest - Starting AwardDaoTest using Java 17.0.10 on xruicc with PID 4280 (started by 15505 in D:\MarketingIntegrationSystem\xfg-frame-archetype-lite-app)
25-03-02.22:16:34.490 [main ] INFO AwardDaoTest - The following 1 profile is active: "dev"
25-03-02.22:16:36.666 [main ] INFO AwardDaoTest - Started AwardDaoTest in 2.655 seconds (JVM running for 3.732)
25-03-02.22:16:37.163 [main ] INFO HikariDataSource - HikariPool-1 - Starting...
25-03-02.22:16:37.722 [main ] INFO HikariDataSource - HikariPool-1 - Start completed.
25-03-02.22:16:38.021 [main ] INFO AwardDaoTest - 测试结果:[{"awardConfig":"1,100","awardDesc":"用户积分【优先透彻规则范围,如果没有则走配置】","awardId":101,"awardKey":"user_credit_random"},{"awardConfig":"5","awardDesc":"OpenAI 增加使用次数","awardId":102,"awardKey":"openai_use_count"},{"awardConfig":"10","awardDesc":"OpenAI 增加使用次数","awardId":103,"awardKey":"openai_use_count"},{"awardConfig":"20","awardDesc":"OpenAI 增加使用次数","awardId":104,"awardKey":"openai_use_count"},{"awardConfig":"gpt-4","awardDesc":"OpenAI 增加模型","awardId":105,"awardKey":"openai_model"},{"awardConfig":"dall-e-2","awardDesc":"OpenAI 增加模型","awardId":106,"awardKey":"openai_model"},{"awardConfig":"dall-e-3","awardDesc":"OpenAI 增加模型","awardId":107,"awardKey":"openai_model"},{"awardConfig":"100","awardDesc":"OpenAI 增加使用次数","awardId":108,"awardKey":"openai_use_count"},{"awardConfig":"gpt-4,dall-e-2,dall-e-3","awardDesc":"OpenAI 增加模型","awardId":109,"awardKey":"openai_model"}]
25-03-02.22:16:38.034 [SpringApplicationShutdownHook] INFO HikariDataSource - HikariPool-1 - Shutdown initiated...
25-03-02.22:16:38.046 [SpringApplicationShutdownHook] INFO HikariDataSource - HikariPool-1 - Shutdown completed.
25-03-02.22:16:34.489 [main ] INFO AwardDaoTest - Starting AwardDaoTest using Java 17.0.10 on xruicc with PID 4280 (started by 15505 in D:\MarketingIntegrationSystem\xfg-frame-archetype-lite-app)
25-03-02.22:16:34.490 [main ] INFO AwardDaoTest - The following 1 profile is active: "dev"
25-03-02.22:16:36.666 [main ] INFO AwardDaoTest - Started AwardDaoTest in 2.655 seconds (JVM running for 3.732)
25-03-02.22:16:37.163 [main ] INFO HikariDataSource - HikariPool-1 - Starting...
25-03-02.22:16:37.722 [main ] INFO HikariDataSource - HikariPool-1 - Start completed.
25-03-02.22:16:38.021 [main ] INFO AwardDaoTest - 测试结果:[{"awardConfig":"1,100","awardDesc":"用户积分【优先透彻规则范围,如果没有则走配置】","awardId":101,"awardKey":"user_credit_random"},{"awardConfig":"5","awardDesc":"OpenAI 增加使用次数","awardId":102,"awardKey":"openai_use_count"},{"awardConfig":"10","awardDesc":"OpenAI 增加使用次数","awardId":103,"awardKey":"openai_use_count"},{"awardConfig":"20","awardDesc":"OpenAI 增加使用次数","awardId":104,"awardKey":"openai_use_count"},{"awardConfig":"gpt-4","awardDesc":"OpenAI 增加模型","awardId":105,"awardKey":"openai_model"},{"awardConfig":"dall-e-2","awardDesc":"OpenAI 增加模型","awardId":106,"awardKey":"openai_model"},{"awardConfig":"dall-e-3","awardDesc":"OpenAI 增加模型","awardId":107,"awardKey":"openai_model"},{"awardConfig":"100","awardDesc":"OpenAI 增加使用次数","awardId":108,"awardKey":"openai_use_count"},{"awardConfig":"gpt-4,dall-e-2,dall-e-3","awardDesc":"OpenAI 增加模型","awardId":109,"awardKey":"openai_model"}]
25-03-02.22:16:38.034 [SpringApplicationShutdownHook] INFO HikariDataSource - HikariPool-1 - Shutdown initiated...
25-03-02.22:16:38.046 [SpringApplicationShutdownHook] INFO HikariDataSource - HikariPool-1 - Shutdown completed.
25-03-03.21:06:31.068 [main ] INFO ApiTest - Starting ApiTest using Java 17.0.10 on xruicc with PID 27140 (started by 15505 in D:\MarketingIntegrationSystem\xfg-frame-archetype-lite-app)
25-03-03.21:06:31.069 [main ] INFO ApiTest - The following 1 profile is active: "dev"
25-03-03.21:06:32.143 [main ] INFO RepositoryConfigurationDelegate - Multiple Spring Data modules found, entering strict repository configuration mode
25-03-03.21:06:32.148 [main ] INFO RepositoryConfigurationDelegate - Bootstrapping Spring Data Redis repositories in DEFAULT mode.
25-03-03.21:06:32.189 [main ] INFO RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 14 ms. Found 0 Redis repository interfaces.
25-03-03.21:06:33.173 [main ] INFO Version - Redisson 3.23.4
25-03-03.21:06:33.646 [redisson-netty-2-4] INFO MasterPubSubConnectionPool - 1 connections initialized for 192.168.1.10/192.168.1.10:6379
25-03-03.21:06:33.662 [redisson-netty-2-13] INFO MasterConnectionPool - 5 connections initialized for 192.168.1.10/192.168.1.10:6379
25-03-03.21:06:35.292 [main ] INFO EndpointLinksResolver - Exposing 1 endpoint(s) beneath base path '/actuator'
25-03-03.21:06:35.347 [main ] INFO ApiTest - Started ApiTest in 4.759 seconds (JVM running for 5.923)
25-03-03.21:06:35.870 [main ] INFO ApiTest - 测试结果:100
25-03-03.22:38:56.367 [main ] INFO StrategyArmoryTest - Starting StrategyArmoryTest using Java 17.0.10 on xruicc with PID 19328 (started by 15505 in D:\MarketingIntegrationSystem\xfg-frame-archetype-lite-app)
25-03-03.22:38:56.368 [main ] INFO StrategyArmoryTest - The following 1 profile is active: "dev"
25-03-03.22:38:57.244 [main ] INFO RepositoryConfigurationDelegate - Multiple Spring Data modules found, entering strict repository configuration mode
25-03-03.22:38:57.246 [main ] INFO RepositoryConfigurationDelegate - Bootstrapping Spring Data Redis repositories in DEFAULT mode.
25-03-03.22:38:57.278 [main ] INFO RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 16 ms. Found 0 Redis repository interfaces.
25-03-03.22:38:58.526 [main ] INFO Version - Redisson 3.23.4
25-03-03.22:38:59.017 [redisson-netty-2-4] INFO MasterPubSubConnectionPool - 1 connections initialized for 192.168.1.10/192.168.1.10:6379
25-03-03.22:38:59.039 [redisson-netty-2-13] INFO MasterConnectionPool - 5 connections initialized for 192.168.1.10/192.168.1.10:6379
25-03-03.22:39:00.415 [main ] INFO EndpointLinksResolver - Exposing 1 endpoint(s) beneath base path '/actuator'
25-03-03.22:39:00.472 [main ] INFO StrategyArmoryTest - Started StrategyArmoryTest in 4.546 seconds (JVM running for 5.706)
25-03-03.22:39:01.015 [main ] INFO HikariDataSource - HikariPool-1 - Starting...
25-03-03.22:39:01.417 [main ] INFO HikariDataSource - HikariPool-1 - Start completed.
25-03-03.22:39:15.717 [SpringApplicationShutdownHook] INFO HikariDataSource - HikariPool-1 - Shutdown initiated...
25-03-03.22:39:15.734 [SpringApplicationShutdownHook] INFO HikariDataSource - HikariPool-1 - Shutdown completed.
25-03-03.22:49:25.462 [main ] INFO StrategyArmoryTest - Starting StrategyArmoryTest using Java 17.0.10 on xruicc with PID 22388 (started by 15505 in D:\MarketingIntegrationSystem\xfg-frame-archetype-lite-app)
25-03-03.22:49:25.462 [main ] INFO StrategyArmoryTest - The following 1 profile is active: "dev"
25-03-03.22:49:26.394 [main ] INFO RepositoryConfigurationDelegate - Multiple Spring Data modules found, entering strict repository configuration mode
25-03-03.22:49:26.396 [main ] INFO RepositoryConfigurationDelegate - Bootstrapping Spring Data Redis repositories in DEFAULT mode.
25-03-03.22:49:26.428 [main ] INFO RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 17 ms. Found 0 Redis repository interfaces.
25-03-03.22:49:27.663 [main ] INFO Version - Redisson 3.23.4
25-03-03.22:49:28.164 [redisson-netty-2-4] INFO MasterPubSubConnectionPool - 1 connections initialized for 192.168.1.10/192.168.1.10:6379
25-03-03.22:49:28.182 [redisson-netty-2-13] INFO MasterConnectionPool - 5 connections initialized for 192.168.1.10/192.168.1.10:6379
25-03-03.22:49:29.535 [main ] INFO EndpointLinksResolver - Exposing 1 endpoint(s) beneath base path '/actuator'
25-03-03.22:49:29.582 [main ] INFO StrategyArmoryTest - Started StrategyArmoryTest in 4.547 seconds (JVM running for 5.653)
25-03-03.22:50:59.298 [main ] INFO StrategyArmoryTest - Starting StrategyArmoryTest using Java 17.0.10 on xruicc with PID 27376 (started by 15505 in D:\MarketingIntegrationSystem\xfg-frame-archetype-lite-app)
25-03-03.22:50:59.299 [main ] INFO StrategyArmoryTest - The following 1 profile is active: "dev"
25-03-03.22:51:00.158 [main ] INFO RepositoryConfigurationDelegate - Multiple Spring Data modules found, entering strict repository configuration mode
25-03-03.22:51:00.160 [main ] INFO RepositoryConfigurationDelegate - Bootstrapping Spring Data Redis repositories in DEFAULT mode.
25-03-03.22:51:00.191 [main ] INFO RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 17 ms. Found 0 Redis repository interfaces.
25-03-03.22:51:01.355 [main ] INFO Version - Redisson 3.23.4
25-03-03.22:51:01.816 [redisson-netty-2-4] INFO MasterPubSubConnectionPool - 1 connections initialized for 192.168.1.10/192.168.1.10:6379
25-03-03.22:51:01.837 [redisson-netty-2-13] INFO MasterConnectionPool - 5 connections initialized for 192.168.1.10/192.168.1.10:6379
25-03-03.22:51:03.156 [main ] INFO EndpointLinksResolver - Exposing 1 endpoint(s) beneath base path '/actuator'
25-03-03.22:51:03.209 [main ] INFO StrategyArmoryTest - Started StrategyArmoryTest in 4.371 seconds (JVM running for 5.499)
25-03-03.22:51:03.710 [main ] INFO HikariDataSource - HikariPool-1 - Starting...
25-03-03.22:51:04.076 [main ] INFO HikariDataSource - HikariPool-1 - Start completed.
25-03-03.22:51:04.349 [SpringApplicationShutdownHook] INFO HikariDataSource - HikariPool-1 - Shutdown initiated...
25-03-03.22:51:04.363 [SpringApplicationShutdownHook] INFO HikariDataSource - HikariPool-1 - Shutdown completed.
25-03-03.22:55:30.466 [main ] INFO StrategyArmoryTest - Starting StrategyArmoryTest using Java 17.0.10 on xruicc with PID 11100 (started by 15505 in D:\MarketingIntegrationSystem\xfg-frame-archetype-lite-app)
25-03-03.22:55:30.467 [main ] INFO StrategyArmoryTest - The following 1 profile is active: "dev"
25-03-03.22:55:31.327 [main ] INFO RepositoryConfigurationDelegate - Multiple Spring Data modules found, entering strict repository configuration mode
25-03-03.22:55:31.332 [main ] INFO RepositoryConfigurationDelegate - Bootstrapping Spring Data Redis repositories in DEFAULT mode.
25-03-03.22:55:31.361 [main ] INFO RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 15 ms. Found 0 Redis repository interfaces.
25-03-03.22:55:32.533 [main ] INFO Version - Redisson 3.23.4
25-03-03.22:55:32.979 [redisson-netty-2-4] INFO MasterPubSubConnectionPool - 1 connections initialized for 192.168.1.10/192.168.1.10:6379
25-03-03.22:55:32.996 [redisson-netty-2-13] INFO MasterConnectionPool - 5 connections initialized for 192.168.1.10/192.168.1.10:6379
25-03-03.22:55:34.444 [main ] INFO EndpointLinksResolver - Exposing 1 endpoint(s) beneath base path '/actuator'
25-03-03.22:55:34.503 [main ] INFO StrategyArmoryTest - Started StrategyArmoryTest in 4.551 seconds (JVM running for 5.69)
25-03-03.23:05:02.637 [main ] INFO StrategyArmoryTest - Starting StrategyArmoryTest using Java 17.0.10 on xruicc with PID 27872 (started by 15505 in D:\MarketingIntegrationSystem\xfg-frame-archetype-lite-app)
25-03-03.23:05:02.638 [main ] INFO StrategyArmoryTest - The following 1 profile is active: "dev"
25-03-03.23:05:03.612 [main ] INFO RepositoryConfigurationDelegate - Multiple Spring Data modules found, entering strict repository configuration mode
25-03-03.23:05:03.615 [main ] INFO RepositoryConfigurationDelegate - Bootstrapping Spring Data Redis repositories in DEFAULT mode.
25-03-03.23:05:03.646 [main ] INFO RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 17 ms. Found 0 Redis repository interfaces.
25-03-03.23:05:05.145 [main ] INFO Version - Redisson 3.23.4
25-03-03.23:05:05.733 [redisson-netty-2-4] INFO MasterPubSubConnectionPool - 1 connections initialized for 192.168.1.10/192.168.1.10:6379
25-03-03.23:05:05.755 [redisson-netty-2-13] INFO MasterConnectionPool - 5 connections initialized for 192.168.1.10/192.168.1.10:6379
25-03-03.23:05:07.553 [main ] INFO EndpointLinksResolver - Exposing 1 endpoint(s) beneath base path '/actuator'
25-03-03.23:05:07.621 [main ] INFO StrategyArmoryTest - Started StrategyArmoryTest in 5.522 seconds (JVM running for 7.136)
25-03-03.23:14:35.673 [main ] INFO StrategyArmoryTest - Starting StrategyArmoryTest using Java 17.0.10 on xruicc with PID 23408 (started by 15505 in D:\MarketingIntegrationSystem\xfg-frame-archetype-lite-app)
25-03-03.23:14:35.674 [main ] INFO StrategyArmoryTest - The following 1 profile is active: "dev"
25-03-03.23:14:36.513 [main ] INFO RepositoryConfigurationDelegate - Multiple Spring Data modules found, entering strict repository configuration mode
25-03-03.23:14:36.517 [main ] INFO RepositoryConfigurationDelegate - Bootstrapping Spring Data Redis repositories in DEFAULT mode.
25-03-03.23:14:36.545 [main ] INFO RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 16 ms. Found 0 Redis repository interfaces.
25-03-03.23:14:37.757 [main ] INFO Version - Redisson 3.23.4
25-03-03.23:14:38.190 [redisson-netty-2-4] INFO MasterPubSubConnectionPool - 1 connections initialized for 192.168.1.10/192.168.1.10:6379
25-03-03.23:14:38.206 [redisson-netty-2-13] INFO MasterConnectionPool - 5 connections initialized for 192.168.1.10/192.168.1.10:6379
25-03-03.23:14:39.491 [main ] INFO EndpointLinksResolver - Exposing 1 endpoint(s) beneath base path '/actuator'
25-03-03.23:14:39.534 [main ] INFO StrategyArmoryTest - Started StrategyArmoryTest in 4.288 seconds (JVM running for 5.373)
25-03-03.23:16:07.203 [main ] INFO StrategyArmoryTest - Starting StrategyArmoryTest using Java 17.0.10 on xruicc with PID 26864 (started by 15505 in D:\MarketingIntegrationSystem\xfg-frame-archetype-lite-app)
25-03-03.23:16:07.204 [main ] INFO StrategyArmoryTest - The following 1 profile is active: "dev"
25-03-03.23:16:08.184 [main ] INFO RepositoryConfigurationDelegate - Multiple Spring Data modules found, entering strict repository configuration mode
25-03-03.23:16:08.186 [main ] INFO RepositoryConfigurationDelegate - Bootstrapping Spring Data Redis repositories in DEFAULT mode.
25-03-03.23:16:08.214 [main ] INFO RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 16 ms. Found 0 Redis repository interfaces.
25-03-03.23:16:09.432 [main ] INFO Version - Redisson 3.23.4
25-03-03.23:16:09.894 [redisson-netty-2-4] INFO MasterPubSubConnectionPool - 1 connections initialized for 192.168.1.10/192.168.1.10:6379
25-03-03.23:16:09.914 [redisson-netty-2-13] INFO MasterConnectionPool - 5 connections initialized for 192.168.1.10/192.168.1.10:6379
25-03-03.23:16:11.278 [main ] INFO EndpointLinksResolver - Exposing 1 endpoint(s) beneath base path '/actuator'
25-03-03.23:16:11.330 [main ] INFO StrategyArmoryTest - Started StrategyArmoryTest in 4.566 seconds (JVM running for 5.677)
25-03-03.23:16:11.810 [main ] INFO StrategyArmoryTest - 测试:101
25-03-03.23:16:11.813 [main ] INFO StrategyArmoryTest - 测试:101
25-03-03.23:16:11.817 [main ] INFO StrategyArmoryTest - 测试:101
......@@ -12,6 +12,10 @@
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
......
package cn.bugstack.config;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.BaseCodec;
import org.redisson.client.protocol.Decoder;
import org.redisson.client.protocol.Encoder;
import org.redisson.codec.JsonJacksonCodec;
import org.redisson.config.Config;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
/**
* Redis 客户端,使用 Redisson <a href="https://github.com/redisson/redisson">Redisson</a>
*
*
*/
@Configuration
@EnableConfigurationProperties(RedisClientConfigProperties.class)
public class RedisClientConfig {
@Bean("redissonClient")
public RedissonClient redissonClient(ConfigurableApplicationContext applicationContext, RedisClientConfigProperties properties) {
Config config = new Config();
// 根据需要可以设定编解码器;https://github.com/redisson/redisson/wiki/4.-%E6%95%B0%E6%8D%AE%E5%BA%8F%E5%88%97%E5%8C%96
config.setCodec(JsonJacksonCodec.INSTANCE);
config.useSingleServer()
.setAddress("redis://" + properties.getHost() + ":" + properties.getPort())
// .setPassword(properties.getPassword())
.setConnectionPoolSize(properties.getPoolSize())
.setConnectionMinimumIdleSize(properties.getMinIdleSize())
.setIdleConnectionTimeout(properties.getIdleTimeout())
.setConnectTimeout(properties.getConnectTimeout())
.setRetryAttempts(properties.getRetryAttempts())
.setRetryInterval(properties.getRetryInterval())
.setPingConnectionInterval(properties.getPingInterval())
.setKeepAlive(properties.isKeepAlive())
;
return Redisson.create(config);
}
static class RedisCodec extends BaseCodec {
private final Encoder encoder = in -> {
ByteBuf out = ByteBufAllocator.DEFAULT.buffer();
try {
ByteBufOutputStream os = new ByteBufOutputStream(out);
JSON.writeJSONString(os, in, SerializerFeature.WriteClassName);
return os.buffer();
} catch (IOException e) {
out.release();
throw e;
} catch (Exception e) {
out.release();
throw new IOException(e);
}
};
private final Decoder<Object> decoder = (buf, state) -> JSON.parseObject(new ByteBufInputStream(buf), Object.class);
@Override
public Decoder<Object> getValueDecoder() {
return decoder;
}
@Override
public Encoder getValueEncoder() {
return encoder;
}
}
}
package cn.bugstack.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
*
* @description Redis 连接配置 <a href="https://github.com/redisson/redisson/tree/master/redisson-spring-boot-starter">redisson-spring-boot-starter</a>
*
*/
@Data
@ConfigurationProperties(prefix = "redis.sdk.config", ignoreInvalidFields = true)
public class RedisClientConfigProperties {
/** host:ip */
private String host;
/** 端口 */
private int port;
/** 账密 */
private String password;
/** 设置连接池的大小,默认为64 */
private int poolSize = 64;
/** 设置连接池的最小空闲连接数,默认为10 */
private int minIdleSize = 10;
/** 设置连接的最大空闲时间(单位:毫秒),超过该时间的空闲连接将被关闭,默认为10000 */
private int idleTimeout = 10000;
/** 设置连接超时时间(单位:毫秒),默认为10000 */
private int connectTimeout = 10000;
/** 设置连接重试次数,默认为3 */
private int retryAttempts = 3;
/** 设置连接重试的间隔时间(单位:毫秒),默认为1000 */
private int retryInterval = 1000;
/** 设置定期检查连接是否可用的时间间隔(单位:毫秒),默认为0,表示不进行定期检查 */
private int pingInterval = 0;
/** 设置是否保持长连接,默认为true */
private boolean keepAlive = true;
}
......@@ -34,6 +34,21 @@ mybatis:
mapper-locations: classpath:/mybatis/mapper/*.xml
config-location: classpath:/mybatis/config/mybatis-config.xml
# Redis
redis:
sdk:
config:
host: 192.168.1.10
port: 6379
pool-size: 10
min-idle-size: 5
idle-timeout: 30000
connect-timeout: 5000
retry-attempts: 3
retry-interval: 1000
ping-interval: 60000
keep-alive: true
# 日志
logging:
level:
......
package cn.bugstack.test;
import cn.bugstack.infrastructure.redis.IRedisService;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.redisson.api.RMap;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class ApiTest {
@Resource
private IRedisService redisService;
@Test
public void test() {
log.info("测试完成");
public void test(){
RMap<Object,Object> map=redisService.getMap("stategy_id_100001");
map.put(1,100);
map.put(2,101);
log.info("测试结果:{}",redisService.getFromMap("stategy_id_100001",1).toString());
}
}
package cn.bugstack.test.domain;
import cn.bugstack.domain.strategy.service.armory.IStrategyArmory;
import cn.bugstack.infrastructure.dao.IStrategyAwardDao;
import cn.bugstack.infrastructure.redis.IRedisService;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.redisson.api.RMap;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class StrategyArmoryTest {
@Resource
private IStrategyArmory strategyArmory;
@Test
public void test(){
boolean success=strategyArmory.assembleLotteryStrategy(100001l);
}
@Test
public void test_getRandomAward(){
log.info("测试:{}", strategyArmory.getRandomAwardId(100001l));
log.info("测试:{}", strategyArmory.getRandomAwardId(100001l));
log.info("测试:{}", strategyArmory.getRandomAwardId(100001l));
}
}
\ No newline at end of file
......@@ -10,6 +10,10 @@
<artifactId>xfg-frame-archetype-lite-domain</artifactId>
<dependencies>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
......
package cn.bugstack.domain.strategy.model.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class StrategyAwardEntity {
/** 抽奖策略ID */
private Long strategyId;
/** 抽奖奖品ID - 内部流转使用 */
private Integer awardId;
/** 抽奖奖品标题 */
private String awardTitle;
/** 抽奖奖品副标题 */
private String awardSubtitle;
/** 奖品库存总量 */
private Integer awardCount;
/** 奖品库存剩余 */
private Integer awardCountSurplus;
/** 奖品中奖概率 */
private BigDecimal awardRate;
/** 排序 */
private Integer sort;
/** 规则模型,rule配置的模型同步到此表,便于使用 */
private String ruleModels;
}
package cn.bugstack.domain.strategy.repository;
import cn.bugstack.domain.strategy.model.entity.StrategyAwardEntity;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* @description 策略服务仓储接口
*/
public interface IStrategyRepository {
List<StrategyAwardEntity> queryStrategyAwardList(Long strategyId);
void storeStrategyAwardSearchRateTable(Long strategyId, int size, Map<Integer, Integer> shuffleStrategyAwardSearchRateTable);
int getRateRange(Long strategyId);
Integer getStrategyAwardAssemble(Long strategyId, Integer rateKey);
// List<StrategyAwardEntity> queryStrategyAwardList(Long strategyId);
//
// <K, V> void storeStrategyAwardSearchRateTable(String key, Integer rateRange, Map<K, V> strategyAwardSearchRateTable);
//
// <K, V> Map<K, V> getMap(String key);
//
// Integer getStrategyAwardAssemble(String key, Integer rateKey);
//
// int getRateRange(Long strategyId);
//
// int getRateRange(String key);
//
// StrategyEntity queryStrategyEntityByStrategyId(Long strategyId);
//
// StrategyRuleEntity queryStrategyRule(Long strategyId, String ruleModel);
//
// String queryStrategyRuleValue(Long strategyId, String ruleModel);
//
// String queryStrategyRuleValue(Long strategyId, Integer awardId, String ruleModel);
//
// StrategyAwardRuleModelVO queryStrategyAwardRuleModelVO(Long strategyId, Integer awardId);
//
// /**
// * 根据规则树ID,查询树结构信息
// *
// * @param treeId 规则树ID
// * @return 树结构信息
// */
// RuleTreeVO queryRuleTreeVOByTreeId(String treeId);
//
// /**
// * 缓存奖品库存
// *
// * @param cacheKey key
// * @param awardCount 库存值
// */
// void cacheStrategyAwardCount(String cacheKey, Integer awardCount);
//
// /**
// * 缓存key,decr 方式扣减库存
// *
// * @param cacheKey 缓存Key
// * @return 扣减结果
// */
// Boolean subtractionAwardStock(String cacheKey);
//
// /**
// * 缓存key,decr 方式扣减库存
// *
// * @param cacheKey 缓存Key
// * @param endDateTime 活动结束时间
// * @return 扣减结果
// */
// Boolean subtractionAwardStock(String cacheKey, Date endDateTime);
//
// /**
// * 写入奖品库存消费队列
// *
// * @param strategyAwardStockKeyVO 对象值对象
// */
// void awardStockConsumeSendQueue(StrategyAwardStockKeyVO strategyAwardStockKeyVO);
//
// /**
// * 获取奖品库存消费队列
// */
// StrategyAwardStockKeyVO takeQueueValue() throws InterruptedException;
//
// /**
// * 获取奖品库存消费队列
// */
// StrategyAwardStockKeyVO takeQueueValue(Long strategyId, Integer awardId) throws InterruptedException;
//
// /**
// * 更新奖品库存消耗
// *
// * @param strategyId 策略ID
// * @param awardId 奖品ID
// */
// void updateStrategyAwardStock(Long strategyId, Integer awardId);
//
// /**
// * 根据策略ID+奖品ID的唯一值组合,查询奖品信息
// *
// * @param strategyId 策略ID
// * @param awardId 奖品ID
// * @return 奖品信息
// */
// StrategyAwardEntity queryStrategyAwardEntity(Long strategyId, Integer awardId);
//
// /**
// * 查询策略ID
// *
// * @param activityId 活动ID
// * @return 策略ID
// */
// Long queryStrategyIdByActivityId(Long activityId);
//
// /**
// * 查询用户抽奖次数 - 当天的;策略ID:活动ID 1:1 的配置,可以直接用 strategyId 查询。
// *
// * @param userId 用户ID
// * @param strategyId 策略ID
// * @return 用户今日参与次数
// */
// Integer queryTodayUserRaffleCount(String userId, Long strategyId);
//
// /**
// * 根据规则树ID集合查询奖品中加锁数量的配置「部分奖品需要抽奖N次解锁」
// *
// * @param treeIds 规则树ID值
// * @return key 规则树,value rule_lock 加锁值
// */
// Map<String, Integer> queryAwardRuleLockCount(String[] treeIds);
//
// /**
// * 根据用户ID、策略ID,查询用户活动账户总使用量
// *
// * @param userId 用户ID
// * @param strategyId 策略ID
// * @return 使用总量
// */
// Integer queryActivityAccountTotalUseCount(String userId, Long strategyId);
//
// /**
// * 查询奖品权重配置
// *
// * @param strategyId 策略ID
// * @return 权重规则
// */
// List<RuleWeightVO> queryAwardRuleWeight(Long strategyId);
//
// /**
// * 查询有效活动的奖品配置
// *
// * @return 奖品配置列表
// */
// List<StrategyAwardStockKeyVO> queryOpenActivityStrategyAwardList();
//
// /**
// * 存储抽奖策略对应的Bean算法
// *
// * @param key 策略ID
// * @param beanName 策略对象名称
// */
// void cacheStrategyArmoryAlgorithm(String key, String beanName);
//
// /**
// * 获取存储抽奖策略对应的Bean算法
// *
// * @param key 策略ID
// * @return 策略对象名称
// */
// String queryStrategyArmoryAlgorithmFromCache(String key);
}
package cn.bugstack.domain.strategy.service.armory;
/**
* @description 策略装配库(兵工厂),负责初始化策略计算
*/
public interface IStrategyArmory {
/**
* 装配抽奖策略配置「触发的时机可以为活动审核通过后进行调用」
*
* @param strategyId 策略ID
* @return 装配结果
*/
boolean assembleLotteryStrategy(Long strategyId);
/**
* 获取抽奖策略装配的随机结果
*
* @param strategyId 策略ID
* @return 抽奖结果
*/
Integer getRandomAwardId(Long strategyId);
}
package cn.bugstack.domain.strategy.service.armory;
import cn.bugstack.domain.strategy.model.entity.StrategyAwardEntity;
import cn.bugstack.domain.strategy.repository.IStrategyRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.security.SecureRandom;
import java.util.Collections;
import java.util.*;
/**
* @desciption 策略装配库
*/
@Slf4j
@Service
public class StrategyArmory implements IStrategyArmory {
@Resource
private IStrategyRepository repository;
@Override
public boolean assembleLotteryStrategy(Long strategyId) {
//取得了配置实体类
List<StrategyAwardEntity> strategyAwardEntities=repository.queryStrategyAwardList(strategyId);
//最小的概率
BigDecimal minAwardRate=strategyAwardEntities.stream()
.map(StrategyAwardEntity :: getAwardRate)
.min(BigDecimal::compareTo)
.orElse(BigDecimal.ZERO);
//总的值
BigDecimal totalAwardRate=strategyAwardEntities.stream()
.map(StrategyAwardEntity::getAwardRate)
.reduce(BigDecimal.ZERO,BigDecimal::add);
BigDecimal rateRage=totalAwardRate.divide(minAwardRate,0, RoundingMode.CEILING);
// 5. 生成策略奖品概率查找表「这里指需要在list集合中,存放上对应的奖品占位即可,占位越多等于概率越高」
List<Integer> strategyAwardSearchRateTables = new ArrayList<>(rateRage.intValue());
for (StrategyAwardEntity strategyAward : strategyAwardEntities) {
Integer awardId = strategyAward.getAwardId();
BigDecimal awardRate = strategyAward.getAwardRate();
// 计算出每个概率值需要存放到查找表的数量,循环填充
for (int i = 0; i < rateRage.multiply(awardRate).setScale(0, RoundingMode.CEILING).intValue(); i++) {
strategyAwardSearchRateTables.add(awardId);
}
}
// 6. 对存储的奖品进行乱序操作
Collections.shuffle(strategyAwardSearchRateTables);
// 7. 生成出Map集合,key值,对应的就是后续的概率值。通过概率来获得对应的奖品ID
Map<Integer, Integer> shuffleStrategyAwardSearchRateTable = new LinkedHashMap<>();
for (int i = 0; i < strategyAwardSearchRateTables.size(); i++) {
shuffleStrategyAwardSearchRateTable.put(i, strategyAwardSearchRateTables.get(i));
}
// 8. 存放到 Redis
repository.storeStrategyAwardSearchRateTable(strategyId, shuffleStrategyAwardSearchRateTable.size(), shuffleStrategyAwardSearchRateTable);
return true;
}
@Override
public Integer getRandomAwardId(Long strategyId) {
// 分布式部署下,不一定为当前应用做的策略装配。也就是值不一定会保存到本应用,而是分布式应用,所以需要从 Redis 中获取。
int rateRange = repository.getRateRange(strategyId);
// 通过生成的随机值,获取概率值奖品查找表的结果
return repository.getStrategyAwardAssemble(strategyId, new SecureRandom().nextInt(rateRange));
}
}
......@@ -18,11 +18,17 @@
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
</dependency>
<!-- 系统模块 -->
<dependency>
<groupId>cn.bugstack</groupId>
<artifactId>xfg-frame-archetype-lite-domain</artifactId>
</dependency>
</dependencies>
<build>
......
package cn.bugstack.infrastructure.adapter.repository;
import cn.bugstack.domain.strategy.model.entity.StrategyAwardEntity;
import cn.bugstack.domain.strategy.repository.IStrategyRepository;
import cn.bugstack.infrastructure.dao.IStrategyAwardDao;
import cn.bugstack.infrastructure.dao.IStrategyDao;
import cn.bugstack.infrastructure.dao.IStrategyRuleDao;
import cn.bugstack.infrastructure.dao.po.Strategy;
import cn.bugstack.infrastructure.dao.po.StrategyAward;
import cn.bugstack.infrastructure.dao.po.StrategyRule;
import cn.bugstack.infrastructure.redis.IRedisService;
import cn.bugstack.types.common.Constants;
import cn.bugstack.types.exception.AppException;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* @description 策略服务仓储实现
*/
@Slf4j
@Repository
public class StrategyRepository implements IStrategyRepository {
@Resource
private IStrategyAwardDao strategyAwardDao;
// @Resource
// private IRaffleActivityDao raffleActivityDao;
// @Resource
// private IStrategyDao strategyDao;
// @Resource
// private IStrategyRuleDao strategyRuleDao;
// @Resource
// private IRaffleActivityAccountDao raffleActivityAccountDao;
// @Resource
// private IRaffleActivityAccountDayDao raffleActivityAccountDayDao;
@Resource
private IRedisService redisService;
// @Resource
// private IRuleTreeDao ruleTreeDao;
// @Resource
// private IRuleTreeNodeDao ruleTreeNodeDao;
// @Resource
// private IRuleTreeNodeLineDao ruleTreeNodeLineDao;
//
@Override
public List<StrategyAwardEntity> queryStrategyAwardList(Long strategyId) {
// 优先从缓存获取
String cacheKey = Constants.RedisKey.STRATEGY_AWARD_LIST_KEY + strategyId;
List<StrategyAwardEntity> strategyAwardEntities = redisService.getValue(cacheKey);
if (null != strategyAwardEntities && !strategyAwardEntities.isEmpty()) return strategyAwardEntities;
// 从库中获取数据
List<StrategyAward> strategyAwards = strategyAwardDao.queryStrategyAwardListByStrategyId(strategyId);
strategyAwardEntities = new ArrayList<>(strategyAwards.size());
for (StrategyAward strategyAward : strategyAwards) {
StrategyAwardEntity strategyAwardEntity = StrategyAwardEntity.builder()
.strategyId(strategyAward.getStrategyId())
.awardId(strategyAward.getAwardId())
.awardTitle(strategyAward.getAwardTitle())
.awardSubtitle(strategyAward.getAwardSubtitle())
.awardCount(strategyAward.getAwardCount())
.awardCountSurplus(strategyAward.getAwardCountSurplus())
.awardRate(strategyAward.getAwardRate())
.sort(strategyAward.getSort())
.ruleModels(strategyAward.getRuleModels())
.build();
strategyAwardEntities.add(strategyAwardEntity);
}
redisService.setValue(cacheKey, strategyAwardEntities);
return strategyAwardEntities;
}
/**
* 在 Redisson 中,当你调用 getMap 方法时,如果指定的 key 不存在,Redisson 并不会立即在 Redis 数据库中创建这个 key。相反,它会返回一个 RMap 对象的实例,这个实例是一个本地的 Java 对象,它代表了 Redis 中的一个哈希(hash)。
* <p>
* 当你开始使用这个 RMap 实例进行操作,比如添加键值对,那么 Redisson 会在 Redis 数据库中创建相应的 key,并将数据存储在这个 key 对应的哈希中。如果你只是获取了 RMap 实例而没有进行任何操作,那么在 Redis 数据库中是不会有任何变化的。
* <p>
* 简单来说,getMap 方法返回的 RMap 对象是懒加载的,只有在你实际进行操作时,Redis 数据库中的数据结构才会被创建或修改。
*/
@Override
public void storeStrategyAwardSearchRateTable(Long strategyId, int size, Map<Integer, Integer> shuffleStrategyAwardSearchRateTable) {
// 1. 存储抽奖策略范围值,如10000,用于生成1000以内的随机数
redisService.setValue(Constants.RedisKey.STRATEGY_RATE_RANGE_KEY + strategyId, size);
// 2. 存储概率查找表
Map<Integer, Integer> cacheRateTable = redisService.getMap(Constants.RedisKey.STRATEGY_RATE_TABLE_KEY + strategyId);
cacheRateTable.putAll(shuffleStrategyAwardSearchRateTable);
}
@Override
public int getRateRange(Long strategyId) {
return redisService.getValue(Constants.RedisKey.STRATEGY_RATE_RANGE_KEY + strategyId);
}
@Override
public Integer getStrategyAwardAssemble(Long strategyId, Integer rateKey) {
return redisService.getFromMap(Constants.RedisKey.STRATEGY_RATE_TABLE_KEY + strategyId,rateKey);
}
//
// @Override
// public <K, V> Map<K, V> getMap(String key) {
// return redisService.getMap(Constants.RedisKey.STRATEGY_RATE_TABLE_KEY + key);
// }
//
// @Override
// public Integer getStrategyAwardAssemble(String key, Integer rateKey) {
// return redisService.getFromMap(Constants.RedisKey.STRATEGY_RATE_TABLE_KEY + key, rateKey);
// }
//
// @Override
// public int getRateRange(Long strategyId) {
// return getRateRange(String.valueOf(strategyId));
// }
//
// @Override
// public int getRateRange(String key) {
// String cacheKey = Constants.RedisKey.STRATEGY_RATE_RANGE_KEY + key;
// if (!redisService.isExists(cacheKey)) {
// throw new AppException(UN_ASSEMBLED_STRATEGY_ARMORY.getCode(), cacheKey + Constants.COLON + UN_ASSEMBLED_STRATEGY_ARMORY.getInfo());
// }
// return redisService.getValue(cacheKey);
// }
//
// @Override
// public StrategyEntity queryStrategyEntityByStrategyId(Long strategyId) {
// // 优先从缓存获取
// String cacheKey = Constants.RedisKey.STRATEGY_KEY + strategyId;
// StrategyEntity strategyEntity = redisService.getValue(cacheKey);
// if (null != strategyEntity) return strategyEntity;
// Strategy strategy = strategyDao.queryStrategyByStrategyId(strategyId);
// if (null == strategy) return StrategyEntity.builder().build();
// strategyEntity = StrategyEntity.builder()
// .strategyId(strategy.getStrategyId())
// .strategyDesc(strategy.getStrategyDesc())
// .ruleModels(strategy.getRuleModels())
// .build();
// redisService.setValue(cacheKey, strategyEntity);
// return strategyEntity;
// }
//
// @Override
// public StrategyRuleEntity queryStrategyRule(Long strategyId, String ruleModel) {
// StrategyRule strategyRuleReq = new StrategyRule();
// strategyRuleReq.setStrategyId(strategyId);
// strategyRuleReq.setRuleModel(ruleModel);
// StrategyRule strategyRuleRes = strategyRuleDao.queryStrategyRule(strategyRuleReq);
// if (null == strategyRuleRes) return null;
// return StrategyRuleEntity.builder()
// .strategyId(strategyRuleRes.getStrategyId())
// .awardId(strategyRuleRes.getAwardId())
// .ruleType(strategyRuleRes.getRuleType())
// .ruleModel(strategyRuleRes.getRuleModel())
// .ruleValue(strategyRuleRes.getRuleValue())
// .ruleDesc(strategyRuleRes.getRuleDesc())
// .build();
// }
//
// @Override
// public String queryStrategyRuleValue(Long strategyId, String ruleModel) {
// return queryStrategyRuleValue(strategyId, null, ruleModel);
// }
//
// @Override
// public String queryStrategyRuleValue(Long strategyId, Integer awardId, String ruleModel) {
// StrategyRule strategyRule = new StrategyRule();
// strategyRule.setStrategyId(strategyId);
// strategyRule.setAwardId(awardId);
// strategyRule.setRuleModel(ruleModel);
// return strategyRuleDao.queryStrategyRuleValue(strategyRule);
// }
//
// @Override
// public StrategyAwardRuleModelVO queryStrategyAwardRuleModelVO(Long strategyId, Integer awardId) {
// StrategyAward strategyAward = new StrategyAward();
// strategyAward.setStrategyId(strategyId);
// strategyAward.setAwardId(awardId);
// String ruleModels = strategyAwardDao.queryStrategyAwardRuleModels(strategyAward);
// if (null == ruleModels) return null;
// return StrategyAwardRuleModelVO.builder().ruleModels(ruleModels).build();
// }
//
// @Override
// public RuleTreeVO queryRuleTreeVOByTreeId(String treeId) {
// // 优先从缓存获取
// String cacheKey = Constants.RedisKey.RULE_TREE_VO_KEY + treeId;
// RuleTreeVO ruleTreeVOCache = redisService.getValue(cacheKey);
// if (null != ruleTreeVOCache) return ruleTreeVOCache;
//
// // 从数据库获取
// RuleTree ruleTree = ruleTreeDao.queryRuleTreeByTreeId(treeId);
// List<RuleTreeNode> ruleTreeNodes = ruleTreeNodeDao.queryRuleTreeNodeListByTreeId(treeId);
// List<RuleTreeNodeLine> ruleTreeNodeLines = ruleTreeNodeLineDao.queryRuleTreeNodeLineListByTreeId(treeId);
//
// // 1. tree node line 转换Map结构
// Map<String, List<RuleTreeNodeLineVO>> ruleTreeNodeLineMap = new HashMap<>();
// for (RuleTreeNodeLine ruleTreeNodeLine : ruleTreeNodeLines) {
// RuleTreeNodeLineVO ruleTreeNodeLineVO = RuleTreeNodeLineVO.builder()
// .treeId(ruleTreeNodeLine.getTreeId())
// .ruleNodeFrom(ruleTreeNodeLine.getRuleNodeFrom())
// .ruleNodeTo(ruleTreeNodeLine.getRuleNodeTo())
// .ruleLimitType(RuleLimitTypeVO.valueOf(ruleTreeNodeLine.getRuleLimitType()))
// .ruleLimitValue(RuleLogicCheckTypeVO.valueOf(ruleTreeNodeLine.getRuleLimitValue()))
// .build();
//
// ruleTreeNodeLineMap
// .computeIfAbsent(ruleTreeNodeLine.getRuleNodeFrom(), k -> new ArrayList<>())
// .add(ruleTreeNodeLineVO);
// }
//
// // 2. tree node 转换为Map结构
// Map<String, RuleTreeNodeVO> treeNodeMap = new HashMap<>();
// for (RuleTreeNode ruleTreeNode : ruleTreeNodes) {
// RuleTreeNodeVO ruleTreeNodeVO = RuleTreeNodeVO.builder()
// .treeId(ruleTreeNode.getTreeId())
// .ruleKey(ruleTreeNode.getRuleKey())
// .ruleDesc(ruleTreeNode.getRuleDesc())
// .ruleValue(ruleTreeNode.getRuleValue())
// .treeNodeLineVOList(ruleTreeNodeLineMap.get(ruleTreeNode.getRuleKey()))
// .build();
// treeNodeMap.put(ruleTreeNode.getRuleKey(), ruleTreeNodeVO);
// }
//
// // 3. 构建 Rule Tree
// RuleTreeVO ruleTreeVODB = RuleTreeVO.builder()
// .treeId(ruleTree.getTreeId())
// .treeName(ruleTree.getTreeName())
// .treeDesc(ruleTree.getTreeDesc())
// .treeRootRuleNode(ruleTree.getTreeRootRuleKey())
// .treeNodeMap(treeNodeMap)
// .build();
//
// redisService.setValue(cacheKey, ruleTreeVODB);
// return ruleTreeVODB;
// }
//
// @Override
// public void cacheStrategyAwardCount(String cacheKey, Integer awardCount) {
// if (redisService.isExists(cacheKey)) return;
// redisService.setAtomicLong(cacheKey, awardCount);
// }
//
// @Override
// public Boolean subtractionAwardStock(String cacheKey) {
// return subtractionAwardStock(cacheKey, null);
// }
//
// /**
// * 扣减库存并加锁操作,decr和0对比,如果是incr操作就和总量对比,和总量对比可以动态添加库存
// *
// * @param cacheKey 缓存Key
// * @param endDateTime 活动结束时间
// */
// @Override
// public Boolean subtractionAwardStock(String cacheKey, Date endDateTime) {
// long surplus = redisService.decr(cacheKey);
// if (surplus < 0) {
// // 库存小于0,恢复为0个
// redisService.setAtomicLong(cacheKey, 0);
// return false;
// }
// // 1. 按照cacheKey decr 后的值,如 99、98、97 和 key 组成为库存锁的key进行使用。
// // 2. 加锁为了兜底,如果后续有恢复库存,手动处理等,也不会超卖。因为所有的可用库存key,都被加锁了。
// String lockKey = cacheKey + Constants.UNDERLINE + surplus;
// Boolean lock = false;
// if (null != endDateTime) {
// long expireMillis = endDateTime.getTime() - System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1);
// lock = redisService.setNx(lockKey, expireMillis, TimeUnit.MILLISECONDS);
// } else {
// lock = redisService.setNx(lockKey);
// }
// if (!lock) {
// log.info("策略奖品库存加锁失败 {}", lockKey);
// }
// return lock;
// }
//
// @Override
// public void awardStockConsumeSendQueue(StrategyAwardStockKeyVO strategyAwardStockKeyVO) {
// String cacheKey = Constants.RedisKey.STRATEGY_AWARD_COUNT_QUERY_KEY + Constants.UNDERLINE + strategyAwardStockKeyVO.getStrategyId() + Constants.UNDERLINE + strategyAwardStockKeyVO.getAwardId();
// RBlockingQueue<StrategyAwardStockKeyVO> blockingQueue = redisService.getBlockingQueue(cacheKey);
// RDelayedQueue<StrategyAwardStockKeyVO> delayedQueue = redisService.getDelayedQueue(blockingQueue);
// delayedQueue.offer(strategyAwardStockKeyVO, 3, TimeUnit.SECONDS);
// }
//
// @Override
// public StrategyAwardStockKeyVO takeQueueValue() throws InterruptedException {
// String cacheKey = Constants.RedisKey.STRATEGY_AWARD_COUNT_QUERY_KEY;
// RBlockingQueue<StrategyAwardStockKeyVO> destinationQueue = redisService.getBlockingQueue(cacheKey);
// return destinationQueue.poll();
// }
//
// @Override
// public StrategyAwardStockKeyVO takeQueueValue(Long strategyId, Integer awardId) throws InterruptedException {
// String cacheKey = Constants.RedisKey.STRATEGY_AWARD_COUNT_QUERY_KEY + Constants.UNDERLINE + strategyId + Constants.UNDERLINE + awardId;
// RBlockingQueue<StrategyAwardStockKeyVO> destinationQueue = redisService.getBlockingQueue(cacheKey);
// return destinationQueue.poll();
// }
//
// @Override
// public void updateStrategyAwardStock(Long strategyId, Integer awardId) {
// StrategyAward strategyAward = new StrategyAward();
// strategyAward.setStrategyId(strategyId);
// strategyAward.setAwardId(awardId);
// strategyAwardDao.updateStrategyAwardStock(strategyAward);
// }
//
// @Override
// public StrategyAwardEntity queryStrategyAwardEntity(Long strategyId, Integer awardId) {
// // 优先从缓存获取
// String cacheKey = Constants.RedisKey.STRATEGY_AWARD_KEY + strategyId + Constants.UNDERLINE + awardId;
// StrategyAwardEntity strategyAwardEntity = redisService.getValue(cacheKey);
// if (null != strategyAwardEntity) return strategyAwardEntity;
// // 查询数据
// StrategyAward strategyAwardReq = new StrategyAward();
// strategyAwardReq.setStrategyId(strategyId);
// strategyAwardReq.setAwardId(awardId);
// StrategyAward strategyAwardRes = strategyAwardDao.queryStrategyAward(strategyAwardReq);
// // 转换数据
// strategyAwardEntity = StrategyAwardEntity.builder()
// .strategyId(strategyAwardRes.getStrategyId())
// .awardId(strategyAwardRes.getAwardId())
// .awardTitle(strategyAwardRes.getAwardTitle())
// .awardSubtitle(strategyAwardRes.getAwardSubtitle())
// .awardCount(strategyAwardRes.getAwardCount())
// .awardCountSurplus(strategyAwardRes.getAwardCountSurplus())
// .awardRate(strategyAwardRes.getAwardRate())
// .sort(strategyAwardRes.getSort())
// .build();
// // 缓存结果
// redisService.setValue(cacheKey, strategyAwardEntity);
// // 返回数据
// return strategyAwardEntity;
// }
//
// @Override
// public Long queryStrategyIdByActivityId(Long activityId) {
// return raffleActivityDao.queryStrategyIdByActivityId(activityId);
// }
//
// @Override
// public Integer queryTodayUserRaffleCount(String userId, Long strategyId) {
// // 活动ID
// Long activityId = raffleActivityDao.queryActivityIdByStrategyId(strategyId);
// // 封装参数
// RaffleActivityAccountDay raffleActivityAccountDayReq = new RaffleActivityAccountDay();
// raffleActivityAccountDayReq.setUserId(userId);
// raffleActivityAccountDayReq.setActivityId(activityId);
// raffleActivityAccountDayReq.setDay(RaffleActivityAccountDay.currentDay());
// RaffleActivityAccountDay raffleActivityAccountDay = raffleActivityAccountDayDao.queryActivityAccountDayByUserId(raffleActivityAccountDayReq);
// if (null == raffleActivityAccountDay) return 0;
// // 总次数 - 剩余的,等于今日参与的
// return raffleActivityAccountDay.getDayCount() - raffleActivityAccountDay.getDayCountSurplus();
// }
//
// @Override
// public Map<String, Integer> queryAwardRuleLockCount(String[] treeIds) {
// if (null == treeIds || treeIds.length == 0) return new HashMap<>();
// List<RuleTreeNode> ruleTreeNodes = ruleTreeNodeDao.queryRuleLocks(treeIds);
// Map<String, Integer> resultMap = new HashMap<>();
// for (RuleTreeNode node : ruleTreeNodes) {
// String treeId = node.getTreeId();
// Integer ruleValue = Integer.valueOf(node.getRuleValue());
// resultMap.put(treeId, ruleValue);
// }
// return resultMap;
// }
//
// @Override
// public Integer queryActivityAccountTotalUseCount(String userId, Long strategyId) {
// Long activityId = raffleActivityDao.queryActivityIdByStrategyId(strategyId);
// RaffleActivityAccount raffleActivityAccount = raffleActivityAccountDao.queryActivityAccountByUserId(RaffleActivityAccount.builder()
// .userId(userId)
// .activityId(activityId)
// .build());
// // 返回计算使用量
// return raffleActivityAccount.getTotalCount() - raffleActivityAccount.getTotalCountSurplus();
// }
//
// @Override
// public List<RuleWeightVO> queryAwardRuleWeight(Long strategyId) {
// // 优先从缓存获取
// String cacheKey = Constants.RedisKey.STRATEGY_RULE_WEIGHT_KEY + strategyId;
// List<RuleWeightVO> ruleWeightVOS = redisService.getValue(cacheKey);
// if (null != ruleWeightVOS) return ruleWeightVOS;
//
// ruleWeightVOS = new ArrayList<>();
// // 1. 查询权重规则配置
// StrategyRule strategyRuleReq = new StrategyRule();
// strategyRuleReq.setStrategyId(strategyId);
// strategyRuleReq.setRuleModel(DefaultChainFactory.LogicModel.RULE_WEIGHT.getCode());
// String ruleValue = strategyRuleDao.queryStrategyRuleValue(strategyRuleReq);
// // 2. 借助实体对象转换规则
// StrategyRuleEntity strategyRuleEntity = new StrategyRuleEntity();
// strategyRuleEntity.setRuleModel(DefaultChainFactory.LogicModel.RULE_WEIGHT.getCode());
// strategyRuleEntity.setRuleValue(ruleValue);
// Map<String, List<Integer>> ruleWeightValues = strategyRuleEntity.getRuleWeightValues();
// // 3. 遍历规则组装奖品配置
// Set<String> ruleWeightKeys = ruleWeightValues.keySet();
// for (String ruleWeightKey : ruleWeightKeys) {
// List<Integer> awardIds = ruleWeightValues.get(ruleWeightKey);
// List<RuleWeightVO.Award> awardList = new ArrayList<>();
// // 也可以修改为一次从数据库查询
// for (Integer awardId : awardIds) {
// StrategyAward strategyAwardReq = new StrategyAward();
// strategyAwardReq.setStrategyId(strategyId);
// strategyAwardReq.setAwardId(awardId);
// StrategyAward strategyAward = strategyAwardDao.queryStrategyAward(strategyAwardReq);
// awardList.add(RuleWeightVO.Award.builder()
// .awardId(strategyAward.getAwardId())
// .awardTitle(strategyAward.getAwardTitle())
// .build());
// }
//
// ruleWeightVOS.add(RuleWeightVO.builder()
// .ruleValue(ruleValue)
// .weight(Integer.valueOf(ruleWeightKey.split(Constants.COLON)[0]))
// .awardIds(awardIds)
// .awardList(awardList)
// .build());
// }
//
// // 设置缓存 - 实际场景中,这类数据,可以在活动下架的时候统一清空缓存。
// redisService.setValue(cacheKey, ruleWeightVOS);
//
// return ruleWeightVOS;
// }
//
// @Override
// public List<StrategyAwardStockKeyVO> queryOpenActivityStrategyAwardList() {
// List<StrategyAward> strategyAwards = strategyAwardDao.queryOpenActivityStrategyAwardList();
// if (null == strategyAwards || strategyAwards.isEmpty()) return null;
//
// List<StrategyAwardStockKeyVO> strategyAwardStockKeyVOS = new ArrayList<>();
// for (StrategyAward strategyAward : strategyAwards) {
// StrategyAwardStockKeyVO strategyAwardStockKeyVO = StrategyAwardStockKeyVO.builder()
// .strategyId(strategyAward.getStrategyId())
// .awardId(strategyAward.getAwardId())
// .build();
// strategyAwardStockKeyVOS.add(strategyAwardStockKeyVO);
// }
//
// return strategyAwardStockKeyVOS;
// }
//
// @Override
// public void cacheStrategyArmoryAlgorithm(String key, String beanName) {
// String cacheKey = Constants.RedisKey.STRATEGY_ARMORY_ALGORITHM_KEY + key;
// redisService.setValue(cacheKey, beanName);
// }
//
// @Override
// public String queryStrategyArmoryAlgorithmFromCache(String key) {
// String cacheKey = Constants.RedisKey.STRATEGY_ARMORY_ALGORITHM_KEY + key;
// if (!redisService.isExists(cacheKey)) return null;
// return redisService.getValue(cacheKey);
// }
}
package cn.bugstack.infrastructure.redis;
import org.redisson.api.*;
import java.util.concurrent.TimeUnit;
public interface IRedisService {
/**
* 设置指定 key 的值
*
* @param key 键
* @param value 值
*/
<T> void setValue(String key, T value);
/**
* 设置指定 key 的值
*
* @param key 键
* @param value 值
* @param expired 过期时间
*/
<T> void setValue(String key, T value, long expired);
/**
* 获取指定 key 的值
*
* @param key 键
* @return 值
*/
<T> T getValue(String key);
/**
* 获取队列
*
* @param key 键
* @param <T> 泛型
* @return 队列
*/
<T> RQueue<T> getQueue(String key);
/**
* 加锁队列
*
* @param key 键
* @param <T> 泛型
* @return 队列
*/
<T> RBlockingQueue<T> getBlockingQueue(String key);
/**
* 延迟队列
*
* @param rBlockingQueue 加锁队列
* @param <T> 泛型
* @return 队列
*/
<T> RDelayedQueue<T> getDelayedQueue(RBlockingQueue<T> rBlockingQueue);
/**
* 设置值
*
* @param key key 键
* @param value 值
*/
void setAtomicLong(String key, long value);
/**
* 获取值
*
* @param key key 键
*/
Long getAtomicLong(String key);
/**
* 自增 Key 的值;1、2、3、4
*
* @param key 键
* @return 自增后的值
*/
long incr(String key);
/**
* 指定值,自增 Key 的值;1、2、3、4
*
* @param key 键
* @return 自增后的值
*/
long incrBy(String key, long delta);
/**
* 自减 Key 的值;1、2、3、4
*
* @param key 键
* @return 自增后的值
*/
long decr(String key);
/**
* 指定值,自增 Key 的值;1、2、3、4
*
* @param key 键
* @return 自增后的值
*/
long decrBy(String key, long delta);
/**
* 移除指定 key 的值
*
* @param key 键
*/
void remove(String key);
/**
* 判断指定 key 的值是否存在
*
* @param key 键
* @return true/false
*/
boolean isExists(String key);
/**
* 将指定的值添加到集合中
*
* @param key 键
* @param value 值
*/
void addToSet(String key, String value);
/**
* 判断指定的值是否是集合的成员
*
* @param key 键
* @param value 值
* @return 如果是集合的成员返回 true,否则返回 false
*/
boolean isSetMember(String key, String value);
/**
* 将指定的值添加到列表中
*
* @param key 键
* @param value 值
*/
void addToList(String key, String value);
/**
* 获取列表中指定索引的值
*
* @param key 键
* @param index 索引
* @return 值
*/
String getFromList(String key, int index);
/**
* 获取Map
*
* @param key 键
* @return 值
*/
<K, V> RMap<K, V> getMap(String key);
/**
* 将指定的键值对添加到哈希表中
*
* @param key 键
* @param field 字段
* @param value 值
*/
void addToMap(String key, String field, String value);
/**
* 获取哈希表中指定字段的值
*
* @param key 键
* @param field 字段
* @return 值
*/
String getFromMap(String key, String field);
/**
* 获取哈希表中指定字段的值
*
* @param key 键
* @param field 字段
* @return 值
*/
<K, V> V getFromMap(String key, K field);
/**
* 将指定的值添加到有序集合中
*
* @param key 键
* @param value 值
*/
void addToSortedSet(String key, String value);
/**
* 获取 Redis 锁(可重入锁)
*
* @param key 键
* @return Lock
*/
RLock getLock(String key);
/**
* 获取 Redis 锁(公平锁)
*
* @param key 键
* @return Lock
*/
RLock getFairLock(String key);
/**
* 获取 Redis 锁(读写锁)
*
* @param key 键
* @return RReadWriteLock
*/
RReadWriteLock getReadWriteLock(String key);
/**
* 获取 Redis 信号量
*
* @param key 键
* @return RSemaphore
*/
RSemaphore getSemaphore(String key);
/**
* 获取 Redis 过期信号量
* <p>
* 基于Redis的Redisson的分布式信号量(Semaphore)Java对象RSemaphore采用了与java.util.concurrent.Semaphore相似的接口和用法。
* 同时还提供了异步(Async)、反射式(Reactive)和RxJava2标准的接口。
*
* @param key 键
* @return RPermitExpirableSemaphore
*/
RPermitExpirableSemaphore getPermitExpirableSemaphore(String key);
/**
* 闭锁
*
* @param key 键
* @return RCountDownLatch
*/
RCountDownLatch getCountDownLatch(String key);
/**
* 布隆过滤器
*
* @param key 键
* @param <T> 存放对象
* @return 返回结果
*/
<T> RBloomFilter<T> getBloomFilter(String key);
Boolean setNx(String key);
Boolean setNx(String key, long expired, TimeUnit timeUnit);
}
package cn.bugstack.infrastructure.redis;
import org.redisson.api.*;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
@Service("redissonService")
public class RedissonService implements IRedisService {
@Resource
private RedissonClient redissonClient;
public <T> void setValue(String key, T value) {
redissonClient.<T>getBucket(key).set(value);
}
@Override
public <T> void setValue(String key, T value, long expired) {
RBucket<T> bucket = redissonClient.getBucket(key);
bucket.set(value, Duration.ofMillis(expired));
}
public <T> T getValue(String key) {
return redissonClient.<T>getBucket(key).get();
}
@Override
public <T> RQueue<T> getQueue(String key) {
return redissonClient.getQueue(key);
}
@Override
public <T> RBlockingQueue<T> getBlockingQueue(String key) {
return redissonClient.getBlockingQueue(key);
}
@Override
public <T> RDelayedQueue<T> getDelayedQueue(RBlockingQueue<T> rBlockingQueue) {
return redissonClient.getDelayedQueue(rBlockingQueue);
}
@Override
public void setAtomicLong(String key, long value) {
redissonClient.getAtomicLong(key).set(value);
}
@Override
public Long getAtomicLong(String key) {
return redissonClient.getAtomicLong(key).get();
}
@Override
public long incr(String key) {
return redissonClient.getAtomicLong(key).incrementAndGet();
}
@Override
public long incrBy(String key, long delta) {
return redissonClient.getAtomicLong(key).addAndGet(delta);
}
@Override
public long decr(String key) {
return redissonClient.getAtomicLong(key).decrementAndGet();
}
@Override
public long decrBy(String key, long delta) {
return redissonClient.getAtomicLong(key).addAndGet(-delta);
}
@Override
public void remove(String key) {
redissonClient.getBucket(key).delete();
}
@Override
public boolean isExists(String key) {
return redissonClient.getBucket(key).isExists();
}
public void addToSet(String key, String value) {
RSet<String> set = redissonClient.getSet(key);
set.add(value);
}
public boolean isSetMember(String key, String value) {
RSet<String> set = redissonClient.getSet(key);
return set.contains(value);
}
public void addToList(String key, String value) {
RList<String> list = redissonClient.getList(key);
list.add(value);
}
public String getFromList(String key, int index) {
RList<String> list = redissonClient.getList(key);
return list.get(index);
}
@Override
public <K, V> RMap<K, V> getMap(String key) {
return redissonClient.getMap(key);
}
public void addToMap(String key, String field, String value) {
RMap<String, String> map = redissonClient.getMap(key);
map.put(field, value);
}
public String getFromMap(String key, String field) {
RMap<String, String> map = redissonClient.getMap(key);
return map.get(field);
}
@Override
public <K, V> V getFromMap(String key, K field) {
return redissonClient.<K, V>getMap(key).get(field);
}
public void addToSortedSet(String key, String value) {
RSortedSet<String> sortedSet = redissonClient.getSortedSet(key);
sortedSet.add(value);
}
@Override
public RLock getLock(String key) {
return redissonClient.getLock(key);
}
@Override
public RLock getFairLock(String key) {
return redissonClient.getFairLock(key);
}
@Override
public RReadWriteLock getReadWriteLock(String key) {
return redissonClient.getReadWriteLock(key);
}
@Override
public RSemaphore getSemaphore(String key) {
return redissonClient.getSemaphore(key);
}
@Override
public RPermitExpirableSemaphore getPermitExpirableSemaphore(String key) {
return redissonClient.getPermitExpirableSemaphore(key);
}
@Override
public RCountDownLatch getCountDownLatch(String key) {
return redissonClient.getCountDownLatch(key);
}
@Override
public <T> RBloomFilter<T> getBloomFilter(String key) {
return redissonClient.getBloomFilter(key);
}
@Override
public Boolean setNx(String key) {
return redissonClient.getBucket(key).trySet("lock");
}
@Override
public Boolean setNx(String key, long expired, TimeUnit timeUnit) {
return redissonClient.getBucket(key).trySet("lock", expired, timeUnit);
}
}
/**
* 提供redis链接配置
*/
package cn.bugstack.infrastructure.redis;
\ No newline at end of file
......@@ -3,5 +3,35 @@ package cn.bugstack.types.common;
public class Constants {
public final static String SPLIT = ",";
public final static String COLON = ":";
public final static String SPACE = " ";
public final static String UNDERLINE = "_";
/**
* 定义出缓存key的前缀标识,
*/
public static class RedisKey {
public static String ACTIVITY_KEY = "big_market_activity_key_";
public static String ACTIVITY_SKU_KEY = "big_market_activity_sku_key_";
public static String ACTIVITY_COUNT_KEY = "big_market_activity_count_key_";
public static String STRATEGY_KEY = "big_market_strategy_key_";
public static String STRATEGY_AWARD_KEY = "big_market_strategy_award_key_";
public static String STRATEGY_AWARD_LIST_KEY = "big_market_strategy_award_list_key_";
public static String STRATEGY_RATE_TABLE_KEY = "big_market_strategy_rate_table_key_";
public static String STRATEGY_RATE_RANGE_KEY = "big_market_strategy_rate_range_key_";
public static String RULE_TREE_VO_KEY = "rule_tree_vo_key_";
public static String STRATEGY_AWARD_COUNT_KEY = "strategy_award_count_key_";
public static String STRATEGY_AWARD_COUNT_QUERY_KEY = "strategy_award_count_query_key";
public static String STRATEGY_RULE_WEIGHT_KEY = "strategy_rule_weight_key_";
public static String ACTIVITY_SKU_COUNT_QUERY_KEY = "activity_sku_count_query_key";
public static String ACTIVITY_SKU_STOCK_COUNT_KEY = "activity_sku_stock_count_key_";
public static String ACTIVITY_SKU_COUNT_CLEAR_KEY = "activity_sku_count_clear_key_";
public static String ACTIVITY_ACCOUNT_LOCK = "activity_account_lock_";
public static String ACTIVITY_ACCOUNT_UPDATE_LOCK = "activity_account_update_lock_";
public static String USER_CREDIT_ACCOUNT_LOCK = "user_credit_account_lock_";
public static String STRATEGY_ARMORY_ALGORITHM_KEY = "strategy_armory_algorithm_key_";
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册