Enhance database management features by adding an API endpoint to drop all tables with double confirmation. Implement configuration file upload and download functionalities, including backup of the existing configuration. Update UI elements for improved user experience, including pagination controls and toast notifications. Adjust Docker and git ignore files to optimize build processes.

This commit is contained in:
2026-01-14 18:27:05 +08:00
parent 866fc1cb06
commit 5b50c9750f
19 changed files with 2424 additions and 166 deletions
+2
View File
@@ -0,0 +1,2 @@
# 此文件用于确保 dist 目录被 Git 跟踪
# dist 目录用于存放离线部署包
+243
View File
@@ -0,0 +1,243 @@
{
"Update": "2026/01/14 09:14:43",
"MySQL_DBInfo": {
"host": "capacity-mysql",
"port": 3306,
"user": "root",
"passwd": "gmcc123",
"dbname": "CapacityReport"
},
"SheetFilter": [
"指标(计数器)",
"Template"
],
"ExtractField": [
{
"Field": "日期时间",
"Extract": [
"开始时间"
],
"Type": "datetime"
},
{
"Field": "网元ID",
"Extract": [
"网元ID",
"网元"
]
},
{
"Field": "eNodeBId",
"Extract": [
"eNodeBId",
"eNodeB"
]
},
{
"Field": "cellLocalId",
"Extract": [
"cellId",
"小区"
]
},
{
"Field": "基站名称",
"Extract": [
"eNodeB CU名称",
"eNodeB名称"
]
},
{
"Field": "小区名称",
"Extract": [
"小区名称",
"E-UTRAN FDD小区名称",
"E-UTRAN TDD小区名称"
]
},
{
"Field": "上下行总流量_GB",
"Extract": [
"集团-上下行总业务量(GB)",
"业务流量上下行(GB)",
"YJ_业务流量上下行(GB)",
"FDD总流量(G)"
]
},
{
"Field": "ERAB流量",
"Extract": [
"ERAB流量(新高负荷)",
"ERAB流量",
"ERAB流量(新高负荷)",
"ERAB流量(新高负荷)-FDD"
]
},
{
"Field": "上行流量_GB",
"Extract": [
"上行流量(新高负荷GB)",
"上行流量(新高负荷GB)",
"高负荷-上行流量(GB)",
"FDD上行流量(G)"
]
},
{
"Field": "下行流量_GB",
"Extract": [
"下行流量(新高负荷GB)",
"下行流量(新高负荷GB)",
"高负荷-下行流量(GB)",
"FDD下行流量(G)"
]
},
{
"Field": "用户面平均激活UE数",
"Extract": [
"用户面平均激活UE数",
"YY-用户面平均激活UE数(个)",
"用户面平均激活UE数(unit)"
]
},
{
"Field": "RRC连接建立最大用户数",
"Extract": [
"YY-RRC连接建立最大用户数(个)",
"RRC连接建立最大用户数(unit)",
"RRC连接建立最大用户数"
]
},
{
"Field": "上行PUSCH利用率",
"Extract": [
"上行PUSCH利用率",
"YY-上行PUSCH利用率",
"上行PUSCH利用率-FDD"
]
},
{
"Field": "下行PDSCH利用率",
"Extract": [
"下行PDSCH利用率",
"YY-下行PDSCH利用率",
"下行PDSCH利用率-FDD"
]
},
{
"Field": "PDCCH资源利用率",
"Extract": [
"PDCCH资源利用率",
"PDCCH资源利用率_1548034713403-33-2",
"PDCCH资源使用率(%)"
]
},
{
"Field": "用户面最大激活UE数",
"Extract": [
"开发者大会保障--最大激活UE数",
"用户面最大激活UE数",
"YY-用户面最大激活UE数(个)",
"用户面最大激活UE数(unit)"
]
},
{
"Field": "CU小区配置名称",
"Extract": [
"CU小区配置名称"
]
},
{
"Field": "gNBId",
"Extract": [
"gNBId"
]
},
{
"Field": "gNBplmn",
"Extract": [
"gNBplmn"
]
},
{
"Field": "5G上下行总流量_GB",
"Extract": [
"5G上下行总流量(上行PDCP PDU数据量+下行PDCP成功发送数据量)(GB)"
]
},
{
"Field": "5G上行流量_GB",
"Extract": [
"5G上行流量(上行PDCP PDU数据量)_1603677232210(GB)",
"5G上行流量(上行PDCP PDU数据量)(GB)"
]
},
{
"Field": "5G下行流量_GB",
"Extract": [
"5G下行流量(下行PDCP成功发送数据量)_1603677232245(GB)",
"5G下行流量(下行PDCP成功发送数据量)(GB)"
]
},
{
"Field": "小区平均激活UE数",
"Extract": [
"小区平均激活UE数"
]
},
{
"Field": "RRC连接最大连接用户数",
"Extract": [
"RRC连接最大连接用户数"
]
},
{
"Field": "RRC连接平均连接用户数",
"Extract": [
"RRC连接平均连接用户数"
]
},
{
"Field": "小区最大激活UE数",
"Extract": [
"小区最大激活UE数"
]
},
{
"Field": "上行PRB平均利用率",
"Extract": [
"上行PRB平均利用率"
]
},
{
"Field": "下行PRB平均利用率",
"Extract": [
"下行PRB平均利用率"
]
},
{
"Field": "小区下行RLC_SDU字节数_MByte",
"Extract": [
"小区下行RLC SDU字节数(MByte)"
]
},
{
"Field": "小区上行RLC_SDU字节数_MByte",
"Extract": [
"小区上行RLC SDU字节数(MByte)"
]
},
{
"Field": "PDCCH信道动态CCE占用率",
"Extract": [
"PDCCH信道CCE占用率(动态)(%)"
]
},
{
"Field": "平均单Flow流量_MB",
"Extract": [
"平均单Flow流量(MB)",
"平均每flow流量(流量/flow(MB/flow))"
]
}
]
}
+471
View File
@@ -0,0 +1,471 @@
# Update: 2024/11/25 09:30:00
SET GLOBAL wait_timeout = 86400;
DROP TABLE IF EXISTS `4G`;
DROP TABLE IF EXISTS `5G`;
DROP TABLE IF EXISTS `4G_Date`;
DROP TABLE IF EXISTS `4G_MAX`;
DROP TABLE IF EXISTS `CCE_MAX`;
DROP TABLE IF EXISTS `ULPRB_MAX`;
DROP TABLE IF EXISTS `DLPRB_MAX`;
DROP TABLE IF EXISTS `4G_MAX_IDX`;
DROP TABLE IF EXISTS `CCE_MAX_IDX`;
DROP TABLE IF EXISTS `ULPRB_MAX_IDX`;
DROP TABLE IF EXISTS `DLPRB_MAX_IDX`;
DROP TABLE IF EXISTS `日流量`;
DROP TABLE IF EXISTS `4G日均流量`;
DROP TABLE IF EXISTS `4G_结果表`;
DROP TABLE IF EXISTS `5G_Date`;
DROP TABLE IF EXISTS `5G_MAX`;
DROP TABLE IF EXISTS `5G_MAX_IDX`;
DROP TABLE IF EXISTS `5G日均流量`;
DROP TABLE IF EXISTS `5G_结果表`;
CREATE TABLE `4G` AS SELECT * FROM `4G_UD`;
CREATE TABLE `5G` AS SELECT * FROM `5G_UD`;
UPDATE 4G SET `日期时间` = DATE_FORMAT(`日期时间`, '%Y-%m-%d %H:%i:%s');
UPDATE 5G SET `日期时间` = DATE_FORMAT(`日期时间`, '%Y-%m-%d %H:%i:%s');
ALTER TABLE `4G`
ADD COLUMN `日期` date NULL FIRST,
MODIFY COLUMN `日期时间` datetime,
ADD COLUMN `CGI` varchar(100) NULL AFTER `日期时间`,
MODIFY COLUMN `网元ID` int,
MODIFY COLUMN `基站名称` varchar(200),
MODIFY COLUMN `小区名称` varchar(200),
MODIFY COLUMN `cellLocalId` int,
MODIFY COLUMN `eNodeBId` int,
MODIFY COLUMN `上下行总流量_GB` float,
MODIFY COLUMN `ERAB流量` float,
MODIFY COLUMN `上行流量_GB` float,
MODIFY COLUMN `下行流量_GB` float,
MODIFY COLUMN `用户面平均激活UE数` float,
MODIFY COLUMN `RRC连接建立最大用户数` int,
MODIFY COLUMN `上行PUSCH利用率` float,
MODIFY COLUMN `下行PDSCH利用率` float,
MODIFY COLUMN `PDCCH资源利用率` float,
MODIFY COLUMN `用户面最大激活UE数` int;
ALTER TABLE `5G`
ADD COLUMN `日期` date NULL FIRST,
MODIFY COLUMN `日期时间` datetime,
ADD COLUMN `NCGI` varchar(100) NULL AFTER `日期时间`,
MODIFY COLUMN `网元ID` int,
MODIFY COLUMN `CU小区配置名称` varchar(200),
MODIFY COLUMN `cellLocalId` int,
MODIFY COLUMN `gNBId` int,
MODIFY COLUMN `gNBplmn` varchar(200),
MODIFY COLUMN `5G上下行总流量_GB` float,
MODIFY COLUMN `5G上行流量_GB` float,
MODIFY COLUMN `5G下行流量_GB` float,
MODIFY COLUMN `小区平均激活UE数` float,
MODIFY COLUMN `RRC连接最大连接用户数` int,
MODIFY COLUMN `RRC连接平均连接用户数` float,
MODIFY COLUMN `小区最大激活UE数` float,
MODIFY COLUMN `上行PRB平均利用率` float,
MODIFY COLUMN `下行PRB平均利用率` float,
MODIFY COLUMN `小区下行RLC_SDU字节数_MByte` float,
MODIFY COLUMN `小区上行RLC_SDU字节数_MByte` float,
MODIFY COLUMN `PDCCH信道动态CCE占用率` float,
MODIFY COLUMN `平均单Flow流量_MB` float;
UPDATE 4G SET `日期` = DATE_FORMAT(`日期时间`, '%Y-%m-%d'), CGI = CONCAT("460-00-",eNodeBId,"-", cellLocalId);
UPDATE 5G SET gNBplmn = "460-00" WHERE gNBplmn IS NULL;
UPDATE 5G SET `日期` = DATE_FORMAT(`日期时间`, '%Y-%m-%d'), NCGI = CONCAT(gNBplmn, "-",gNBId,"-", cellLocalId);
ALTER TABLE 4G
ADD INDEX(`日期`),
ADD INDEX(`日期时间`),
ADD INDEX(`CGI`),
ADD INDEX `TC`(`日期时间`, `CGI`),
ADD INDEX `DC`(`日期`, `CGI`);
ALTER TABLE 5G
ADD INDEX(`日期`),
ADD INDEX(`日期时间`),
ADD INDEX(`NCGI`),
ADD INDEX `TC`(`日期时间`, `NCGI`),
ADD INDEX `DC`(`日期`, `NCGI`);
CREATE TEMPORARY TABLE IF NOT EXISTS `CCE_MAX_IDX`(
SELECT
`日期`,
CGI,
MAX( `PDCCH资源利用率`) AS `PDCCH资源利用率_忙时`
FROM 4G
GROUP BY `日期`, CGI
);
ALTER TABLE `CCE_MAX_IDX`
ADD COLUMN `日期时间` datetime,
ADD INDEX `DC`(`日期`, `CGI`, `PDCCH资源利用率_忙时`),
ADD INDEX `TC`(`日期时间`, `CGI`);
UPDATE `CCE_MAX_IDX` INNER JOIN 4G
ON
`CCE_MAX_IDX`.`日期` = 4G.`日期` AND
`CCE_MAX_IDX`.CGI=4G.CGI AND
`CCE_MAX_IDX`.`PDCCH资源利用率_忙时`=4G.`PDCCH资源利用率`
SET
`CCE_MAX_IDX`.`日期时间`=4G.`日期时间`;
CREATE TEMPORARY TABLE IF NOT EXISTS `CCE_MAX` (
SELECT 4G.*
FROM `CCE_MAX_IDX` LEFT JOIN 4G
ON `CCE_MAX_IDX`.CGI=4G.CGI AND
`CCE_MAX_IDX`.`日期时间`=4G.`日期时间`
);
DROP TABLE IF EXISTS `CCE_MAX_IDX`;
CREATE TEMPORARY TABLE IF NOT EXISTS `ULPRB_MAX_IDX`(
SELECT
`日期`,
CGI,
MAX( `上行PUSCH利用率`) AS `上行PUSCH利用率_忙时`
FROM 4G
GROUP BY `日期`, CGI
);
ALTER TABLE `ULPRB_MAX_IDX`
ADD COLUMN `日期时间` datetime,
ADD INDEX `DC`(`日期`, `CGI`, `上行PUSCH利用率_忙时`),
ADD INDEX `TC`(`日期时间`, `CGI`);
UPDATE `ULPRB_MAX_IDX` INNER JOIN 4G
ON
`ULPRB_MAX_IDX`.`日期` = 4G.`日期` AND
`ULPRB_MAX_IDX`.CGI=4G.CGI AND
`ULPRB_MAX_IDX`.`上行PUSCH利用率_忙时`=4G.`上行PUSCH利用率`
SET
`ULPRB_MAX_IDX`.`日期时间`=4G.`日期时间`;
CREATE TABLE IF NOT EXISTS `ULPRB_MAX` (
SELECT 4G.*
FROM `ULPRB_MAX_IDX` LEFT JOIN 4G
ON `ULPRB_MAX_IDX`.CGI=4G.CGI AND
`ULPRB_MAX_IDX`.`日期时间`=4G.`日期时间`
);
DROP TABLE IF EXISTS `ULPRB_MAX_IDX`;
CREATE TEMPORARY TABLE IF NOT EXISTS `DLPRB_MAX_IDX`(
SELECT
`日期`,
CGI,
MAX( `下行PDSCH利用率`) AS `下行PDSCH利用率_忙时`
FROM 4G
GROUP BY `日期`, CGI
);
ALTER TABLE `DLPRB_MAX_IDX`
ADD COLUMN `日期时间` datetime,
ADD INDEX `DC`(`日期`, `CGI`, `下行PDSCH利用率_忙时`),
ADD INDEX `TC`(`日期时间`, `CGI`);
UPDATE `DLPRB_MAX_IDX` INNER JOIN 4G
ON
`DLPRB_MAX_IDX`.`日期` = 4G.`日期` AND
`DLPRB_MAX_IDX`.CGI=4G.CGI AND
`DLPRB_MAX_IDX`.`下行PDSCH利用率_忙时`=4G.`下行PDSCH利用率`
SET
`DLPRB_MAX_IDX`.`日期时间`=4G.`日期时间`;
CREATE TEMPORARY TABLE IF NOT EXISTS `DLPRB_MAX` (
SELECT 4G.*
FROM `DLPRB_MAX_IDX` LEFT JOIN 4G
ON `DLPRB_MAX_IDX`.CGI=4G.CGI AND
`DLPRB_MAX_IDX`.`日期时间`=4G.`日期时间`
);
DROP TABLE IF EXISTS `DLPRB_MAX_IDX`;
ALTER TABLE `CCE_MAX` ADD INDEX `DC`(`日期`, `CGI`);
ALTER TABLE `DLPRB_MAX` ADD INDEX `DC`(`日期`, `CGI`);
ALTER TABLE `ULPRB_MAX` ADD INDEX `DC`(`日期`, `CGI`);
CREATE TEMPORARY TABLE IF NOT EXISTS `4G_Date` (
SELECT `日期`, CGI FROM 4G GROUP BY `日期`, CGI
);
ALTER TABLE `4G_Date` ADD INDEX `DC`(`日期`, CGI);
CREATE TEMPORARY TABLE IF NOT EXISTS `4G_MAX_IDX` (
SELECT
4G_Date.`日期`,
4G_Date.CGI,
GREATEST(`ULPRB_MAX`.`上行PUSCH利用率`, DLPRB_MAX.`下行PDSCH利用率`,CCE_MAX.`PDCCH资源利用率`) AS `最大值`
FROM
4G_Date
INNER JOIN
DLPRB_MAX
ON
4G_Date.`日期` = DLPRB_MAX.`日期` AND
4G_Date.CGI = DLPRB_MAX.CGI
INNER JOIN
CCE_MAX
ON
4G_Date.`日期` = CCE_MAX.`日期` AND
4G_Date.CGI = CCE_MAX.CGI
INNER JOIN
ULPRB_MAX
ON
4G_Date.`日期` = ULPRB_MAX.`日期` AND
4G_Date.CGI = ULPRB_MAX.CGI
);
DROP TABLE IF EXISTS `4G_Date`;
ALTER TABLE `4G_MAX_IDX` ADD INDEX `DC`(`日期`, CGI), ADD INDEX (`最大值`);
CREATE TABLE IF NOT EXISTS `4G_MAX` (
SELECT * FROM 4G LIMIT 0
);
ALTER TABLE `4G_MAX` ADD INDEX (`日期`), ADD INDEX (`CGI`), ADD INDEX `DC` (`日期`,CGI), ADD INDEX `idx`(`CGI`, `基站名称`, `小区名称`);
ALTER TABLE DLPRB_MAX ADD COLUMN `insert` INT NOT NULL DEFAULT 0;
ALTER TABLE CCE_MAX ADD COLUMN `insert` INT NOT NULL DEFAULT 0;
TRUNCATE `4G_MAX`;
INSERT INTO `4G_MAX`
SELECT ULPRB_MAX.* FROM ULPRB_MAX INNER JOIN `4G_MAX_IDX`
ON
ULPRB_MAX.`日期` = 4G_MAX_IDX.`日期` AND
ULPRB_MAX.CGI = 4G_MAX_IDX.`CGI` AND
ULPRB_MAX.`上行PUSCH利用率` = 4G_MAX_IDX.`最大值`;
UPDATE DLPRB_MAX INNER JOIN 4G_MAX ON DLPRB_MAX.`日期` = 4G_MAX.`日期` AND DLPRB_MAX.CGI = 4G_MAX.CGI SET DLPRB_MAX.`insert` = 1;
ALTER TABLE DLPRB_MAX ADD INDEX (`insert`);
DELETE FROM DLPRB_MAX WHERE `insert` = 1;
ALTER TABLE DLPRB_MAX DROP COLUMN `insert`;
INSERT INTO `4G_MAX`
SELECT DLPRB_MAX.* FROM DLPRB_MAX INNER JOIN `4G_MAX_IDX`
ON
DLPRB_MAX.`日期` = 4G_MAX_IDX.`日期` AND
DLPRB_MAX.CGI = 4G_MAX_IDX.`CGI` AND
DLPRB_MAX.`下行PDSCH利用率` = 4G_MAX_IDX.`最大值`;
UPDATE CCE_MAX INNER JOIN 4G_MAX ON CCE_MAX.`日期` = 4G_MAX.`日期` AND CCE_MAX.CGI = 4G_MAX.CGI SET CCE_MAX.`insert` = 1;
ALTER TABLE CCE_MAX ADD INDEX (`insert`);
DELETE FROM CCE_MAX WHERE `insert` = 1;
ALTER TABLE CCE_MAX DROP COLUMN `insert`;
INSERT INTO `4G_MAX`
SELECT CCE_MAX.* FROM CCE_MAX INNER JOIN `4G_MAX_IDX`
ON
CCE_MAX.`日期` = 4G_MAX_IDX.`日期` AND
CCE_MAX.CGI = 4G_MAX_IDX.`CGI` AND
CCE_MAX.`PDCCH资源利用率` = 4G_MAX_IDX.`最大值`;
DROP TABLE IF EXISTS `CCE_MAX`;
DROP TABLE IF EXISTS `ULPRB_MAX`;
DROP TABLE IF EXISTS `DLPRB_MAX`;
DROP TABLE IF EXISTS `4G_MAX_IDX`;
CREATE TEMPORARY TABLE IF NOT EXISTS `日流量`(SELECT `日期`,CGI, SUM(`上下行总流量_GB`) AS `日流量` FROM 4G GROUP BY `日期`,CGI);
ALTER TABLE `日流量` ADD INDEX (`CGI`);
DROP TABLE IF EXISTS `4G日均流量`;
CREATE TEMPORARY TABLE IF NOT EXISTS `4G日均流量` (
SELECT CGI, AVG(`日流量`) AS `日均流量`, COUNT(`日期`) AS `流量有效天数` FROM `日流量` WHERE `日流量` > 0 GROUP BY CGI
);
CREATE TABLE IF NOT EXISTS `4G_结果表` (
SELECT CGI, `基站名称`, `小区名称`,
ROUND(AVG(`上下行总流量_GB`),6) AS `自忙时流量(GB)`,
ROUND(AVG(`ERAB流量`),6) AS `ERAB流量`,
ROUND(AVG(`上行流量_GB`),6) AS `上行流量(GB)`,
ROUND(AVG(`下行流量_GB`),6) AS `下行流量(GB)`,
ROUND(AVG(`用户面平均激活UE数`),6) AS `用户面平均激活UE数`,
ROUND(AVG(`RRC连接建立最大用户数`),6) AS `YY-RRC连接建立最大用户数`,
ROUND(AVG(`上行PUSCH利用率`) / 100,6) AS `上行PUSCH利用率`,
ROUND(AVG(`下行PDSCH利用率`) / 100,6) AS `下行PDSCH利用率`,
ROUND(AVG(`PDCCH资源利用率`) / 100,6) AS `PDCCH资源利用率`,
ROUND(AVG(`用户面最大激活UE数`),6) AS `用户面最大激活UE数`
FROM 4G_MAX GROUP BY CGI, `基站名称`, `小区名称`
);
DROP TABLE IF EXISTS `4G_MAX`;
ALTER TABLE `4G_结果表` ADD COLUMN `日均流量(GB)` DOUBLE AFTER `小区名称`, ADD INDEX (`CGI`);
ALTER TABLE `4G日均流量` ADD INDEX (CGI);
UPDATE `4G_结果表` INNER JOIN `4G日均流量`
ON `4G_结果表`.CGI=`4G日均流量`.CGI
SET `4G_结果表`.`日均流量(GB)` = ROUND(`4G日均流量`.`日均流量`,6);
DROP TABLE IF EXISTS `日流量`;
DROP TABLE IF EXISTS `4G日均流量`;
DROP TABLE IF EXISTS `4G`;
# 5G脚本
CREATE TEMPORARY TABLE IF NOT EXISTS `ULPRB_MAX_IDX`(
SELECT
`日期`,
NCGI,
MAX(`上行PRB平均利用率`) AS `上行PRB平均利用率_忙时`
FROM 5G
GROUP BY `日期`, NCGI
);
ALTER TABLE `ULPRB_MAX_IDX`
ADD COLUMN `日期时间` datetime,
ADD INDEX `DC`(`日期`, `NCGI`, `上行PRB平均利用率_忙时`),
ADD INDEX `TC`(`日期时间`, `NCGI`);
UPDATE `ULPRB_MAX_IDX` INNER JOIN 5G
ON
`ULPRB_MAX_IDX`.`日期` = 5G.`日期` AND
`ULPRB_MAX_IDX`.NCGI=5G.NCGI AND
`ULPRB_MAX_IDX`.`上行PRB平均利用率_忙时`=5G.`上行PRB平均利用率`
SET
`ULPRB_MAX_IDX`.`日期时间`=5G.`日期时间`;
CREATE TEMPORARY TABLE IF NOT EXISTS `ULPRB_MAX` (
SELECT 5G.*
FROM `ULPRB_MAX_IDX` LEFT JOIN 5G
ON `ULPRB_MAX_IDX`.NCGI=5G.NCGI AND
`ULPRB_MAX_IDX`.`日期时间`=5G.`日期时间`
);
# 下行PRB MAX表
CREATE TEMPORARY TABLE IF NOT EXISTS `DLPRB_MAX_IDX`(
SELECT
`日期`,
NCGI,
MAX( `下行PRB平均利用率`) AS `下行PRB平均利用率_忙时`
FROM 5G
GROUP BY `日期`, NCGI
);
ALTER TABLE `DLPRB_MAX_IDX`
ADD COLUMN `日期时间` datetime,
ADD INDEX `DC`(`日期`, `NCGI`, `下行PRB平均利用率_忙时`),
ADD INDEX `TC`(`日期时间`, `NCGI`);
UPDATE `DLPRB_MAX_IDX` INNER JOIN 5G
ON
`DLPRB_MAX_IDX`.`日期` = 5G.`日期` AND
`DLPRB_MAX_IDX`.NCGI=5G.NCGI AND
`DLPRB_MAX_IDX`.`下行PRB平均利用率_忙时`=5G.`下行PRB平均利用率`
SET
`DLPRB_MAX_IDX`.`日期时间`=5G.`日期时间`;
CREATE TEMPORARY TABLE IF NOT EXISTS `DLPRB_MAX` (
SELECT 5G.*
FROM `DLPRB_MAX_IDX` LEFT JOIN 5G
ON `DLPRB_MAX_IDX`.NCGI=5G.NCGI AND
`DLPRB_MAX_IDX`.`日期时间`=5G.`日期时间`
);
DROP TABLE IF EXISTS `DLPRB_MAX_IDX`;
ALTER TABLE `DLPRB_MAX` ADD INDEX `DC`(`日期`, `NCGI`);
ALTER TABLE `ULPRB_MAX` ADD INDEX `DC`(`日期`, `NCGI`);
# 5G 统计MAX数据
DROP TABLE IF EXISTS `5G_Date`;
CREATE TEMPORARY TABLE IF NOT EXISTS `5G_Date` (
SELECT `日期`, NCGI FROM 5G GROUP BY `日期`, NCGI
);
ALTER TABLE `5G_Date` ADD INDEX `DC`(`日期`, NCGI);
CREATE TEMPORARY TABLE IF NOT EXISTS `5G_MAX_IDX` (
SELECT
5G_Date.`日期`,
5G_Date.NCGI,
GREATEST(ULPRB_MAX.`上行PRB平均利用率`, DLPRB_MAX.`下行PRB平均利用率`) AS `最大值`
FROM
5G_Date
INNER JOIN
DLPRB_MAX
ON
5G_Date.`日期` = DLPRB_MAX.`日期` AND
5G_Date.NCGI = DLPRB_MAX.NCGI
INNER JOIN
ULPRB_MAX
ON
5G_Date.`日期` = ULPRB_MAX.`日期` AND
5G_Date.NCGI = ULPRB_MAX.NCGI
);
DROP TABLE IF EXISTS `5G_Date`;
ALTER TABLE `5G_MAX_IDX` ADD INDEX `DC`(`日期`, NCGI), ADD INDEX (`最大值`);
CREATE TABLE IF NOT EXISTS `5G_MAX` (
SELECT * FROM 5G LIMIT 0
);
ALTER TABLE `5G_MAX` ADD INDEX (`日期`), ADD INDEX (`NCGI`), ADD INDEX `DC` (`日期`,NCGI), ADD INDEX `idx`(`NCGI`, `CU小区配置名称`);
ALTER TABLE DLPRB_MAX ADD COLUMN `insert` INT NOT NULL DEFAULT 0;
TRUNCATE `5G_MAX`;
INSERT INTO `5G_MAX`
SELECT ULPRB_MAX.* FROM ULPRB_MAX INNER JOIN `5G_MAX_IDX`
ON
ULPRB_MAX.`日期` = 5G_MAX_IDX.`日期` AND
ULPRB_MAX.NCGI = 5G_MAX_IDX.`NCGI` AND
ULPRB_MAX.`上行PRB平均利用率` = 5G_MAX_IDX.`最大值`;
UPDATE DLPRB_MAX INNER JOIN 5G_MAX ON DLPRB_MAX.`日期` = 5G_MAX.`日期` AND DLPRB_MAX.NCGI = 5G_MAX.NCGI SET DLPRB_MAX.`insert` = 1;
ALTER TABLE DLPRB_MAX ADD INDEX (`insert`);
DELETE FROM DLPRB_MAX WHERE `insert` = 1;
ALTER TABLE DLPRB_MAX DROP COLUMN `insert`;
INSERT INTO `5G_MAX`
SELECT DLPRB_MAX.* FROM DLPRB_MAX INNER JOIN `5G_MAX_IDX`
ON
DLPRB_MAX.`日期` = 5G_MAX_IDX.`日期` AND
DLPRB_MAX.NCGI = 5G_MAX_IDX.`NCGI` AND
DLPRB_MAX.`下行PRB平均利用率` = 5G_MAX_IDX.`最大值`;
DROP TABLE IF EXISTS `ULPRB_MAX`;
DROP TABLE IF EXISTS `DLPRB_MAX`;
DROP TABLE IF EXISTS `5G_MAX_IDX`;
CREATE TEMPORARY TABLE IF NOT EXISTS `日流量`(SELECT `日期`,NCGI, SUM(`5G上下行总流量_GB`) AS `日流量` FROM 5G GROUP BY `日期`,NCGI);
ALTER TABLE `日流量` ADD INDEX (`NCGI`);
CREATE TEMPORARY TABLE IF NOT EXISTS `5G日均流量` (
SELECT NCGI, AVG(`日流量`) AS `日均流量`, COUNT(`日期`) AS `流量有效天数` FROM `日流量` WHERE `日流量` > 0 GROUP BY NCGI
);
CREATE TABLE IF NOT EXISTS `5G_结果表` (
SELECT NCGI, `CU小区配置名称`,
ROUND(AVG(`5G上下行总流量_GB`),6) AS `5G上下行总流量(上行PDCP PDU数据量+下行PDCP成功发送数据量)(GB)`,
ROUND(AVG(`5G上行流量_GB`),6) AS `5G上行流量(上行PDCP PDU数据量)(GB)`,
ROUND(AVG(`5G下行流量_GB`),6) AS `5G下行流量(下行PDCP成功发送数据量)(GB)`,
ROUND(AVG(`小区平均激活UE数`),6) AS `小区平均激活UE数`,
ROUND(AVG(`RRC连接最大连接用户数`),6) AS `RRC连接最大连接用户数`,
ROUND(AVG(`RRC连接平均连接用户数`),6) AS `RRC连接平均连接用户数`,
ROUND(AVG(`小区最大激活UE数`),6) AS `小区最大激活UE数`,
ROUND(AVG(`上行PRB平均利用率`) / 100,6) AS `上行PRB平均利用率`,
ROUND(AVG(`下行PRB平均利用率`) / 100,6) AS `下行PRB平均利用率`,
ROUND(AVG(`PDCCH信道动态CCE占用率`) / 100,6) AS `PDCCH信道CCE占用率(动态)(%)`,
ROUND(AVG(`小区下行RLC_SDU字节数_MByte`),6) AS `小区下行RLC SDU字节数(MByte)`,
ROUND(AVG(`小区上行RLC_SDU字节数_MByte`),6) AS `小区上行RLC SDU字节数(MByte)`,
ROUND(AVG(`平均单Flow流量_MB`),6) AS `单Flow流量(MB)`
FROM 5G_MAX GROUP BY NCGI, `CU小区配置名称`
);
ALTER TABLE `5G_结果表` ADD COLUMN `日均流量(GB)` DOUBLE AFTER `CU小区配置名称`, ADD INDEX (`NCGI`);
ALTER TABLE `5G日均流量` ADD INDEX (NCGI);
UPDATE `5G_结果表` INNER JOIN `5G日均流量`
ON `5G_结果表`.NCGI=`5G日均流量`.NCGI
SET `5G_结果表`.`日均流量(GB)` = ROUND(`5G日均流量`.`日均流量`,6);
DROP TABLE IF EXISTS `日流量`;
DROP TABLE IF EXISTS `5G日均流量`;
DROP TABLE IF EXISTS `4G`;
DROP TABLE IF EXISTS `5G`;
+220
View File
@@ -0,0 +1,220 @@
@echo off
chcp 65001 >nul
REM CapacityReport 镜像构建和导出脚本 (Windows)
REM 在有外网的 Windows 机器上运行此脚本,生成离线部署包
@REM set http_proxy=http://127.0.0.1:7897
@REM set https_proxy=http://127.0.0.1:7897
setlocal enabledelayedexpansion
set SCRIPT_DIR=%~dp0
set "PROJECT_ROOT=%~dp0..\"
set DIST_DIR=%~dp0
REM 规范化路径(去掉末尾的反斜杠,如果有的话)
if "%PROJECT_ROOT:~-1%"=="\" set "PROJECT_ROOT=%PROJECT_ROOT:~0,-1%"
echo ==========================================
echo CapacityReport 离线部署包构建脚本
echo ==========================================
echo.
REM 清理旧的构建文件
echo 清理旧的构建文件...
if exist "%DIST_DIR%images\capacity-images.tar" (
del /f /q "%DIST_DIR%images\capacity-images.tar"
echo - 已删除旧镜像文件
)
if exist "%DIST_DIR%Configure.json" (
del /f /q "%DIST_DIR%Configure.json"
echo - 已删除旧 Configure.json
)
if exist "%DIST_DIR%ReportScript.sql" (
del /f /q "%DIST_DIR%ReportScript.sql"
echo - 已删除旧 ReportScript.sql
)
if exist "%DIST_DIR%mysql\conf.d\custom.cnf" (
del /f /q "%DIST_DIR%mysql\conf.d\custom.cnf"
echo - 已删除旧 mysql\conf.d\custom.cnf
)
if exist "%DIST_DIR%mysql\init\01-init-db.sql" (
del /f /q "%DIST_DIR%mysql\init\01-init-db.sql"
echo - 已删除旧 mysql\init\01-init-db.sql
)
echo 清理完成
echo.
REM 检查 Docker 是否安装
where docker >nul 2>&1
if %errorlevel% neq 0 (
echo 错误: 未检测到 Docker,请先安装 Docker Desktop
exit /b 1
)
REM 检查 Docker Compose 是否安装
docker compose version >nul 2>&1
if %errorlevel% neq 0 (
docker-compose version >nul 2>&1
if %errorlevel% neq 0 (
echo 错误: 未检测到 Docker Compose,请先安装 Docker Compose
exit /b 1
)
)
echo 步骤 1: 拉取基础镜像...
REM 检查 Dockerfile 中的 Python 版本
set PYTHON_TAG=
for /f "tokens=2 delims=:" %%a in ('findstr /R "^FROM python:" "!PROJECT_ROOT!\Dockerfile"') do (
set "PYTHON_TAG=%%a"
goto :found_python
)
:found_python
if defined PYTHON_TAG (
REM 去掉可能的空格
for /f "tokens=1 delims= " %%b in ("!PYTHON_TAG!") do (
set "PYTHON_TAG=%%b"
)
echo 从 Dockerfile 检测到 Python 镜像标签: !PYTHON_TAG!
) else (
echo 错误: 无法从 Dockerfile 中找到 Python 镜像标签
echo 请确保 Dockerfile 中包含 "FROM python:xxx" 行
exit /b 1
)
REM 检查 Python 镜像是否已存在
docker images --format "{{.Repository}}:{{.Tag}}" | findstr /C:"python:!PYTHON_TAG!" >nul
if %errorlevel% equ 0 (
echo Python 镜像 python:!PYTHON_TAG! 已存在,跳过拉取
) else (
echo 正在拉取 python:!PYTHON_TAG!...
docker pull python:!PYTHON_TAG!
if %errorlevel% neq 0 (
echo 错误: 拉取 Python 镜像失败
echo 提示: 如果遇到网络问题,请检查网络连接或稍后重试
echo 镜像标签: python:!PYTHON_TAG!
exit /b 1
)
)
REM 检查 MySQL 镜像是否已存在
docker images --format "{{.Repository}}:{{.Tag}}" | findstr /C:"mysql:8.0.44" >nul
if %errorlevel% equ 0 (
echo MySQL 镜像 mysql:8.0.44 已存在,跳过拉取
) else (
echo 正在拉取 mysql:8.0.44...
docker pull mysql:8.0.44
if %errorlevel% neq 0 (
echo 错误: 拉取 MySQL 镜像失败
exit /b 1
)
)
echo.
echo 步骤 2: 构建应用镜像...
cd /d "!PROJECT_ROOT!"
docker build -t capacity-report-app:latest -f Dockerfile .
if %errorlevel% neq 0 (
echo 错误: 构建应用镜像失败
exit /b 1
)
echo.
echo 步骤 3: 标记 MySQL 镜像...
docker tag mysql:8.0.44 capacity-mysql:8.0.44
if %errorlevel% neq 0 (
echo 错误: 标记 MySQL 镜像失败
exit /b 1
)
echo.
echo 步骤 4: 导出镜像到 dist 目录...
if not exist "%DIST_DIR%images" mkdir "%DIST_DIR%images"
echo 正在导出镜像到单个 tar 文件:
echo - capacity-report-app:latest (应用镜像)
echo - capacity-mysql:8.0.44 (MySQL 镜像)
docker save capacity-report-app:latest capacity-mysql:8.0.44 -o "%DIST_DIR%images\capacity-images.tar"
if %errorlevel% neq 0 (
echo 错误: 导出镜像失败
exit /b 1
)
echo 镜像导出完成: %DIST_DIR%images\capacity-images.tar
echo 注意: 此文件包含应用镜像和 MySQL 镜像,部署时会自动加载所有镜像
echo.
echo 步骤 5: 复制必要文件到 dist 目录...
REM 创建必要的目录结构
if not exist "%DIST_DIR%mysql\conf.d" mkdir "%DIST_DIR%mysql\conf.d"
if not exist "%DIST_DIR%mysql\init" mkdir "%DIST_DIR%mysql\init"
REM 复制配置文件
echo 正在复制配置文件...
if exist "!PROJECT_ROOT!\Configure.json" (
copy "!PROJECT_ROOT!\Configure.json" "!DIST_DIR!" >nul
echo - Configure.json 已复制
REM 更新为 Docker 环境配置
echo 正在更新 Configure.json 为 Docker 环境配置...
powershell -NoProfile -Command "$json = Get-Content \"%DIST_DIR%Configure.json\" -Raw -Encoding UTF8 | ConvertFrom-Json; $json.MySQL_DBInfo.host = 'capacity-mysql'; $json.MySQL_DBInfo.port = 3306; $json.MySQL_DBInfo.user = 'root'; $json.MySQL_DBInfo.passwd = 'gmcc123'; $json.MySQL_DBInfo.dbname = 'CapacityReport'; $json | ConvertTo-Json -Depth 10 | Set-Content \"%DIST_DIR%Configure.json\" -Encoding UTF8"
if %errorlevel% equ 0 (
echo - 已更新数据库配置: host=capacity-mysql, port=3306, user=root, passwd=gmcc123
) else (
echo 警告: 无法自动更新 Configure.json,请手动修改数据库配置
)
) else (
echo 警告: Configure.json 不存在: !PROJECT_ROOT!\Configure.json
)
if exist "!PROJECT_ROOT!\ReportScript.sql" (
copy "!PROJECT_ROOT!\ReportScript.sql" "!DIST_DIR!" >nul
echo - ReportScript.sql 已复制
) else (
echo 警告: ReportScript.sql 不存在: !PROJECT_ROOT!\ReportScript.sql
)
if exist "!PROJECT_ROOT!\mysql\conf.d\custom.cnf" (
copy "!PROJECT_ROOT!\mysql\conf.d\custom.cnf" "!DIST_DIR!mysql\conf.d\" >nul
echo - mysql\conf.d\custom.cnf 已复制
) else (
echo 警告: mysql\conf.d\custom.cnf 不存在: !PROJECT_ROOT!\mysql\conf.d\custom.cnf
)
if exist "!PROJECT_ROOT!\mysql\init\01-init-db.sql" (
copy "!PROJECT_ROOT!\mysql\init\01-init-db.sql" "!DIST_DIR!mysql\init\" >nul
echo - mysql\init\01-init-db.sql 已复制
) else (
echo 警告: mysql\init\01-init-db.sql 不存在: !PROJECT_ROOT!\mysql\init\01-init-db.sql
)
echo.
echo 步骤 6: 计算文件大小...
for %%A in ("%DIST_DIR%images\capacity-images.tar") do (
set SIZE=%%~zA
set /a SIZE_MB=!SIZE!/1024/1024
echo 镜像文件大小: !SIZE_MB! MB
)
echo.
echo 步骤 7: 清理临时目录...
if exist "%DIST_DIR%cache" (
rd /s /q "%DIST_DIR%cache" 2>nul
echo - 已删除 cache 目录
)
if exist "%DIST_DIR%logs" (
rd /s /q "%DIST_DIR%logs" 2>nul
echo - 已删除 logs 目录
)
echo.
echo ==========================================
echo 构建完成!
echo ==========================================
echo.
echo 离线部署包位置: %DIST_DIR%
echo.
echo 部署步骤:
echo 1. 将整个 dist 目录传输到目标 Linux 机器
echo 2. 在目标机器上执行: sh dist/deploy.sh
echo.
echo 端口映射:
echo - 应用端口: 19081 (原 9081)
echo - MySQL 端口: 13306 (原 3306)
echo.
endlocal
+313
View File
@@ -0,0 +1,313 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
CapacityReport 镜像构建和导出脚本 (Windows)
在有外网的 Windows 机器上运行此脚本,生成离线部署包
"""
import os
import sys
import json
import shutil
import subprocess
import re
from pathlib import Path
# 脚本所在目录
SCRIPT_DIR = Path(__file__).parent.absolute()
PROJECT_ROOT = SCRIPT_DIR.parent
DIST_DIR = SCRIPT_DIR
# 颜色输出(Windows 支持)
import platform
if platform.system() == 'Windows':
try:
import ctypes
kernel32 = ctypes.windll.kernel32
kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
except:
pass
class Colors:
GREEN = '\033[92m'
YELLOW = '\033[93m'
RED = '\033[91m'
RESET = '\033[0m'
def print_step(msg):
print(f"\n{Colors.GREEN}{msg}{Colors.RESET}")
def print_info(msg):
print(f" {msg}")
def print_warning(msg):
print(f"{Colors.YELLOW}警告: {msg}{Colors.RESET}")
def print_error(msg):
print(f"{Colors.RED}错误: {msg}{Colors.RESET}")
def run_cmd(cmd, check=True, capture_output=False):
"""执行命令"""
if isinstance(cmd, str):
cmd = cmd.split()
try:
result = subprocess.run(
cmd,
check=check,
capture_output=capture_output,
text=True
)
return result
except subprocess.CalledProcessError as e:
if check:
print_error(f"命令执行失败: {' '.join(cmd)}")
sys.exit(1)
return None
except FileNotFoundError:
print_error(f"命令未找到: {cmd[0]}")
sys.exit(1)
def check_command(cmd):
"""检查命令是否存在"""
try:
if cmd == "docker":
subprocess.run([cmd, "--version"], capture_output=True, check=True)
elif cmd == "docker-compose":
subprocess.run([cmd, "version"], capture_output=True, check=True)
return True
except (subprocess.CalledProcessError, FileNotFoundError):
return False
def get_python_tag_from_dockerfile():
"""从 Dockerfile 中提取 Python 镜像标签"""
dockerfile_path = PROJECT_ROOT / "Dockerfile"
if not dockerfile_path.exists():
print_error(f"Dockerfile 不存在: {dockerfile_path}")
sys.exit(1)
with open(dockerfile_path, 'r', encoding='utf-8') as f:
for line in f:
match = re.match(r'^FROM\s+python:(.+?)(?:\s|$)', line, re.IGNORECASE)
if match:
tag = match.group(1).strip()
return tag
print_error("无法从 Dockerfile 中找到 Python 镜像标签")
print_error("请确保 Dockerfile 中包含 'FROM python:xxx' 行")
sys.exit(1)
def image_exists(image_name):
"""检查镜像是否已存在"""
try:
result = subprocess.run(
["docker", "images", "--format", "{{.Repository}}:{{.Tag}}"],
capture_output=True,
text=True,
check=True
)
return image_name in result.stdout
except:
return False
def format_size(size_bytes):
"""格式化文件大小"""
for unit in ['B', 'KB', 'MB', 'GB']:
if size_bytes < 1024.0:
return f"{size_bytes:.1f} {unit}"
size_bytes /= 1024.0
return f"{size_bytes:.1f} TB"
def main():
print("=" * 50)
print("CapacityReport 离线部署包构建脚本")
print("=" * 50)
print()
# 清理旧的构建文件
print_step("清理旧的构建文件...")
files_to_clean = [
DIST_DIR / "images" / "capacity-images.tar",
DIST_DIR / "Configure.json",
DIST_DIR / "ReportScript.sql",
DIST_DIR / "mysql" / "conf.d" / "custom.cnf",
DIST_DIR / "mysql" / "init" / "01-init-db.sql",
]
for file_path in files_to_clean:
if file_path.exists():
if file_path.is_file():
file_path.unlink()
print_info(f"已删除旧 {file_path.name}")
elif file_path.is_dir():
shutil.rmtree(file_path)
print_info(f"已删除旧 {file_path.name}")
print_info("清理完成")
# 检查 Docker
print_step("检查 Docker 环境...")
if not check_command("docker"):
print_error("未检测到 Docker,请先安装 Docker Desktop")
sys.exit(1)
print_info("Docker: OK")
# 检查 Docker Compose
docker_compose_cmd = None
try:
subprocess.run(["docker", "compose", "version"], capture_output=True, check=True)
docker_compose_cmd = "docker compose"
print_info("Docker Compose: OK (docker compose)")
except:
if check_command("docker-compose"):
docker_compose_cmd = "docker-compose"
print_info("Docker Compose: OK (docker-compose)")
else:
print_error("未检测到 Docker Compose,请先安装 Docker Compose")
sys.exit(1)
# 步骤 1: 拉取基础镜像
print_step("步骤 1: 拉取基础镜像...")
# 获取 Python 版本
python_tag = get_python_tag_from_dockerfile()
python_image = f"python:{python_tag}"
print_info(f"从 Dockerfile 检测到 Python 镜像标签: {python_tag}")
# 检查并拉取 Python 镜像
if image_exists(python_image):
print_info(f"Python 镜像 {python_image} 已存在,跳过拉取")
else:
print_info(f"正在拉取 {python_image}...")
run_cmd(["docker", "pull", python_image])
print_info(f"Python 镜像拉取完成")
# 检查并拉取 MySQL 镜像
mysql_image = "mysql:8.0.44"
if image_exists(mysql_image):
print_info(f"MySQL 镜像 {mysql_image} 已存在,跳过拉取")
else:
print_info(f"正在拉取 {mysql_image}...")
run_cmd(["docker", "pull", mysql_image])
print_info(f"MySQL 镜像拉取完成")
# 步骤 2: 构建应用镜像
print_step("步骤 2: 构建应用镜像...")
os.chdir(PROJECT_ROOT)
run_cmd(["docker", "build", "-t", "capacity-report-app:latest", "-f", "Dockerfile", "."])
print_info("应用镜像构建完成")
# 步骤 3: 标记 MySQL 镜像
print_step("步骤 3: 标记 MySQL 镜像...")
run_cmd(["docker", "tag", mysql_image, "capacity-mysql:8.0.44"])
print_info("MySQL 镜像标记完成")
# 步骤 4: 导出镜像
print_step("步骤 4: 导出镜像到 dist 目录...")
images_dir = DIST_DIR / "images"
images_dir.mkdir(exist_ok=True)
tar_file = images_dir / "capacity-images.tar"
print_info("正在导出镜像到单个 tar 文件:")
print_info(" - capacity-report-app:latest (应用镜像)")
print_info(" - capacity-mysql:8.0.44 (MySQL 镜像)")
run_cmd([
"docker", "save",
"capacity-report-app:latest",
"capacity-mysql:8.0.44",
"-o", str(tar_file)
])
print_info(f"镜像导出完成: {tar_file}")
print_info("注意: 此文件包含应用镜像和 MySQL 镜像,部署时会自动加载所有镜像")
# 步骤 5: 复制必要文件
print_step("步骤 5: 复制必要文件到 dist 目录...")
# 创建必要的目录结构
(DIST_DIR / "mysql" / "conf.d").mkdir(parents=True, exist_ok=True)
(DIST_DIR / "mysql" / "init").mkdir(parents=True, exist_ok=True)
# 复制配置文件
print_info("正在复制配置文件...")
files_to_copy = [
(PROJECT_ROOT / "Configure.json", DIST_DIR / "Configure.json"),
(PROJECT_ROOT / "ReportScript.sql", DIST_DIR / "ReportScript.sql"),
(PROJECT_ROOT / "mysql" / "conf.d" / "custom.cnf", DIST_DIR / "mysql" / "conf.d" / "custom.cnf"),
(PROJECT_ROOT / "mysql" / "init" / "01-init-db.sql", DIST_DIR / "mysql" / "init" / "01-init-db.sql"),
]
for src, dst in files_to_copy:
if src.exists():
shutil.copy2(src, dst)
print_info(f"{src.name} 已复制")
else:
print_warning(f"{src.name} 不存在: {src}")
# 更新 Configure.json 为 Docker 环境配置
config_file = DIST_DIR / "Configure.json"
if config_file.exists():
print_info("正在更新 Configure.json 为 Docker 环境配置...")
try:
with open(config_file, 'r', encoding='utf-8') as f:
config = json.load(f)
config['MySQL_DBInfo'] = {
'host': 'capacity-mysql',
'port': 3306,
'user': 'root',
'passwd': 'gmcc123',
'dbname': 'CapacityReport'
}
with open(config_file, 'w', encoding='utf-8') as f:
json.dump(config, f, ensure_ascii=False, indent=2)
print_info("已更新数据库配置: host=capacity-mysql, port=3306, user=root, passwd=gmcc123")
except Exception as e:
print_warning(f"无法自动更新 Configure.json: {e}")
print_warning("请手动修改数据库配置")
# 步骤 6: 计算文件大小
print_step("步骤 6: 计算文件大小...")
if tar_file.exists():
size = tar_file.stat().st_size
print_info(f"镜像文件大小: {format_size(size)}")
# 步骤 7: 清理临时目录
print_step("步骤 7: 清理临时目录...")
temp_dirs = [DIST_DIR / "cache", DIST_DIR / "logs"]
for temp_dir in temp_dirs:
if temp_dir.exists():
shutil.rmtree(temp_dir, ignore_errors=True)
print_info(f"已删除 {temp_dir.name} 目录")
# 完成
print()
print("=" * 50)
print("构建完成!")
print("=" * 50)
print()
print(f"离线部署包位置: {DIST_DIR}")
print()
print("部署步骤:")
print("1. 将整个 dist 目录传输到目标 Linux 机器")
print("2. 在目标机器上执行: sh dist/deploy.sh")
print()
print("端口映射:")
print(" - 应用端口: 19081 (原 9081)")
print(" - MySQL 端口: 13306 (原 3306)")
print()
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\n\n构建已取消")
sys.exit(1)
except Exception as e:
print_error(f"构建失败: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
+240
View File
@@ -0,0 +1,240 @@
#!/bin/bash
# CapacityReport Deployment Script
# Run this script on offline machine to import images and start services
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
echo "=========================================="
echo "CapacityReport Deployment"
echo "=========================================="
echo ""
# Set permissions for dist directory and make scripts executable
echo "Setting permissions..."
chmod -R 0777 "$SCRIPT_DIR" 2>/dev/null || true
find "$SCRIPT_DIR" -type f \( -name "*.sh" -o -name "*.py" \) -exec chmod +x {} \; 2>/dev/null || true
echo " Permissions set"
echo ""
# Check Docker
if ! command -v docker &> /dev/null; then
echo "Error: Docker not found, please install Docker"
exit 1
fi
# Check Docker Compose
DOCKER_COMPOSE_CMD=""
if docker compose version &> /dev/null; then
DOCKER_COMPOSE_CMD="docker compose"
elif command -v docker-compose &> /dev/null; then
DOCKER_COMPOSE_CMD="docker-compose"
else
echo "Error: Docker Compose not found, please install Docker Compose"
exit 1
fi
# Check image file
if [ ! -f "images/capacity-images.tar" ]; then
echo "Error: Image file not found: images/capacity-images.tar"
exit 1
fi
echo "Step 1: Checking ports..."
# 检查端口是否被占用
check_port() {
local port=$1
local name=$2
local in_use=false
# 尝试多种方法检查端口
if command -v lsof &> /dev/null; then
if lsof -Pi :$port -sTCP:LISTEN -t >/dev/null 2>&1; then
in_use=true
fi
elif command -v netstat &> /dev/null; then
if netstat -tuln 2>/dev/null | grep -q ":$port "; then
in_use=true
fi
elif command -v ss &> /dev/null; then
if ss -tuln 2>/dev/null | grep -q ":$port "; then
in_use=true
fi
fi
if [ "$in_use" = true ]; then
echo "Warning: Port $port ($name) is in use"
read -p "Continue? (y/n) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
fi
}
check_port 19081 "App"
check_port 13306 "MySQL"
echo ""
echo "Step 2: Loading images..."
docker load -i images/capacity-images.tar >/dev/null 2>&1
echo ""
echo "Step 3: Verifying images..."
if docker images | grep -q "capacity-report-app.*latest"; then
echo " App image: OK"
else
echo " App image: FAILED"
exit 1
fi
if docker images | grep -q "capacity-mysql.*8.0.44"; then
echo " MySQL image: OK"
else
echo " MySQL image: FAILED"
exit 1
fi
echo ""
echo "Step 4: Creating directories..."
mkdir -p cache logs
chmod 777 cache logs 2>/dev/null || true
echo ""
echo "Step 5: Checking config..."
if [ ! -f "Configure.json" ]; then
echo " Creating default Configure.json..."
cat > Configure.json << 'EOF'
{
"MySQL_DBInfo": {
"host": "capacity-mysql",
"port": 3306,
"user": "root",
"passwd": "gmcc123",
"dbname": "CapacityReport"
},
"ExtractField": []
}
EOF
else
# Update config for Docker environment
if command -v python3 &> /dev/null || command -v python &> /dev/null; then
(command -v python3 >/dev/null && python3 || python) << 'PYEOF'
import json
import sys
try:
with open('Configure.json', 'r', encoding='utf-8') as f:
config = json.load(f)
db_info = config.get('MySQL_DBInfo', {})
needs_update = False
if db_info.get('host') != 'capacity-mysql':
db_info['host'] = 'capacity-mysql'
needs_update = True
if db_info.get('port') != 3306:
db_info['port'] = 3306
needs_update = True
if db_info.get('user') != 'root':
db_info['user'] = 'root'
needs_update = True
if db_info.get('passwd') != 'gmcc123':
db_info['passwd'] = 'gmcc123'
needs_update = True
if db_info.get('dbname') != 'CapacityReport':
db_info['dbname'] = 'CapacityReport'
needs_update = True
if needs_update:
with open('Configure.json', 'w', encoding='utf-8') as f:
json.dump(config, f, ensure_ascii=False, indent=2)
print(" Updated Configure.json")
except:
pass
PYEOF
fi
fi
if [ ! -f "ReportScript.sql" ]; then
touch ReportScript.sql
fi
echo ""
echo "Step 6: Starting services..."
$DOCKER_COMPOSE_CMD down 2>/dev/null || true
$DOCKER_COMPOSE_CMD up -d >/dev/null 2>&1
echo ""
echo "Step 7: Waiting for services..."
for i in {1..30}; do
if docker exec capacity-mysql mysqladmin ping -h localhost -u root -pgmcc123 --silent 2>/dev/null; then
echo " MySQL: Ready"
break
fi
if [ $i -eq 30 ]; then
echo " MySQL: Timeout"
exit 1
fi
sleep 2
done
if command -v curl &> /dev/null; then
for i in {1..30}; do
if curl -f http://localhost:19081/health >/dev/null 2>&1; then
echo " App: Ready"
break
fi
if [ $i -eq 30 ]; then
echo " App: Timeout"
echo " Check logs: $DOCKER_COMPOSE_CMD logs capacity-app"
exit 1
fi
sleep 2
done
else
for i in {1..30}; do
if docker ps --filter "name=capacity-report-app" --filter "status=running" --format "{{.Names}}" | grep -q "capacity-report-app"; then
if docker inspect capacity-report-app --format='{{.State.Health.Status}}' 2>/dev/null | grep -q "healthy"; then
echo " App: Ready"
break
elif [ $i -gt 10 ]; then
echo " App: Running"
break
fi
fi
if [ $i -eq 30 ]; then
echo " App: Timeout"
exit 1
fi
sleep 2
done
fi
echo ""
echo "=========================================="
echo "Deployment completed!"
echo "=========================================="
echo ""
echo "Access: http://localhost:19081"
echo ""
echo "Commands:"
echo " Logs: $DOCKER_COMPOSE_CMD logs -f"
echo " Stop: $DOCKER_COMPOSE_CMD down"
echo " Restart: $DOCKER_COMPOSE_CMD restart"
echo " Status: $DOCKER_COMPOSE_CMD ps"
echo ""
echo "Database:"
echo " Host: localhost"
echo " Port: 13306"
echo " User: root"
echo " Password: gmcc123"
echo " Database: CapacityReport"
echo ""
+79
View File
@@ -0,0 +1,79 @@
# CapacityReport Docker 编排文件
# 用于内网离线部署
#
# 使用方法:
# sh deploy.sh # 一键导入镜像并启动服务
# docker-compose up -d # 启动所有服务
# docker-compose down # 停止所有服务
version: '3.8'
services:
# 容量报表应用
capacity-app:
image: capacity-report-app:latest
container_name: capacity-report-app
restart: unless-stopped
ports:
- "19081:9081"
volumes:
# 持久化数据
- ./cache:/app/cache
- ./Configure.json:/app/Configure.json
- ./ReportScript.sql:/app/ReportScript.sql
# 日志目录
- ./logs:/var/log/supervisor
environment:
- TZ=Asia/Shanghai
- PYTHONUNBUFFERED=1
- SUPERVISOR_ENABLED=1
depends_on:
capacity-mysql:
condition: service_healthy
networks:
- capacity-network
# MySQL 数据库
capacity-mysql:
image: capacity-mysql:8.0.44
container_name: capacity-mysql
restart: unless-stopped
ports:
- "13306:3306"
volumes:
# 数据持久化
- capacity-mysql-data:/var/lib/mysql
# 自定义配置(启用 LOAD DATA INFILE)
- ./mysql/conf.d:/etc/mysql/conf.d:ro
# 初始化脚本(仅在首次启动时执行)
- ./mysql/init:/docker-entrypoint-initdb.d:ro
environment:
- TZ=Asia/Shanghai
- MYSQL_ROOT_PASSWORD=gmcc123
- MYSQL_DATABASE=CapacityReport
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --local-infile=ON
- --secure-file-priv=
- --max_allowed_packet=256M
- --innodb_buffer_pool_size=512M
- --innodb_log_file_size=128M
- --innodb_flush_log_at_trx_commit=2
- --sync_binlog=0
networks:
- capacity-network
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-pgmcc123"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
networks:
capacity-network:
driver: bridge
volumes:
capacity-mysql-data:
driver: local
+35
View File
@@ -0,0 +1,35 @@
# MySQL 自定义配置 - CapacityReport
# 启用 LOAD DATA LOCAL INFILE 支持
[mysqld]
# 字符集
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
# 启用 LOAD DATA LOCAL INFILE
local_infile = ON
secure_file_priv = ""
# 性能优化
max_allowed_packet = 256M
innodb_buffer_pool_size = 512M
innodb_log_file_size = 128M
innodb_flush_log_at_trx_commit = 2
sync_binlog = 0
# 连接设置
max_connections = 200
wait_timeout = 28800
interactive_timeout = 28800
# 日志(可选,生产环境建议注释掉)
# general_log = 1
# general_log_file = /var/lib/mysql/general.log
[mysql]
# 客户端设置
local_infile = ON
default-character-set = utf8mb4
[client]
default-character-set = utf8mb4
+13
View File
@@ -0,0 +1,13 @@
-- MySQL 初始化脚本 - 确保数据库以正确的字符集创建
-- 此脚本仅在首次初始化时执行(数据目录为空时)
-- 如果数据库不存在,则创建(使用 utf8mb4 字符集)
CREATE DATABASE IF NOT EXISTS CapacityReport
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
-- 切换到数据库
USE CapacityReport;
-- 输出确认信息
SELECT 'CapacityReport database initialized with utf8mb4 charset' AS status;