Files
makemd/server/src/services/AutoExecutionConfigService.ts

817 lines
25 KiB
TypeScript
Raw Normal View History

/**
* [BE-AIAUTO-001~002] AI决策自动化配置服务
* AI决策自动执行的配置
* AI注意: 所有自动执行决策必须通过此服务校验
*/
import db from '../config/database';
import { logger } from '../utils/logger';
import { EventBusService } from './EventBusService';
import RedisService from './RedisService';
// 自动化等级 (L1-L4)
export type AutomationLevel = 'L1' | 'L2' | 'L3' | 'L4';
// 风险等级
export type RiskLevel = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
// 决策模块类型
export type DecisionModule =
| 'PRICING' // 定价模块
| 'INVENTORY' // 库存模块
| 'AD_OPTIMIZE' // 广告优化
| 'PRODUCT_SELECT' // 选品模块
| 'LOGISTICS' // 物流模块
| 'RISK_CONTROL' // 风控模块
| 'CUSTOMER_SERVICE' // 客服模块
| 'SETTLEMENT'; // 结算模块
// 自动执行配置实体
export interface AutoExecutionConfig {
id: string;
tenant_id: string;
shop_id: string;
module: DecisionModule;
// 自动化等级
automation_level: AutomationLevel;
// 置信度阈值配置
confidence_thresholds: {
auto_execute: number; // 自动执行阈值
pending_review: number; // 待审核阈值
auto_reject: number; // 自动拒绝阈值
};
// 风险限制配置
risk_limits: {
max_amount: number; // 最大金额限制
max_quantity: number; // 最大数量限制
allowed_risk_levels: RiskLevel[]; // 允许的风险等级
};
// 时间限制
time_restrictions: {
allowed_hours: number[]; // 允许执行的小时 (0-23)
excluded_dates: string[]; // 排除的日期 (YYYY-MM-DD)
};
// 回滚配置
rollback_config: {
enabled: boolean;
max_attempts: number;
cooldown_minutes: number;
};
// 通知配置
notification_config: {
on_execute: boolean;
on_fail: boolean;
on_rollback: boolean;
channels: ('EMAIL' | 'SMS' | 'WEBHOOK')[];
};
// 统计数据
statistics: {
total_executions: number;
success_count: number;
fail_count: number;
rollback_count: number;
avg_confidence: number;
last_execution_at?: Date;
};
// 状态
status: 'ACTIVE' | 'INACTIVE' | 'SUSPENDED';
// 审计
created_by: string;
updated_by: string;
created_at: Date;
updated_at: Date;
}
// 执行请求
export interface ExecutionRequest {
tenant_id: string;
shop_id: string;
module: DecisionModule;
decision_id: string;
confidence: number;
risk_level: RiskLevel;
estimated_impact: {
amount?: number;
quantity?: number;
};
metadata?: Record<string, any>;
}
// 执行结果
export interface ExecutionResult {
allowed: boolean;
reason: string;
required_action: 'AUTO_EXECUTE' | 'PENDING_REVIEW' | 'AUTO_REJECT';
config_snapshot: Partial<AutoExecutionConfig>;
warnings: string[];
}
// 等级演进记录
export interface LevelEvolutionRecord {
id: string;
tenant_id: string;
shop_id: string;
module: DecisionModule;
from_level: AutomationLevel;
to_level: AutomationLevel;
reason: string;
metrics: {
success_rate: number;
total_executions: number;
avg_confidence: number;
};
approved_by: string;
created_at: Date;
}
// 模块默认配置
const MODULE_DEFAULT_CONFIGS: Record<DecisionModule, Partial<AutoExecutionConfig>> = {
PRICING: {
confidence_thresholds: { auto_execute: 0.85, pending_review: 0.60, auto_reject: 0.40 },
risk_limits: { max_amount: 5000, max_quantity: 100, allowed_risk_levels: ['LOW', 'MEDIUM'] },
},
INVENTORY: {
confidence_thresholds: { auto_execute: 0.90, pending_review: 0.70, auto_reject: 0.50 },
risk_limits: { max_amount: 10000, max_quantity: 500, allowed_risk_levels: ['LOW', 'MEDIUM'] },
},
AD_OPTIMIZE: {
confidence_thresholds: { auto_execute: 0.80, pending_review: 0.55, auto_reject: 0.35 },
risk_limits: { max_amount: 2000, max_quantity: 50, allowed_risk_levels: ['LOW', 'MEDIUM', 'HIGH'] },
},
PRODUCT_SELECT: {
confidence_thresholds: { auto_execute: 0.75, pending_review: 0.50, auto_reject: 0.30 },
risk_limits: { max_amount: 3000, max_quantity: 200, allowed_risk_levels: ['LOW', 'MEDIUM'] },
},
LOGISTICS: {
confidence_thresholds: { auto_execute: 0.88, pending_review: 0.65, auto_reject: 0.45 },
risk_limits: { max_amount: 1000, max_quantity: 100, allowed_risk_levels: ['LOW', 'MEDIUM'] },
},
RISK_CONTROL: {
confidence_thresholds: { auto_execute: 0.95, pending_review: 0.80, auto_reject: 0.60 },
risk_limits: { max_amount: 50000, max_quantity: 1000, allowed_risk_levels: ['LOW'] },
},
CUSTOMER_SERVICE: {
confidence_thresholds: { auto_execute: 0.82, pending_review: 0.60, auto_reject: 0.40 },
risk_limits: { max_amount: 500, max_quantity: 10, allowed_risk_levels: ['LOW', 'MEDIUM'] },
},
SETTLEMENT: {
confidence_thresholds: { auto_execute: 0.98, pending_review: 0.90, auto_reject: 0.70 },
risk_limits: { max_amount: 100000, max_quantity: 100, allowed_risk_levels: ['LOW'] },
},
};
// 等级能力矩阵
const LEVEL_CAPABILITIES: Record<AutomationLevel, {
max_risk_level: RiskLevel;
max_amount_multiplier: number;
requires_approval: boolean;
description: string;
}> = {
L1: {
max_risk_level: 'LOW',
max_amount_multiplier: 0.5,
requires_approval: true,
description: '仅建议,所有操作需人工确认',
},
L2: {
max_risk_level: 'MEDIUM',
max_amount_multiplier: 1.0,
requires_approval: false,
description: '低风险操作可自动执行,中高风险需审核',
},
L3: {
max_risk_level: 'HIGH',
max_amount_multiplier: 2.0,
requires_approval: false,
description: '大部分操作可自动执行,仅高风险需审核',
},
L4: {
max_risk_level: 'CRITICAL',
max_amount_multiplier: 5.0,
requires_approval: false,
description: '全自动化,包含关键操作,需严格监控',
},
};
export class AutoExecutionConfigService {
private static readonly TABLE_NAME = 'cf_auto_execution_config';
private static readonly EVOLUTION_TABLE = 'cf_automation_level_evolution';
private static readonly CACHE_PREFIX = 'auto_exec_config:';
private static readonly CACHE_TTL = 1800; // 30分钟
/**
* [BE-AIAUTO-001]
*/
static async initTables() {
// 主配置表
const hasTable = await db.schema.hasTable(this.TABLE_NAME);
if (!hasTable) {
logger.info(`Creating ${this.TABLE_NAME} table...`);
await db.schema.createTable(this.TABLE_NAME, (table) => {
table.string('id', 50).primary();
table.string('tenant_id', 50).notNullable().index();
table.string('shop_id', 50).notNullable().index();
table.enum('module', [
'PRICING', 'INVENTORY', 'AD_OPTIMIZE', 'PRODUCT_SELECT',
'LOGISTICS', 'RISK_CONTROL', 'CUSTOMER_SERVICE', 'SETTLEMENT'
]).notNullable();
// 自动化等级
table.enum('automation_level', ['L1', 'L2', 'L3', 'L4']).notNullable().defaultTo('L1');
// JSON配置字段
table.json('confidence_thresholds').notNullable();
table.json('risk_limits').notNullable();
table.json('time_restrictions').notNullable();
table.json('rollback_config').notNullable();
table.json('notification_config').notNullable();
table.json('statistics').notNullable();
// 状态
table.enum('status', ['ACTIVE', 'INACTIVE', 'SUSPENDED']).notNullable().defaultTo('ACTIVE');
// 审计
table.string('created_by', 50).notNullable();
table.string('updated_by', 50).notNullable();
table.timestamp('created_at').notNullable().defaultTo(db.fn.now());
table.timestamp('updated_at').notNullable().defaultTo(db.fn.now());
// 唯一约束
table.unique(['tenant_id', 'shop_id', 'module']);
table.index(['tenant_id', 'status']);
});
logger.info(`${this.TABLE_NAME} table created successfully`);
}
// 等级演进记录表
const hasEvolutionTable = await db.schema.hasTable(this.EVOLUTION_TABLE);
if (!hasEvolutionTable) {
logger.info(`Creating ${this.EVOLUTION_TABLE} table...`);
await db.schema.createTable(this.EVOLUTION_TABLE, (table) => {
table.string('id', 50).primary();
table.string('tenant_id', 50).notNullable().index();
table.string('shop_id', 50).notNullable().index();
table.enum('module', [
'PRICING', 'INVENTORY', 'AD_OPTIMIZE', 'PRODUCT_SELECT',
'LOGISTICS', 'RISK_CONTROL', 'CUSTOMER_SERVICE', 'SETTLEMENT'
]).notNullable();
table.enum('from_level', ['L1', 'L2', 'L3', 'L4']).notNullable();
table.enum('to_level', ['L1', 'L2', 'L3', 'L4']).notNullable();
table.text('reason').notNullable();
table.json('metrics').notNullable();
table.string('approved_by', 50).notNullable();
table.timestamp('created_at').notNullable().defaultTo(db.fn.now());
table.index(['tenant_id', 'module', 'created_at']);
});
logger.info(`${this.EVOLUTION_TABLE} table created successfully`);
}
}
/**
* [BE-AIAUTO-002]
*/
static async getOrCreateConfig(
tenantId: string,
shopId: string,
module: DecisionModule,
userId: string
): Promise<AutoExecutionConfig> {
// 尝试从缓存获取
const cacheKey = `${this.CACHE_PREFIX}${tenantId}:${shopId}:${module}`;
const cached = await RedisService.get(cacheKey);
if (cached) {
return JSON.parse(cached);
}
// 查询数据库
let config = await db(this.TABLE_NAME)
.where({ tenant_id: tenantId, shop_id: shopId, module })
.first();
if (!config) {
// 创建默认配置
const defaultConfig = MODULE_DEFAULT_CONFIGS[module];
const id = `AEC-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
const newConfig = {
id,
tenant_id: tenantId,
shop_id: shopId,
module,
automation_level: 'L1' as AutomationLevel,
confidence_thresholds: JSON.stringify(defaultConfig.confidence_thresholds || {
auto_execute: 0.80,
pending_review: 0.50,
auto_reject: 0.30,
}),
risk_limits: JSON.stringify(defaultConfig.risk_limits || {
max_amount: 1000,
max_quantity: 100,
allowed_risk_levels: ['LOW', 'MEDIUM'],
}),
time_restrictions: JSON.stringify({
allowed_hours: Array.from({ length: 24 }, (_, i) => i),
excluded_dates: [],
}),
rollback_config: JSON.stringify({
enabled: true,
max_attempts: 3,
cooldown_minutes: 30,
}),
notification_config: JSON.stringify({
on_execute: true,
on_fail: true,
on_rollback: true,
channels: ['EMAIL'] as ('EMAIL' | 'SMS' | 'WEBHOOK')[],
}),
statistics: JSON.stringify({
total_executions: 0,
success_count: 0,
fail_count: 0,
rollback_count: 0,
avg_confidence: 0,
}),
status: 'ACTIVE',
created_by: userId,
updated_by: userId,
created_at: new Date(),
updated_at: new Date(),
};
await db(this.TABLE_NAME).insert(newConfig);
config = newConfig;
logger.info(`[AutoExecutionConfig] Created default config for ${module}`);
}
const result = this.parseConfigRecord(config);
// 缓存结果
await RedisService.setex(cacheKey, this.CACHE_TTL, JSON.stringify(result));
return result;
}
/**
* [BE-AIAUTO-002]
*/
static async validateExecution(request: ExecutionRequest): Promise<ExecutionResult> {
const config = await this.getOrCreateConfig(
request.tenant_id,
request.shop_id,
request.module,
'SYSTEM'
);
const warnings: string[] = [];
let allowed = true;
let reason = '';
let requiredAction: 'AUTO_EXECUTE' | 'PENDING_REVIEW' | 'AUTO_REJECT' = 'AUTO_EXECUTE';
// 检查配置状态
if (config.status !== 'ACTIVE') {
return {
allowed: false,
reason: `配置状态为 ${config.status},不允许自动执行`,
required_action: 'PENDING_REVIEW',
config_snapshot: { status: config.status },
warnings: ['模块配置未激活'],
};
}
// 检查置信度阈值
const { confidence_thresholds } = config;
if (request.confidence >= confidence_thresholds.auto_execute) {
requiredAction = 'AUTO_EXECUTE';
reason = '置信度达到自动执行阈值';
} else if (request.confidence >= confidence_thresholds.pending_review) {
requiredAction = 'PENDING_REVIEW';
reason = '置信度处于待审核区间';
allowed = false;
} else {
requiredAction = 'AUTO_REJECT';
reason = '置信度低于最低阈值,自动拒绝';
allowed = false;
}
// 检查风险等级
const { risk_limits } = config;
if (!risk_limits.allowed_risk_levels.includes(request.risk_level)) {
warnings.push(`风险等级 ${request.risk_level} 不在允许范围内`);
if (request.risk_level === 'CRITICAL' || request.risk_level === 'HIGH') {
requiredAction = 'PENDING_REVIEW';
allowed = false;
reason = '风险等级过高,需要人工审核';
}
}
// 检查等级能力限制
const levelCapability = LEVEL_CAPABILITIES[config.automation_level];
const riskLevelOrder: RiskLevel[] = ['LOW', 'MEDIUM', 'HIGH', 'CRITICAL'];
const requestRiskIndex = riskLevelOrder.indexOf(request.risk_level);
const maxRiskIndex = riskLevelOrder.indexOf(levelCapability.max_risk_level);
if (requestRiskIndex > maxRiskIndex) {
warnings.push(`当前等级 ${config.automation_level} 不支持风险等级 ${request.risk_level}`);
requiredAction = 'PENDING_REVIEW';
allowed = false;
reason = '超出当前自动化等级的风险承受能力';
}
// 检查金额/数量限制
const maxAmount = risk_limits.max_amount * levelCapability.max_amount_multiplier;
if (request.estimated_impact.amount && request.estimated_impact.amount > maxAmount) {
warnings.push(`预估金额 ${request.estimated_impact.amount} 超出限制 ${maxAmount}`);
requiredAction = 'PENDING_REVIEW';
allowed = false;
reason = '预估影响金额超出当前等级限制';
}
if (request.estimated_impact.quantity && request.estimated_impact.quantity > risk_limits.max_quantity) {
warnings.push(`预估数量 ${request.estimated_impact.quantity} 超出限制 ${risk_limits.max_quantity}`);
requiredAction = 'PENDING_REVIEW';
allowed = false;
reason = '预估影响数量超出限制';
}
// 检查时间限制
const { time_restrictions } = config;
const now = new Date();
const currentHour = now.getHours();
const currentDate = now.toISOString().split('T')[0];
if (!time_restrictions.allowed_hours.includes(currentHour)) {
warnings.push(`当前时间 ${currentHour}:00 不在允许执行的时间段内`);
requiredAction = 'PENDING_REVIEW';
allowed = false;
reason = '不在允许执行的时间段';
}
if (time_restrictions.excluded_dates.includes(currentDate)) {
warnings.push(`当前日期 ${currentDate} 被排除执行`);
requiredAction = 'PENDING_REVIEW';
allowed = false;
reason = '当前日期不允许自动执行';
}
// L1等级始终需要审核
if (config.automation_level === 'L1' && requiredAction === 'AUTO_EXECUTE') {
requiredAction = 'PENDING_REVIEW';
allowed = false;
reason = 'L1等级所有操作都需要人工确认';
}
return {
allowed,
reason,
required_action: requiredAction,
config_snapshot: {
automation_level: config.automation_level,
confidence_thresholds: config.confidence_thresholds,
risk_limits: config.risk_limits,
},
warnings,
};
}
/**
* [BE-AIAUTO-002]
*/
static async updateConfig(
configId: string,
updates: Partial<AutoExecutionConfig>,
userId: string
): Promise<AutoExecutionConfig> {
const updateData: any = {
updated_by: userId,
updated_at: new Date(),
};
// 处理JSON字段
if (updates.confidence_thresholds) {
updateData.confidence_thresholds = JSON.stringify(updates.confidence_thresholds);
}
if (updates.risk_limits) {
updateData.risk_limits = JSON.stringify(updates.risk_limits);
}
if (updates.time_restrictions) {
updateData.time_restrictions = JSON.stringify(updates.time_restrictions);
}
if (updates.rollback_config) {
updateData.rollback_config = JSON.stringify(updates.rollback_config);
}
if (updates.notification_config) {
updateData.notification_config = JSON.stringify(updates.notification_config);
}
if (updates.automation_level) {
updateData.automation_level = updates.automation_level;
}
if (updates.status) {
updateData.status = updates.status;
}
await db(this.TABLE_NAME)
.where('id', configId)
.update(updateData);
// 清除缓存
const config = await db(this.TABLE_NAME).where('id', configId).first();
if (config) {
const cacheKey = `${this.CACHE_PREFIX}${config.tenant_id}:${config.shop_id}:${config.module}`;
await RedisService.del(cacheKey);
}
// 发布事件
await EventBusService.publish({
type: 'auto_execution.config_updated',
data: {
configId,
updates,
userId,
timestamp: new Date(),
}
});
logger.info(`[AutoExecutionConfig] Updated config ${configId}`);
return this.parseConfigRecord(config);
}
/**
* [BE-AIAUTO-002]
*/
static async recordExecution(
tenantId: string,
shopId: string,
module: DecisionModule,
success: boolean,
confidence: number,
rolledBack: boolean = false
): Promise<void> {
const config = await this.getOrCreateConfig(tenantId, shopId, module, 'SYSTEM');
const stats = config.statistics;
stats.total_executions += 1;
if (success) {
stats.success_count += 1;
} else {
stats.fail_count += 1;
}
if (rolledBack) {
stats.rollback_count += 1;
}
stats.avg_confidence = ((stats.avg_confidence * (stats.total_executions - 1)) + confidence) / stats.total_executions;
stats.last_execution_at = new Date();
await db(this.TABLE_NAME)
.where('id', config.id)
.update({
statistics: JSON.stringify(stats),
updated_at: new Date(),
});
// 清除缓存
const cacheKey = `${this.CACHE_PREFIX}${tenantId}:${shopId}:${module}`;
await RedisService.del(cacheKey);
}
/**
* [BE-AIAUTO-002]
*/
static async upgradeLevel(
tenantId: string,
shopId: string,
module: DecisionModule,
targetLevel: AutomationLevel,
reason: string,
approvedBy: string
): Promise<LevelEvolutionRecord> {
const config = await this.getOrCreateConfig(tenantId, shopId, module, approvedBy);
const currentLevel = config.automation_level;
// 验证等级演进顺序
const levelOrder: AutomationLevel[] = ['L1', 'L2', 'L3', 'L4'];
const currentIndex = levelOrder.indexOf(currentLevel);
const targetIndex = levelOrder.indexOf(targetLevel);
if (targetIndex !== currentIndex + 1) {
throw new Error(`只能逐级升级,当前等级 ${currentLevel},目标等级 ${targetLevel}`);
}
// 检查升级条件
const stats = config.statistics;
if (stats.total_executions < 100) {
throw new Error(`执行次数不足100次当前 ${stats.total_executions}`);
}
const successRate = stats.success_count / stats.total_executions;
if (successRate < 0.95) {
throw new Error(`成功率不足95%,当前 ${(successRate * 100).toFixed(1)}%`);
}
// 更新配置
await this.updateConfig(config.id, { automation_level: targetLevel }, approvedBy);
// 记录演进
const evolutionId = `EVO-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
const evolution: LevelEvolutionRecord = {
id: evolutionId,
tenant_id: tenantId,
shop_id: shopId,
module,
from_level: currentLevel,
to_level: targetLevel,
reason,
metrics: {
success_rate: successRate,
total_executions: stats.total_executions,
avg_confidence: stats.avg_confidence,
},
approved_by: approvedBy,
created_at: new Date(),
};
await db(this.EVOLUTION_TABLE).insert({
...evolution,
metrics: JSON.stringify(evolution.metrics),
});
// 发布事件
await EventBusService.publish({
type: 'auto_execution.level_upgraded',
data: {
tenantId,
shopId,
module,
fromLevel: currentLevel,
toLevel: targetLevel,
approvedBy,
timestamp: new Date(),
}
});
logger.info(`[AutoExecutionConfig] Upgraded ${module} from ${currentLevel} to ${targetLevel}`);
return evolution;
}
/**
* [BE-AIAUTO-002]
*/
static async downgradeLevel(
tenantId: string,
shopId: string,
module: DecisionModule,
targetLevel: AutomationLevel,
reason: string,
approvedBy: string
): Promise<LevelEvolutionRecord> {
const config = await this.getOrCreateConfig(tenantId, shopId, module, approvedBy);
const currentLevel = config.automation_level;
// 验证等级演进顺序
const levelOrder: AutomationLevel[] = ['L1', 'L2', 'L3', 'L4'];
const currentIndex = levelOrder.indexOf(currentLevel);
const targetIndex = levelOrder.indexOf(targetLevel);
if (targetIndex >= currentIndex) {
throw new Error(`降级目标等级必须低于当前等级`);
}
// 更新配置
await this.updateConfig(config.id, { automation_level: targetLevel }, approvedBy);
// 记录演进
const evolutionId = `EVO-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
const stats = config.statistics;
const successRate = stats.total_executions > 0 ? stats.success_count / stats.total_executions : 0;
const evolution: LevelEvolutionRecord = {
id: evolutionId,
tenant_id: tenantId,
shop_id: shopId,
module,
from_level: currentLevel,
to_level: targetLevel,
reason,
metrics: {
success_rate: successRate,
total_executions: stats.total_executions,
avg_confidence: stats.avg_confidence,
},
approved_by: approvedBy,
created_at: new Date(),
};
await db(this.EVOLUTION_TABLE).insert({
...evolution,
metrics: JSON.stringify(evolution.metrics),
});
// 发布事件
await EventBusService.publish({
type: 'auto_execution.level_downgraded',
data: {
tenantId,
shopId,
module,
fromLevel: currentLevel,
toLevel: targetLevel,
approvedBy,
reason,
timestamp: new Date(),
}
});
logger.info(`[AutoExecutionConfig] Downgraded ${module} from ${currentLevel} to ${targetLevel}`);
return evolution;
}
/**
* [BE-AIAUTO-002]
*/
static async getAllConfigs(
tenantId: string,
shopId?: string
): Promise<AutoExecutionConfig[]> {
let query = db(this.TABLE_NAME).where('tenant_id', tenantId);
if (shopId) {
query = query.where('shop_id', shopId);
}
const records = await query.orderBy('module', 'asc');
return records.map(this.parseConfigRecord);
}
/**
* [BE-AIAUTO-002]
*/
static async getEvolutionHistory(
tenantId: string,
shopId?: string,
module?: DecisionModule
): Promise<LevelEvolutionRecord[]> {
let query = db(this.EVOLUTION_TABLE).where('tenant_id', tenantId);
if (shopId) {
query = query.where('shop_id', shopId);
}
if (module) {
query = query.where('module', module);
}
const records = await query.orderBy('created_at', 'desc');
return records.map(r => ({
...r,
metrics: typeof r.metrics === 'string' ? JSON.parse(r.metrics) : r.metrics,
}));
}
/**
* [BE-AIAUTO-002]
*/
static getLevelCapabilities(): Record<AutomationLevel, typeof LEVEL_CAPABILITIES[AutomationLevel]> {
return LEVEL_CAPABILITIES;
}
/**
* [BE-AIAUTO-002]
*/
static getModuleDefaults(): Record<DecisionModule, Partial<AutoExecutionConfig>> {
return MODULE_DEFAULT_CONFIGS;
}
/**
*
*/
private static parseConfigRecord(record: any): AutoExecutionConfig {
return {
...record,
confidence_thresholds: typeof record.confidence_thresholds === 'string'
? JSON.parse(record.confidence_thresholds)
: record.confidence_thresholds,
risk_limits: typeof record.risk_limits === 'string'
? JSON.parse(record.risk_limits)
: record.risk_limits,
time_restrictions: typeof record.time_restrictions === 'string'
? JSON.parse(record.time_restrictions)
: record.time_restrictions,
rollback_config: typeof record.rollback_config === 'string'
? JSON.parse(record.rollback_config)
: record.rollback_config,
notification_config: typeof record.notification_config === 'string'
? JSON.parse(record.notification_config)
: record.notification_config,
statistics: typeof record.statistics === 'string'
? JSON.parse(record.statistics)
: record.statistics,
};
}
}