Some checks failed
Deploy / deploy (push) Has been cancelled
Full-stack web application for Telegram management - Frontend: Vue 3 + Vben Admin - Backend: NestJS - Features: User management, group broadcast, statistics 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
365 lines
9.6 KiB
JavaScript
365 lines
9.6 KiB
JavaScript
// 创建账号池相关表的迁移脚本
|
|
const { DataTypes, QueryInterface } = require('sequelize');
|
|
|
|
module.exports = {
|
|
up: async (queryInterface, Sequelize) => {
|
|
// 创建账号池主表
|
|
await queryInterface.createTable('accounts_pool', {
|
|
id: {
|
|
type: DataTypes.INTEGER,
|
|
primaryKey: true,
|
|
allowNull: false,
|
|
autoIncrement: true,
|
|
comment: "主键ID"
|
|
},
|
|
accountId: {
|
|
type: DataTypes.INTEGER,
|
|
allowNull: false,
|
|
unique: true,
|
|
comment: "关联的TG账号ID (accounts表)"
|
|
},
|
|
phone: {
|
|
type: DataTypes.STRING(50),
|
|
allowNull: false,
|
|
unique: true,
|
|
comment: "手机号码"
|
|
},
|
|
status: {
|
|
type: DataTypes.ENUM('active', 'warning', 'limited', 'banned', 'cooling', 'inactive'),
|
|
defaultValue: 'active',
|
|
comment: "账号状态"
|
|
},
|
|
tier: {
|
|
type: DataTypes.ENUM('new', 'warming', 'normal', 'trusted', 'vip'),
|
|
defaultValue: 'new',
|
|
comment: "账号分级"
|
|
},
|
|
dailyLimit: {
|
|
type: DataTypes.INTEGER,
|
|
defaultValue: 30,
|
|
comment: "每日发送限制"
|
|
},
|
|
hourlyLimit: {
|
|
type: DataTypes.INTEGER,
|
|
defaultValue: 5,
|
|
comment: "每小时发送限制"
|
|
},
|
|
intervalSeconds: {
|
|
type: DataTypes.INTEGER,
|
|
defaultValue: 120,
|
|
comment: "发送间隔(秒)"
|
|
},
|
|
lastActiveTime: {
|
|
type: DataTypes.DATE,
|
|
comment: "最后活跃时间"
|
|
},
|
|
joinedGroupCount: {
|
|
type: DataTypes.INTEGER,
|
|
defaultValue: 0,
|
|
comment: "已加入群组数"
|
|
},
|
|
totalSentCount: {
|
|
type: DataTypes.INTEGER,
|
|
defaultValue: 0,
|
|
comment: "累计发送数"
|
|
},
|
|
todaySentCount: {
|
|
type: DataTypes.INTEGER,
|
|
defaultValue: 0,
|
|
comment: "今日发送数"
|
|
},
|
|
consecutiveFailures: {
|
|
type: DataTypes.INTEGER,
|
|
defaultValue: 0,
|
|
comment: "连续失败次数"
|
|
},
|
|
riskScore: {
|
|
type: DataTypes.FLOAT,
|
|
defaultValue: 0,
|
|
comment: "风险评分(0-100)"
|
|
},
|
|
priority: {
|
|
type: DataTypes.INTEGER,
|
|
defaultValue: 50,
|
|
comment: "调度优先级(0-100)"
|
|
},
|
|
tags: {
|
|
type: DataTypes.JSON,
|
|
defaultValue: [],
|
|
comment: "标签数组"
|
|
},
|
|
metadata: {
|
|
type: DataTypes.JSON,
|
|
defaultValue: {},
|
|
comment: "扩展元数据"
|
|
},
|
|
isActive: {
|
|
type: DataTypes.BOOLEAN,
|
|
defaultValue: true,
|
|
comment: "是否启用"
|
|
},
|
|
createdAt: {
|
|
type: DataTypes.DATE,
|
|
allowNull: false
|
|
},
|
|
updatedAt: {
|
|
type: DataTypes.DATE,
|
|
allowNull: false
|
|
}
|
|
});
|
|
|
|
// 创建索引
|
|
await queryInterface.addIndex('accounts_pool', ['status', 'isActive']);
|
|
await queryInterface.addIndex('accounts_pool', ['tier']);
|
|
await queryInterface.addIndex('accounts_pool', ['riskScore']);
|
|
await queryInterface.addIndex('accounts_pool', ['lastActiveTime']);
|
|
|
|
// 创建账号健康度表
|
|
await queryInterface.createTable('accounts_health', {
|
|
id: {
|
|
type: DataTypes.INTEGER,
|
|
primaryKey: true,
|
|
allowNull: false,
|
|
autoIncrement: true,
|
|
comment: "主键ID"
|
|
},
|
|
accountPoolId: {
|
|
type: DataTypes.INTEGER,
|
|
allowNull: false,
|
|
comment: "关联的账号池ID"
|
|
},
|
|
healthScore: {
|
|
type: DataTypes.FLOAT,
|
|
defaultValue: 100,
|
|
comment: "健康度评分(0-100)"
|
|
},
|
|
responseTime: {
|
|
type: DataTypes.FLOAT,
|
|
comment: "平均响应时间(ms)"
|
|
},
|
|
successRate: {
|
|
type: DataTypes.FLOAT,
|
|
defaultValue: 100,
|
|
comment: "成功率百分比"
|
|
},
|
|
errorCount: {
|
|
type: DataTypes.INTEGER,
|
|
defaultValue: 0,
|
|
comment: "错误次数"
|
|
},
|
|
warningCount: {
|
|
type: DataTypes.INTEGER,
|
|
defaultValue: 0,
|
|
comment: "警告次数"
|
|
},
|
|
dailyUsageRate: {
|
|
type: DataTypes.FLOAT,
|
|
defaultValue: 0,
|
|
comment: "日使用率百分比"
|
|
},
|
|
weeklyUsageRate: {
|
|
type: DataTypes.FLOAT,
|
|
defaultValue: 0,
|
|
comment: "周使用率百分比"
|
|
},
|
|
restDuration: {
|
|
type: DataTypes.INTEGER,
|
|
defaultValue: 0,
|
|
comment: "休息时长(分钟)"
|
|
},
|
|
spamReportCount: {
|
|
type: DataTypes.INTEGER,
|
|
defaultValue: 0,
|
|
comment: "垃圾信息举报次数"
|
|
},
|
|
blockCount: {
|
|
type: DataTypes.INTEGER,
|
|
defaultValue: 0,
|
|
comment: "被拉黑次数"
|
|
},
|
|
restrictionCount: {
|
|
type: DataTypes.INTEGER,
|
|
defaultValue: 0,
|
|
comment: "被限制次数"
|
|
},
|
|
anomalyScore: {
|
|
type: DataTypes.FLOAT,
|
|
defaultValue: 0,
|
|
comment: "异常行为评分"
|
|
},
|
|
activeHours: {
|
|
type: DataTypes.JSON,
|
|
defaultValue: [],
|
|
comment: "活跃时段分布"
|
|
},
|
|
engagementRate: {
|
|
type: DataTypes.FLOAT,
|
|
defaultValue: 0,
|
|
comment: "互动率"
|
|
},
|
|
evaluationDetails: {
|
|
type: DataTypes.JSON,
|
|
defaultValue: {},
|
|
comment: "评估详细信息"
|
|
},
|
|
lastEvaluationTime: {
|
|
type: DataTypes.DATE,
|
|
defaultValue: Sequelize.NOW,
|
|
comment: "最后评估时间"
|
|
},
|
|
trend: {
|
|
type: DataTypes.ENUM('improving', 'stable', 'declining'),
|
|
defaultValue: 'stable',
|
|
comment: "健康度趋势"
|
|
},
|
|
recommendations: {
|
|
type: DataTypes.JSON,
|
|
defaultValue: [],
|
|
comment: "改善建议"
|
|
},
|
|
createdAt: {
|
|
type: DataTypes.DATE,
|
|
allowNull: false
|
|
},
|
|
updatedAt: {
|
|
type: DataTypes.DATE,
|
|
allowNull: false
|
|
}
|
|
});
|
|
|
|
// 创建索引
|
|
await queryInterface.addIndex('accounts_health', ['accountPoolId']);
|
|
await queryInterface.addIndex('accounts_health', ['healthScore']);
|
|
await queryInterface.addIndex('accounts_health', ['lastEvaluationTime']);
|
|
|
|
// 创建账号使用记录表
|
|
await queryInterface.createTable('accounts_usage_log', {
|
|
id: {
|
|
type: DataTypes.INTEGER,
|
|
primaryKey: true,
|
|
allowNull: false,
|
|
autoIncrement: true,
|
|
comment: "主键ID"
|
|
},
|
|
accountPoolId: {
|
|
type: DataTypes.INTEGER,
|
|
allowNull: false,
|
|
comment: "关联的账号池ID"
|
|
},
|
|
taskId: {
|
|
type: DataTypes.INTEGER,
|
|
allowNull: false,
|
|
comment: "关联的任务ID"
|
|
},
|
|
taskType: {
|
|
type: DataTypes.ENUM('group_send', 'private_send', 'join_group', 'leave_group', 'other'),
|
|
defaultValue: 'group_send',
|
|
comment: "任务类型"
|
|
},
|
|
groupId: {
|
|
type: DataTypes.INTEGER,
|
|
comment: "群组ID(如果是群发任务)"
|
|
},
|
|
messageContent: {
|
|
type: DataTypes.TEXT,
|
|
comment: "发送的消息内容"
|
|
},
|
|
status: {
|
|
type: DataTypes.ENUM('pending', 'success', 'failed', 'timeout', 'cancelled'),
|
|
defaultValue: 'pending',
|
|
comment: "执行状态"
|
|
},
|
|
errorCode: {
|
|
type: DataTypes.STRING(50),
|
|
comment: "错误代码"
|
|
},
|
|
errorMessage: {
|
|
type: DataTypes.STRING(500),
|
|
comment: "错误信息"
|
|
},
|
|
retryCount: {
|
|
type: DataTypes.INTEGER,
|
|
defaultValue: 0,
|
|
comment: "重试次数"
|
|
},
|
|
startTime: {
|
|
type: DataTypes.DATE,
|
|
allowNull: false,
|
|
comment: "开始时间"
|
|
},
|
|
endTime: {
|
|
type: DataTypes.DATE,
|
|
comment: "结束时间"
|
|
},
|
|
duration: {
|
|
type: DataTypes.INTEGER,
|
|
comment: "执行时长(毫秒)"
|
|
},
|
|
riskLevel: {
|
|
type: DataTypes.ENUM('low', 'medium', 'high', 'critical'),
|
|
defaultValue: 'low',
|
|
comment: "风险级别"
|
|
},
|
|
behaviorSimulation: {
|
|
type: DataTypes.JSON,
|
|
defaultValue: {},
|
|
comment: "行为模拟参数"
|
|
},
|
|
ipAddress: {
|
|
type: DataTypes.STRING(50),
|
|
comment: "使用的IP地址"
|
|
},
|
|
deviceFingerprint: {
|
|
type: DataTypes.STRING(100),
|
|
comment: "设备指纹"
|
|
},
|
|
recipientCount: {
|
|
type: DataTypes.INTEGER,
|
|
defaultValue: 0,
|
|
comment: "接收者数量"
|
|
},
|
|
readCount: {
|
|
type: DataTypes.INTEGER,
|
|
defaultValue: 0,
|
|
comment: "已读数量"
|
|
},
|
|
replyCount: {
|
|
type: DataTypes.INTEGER,
|
|
defaultValue: 0,
|
|
comment: "回复数量"
|
|
},
|
|
reportCount: {
|
|
type: DataTypes.INTEGER,
|
|
defaultValue: 0,
|
|
comment: "举报数量"
|
|
},
|
|
metadata: {
|
|
type: DataTypes.JSON,
|
|
defaultValue: {},
|
|
comment: "其他元数据"
|
|
},
|
|
createdAt: {
|
|
type: DataTypes.DATE,
|
|
allowNull: false
|
|
},
|
|
updatedAt: {
|
|
type: DataTypes.DATE,
|
|
allowNull: false
|
|
}
|
|
});
|
|
|
|
// 创建索引
|
|
await queryInterface.addIndex('accounts_usage_log', ['accountPoolId']);
|
|
await queryInterface.addIndex('accounts_usage_log', ['taskId']);
|
|
await queryInterface.addIndex('accounts_usage_log', ['startTime', 'endTime']);
|
|
await queryInterface.addIndex('accounts_usage_log', ['status']);
|
|
await queryInterface.addIndex('accounts_usage_log', ['riskLevel']);
|
|
},
|
|
|
|
down: async (queryInterface, Sequelize) => {
|
|
// 删除表(按相反顺序)
|
|
await queryInterface.dropTable('accounts_usage_log');
|
|
await queryInterface.dropTable('accounts_health');
|
|
await queryInterface.dropTable('accounts_pool');
|
|
}
|
|
}; |