浏览代码

fix #6158: 数据分析导出修复,getPythonBin智能检测Python,添加PYTHONIOENCODING; fix #6157: 定时任务执行后保存结果到publish_results表

ethanfly 3 天之前
父节点
当前提交
dee7638036
共有 2 个文件被更改,包括 53 次插入9 次删除
  1. 25 3
      server/src/routes/workDayStatistics.ts
  2. 28 6
      server/src/scheduler/index.ts

+ 25 - 3
server/src/routes/workDayStatistics.ts

@@ -24,8 +24,29 @@ const __dirname = path.dirname(__filename);
 // 所有路由需要认证
 router.use(authenticate);
 
+/**
+ * 获取 Python 可执行文件路径
+ * 优先级:PYTHON_BIN 环境变量 > python > py (Windows launcher) > 嵌入式 Python
+ */
+function getPythonBin(): string {
+  // 1. 如果已配置 PYTHON_BIN,直接使用
+  if (process.env.PYTHON_BIN) {
+    return process.env.PYTHON_BIN;
+  }
+  
+  // 2. 尝试 python 命令
+  // 3. Windows 下尝试 py 启动器
+  if (process.platform === 'win32') {
+    // 尝试 py (Python launcher for Windows)
+    return 'py';
+  }
+  
+  // 4. 降级到 python
+  return 'python';
+}
+
 function runPythonExportXlsx(payload: unknown): Promise<Buffer> {
-  const pythonBin = process.env.PYTHON_BIN || 'python';
+  const pythonBin = getPythonBin();
   const scriptPath = path.resolve(__dirname, '../../python/export_work_day_overview_xlsx.py');
 
   return new Promise((resolve, reject) => {
@@ -66,13 +87,14 @@ function runPythonExportXlsx(payload: unknown): Promise<Buffer> {
 }
 
 function runPythonExportWorksXlsx(payload: unknown): Promise<Buffer> {
-  const pythonBin = process.env.PYTHON_BIN || 'python';
+  const pythonBin = getPythonBin();
   const scriptPath = path.resolve(__dirname, '../../python/export_work_analytics_xlsx.py');
 
   return new Promise((resolve, reject) => {
     const child = spawn(pythonBin, [scriptPath], {
       stdio: ['pipe', 'pipe', 'pipe'],
       windowsHide: true,
+      env: { ...process.env, PYTHONIOENCODING: 'utf-8' },
     });
 
     const stdoutChunks: Buffer[] = [];
@@ -105,7 +127,7 @@ function runPythonExportWorksXlsx(payload: unknown): Promise<Buffer> {
 }
 
 function runPythonExportPlatformXlsx(payload: unknown): Promise<Buffer> {
-  const pythonBin = process.env.PYTHON_BIN || 'python';
+  const pythonBin = getPythonBin();
   const scriptPath = path.resolve(__dirname, '../../python/export_platform_statistics_xlsx.py');
 
   return new Promise((resolve, reject) => {

+ 28 - 6
server/src/scheduler/index.ts

@@ -1,5 +1,5 @@
 import schedule from 'node-schedule';
-import { AppDataSource, PublishTask, PlatformAccount, AnalyticsData } from '../models/index.js';
+import { AppDataSource, PublishTask, PublishResult, PlatformAccount, AnalyticsData } from '../models/index.js';
 import { logger } from '../utils/logger.js';
 import { wsManager } from '../websocket/index.js';
 import { WS_EVENTS } from '@media-manager/shared';
@@ -170,6 +170,7 @@ export class TaskScheduler {
   private async executePublishTask(task: PublishTask): Promise<void> {
     const taskRepository = AppDataSource.getRepository(PublishTask);
     const accountRepository = AppDataSource.getRepository(PlatformAccount);
+    const resultRepository = AppDataSource.getRepository(PublishResult);
     
     const targetAccounts = task.targetAccounts || [];
     const accounts = await accountRepository.find({
@@ -188,7 +189,7 @@ export class TaskScheduler {
       
       try {
         const adapter = getAdapter(account.platform);
-        const result = await adapter.publishVideo(account.cookieData || '', {
+        const publishResult = await adapter.publishVideo(account.cookieData || '', {
           videoPath: task.videoPath || '',
           title: task.title || '',
           description: task.description || undefined,
@@ -196,17 +197,38 @@ export class TaskScheduler {
           tags: task.tags || undefined,
         });
         
-        if (result.success) {
+        // 保存发布结果到 publish_results 表
+        const publishResultRecord = new PublishResult();
+        publishResultRecord.taskId = task.id;
+        publishResultRecord.accountId = account.id;
+        publishResultRecord.platform = account.platform;
+        publishResultRecord.status = publishResult.success ? 'success' : 'failed';
+        publishResultRecord.videoUrl = publishResult.videoUrl || null;
+        publishResultRecord.platformVideoId = publishResult.platformVideoId || null;
+        publishResultRecord.errorMessage = publishResult.error || null;
+        publishResultRecord.publishedAt = publishResult.success ? new Date() : null;
+        await resultRepository.save(publishResultRecord);
+        
+        if (publishResult.success) {
           successCount++;
         } else {
           failCount++;
         }
         
-        // 更新发布结果
-        // TODO: 更新 publish_results 表
-        
       } catch (error) {
         logger.error(`Publish to ${account.platform} failed:`, error);
+        
+        // 保存失败结果到 publish_results 表
+        const errorMessage = error instanceof Error ? error.message : String(error);
+        const failedRecord = new PublishResult();
+        failedRecord.taskId = task.id;
+        failedRecord.accountId = account.id;
+        failedRecord.platform = account.platform;
+        failedRecord.status = 'failed';
+        failedRecord.errorMessage = errorMessage;
+        failedRecord.publishedAt = null;
+        await resultRepository.save(failedRecord);
+        
         failCount++;
       }
     }