-- 2026-05-21_project_geo_modules.sql
-- 迁移源项目的项目管理与账号环境管理模块。
-- 适用：MySQL 8 / InnoDB / utf8mb4。生产环境 typeorm.synchronize=false 时请先执行本脚本。

CREATE TABLE IF NOT EXISTS `project_info` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `createTime` varchar(255) NOT NULL COMMENT '创建时间',
  `updateTime` varchar(255) NOT NULL COMMENT '更新时间',
  `tenantId` int NULL COMMENT '租户ID',
  `name` varchar(100) NOT NULL COMMENT '项目名称',
  `description` text NULL COMMENT '项目描述',
  `status` int NOT NULL DEFAULT 0 COMMENT '状态 0未开始 1进行中 2已完成 3已归档',
  `startDate` date NULL COMMENT '计划开始日期',
  `endDate` date NULL COMMENT '计划结束日期',
  `progress` int NOT NULL DEFAULT 0 COMMENT '进度百分比 0-100',
  `ownerId` int NULL COMMENT '项目经理ID',
  `ownerName` varchar(50) NULL COMMENT '项目经理姓名',
  `color` varchar(20) NULL COMMENT '主题色',
  PRIMARY KEY (`id`),
  KEY `IDX_project_info_createTime` (`createTime`),
  KEY `IDX_project_info_updateTime` (`updateTime`),
  KEY `IDX_project_info_tenantId` (`tenantId`),
  KEY `IDX_project_info_ownerId` (`ownerId`),
  KEY `IDX_project_info_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='项目信息';

CREATE TABLE IF NOT EXISTS `project_phase` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `createTime` varchar(255) NOT NULL COMMENT '创建时间',
  `updateTime` varchar(255) NOT NULL COMMENT '更新时间',
  `tenantId` int NULL COMMENT '租户ID',
  `projectId` int NOT NULL COMMENT '所属项目ID',
  `name` varchar(100) NOT NULL COMMENT '阶段名称',
  `type` varchar(50) NULL COMMENT '分类',
  `status` int NOT NULL DEFAULT 0 COMMENT '状态 0未开始 1进行中 2已完成',
  `startDate` date NULL COMMENT '开始日期',
  `endDate` date NULL COMMENT '结束日期',
  `progress` int NOT NULL DEFAULT 0 COMMENT '进度 0-100',
  `sortOrder` int NOT NULL DEFAULT 0 COMMENT '排序序号',
  PRIMARY KEY (`id`),
  KEY `IDX_project_phase_createTime` (`createTime`),
  KEY `IDX_project_phase_updateTime` (`updateTime`),
  KEY `IDX_project_phase_tenantId` (`tenantId`),
  KEY `IDX_project_phase_projectId` (`projectId`),
  KEY `IDX_project_phase_project_sort` (`projectId`, `sortOrder`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='项目阶段';

CREATE TABLE IF NOT EXISTS `project_task` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `createTime` varchar(255) NOT NULL COMMENT '创建时间',
  `updateTime` varchar(255) NOT NULL COMMENT '更新时间',
  `tenantId` int NULL COMMENT '租户ID',
  `projectId` int NOT NULL COMMENT '所属项目ID',
  `phaseId` int NULL COMMENT '所属阶段ID',
  `parentId` int NULL COMMENT '父任务ID',
  `name` varchar(200) NOT NULL COMMENT '任务名称',
  `description` text NULL COMMENT '任务描述',
  `status` int NOT NULL DEFAULT 0 COMMENT '状态 0待办 1进行中 2已完成 3已关闭',
  `priority` int NOT NULL DEFAULT 2 COMMENT '优先级 0紧急 1高 2中 3低',
  `category` varchar(50) NULL COMMENT '分类',
  `assigneeId` int NULL COMMENT '负责人ID',
  `assigneeName` varchar(50) NULL COMMENT '负责人姓名',
  `startDate` date NULL COMMENT '计划开始日期',
  `endDate` date NULL COMMENT '计划结束日期',
  `estimatedHours` decimal(8,1) NOT NULL DEFAULT 0.0 COMMENT '预估工时(小时)',
  `actualHours` decimal(8,1) NOT NULL DEFAULT 0.0 COMMENT '实际工时(小时)',
  `progress` int NOT NULL DEFAULT 0 COMMENT '进度 0-100',
  `sortOrder` int NOT NULL DEFAULT 0 COMMENT '排序序号',
  `color` varchar(20) NULL COMMENT '自定义颜色',
  PRIMARY KEY (`id`),
  KEY `IDX_project_task_createTime` (`createTime`),
  KEY `IDX_project_task_updateTime` (`updateTime`),
  KEY `IDX_project_task_tenantId` (`tenantId`),
  KEY `IDX_project_task_projectId` (`projectId`),
  KEY `IDX_project_task_phaseId` (`phaseId`),
  KEY `IDX_project_task_parentId` (`parentId`),
  KEY `IDX_project_task_assigneeId` (`assigneeId`),
  KEY `IDX_project_task_project_status` (`projectId`, `status`, `sortOrder`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='项目任务';

CREATE TABLE IF NOT EXISTS `project_task_dependency` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `createTime` varchar(255) NOT NULL COMMENT '创建时间',
  `updateTime` varchar(255) NOT NULL COMMENT '更新时间',
  `tenantId` int NULL COMMENT '租户ID',
  `taskId` int NOT NULL COMMENT '当前任务ID',
  `dependsOnTaskId` int NOT NULL COMMENT '前置任务ID',
  `type` int NOT NULL DEFAULT 0 COMMENT '依赖类型 0:FS 1:SS 2:FF 3:SF',
  PRIMARY KEY (`id`),
  KEY `IDX_project_task_dependency_createTime` (`createTime`),
  KEY `IDX_project_task_dependency_updateTime` (`updateTime`),
  KEY `IDX_project_task_dependency_tenantId` (`tenantId`),
  KEY `IDX_project_task_dependency_taskId` (`taskId`),
  KEY `IDX_project_task_dependency_dependsOnTaskId` (`dependsOnTaskId`),
  UNIQUE KEY `UK_project_task_dependency_pair` (`taskId`, `dependsOnTaskId`, `type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='任务依赖关系';

CREATE TABLE IF NOT EXISTS `project_time_log` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `createTime` varchar(255) NOT NULL COMMENT '创建时间',
  `updateTime` varchar(255) NOT NULL COMMENT '更新时间',
  `tenantId` int NULL COMMENT '租户ID',
  `taskId` int NOT NULL COMMENT '所属任务ID',
  `userId` int NOT NULL COMMENT '记录人ID',
  `userName` varchar(50) NOT NULL COMMENT '记录人姓名',
  `logDate` date NOT NULL COMMENT '工作日期',
  `hours` decimal(5,1) NOT NULL COMMENT '工时(小时)',
  `description` varchar(500) NULL COMMENT '工作内容描述',
  PRIMARY KEY (`id`),
  KEY `IDX_project_time_log_createTime` (`createTime`),
  KEY `IDX_project_time_log_updateTime` (`updateTime`),
  KEY `IDX_project_time_log_tenantId` (`tenantId`),
  KEY `IDX_project_time_log_taskId` (`taskId`),
  KEY `IDX_project_time_log_userId` (`userId`),
  KEY `IDX_project_time_log_task_date` (`taskId`, `logDate`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工时记录';

CREATE TABLE IF NOT EXISTS `geo_proxy_ip` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `createTime` varchar(255) NOT NULL COMMENT '创建时间',
  `updateTime` varchar(255) NOT NULL COMMENT '更新时间',
  `tenantId` int NULL COMMENT '租户ID',
  `name` varchar(255) NOT NULL COMMENT '名称',
  `provider` varchar(32) NOT NULL COMMENT '所属 Provider: local / tianqi',
  `mode` varchar(16) NOT NULL COMMENT '模式: local / third_party',
  `host` varchar(128) NULL COMMENT '代理主机',
  `port` int NULL COMMENT '代理端口',
  `protocol` varchar(8) NOT NULL DEFAULT 'http' COMMENT '协议 http/socks5',
  `username` varchar(256) NULL COMMENT '用户名（明文）',
  `password` varchar(512) NULL COMMENT '密码（明文）',
  `region` varchar(64) NULL COMMENT '区域',
  `isp` varchar(32) NULL COMMENT 'ISP',
  `exitIp` varchar(64) NULL COMMENT '出口 IP（与代理 IP 不同时填，测试得出）',
  `city` varchar(64) NULL COMMENT '开通城市',
  `packageId` varchar(64) NULL COMMENT '所属套餐 ID',
  `externalId` varchar(128) NULL COMMENT 'Provider 侧外部 ID',
  `bindAccountId` int NULL COMMENT '绑定账号 ID（强 1:1）',
  `status` varchar(16) NOT NULL DEFAULT 'unbound' COMMENT '状态 active/expired/error/unbound',
  `latencyMs` int NULL COMMENT '健康检查延迟 ms',
  `lastCheckAt` datetime NULL COMMENT '上次检查时间',
  `expiresAt` datetime NULL COMMENT '过期时间',
  `extra` json NULL COMMENT '扩展字段',
  PRIMARY KEY (`id`),
  KEY `IDX_geo_proxy_ip_createTime` (`createTime`),
  KEY `IDX_geo_proxy_ip_updateTime` (`updateTime`),
  KEY `IDX_geo_proxy_ip_tenantId` (`tenantId`),
  KEY `IDX_geo_proxy_ip_provider` (`provider`),
  KEY `IDX_geo_proxy_ip_mode` (`mode`),
  KEY `IDX_geo_proxy_ip_status` (`status`),
  KEY `IDX_geo_proxy_ip_packageId` (`packageId`),
  UNIQUE KEY `UK_geo_proxy_ip_bindAccountId` (`bindAccountId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='代理 IP 池';

CREATE TABLE IF NOT EXISTS `geo_account` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `createTime` varchar(255) NOT NULL COMMENT '创建时间',
  `updateTime` varchar(255) NOT NULL COMMENT '更新时间',
  `tenantId` int NULL COMMENT '租户ID',
  `name` varchar(255) NOT NULL COMMENT '账号昵称（用户填写）',
  `platform` varchar(32) NOT NULL COMMENT '平台 xiaohongshu/douyin/weibo/zhihu/taobao/wechat',
  `loginAccount` varchar(128) NULL COMMENT '登录账号/手机号',
  `sessionName` varchar(128) NULL COMMENT 'netabrowser-cli sessionName',
  `agentId` int NULL COMMENT '关联 Agent ID',
  `fingerprintSeed` int NULL COMMENT '浏览器指纹 seed',
  `cookies` text NULL COMMENT 'cookies（明文 JSON，本地后台工具）',
  `cookieCapturedAt` datetime NULL COMMENT 'Cookie 抓取时间',
  `cookieExpiresAt` datetime NULL COMMENT 'Cookie 最早过期时间',
  `loginStatus` varchar(16) NOT NULL DEFAULT 'never' COMMENT '登录状态 never/logged_in/expired/banned/deleted',
  `proxyId` int NULL COMMENT '绑定 IP',
  `lastActiveAt` datetime NULL COMMENT '上次活跃时间',
  `extra` json NULL COMMENT '平台特定字段',
  PRIMARY KEY (`id`),
  KEY `IDX_geo_account_createTime` (`createTime`),
  KEY `IDX_geo_account_updateTime` (`updateTime`),
  KEY `IDX_geo_account_tenantId` (`tenantId`),
  KEY `IDX_geo_account_platform` (`platform`),
  KEY `IDX_geo_account_agentId` (`agentId`),
  KEY `IDX_geo_account_loginStatus` (`loginStatus`),
  UNIQUE KEY `UK_geo_account_sessionName` (`sessionName`),
  UNIQUE KEY `UK_geo_account_proxyId` (`proxyId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='账号矩阵';

SET @now = DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s');

INSERT INTO `base_sys_menu`
(`parentId`, `name`, `router`, `perms`, `type`, `icon`, `orderNum`, `viewPath`, `keepAlive`, `isShow`, `createTime`, `updateTime`, `tenantId`)
SELECT NULL, '项目管理', NULL, NULL, 0, 'icon-task', 9, NULL, 1, 0, @now, @now, NULL
WHERE NOT EXISTS (SELECT 1 FROM `base_sys_menu` WHERE `name` = '项目管理' AND `parentId` IS NULL);

SET @project_parent_id = (
  SELECT `id` FROM `base_sys_menu`
  WHERE `name` = '项目管理' AND `parentId` IS NULL
  ORDER BY `id` DESC LIMIT 1
);

INSERT INTO `base_sys_menu`
(`parentId`, `name`, `router`, `perms`, `type`, `icon`, `orderNum`, `viewPath`, `keepAlive`, `isShow`, `createTime`, `updateTime`, `tenantId`)
SELECT @project_parent_id, '项目列表', '/project/list', NULL, 1, 'icon-list', 0, 'modules/project/views/list.vue', 1, 0, @now, @now, NULL
WHERE NOT EXISTS (SELECT 1 FROM `base_sys_menu` WHERE `router` = '/project/list');

SET @project_list_id = (SELECT `id` FROM `base_sys_menu` WHERE `router` = '/project/list' ORDER BY `id` DESC LIMIT 1);

INSERT INTO `base_sys_menu`
(`parentId`, `name`, `router`, `perms`, `type`, `icon`, `orderNum`, `viewPath`, `keepAlive`, `isShow`, `createTime`, `updateTime`, `tenantId`)
SELECT @project_list_id, '权限', NULL,
  'project:info:page,project:info:list,project:info:info,project:info:add,project:info:update,project:info:delete,project:phase:page,project:phase:list,project:phase:info,project:phase:add,project:phase:update,project:phase:delete,project:task:page,project:task:list,project:task:info,project:task:add,project:task:update,project:task:delete,project:task:tree,project:task:ganttData,project:task:ganttUpdate,project:task:kanban,project:task:kanbanSort,project:task:hasChildren,project:task:cascadeStatus,project:task_dependency:list,project:task_dependency:add,project:task_dependency:delete,project:time_log:page,project:time_log:add,project:time_log:delete',
  2, NULL, 0, NULL, 1, 0, @now, @now, NULL
WHERE NOT EXISTS (SELECT 1 FROM `base_sys_menu` WHERE `parentId` = @project_list_id AND `name` = '权限');

INSERT INTO `base_sys_menu`
(`parentId`, `name`, `router`, `perms`, `type`, `icon`, `orderNum`, `viewPath`, `keepAlive`, `isShow`, `createTime`, `updateTime`, `tenantId`)
SELECT NULL, '账号环境管理', NULL, NULL, 0, 'icon-data', 12, NULL, 1, 1, @now, @now, NULL
WHERE NOT EXISTS (SELECT 1 FROM `base_sys_menu` WHERE `name` = '账号环境管理' AND `parentId` IS NULL);

SET @geo_parent_id = (
  SELECT `id` FROM `base_sys_menu`
  WHERE `name` = '账号环境管理' AND `parentId` IS NULL
  ORDER BY `id` DESC LIMIT 1
);

INSERT INTO `base_sys_menu`
(`parentId`, `name`, `router`, `perms`, `type`, `icon`, `orderNum`, `viewPath`, `keepAlive`, `isShow`, `createTime`, `updateTime`, `tenantId`)
SELECT @geo_parent_id, '环境总览', '/geo/dashboard', NULL, 1, 'icon-count', 0, 'modules/geo/views/dashboard.vue', 1, 1, @now, @now, NULL
WHERE NOT EXISTS (SELECT 1 FROM `base_sys_menu` WHERE `router` = '/geo/dashboard');

INSERT INTO `base_sys_menu`
(`parentId`, `name`, `router`, `perms`, `type`, `icon`, `orderNum`, `viewPath`, `keepAlive`, `isShow`, `createTime`, `updateTime`, `tenantId`)
SELECT @geo_parent_id, '账号矩阵', '/geo/accounts', NULL, 1, 'icon-user', 1, 'modules/geo/views/accounts.vue', 1, 1, @now, @now, NULL
WHERE NOT EXISTS (SELECT 1 FROM `base_sys_menu` WHERE `router` = '/geo/accounts');

INSERT INTO `base_sys_menu`
(`parentId`, `name`, `router`, `perms`, `type`, `icon`, `orderNum`, `viewPath`, `keepAlive`, `isShow`, `createTime`, `updateTime`, `tenantId`)
SELECT @geo_parent_id, 'IP 池', '/geo/proxies', NULL, 1, 'icon-data', 2, 'modules/geo/views/proxies.vue', 1, 1, @now, @now, NULL
WHERE NOT EXISTS (SELECT 1 FROM `base_sys_menu` WHERE `router` = '/geo/proxies');

SET @geo_accounts_id = (SELECT `id` FROM `base_sys_menu` WHERE `router` = '/geo/accounts' ORDER BY `id` DESC LIMIT 1);
SET @geo_proxies_id = (SELECT `id` FROM `base_sys_menu` WHERE `router` = '/geo/proxies' ORDER BY `id` DESC LIMIT 1);

INSERT INTO `base_sys_menu`
(`parentId`, `name`, `router`, `perms`, `type`, `icon`, `orderNum`, `viewPath`, `keepAlive`, `isShow`, `createTime`, `updateTime`, `tenantId`)
SELECT @geo_accounts_id, '权限', NULL,
  'geo:account:page,geo:account:list,geo:account:info,geo:account:add,geo:account:update,geo:account:delete,geo:account:launch,geo:account:close,geo:account:captureCookies,geo:account:resetSession,geo:account:rebindIp,geo:account:setProxy,geo:account:deleteAccount,netaclaw:agent:options',
  2, NULL, 0, NULL, 1, 1, @now, @now, NULL
WHERE NOT EXISTS (SELECT 1 FROM `base_sys_menu` WHERE `parentId` = @geo_accounts_id AND `name` = '权限');

INSERT INTO `base_sys_menu`
(`parentId`, `name`, `router`, `perms`, `type`, `icon`, `orderNum`, `viewPath`, `keepAlive`, `isShow`, `createTime`, `updateTime`, `tenantId`)
SELECT @geo_proxies_id, '权限', NULL,
  'geo:proxy_ip:page,geo:proxy_ip:list,geo:proxy_ip:info,geo:proxy_ip:add,geo:proxy_ip:update,geo:proxy_ip:delete,geo:proxy_ip:test,geo:proxy_ip:healthCheck,geo:proxy_ip:healthCheckAll',
  2, NULL, 0, NULL, 1, 1, @now, @now, NULL
WHERE NOT EXISTS (SELECT 1 FROM `base_sys_menu` WHERE `parentId` = @geo_proxies_id AND `name` = '权限');

INSERT INTO `base_sys_role_menu` (`roleId`, `menuId`, `createTime`, `updateTime`, `tenantId`)
SELECT 1, m.`id`, @now, @now, NULL
FROM `base_sys_menu` m
WHERE (
    m.`name` IN ('项目管理', '项目列表', '账号环境管理', '环境总览', '账号矩阵', 'IP 池')
    OR (m.`name` = '权限' AND m.`parentId` IN (@project_list_id, @geo_accounts_id, @geo_proxies_id))
  )
  AND NOT EXISTS (
    SELECT 1 FROM `base_sys_role_menu` rm
    WHERE rm.`roleId` = 1 AND rm.`menuId` = m.`id`
  );
