Browse Source
- 扩展nodeTypes映射,支持多种节点类型(startStep、endStep、processStep、decisionStep、testStep) - 添加getNodeType函数,根据stepType动态返回对应节点类型 - 修改节点创建逻辑,使用动态类型替代硬编码的'testStep' - 更新TestCaseDetailDrawer组件,保持节点类型映射一致性 - 修复保存数据中节点类型不对称问题,便于区分和管理不同类型节点 - 保持向后兼容性,所有节点仍使用相同TestStepNode组件渲染 影响范围: - ReactFlowDesigner.tsx: 节点类型映射和创建逻辑 - TestCaseDetailDrawer.tsx: 节点数据转换逻辑release/web-ui-v1.0.0
19 changed files with 1333 additions and 128 deletions
@ -0,0 +1,49 @@ |
|||
namespace X1.Domain.Options; |
|||
|
|||
/// <summary>
|
|||
/// Redis缓存配置选项
|
|||
/// 避免与官方Microsoft.Extensions.Caching.StackExchangeRedis.RedisOptions重名
|
|||
/// </summary>
|
|||
public class RedisCacheOptions |
|||
{ |
|||
public const string SectionName = "X1_Redis"; |
|||
|
|||
public string Host { get; set; } = "localhost"; |
|||
public int Port { get; set; } = 6379; |
|||
public string? Password { get; set; } |
|||
public int Database { get; set; } = 0; |
|||
public int ConnectTimeout { get; set; } = 5000; |
|||
public int OperationTimeout { get; set; } = 3000; |
|||
public int PoolSize { get; set; } = 50; |
|||
public bool EnableSsl { get; set; } = false; |
|||
public bool EnableRetry { get; set; } = true; |
|||
public int RetryCount { get; set; } = 3; |
|||
public int RetryDelay { get; set; } = 1000; |
|||
public int DefaultExpirationMinutes { get; set; } = 30; |
|||
public int MaxExpirationMinutes { get; set; } = 1440; |
|||
public string KeyPrefix { get; set; } = "X1:"; |
|||
public bool EnableCompression { get; set; } = true; |
|||
public int CompressionThreshold { get; set; } = 1024; |
|||
|
|||
public string GetConnectionString() |
|||
{ |
|||
if (string.IsNullOrEmpty(Password)) |
|||
{ |
|||
throw new InvalidOperationException("Redis密码不能为空,请配置X1_Redis:Password"); |
|||
} |
|||
|
|||
var connectionString = $"{Host}:{Port}"; |
|||
connectionString = $"{connectionString},password={Password}"; |
|||
connectionString = $"{connectionString},defaultDatabase={Database}"; |
|||
connectionString = $"{connectionString},connectTimeout={ConnectTimeout}"; |
|||
connectionString = $"{connectionString},syncTimeout={OperationTimeout}"; |
|||
connectionString = $"{connectionString},responseTimeout={OperationTimeout}"; |
|||
connectionString = $"{connectionString},connectRetry={RetryCount}"; |
|||
connectionString = $"{connectionString},reconnectRetryPolicy=LinearRetry"; |
|||
if (EnableSsl) |
|||
{ |
|||
connectionString = $"{connectionString},ssl=true"; |
|||
} |
|||
return connectionString; |
|||
} |
|||
} |
|||
@ -0,0 +1,99 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace X1.Domain.Services; |
|||
|
|||
/// <summary>
|
|||
/// Redis缓存服务接口
|
|||
/// 定义Redis缓存操作的核心业务逻辑
|
|||
/// </summary>
|
|||
public interface IRedisCacheService |
|||
{ |
|||
#region 基础操作
|
|||
|
|||
/// <summary>
|
|||
/// 设置键值对
|
|||
/// </summary>
|
|||
bool Set<T>(string key, T value, TimeSpan? expiry = null); |
|||
|
|||
/// <summary>
|
|||
/// 异步设置键值对
|
|||
/// </summary>
|
|||
Task<bool> SetAsync<T>(string key, T value, TimeSpan? expiry = null); |
|||
|
|||
/// <summary>
|
|||
/// 获取值
|
|||
/// </summary>
|
|||
T? Get<T>(string key); |
|||
|
|||
/// <summary>
|
|||
/// 异步获取值
|
|||
/// </summary>
|
|||
Task<T?> GetAsync<T>(string key); |
|||
|
|||
/// <summary>
|
|||
/// 删除键
|
|||
/// </summary>
|
|||
bool Remove(string key); |
|||
|
|||
/// <summary>
|
|||
/// 异步删除键
|
|||
/// </summary>
|
|||
Task<bool> RemoveAsync(string key); |
|||
|
|||
/// <summary>
|
|||
/// 检查键是否存在
|
|||
/// </summary>
|
|||
bool Exists(string key); |
|||
|
|||
/// <summary>
|
|||
/// 异步检查键是否存在
|
|||
/// </summary>
|
|||
Task<bool> ExistsAsync(string key); |
|||
|
|||
#endregion
|
|||
|
|||
#region 分布式锁
|
|||
|
|||
/// <summary>
|
|||
/// 获取分布式锁
|
|||
/// </summary>
|
|||
bool AcquireLock(string key, string value, TimeSpan expiry); |
|||
|
|||
/// <summary>
|
|||
/// 异步获取分布式锁
|
|||
/// </summary>
|
|||
Task<bool> AcquireLockAsync(string key, string value, TimeSpan expiry); |
|||
|
|||
/// <summary>
|
|||
/// 释放分布式锁
|
|||
/// </summary>
|
|||
bool ReleaseLock(string key, string value); |
|||
|
|||
/// <summary>
|
|||
/// 异步释放分布式锁
|
|||
/// </summary>
|
|||
Task<bool> ReleaseLockAsync(string key, string value); |
|||
|
|||
#endregion
|
|||
|
|||
#region 工具方法
|
|||
|
|||
/// <summary>
|
|||
/// 生成带前缀的键
|
|||
/// </summary>
|
|||
string GetPrefixedKey(string key); |
|||
|
|||
/// <summary>
|
|||
/// 批量删除键
|
|||
/// </summary>
|
|||
long DeleteByPattern(string pattern); |
|||
|
|||
/// <summary>
|
|||
/// 异步批量删除键
|
|||
/// </summary>
|
|||
Task<long> DeleteByPatternAsync(string pattern); |
|||
|
|||
#endregion
|
|||
} |
|||
@ -0,0 +1,318 @@ |
|||
using System; |
|||
using System.Text.Json; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Options; |
|||
using StackExchange.Redis; |
|||
using X1.Domain.Options; |
|||
using X1.Domain.Services; |
|||
|
|||
namespace X1.Infrastructure.Services.Infrastructure; |
|||
|
|||
/// <summary>
|
|||
/// Redis缓存服务实现
|
|||
/// 提供Redis缓存操作的具体实现
|
|||
/// </summary>
|
|||
public sealed class RedisCacheService : IRedisCacheService |
|||
{ |
|||
private readonly IConnectionMultiplexer _redis; |
|||
private readonly IDatabase _database; |
|||
private readonly RedisCacheOptions _options; |
|||
private readonly ILogger<RedisCacheService> _logger; |
|||
|
|||
/// <summary>
|
|||
/// 构造函数
|
|||
/// </summary>
|
|||
public RedisCacheService( |
|||
IConnectionMultiplexer redis, |
|||
IOptions<RedisCacheOptions> options, |
|||
ILogger<RedisCacheService> logger) |
|||
{ |
|||
_redis = redis ?? throw new ArgumentNullException(nameof(redis)); |
|||
_database = redis.GetDatabase(); |
|||
_options = options?.Value ?? throw new ArgumentNullException(nameof(options)); |
|||
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); |
|||
} |
|||
|
|||
#region 基础操作
|
|||
|
|||
/// <inheritdoc />
|
|||
public bool Set<T>(string key, T value, TimeSpan? expiry = null) |
|||
{ |
|||
try |
|||
{ |
|||
var prefixedKey = GetPrefixedKey(key); |
|||
var serializedValue = JsonSerializer.Serialize(value); |
|||
|
|||
return _database.StringSet(prefixedKey, serializedValue, expiry); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogError(ex, "设置Redis键值对失败: {Key}", key); |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public async Task<bool> SetAsync<T>(string key, T value, TimeSpan? expiry = null) |
|||
{ |
|||
try |
|||
{ |
|||
var prefixedKey = GetPrefixedKey(key); |
|||
var serializedValue = JsonSerializer.Serialize(value); |
|||
|
|||
return await _database.StringSetAsync(prefixedKey, serializedValue, expiry); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogError(ex, "异步设置Redis键值对失败: {Key}", key); |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public T? Get<T>(string key) |
|||
{ |
|||
try |
|||
{ |
|||
var prefixedKey = GetPrefixedKey(key); |
|||
var value = _database.StringGet(prefixedKey); |
|||
|
|||
if (!value.HasValue) |
|||
return default; |
|||
|
|||
return JsonSerializer.Deserialize<T>(value!); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogError(ex, "获取Redis值失败: {Key}", key); |
|||
return default; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public async Task<T?> GetAsync<T>(string key) |
|||
{ |
|||
try |
|||
{ |
|||
var prefixedKey = GetPrefixedKey(key); |
|||
var value = await _database.StringGetAsync(prefixedKey); |
|||
|
|||
if (!value.HasValue) |
|||
return default; |
|||
|
|||
return JsonSerializer.Deserialize<T>(value!); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogError(ex, "异步获取Redis值失败: {Key}", key); |
|||
return default; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public bool Remove(string key) |
|||
{ |
|||
try |
|||
{ |
|||
var prefixedKey = GetPrefixedKey(key); |
|||
return _database.KeyDelete(prefixedKey); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogError(ex, "删除Redis键失败: {Key}", key); |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public async Task<bool> RemoveAsync(string key) |
|||
{ |
|||
try |
|||
{ |
|||
var prefixedKey = GetPrefixedKey(key); |
|||
return await _database.KeyDeleteAsync(prefixedKey); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogError(ex, "异步删除Redis键失败: {Key}", key); |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public bool Exists(string key) |
|||
{ |
|||
try |
|||
{ |
|||
var prefixedKey = GetPrefixedKey(key); |
|||
return _database.KeyExists(prefixedKey); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogError(ex, "检查Redis键是否存在失败: {Key}", key); |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public async Task<bool> ExistsAsync(string key) |
|||
{ |
|||
try |
|||
{ |
|||
var prefixedKey = GetPrefixedKey(key); |
|||
return await _database.KeyExistsAsync(prefixedKey); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogError(ex, "异步检查Redis键是否存在失败: {Key}", key); |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region 分布式锁
|
|||
|
|||
/// <inheritdoc />
|
|||
public bool AcquireLock(string key, string value, TimeSpan expiry) |
|||
{ |
|||
try |
|||
{ |
|||
var lockKey = GetPrefixedKey($"lock:{key}"); |
|||
return _database.StringSet(lockKey, value, expiry, When.NotExists); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogError(ex, "获取分布式锁失败: {Key}", key); |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public async Task<bool> AcquireLockAsync(string key, string value, TimeSpan expiry) |
|||
{ |
|||
try |
|||
{ |
|||
var lockKey = GetPrefixedKey($"lock:{key}"); |
|||
return await _database.StringSetAsync(lockKey, value, expiry, When.NotExists); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogError(ex, "异步获取分布式锁失败: {Key}", key); |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public bool ReleaseLock(string key, string value) |
|||
{ |
|||
try |
|||
{ |
|||
var lockKey = GetPrefixedKey($"lock:{key}"); |
|||
|
|||
// 使用Lua脚本确保原子性操作
|
|||
var script = @"
|
|||
if redis.call('get', KEYS[1]) == ARGV[1] then |
|||
return redis.call('del', KEYS[1]) |
|||
else |
|||
return 0 |
|||
end";
|
|||
|
|||
var result = _database.ScriptEvaluate(script, new RedisKey[] { lockKey }, new RedisValue[] { value }); |
|||
return result.ToString() == "1"; |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogError(ex, "释放分布式锁失败: {Key}", key); |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public async Task<bool> ReleaseLockAsync(string key, string value) |
|||
{ |
|||
try |
|||
{ |
|||
var lockKey = GetPrefixedKey($"lock:{key}"); |
|||
|
|||
// 使用Lua脚本确保原子性操作
|
|||
var script = @"
|
|||
if redis.call('get', KEYS[1]) == ARGV[1] then |
|||
return redis.call('del', KEYS[1]) |
|||
else |
|||
return 0 |
|||
end";
|
|||
|
|||
var result = await _database.ScriptEvaluateAsync(script, new RedisKey[] { lockKey }, new RedisValue[] { value }); |
|||
return result.ToString() == "1"; |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogError(ex, "异步释放分布式锁失败: {Key}", key); |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region 工具方法
|
|||
|
|||
/// <inheritdoc />
|
|||
public string GetPrefixedKey(string key) |
|||
{ |
|||
return $"{_options.KeyPrefix}{key}"; |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public long DeleteByPattern(string pattern) |
|||
{ |
|||
try |
|||
{ |
|||
var prefixedPattern = GetPrefixedKey(pattern); |
|||
var server = _redis.GetServer(_redis.GetEndPoints()[0]); |
|||
var keys = server.Keys(pattern: prefixedPattern); |
|||
|
|||
long deletedCount = 0; |
|||
foreach (var key in keys) |
|||
{ |
|||
if (_database.KeyDelete(key)) |
|||
deletedCount++; |
|||
} |
|||
|
|||
return deletedCount; |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogError(ex, "批量删除Redis键失败: {Pattern}", pattern); |
|||
return 0; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public async Task<long> DeleteByPatternAsync(string pattern) |
|||
{ |
|||
try |
|||
{ |
|||
var prefixedPattern = GetPrefixedKey(pattern); |
|||
var server = _redis.GetServer(_redis.GetEndPoints()[0]); |
|||
var keys = server.Keys(pattern: prefixedPattern); |
|||
|
|||
long deletedCount = 0; |
|||
foreach (var key in keys) |
|||
{ |
|||
if (await _database.KeyDeleteAsync(key)) |
|||
deletedCount++; |
|||
} |
|||
|
|||
return deletedCount; |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogError(ex, "异步批量删除Redis键失败: {Pattern}", pattern); |
|||
return 0; |
|||
} |
|||
} |
|||
|
|||
#endregion
|
|||
} |
|||
@ -0,0 +1,303 @@ |
|||
#!/bin/bash |
|||
|
|||
# 颜色定义 |
|||
RED='\033[0;31m' |
|||
GREEN='\033[0;32m' |
|||
YELLOW='\033[1;33m' |
|||
BLUE='\033[0;34m' |
|||
NC='\033[0m' # No Color |
|||
|
|||
# 默认配置 |
|||
CONTAINER_NAME="redis-server" |
|||
DEFAULT_DATA_DIR="/data/redis" |
|||
DEFAULT_LOG_DIR="/var/log/redis" |
|||
DEFAULT_REDIS_PASSWORD="X1Redis2025!" # 默认Redis密码 |
|||
|
|||
# 函数:显示帮助信息 |
|||
show_help() { |
|||
echo -e "${BLUE}Redis Docker部署脚本${NC}" |
|||
echo "" |
|||
echo "用法: $0 [选项]" |
|||
echo "" |
|||
echo "选项:" |
|||
echo " -e, --environment ENV 部署环境 (test|prod) [必需]" |
|||
echo " -p, --password PASSWORD Redis密码 (默认: X1Redis2025!)" |
|||
echo " -d, --data-dir DIR 数据目录 (默认: /data/redis)" |
|||
echo " -l, --log-dir DIR 日志目录 (默认: /var/log/redis)" |
|||
echo " -n, --name NAME 容器名称 (默认: redis-server)" |
|||
echo " -h, --help 显示帮助信息" |
|||
echo "" |
|||
echo "端口配置:" |
|||
echo " 测试环境: 12996" |
|||
echo " 生产环境: 12995" |
|||
echo "" |
|||
echo "示例:" |
|||
echo " $0 -e test # 部署测试环境" |
|||
echo " $0 -e prod # 部署生产环境" |
|||
echo " $0 -e test -p mypassword # 自定义密码" |
|||
echo " $0 -e test -d /custom/data # 自定义数据目录" |
|||
} |
|||
|
|||
# 函数:检查Docker是否安装 |
|||
check_docker() { |
|||
if ! command -v docker &> /dev/null; then |
|||
echo -e "${RED}错误: Docker未安装${NC}" |
|||
exit 1 |
|||
fi |
|||
|
|||
if ! docker info &> /dev/null; then |
|||
echo -e "${RED}错误: Docker服务未运行${NC}" |
|||
exit 1 |
|||
fi |
|||
} |
|||
|
|||
# 函数:创建目录 |
|||
create_directories() { |
|||
local data_dir=$1 |
|||
local log_dir=$2 |
|||
|
|||
echo -e "${BLUE}创建必要的目录...${NC}" |
|||
|
|||
# 创建数据目录 |
|||
if [ ! -d "$data_dir" ]; then |
|||
sudo mkdir -p "$data_dir" |
|||
sudo chown 999:999 "$data_dir" # Redis容器用户ID |
|||
echo -e "${GREEN}✓ 创建数据目录: $data_dir${NC}" |
|||
else |
|||
echo -e "${YELLOW}⚠ 数据目录已存在: $data_dir${NC}" |
|||
fi |
|||
|
|||
# 创建日志目录 |
|||
if [ ! -d "$log_dir" ]; then |
|||
sudo mkdir -p "$log_dir" |
|||
sudo chown 999:999 "$log_dir" # Redis容器用户ID |
|||
echo -e "${GREEN}✓ 创建日志目录: $log_dir${NC}" |
|||
else |
|||
echo -e "${YELLOW}⚠ 日志目录已存在: $log_dir${NC}" |
|||
fi |
|||
} |
|||
|
|||
# 函数:停止并删除现有容器 |
|||
cleanup_container() { |
|||
local container_name=$1 |
|||
|
|||
echo -e "${BLUE}清理现有容器...${NC}" |
|||
|
|||
if docker ps -a --format "table {{.Names}}" | grep -q "^${container_name}$"; then |
|||
echo -e "${YELLOW}停止容器: $container_name${NC}" |
|||
docker stop "$container_name" 2>/dev/null || true |
|||
|
|||
echo -e "${YELLOW}删除容器: $container_name${NC}" |
|||
docker rm "$container_name" 2>/dev/null || true |
|||
|
|||
echo -e "${GREEN}✓ 容器清理完成${NC}" |
|||
else |
|||
echo -e "${YELLOW}⚠ 容器不存在: $container_name${NC}" |
|||
fi |
|||
} |
|||
|
|||
# 函数:部署Redis |
|||
deploy_redis() { |
|||
local environment=$1 |
|||
local port=$2 |
|||
local container_name=$3 |
|||
local data_dir=$4 |
|||
local log_dir=$5 |
|||
local redis_password=$6 |
|||
|
|||
echo -e "${BLUE}开始部署Redis (环境: $environment, 端口: $port)...${NC}" |
|||
|
|||
# 创建目录 |
|||
create_directories "$data_dir" "$log_dir" |
|||
|
|||
# 清理现有容器 |
|||
cleanup_container "$container_name" |
|||
|
|||
# 生成Redis配置文件 |
|||
local redis_conf="/tmp/redis-${environment}.conf" |
|||
cat > "$redis_conf" << EOF |
|||
# Redis配置文件 - $environment环境 |
|||
bind 0.0.0.0 |
|||
port 6379 |
|||
timeout 300 |
|||
tcp-keepalive 60 |
|||
loglevel notice |
|||
logfile /var/log/redis/redis.log |
|||
databases 16 |
|||
save 900 1 |
|||
save 300 10 |
|||
save 60 10000 |
|||
stop-writes-on-bgsave-error yes |
|||
rdbcompression yes |
|||
rdbchecksum yes |
|||
dbfilename dump.rdb |
|||
dir /data |
|||
maxmemory 256mb |
|||
maxmemory-policy allkeys-lru |
|||
appendonly yes |
|||
appendfilename "appendonly.aof" |
|||
appendfsync everysec |
|||
no-appendfsync-on-rewrite no |
|||
auto-aof-rewrite-percentage 100 |
|||
auto-aof-rewrite-min-size 64mb |
|||
|
|||
# 安全配置 |
|||
requirepass $redis_password |
|||
rename-command FLUSHDB "" |
|||
rename-command FLUSHALL "" |
|||
rename-command DEBUG "" |
|||
rename-command CONFIG "" |
|||
rename-command SHUTDOWN "" |
|||
EOF |
|||
|
|||
echo -e "${GREEN}✓ Redis配置文件已生成: $redis_conf${NC}" |
|||
|
|||
# 启动Redis容器 |
|||
echo -e "${BLUE}启动Redis容器...${NC}" |
|||
|
|||
docker run -d \ |
|||
--name "$container_name" \ |
|||
--restart unless-stopped \ |
|||
-p "$port:6379" \ |
|||
-v "$data_dir:/data" \ |
|||
-v "$log_dir:/var/log/redis" \ |
|||
-v "$redis_conf:/usr/local/etc/redis/redis.conf" \ |
|||
redis:7-alpine \ |
|||
redis-server /usr/local/etc/redis/redis.conf |
|||
|
|||
if [ $? -eq 0 ]; then |
|||
echo -e "${GREEN}✓ Redis容器启动成功${NC}" |
|||
else |
|||
echo -e "${RED}✗ Redis容器启动失败${NC}" |
|||
exit 1 |
|||
fi |
|||
|
|||
# 等待容器启动 |
|||
echo -e "${BLUE}等待Redis服务启动...${NC}" |
|||
sleep 5 |
|||
|
|||
# 检查容器状态 |
|||
if docker ps --format "table {{.Names}}" | grep -q "^${container_name}$"; then |
|||
echo -e "${GREEN}✓ Redis服务运行正常${NC}" |
|||
else |
|||
echo -e "${RED}✗ Redis服务启动失败${NC}" |
|||
docker logs "$container_name" |
|||
exit 1 |
|||
fi |
|||
|
|||
# 测试Redis连接(带密码) |
|||
echo -e "${BLUE}测试Redis连接...${NC}" |
|||
if docker exec "$container_name" redis-cli -a "$redis_password" ping | grep -q "PONG"; then |
|||
echo -e "${GREEN}✓ Redis连接测试成功${NC}" |
|||
else |
|||
echo -e "${RED}✗ Redis连接测试失败${NC}" |
|||
exit 1 |
|||
fi |
|||
|
|||
# 清理临时配置文件 |
|||
rm -f "$redis_conf" |
|||
|
|||
echo -e "${GREEN}✓ Redis部署完成!${NC}" |
|||
echo -e "${BLUE}部署信息:${NC}" |
|||
echo -e " 环境: ${YELLOW}$environment${NC}" |
|||
echo -e " 端口: ${YELLOW}$port${NC}" |
|||
echo -e " 容器名: ${YELLOW}$container_name${NC}" |
|||
echo -e " 数据目录: ${YELLOW}$data_dir${NC}" |
|||
echo -e " 日志目录: ${YELLOW}$log_dir${NC}" |
|||
echo -e " Redis密码: ${YELLOW}$redis_password${NC}" |
|||
echo -e " 容器内连接: ${YELLOW}docker exec -it $container_name redis-cli -a '$redis_password'${NC}" |
|||
echo -e " 外部连接: ${YELLOW}redis-cli -h localhost -p $port -a '$redis_password'${NC}" |
|||
echo -e " 连接URL: ${YELLOW}redis://:$redis_password@localhost:$port${NC}" |
|||
} |
|||
|
|||
# 主函数 |
|||
main() { |
|||
# 解析命令行参数 |
|||
local environment="" |
|||
local redis_password="$DEFAULT_REDIS_PASSWORD" |
|||
local data_dir="$DEFAULT_DATA_DIR" |
|||
local log_dir="$DEFAULT_LOG_DIR" |
|||
local container_name="$CONTAINER_NAME" |
|||
|
|||
while [[ $# -gt 0 ]]; do |
|||
case $1 in |
|||
-e|--environment) |
|||
environment="$2" |
|||
shift 2 |
|||
;; |
|||
-p|--password) |
|||
redis_password="$2" |
|||
shift 2 |
|||
;; |
|||
-d|--data-dir) |
|||
data_dir="$2" |
|||
shift 2 |
|||
;; |
|||
-l|--log-dir) |
|||
log_dir="$2" |
|||
shift 2 |
|||
;; |
|||
-n|--name) |
|||
container_name="$2" |
|||
shift 2 |
|||
;; |
|||
-h|--help) |
|||
show_help |
|||
exit 0 |
|||
;; |
|||
*) |
|||
echo -e "${RED}未知参数: $1${NC}" |
|||
show_help |
|||
exit 1 |
|||
;; |
|||
esac |
|||
done |
|||
|
|||
# 验证必需参数 |
|||
if [ -z "$environment" ]; then |
|||
echo -e "${RED}错误: 必须指定部署环境 (-e 或 --environment)${NC}" |
|||
show_help |
|||
exit 1 |
|||
fi |
|||
|
|||
# 验证环境参数 |
|||
if [ "$environment" != "test" ] && [ "$environment" != "prod" ]; then |
|||
echo -e "${RED}错误: 环境参数必须是 'test' 或 'prod'${NC}" |
|||
exit 1 |
|||
fi |
|||
|
|||
# 根据环境设置端口 |
|||
local port |
|||
if [ "$environment" = "test" ]; then |
|||
port=12996 |
|||
else |
|||
port=12995 |
|||
fi |
|||
|
|||
# 检查Docker |
|||
check_docker |
|||
|
|||
# 显示部署信息 |
|||
echo -e "${BLUE}=== Redis Docker部署脚本 ===${NC}" |
|||
echo -e "环境: ${YELLOW}$environment${NC}" |
|||
echo -e "端口: ${YELLOW}$port${NC}" |
|||
echo -e "容器名: ${YELLOW}$container_name${NC}" |
|||
echo -e "数据目录: ${YELLOW}$data_dir${NC}" |
|||
echo -e "日志目录: ${YELLOW}$log_dir${NC}" |
|||
echo -e "Redis密码: ${YELLOW}$redis_password${NC}" |
|||
echo "" |
|||
|
|||
# 确认部署 |
|||
read -p "确认部署? (y/N): " -n 1 -r |
|||
echo |
|||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then |
|||
echo -e "${YELLOW}部署已取消${NC}" |
|||
exit 0 |
|||
fi |
|||
|
|||
# 执行部署 |
|||
deploy_redis "$environment" "$port" "$container_name" "$data_dir" "$log_dir" "$redis_password" |
|||
} |
|||
|
|||
# 执行主函数 |
|||
main "$@" |
|||
Loading…
Reference in new issue