Initial commit: Telegram Management System
Some checks failed
Deploy / deploy (push) Has been cancelled
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>
This commit is contained in:
365
backend/migrations/20241228_create_account_pool_tables.js
Normal file
365
backend/migrations/20241228_create_account_pool_tables.js
Normal file
@@ -0,0 +1,365 @@
|
||||
// 创建账号池相关表的迁移脚本
|
||||
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');
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user