diff --git a/server/internal/api/exports.go b/server/internal/api/exports.go index 8a6879f..53916e1 100644 --- a/server/internal/api/exports.go +++ b/server/internal/api/exports.go @@ -393,6 +393,24 @@ func (a *ExportsAPI) runJob(id uint64, db *sql.DB, q string, args []interface{}, } } } + + // 检查预估行数,如果超过阈值且格式是xlsx,强制改为csv + if fmt == "xlsx" { + var rowEstimate int64 + estRow := a.meta.QueryRow("SELECT row_estimate FROM export_jobs WHERE id=?", id) + _ = estRow.Scan(&rowEstimate) + if rowEstimate > constants.ExportThresholds.XlsxMaxRows { + logging.JSON("INFO", map[string]interface{}{ + "event": "force_csv_format", + "job_id": id, + "row_estimate": rowEstimate, + "threshold": constants.ExportThresholds.XlsxMaxRows, + "reason": "row_estimate exceeds xlsx max rows, forcing csv format", + }) + fmt = "csv" + } + } + rrepo.StartJob(a.meta, id) if fmt == "csv" { newBaseWriter := func() (exporter.RowWriter, error) { diff --git a/server/internal/constants/constants.go b/server/internal/constants/constants.go index 39d14f9..ebcfc4b 100644 --- a/server/internal/constants/constants.go +++ b/server/internal/constants/constants.go @@ -60,12 +60,15 @@ var ExportThresholds = struct { ChunkThreshold int64 // ProgressUpdateInterval 进度更新间隔(行数) ProgressUpdateInterval int64 + // XlsxMaxRows xlsx格式最大行数,超过则强制使用csv + XlsxMaxRows int64 }{ MaxRowsPerFile: 300000, PassScoreThreshold: 60, ChunkDays: 10, ChunkThreshold: 50000, ProgressUpdateInterval: 1000, + XlsxMaxRows: 100000, } // BatchSizes 批量处理大小配置