feat: 飞猪自动化调试完成
This commit is contained in:
@@ -3,6 +3,7 @@ import { ipcMain, app } from 'electron';
|
|||||||
import { IPC_EVENTS } from '@common/constants';
|
import { IPC_EVENTS } from '@common/constants';
|
||||||
import { launchLocalChrome } from '@main/utils/chrome/launchLocalChrome'
|
import { launchLocalChrome } from '@main/utils/chrome/launchLocalChrome'
|
||||||
import { executeScriptService } from '@main/service/execute-script-service';
|
import { executeScriptService } from '@main/service/execute-script-service';
|
||||||
|
import fs from 'fs'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import log from 'electron-log';
|
import log from 'electron-log';
|
||||||
|
|
||||||
@@ -11,81 +12,48 @@ export function runTaskOperationService() {
|
|||||||
|
|
||||||
ipcMain.handle(IPC_EVENTS.EXECUTE_SCRIPT, async (_event, options: any) => {
|
ipcMain.handle(IPC_EVENTS.EXECUTE_SCRIPT, async (_event, options: any) => {
|
||||||
try {
|
try {
|
||||||
/**
|
|
||||||
* options参数包括:房型、日期范围
|
|
||||||
* 房型:亲子房、雅致套房
|
|
||||||
* 日期范围:2023-10-01至2023-10-07
|
|
||||||
* 备注:操作的渠道有:飞猪、美团、抖音来客
|
|
||||||
* 这里需要一个排队任务,用队列来处理各渠道的操作路径自动化,先将任务加入队列,然后按顺序执行脚本
|
|
||||||
*/
|
|
||||||
|
|
||||||
await launchLocalChrome(options)
|
await launchLocalChrome(options)
|
||||||
|
|
||||||
// 脚本路径处理
|
const channels = ['feizhu', 'meituan', 'douyin', 'xiaocheng']
|
||||||
let scriptPath = ''
|
|
||||||
if (app.isPackaged) {
|
const scriptMap: Record<string, string> = {
|
||||||
scriptPath = path.join(process.resourcesPath, 'scripts/fg_trace.js')
|
feizhu: 'fg_trace.mjs',
|
||||||
} else {
|
meituan: 'mt_trace.mjs',
|
||||||
scriptPath = path.join(process.cwd(), 'src/main/scripts/fg_trace.js')
|
douyin: 'dy_trace.mjs',
|
||||||
|
xiaocheng: 'xc_trace.mjs',
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info(`Launching script with path: ${scriptPath}`);
|
const scriptsDir = app.isPackaged
|
||||||
const result = await executeScriptServiceInstance.executeScript(scriptPath, options)
|
? path.join(process.resourcesPath, 'scripts')
|
||||||
|
: path.join(process.cwd(), 'src/main/scripts')
|
||||||
|
|
||||||
return { success: true, result };
|
const scriptPaths = channels.map((channel: string) => {
|
||||||
|
const fileName = scriptMap[channel]
|
||||||
|
if (!fileName) {
|
||||||
|
throw new Error(`Unknown channel: ${channel}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const p = path.join(scriptsDir, fileName)
|
||||||
|
if (!fs.existsSync(p)) {
|
||||||
|
throw new Error(`Script not found for channel ${channel}: ${p}`)
|
||||||
|
}
|
||||||
|
return { channel, scriptPath: p }
|
||||||
|
})
|
||||||
|
|
||||||
|
const results: any[] = []
|
||||||
|
for (const item of scriptPaths) {
|
||||||
|
log.info(`Launching script for channel ${item.channel}: ${item.scriptPath}`)
|
||||||
|
const result = await executeScriptServiceInstance.executeScript(item.scriptPath, options)
|
||||||
|
results.push({
|
||||||
|
channel: item.channel,
|
||||||
|
scriptPath: item.scriptPath,
|
||||||
|
...result,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: true, result: results };
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
return { success: false, error: error.message };
|
return { success: false, error: error.message };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// export function runTaskOperationService() {
|
|
||||||
// ipcMain.handle(IPC_EVENTS.EXECUTE_SCRIPT, async (event, params) => {
|
|
||||||
// logManager.info('Received task operation:', params);
|
|
||||||
|
|
||||||
// return new Promise((resolve, reject) => {
|
|
||||||
// // 脚本路径
|
|
||||||
// const scriptPath = path.join(__dirname, '../../scripts/fg_trace.js');
|
|
||||||
|
|
||||||
// const child = fork(scriptPath, [], {
|
|
||||||
// stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
|
|
||||||
// env: { ...process.env, ...params } // 传递环境变量
|
|
||||||
// });
|
|
||||||
|
|
||||||
// let output = '';
|
|
||||||
// let errorOutput = '';
|
|
||||||
|
|
||||||
// if (child.stdout) {
|
|
||||||
// child.stdout.on('data', (data) => {
|
|
||||||
// const msg = data.toString();
|
|
||||||
// logManager.info(`[Task Script]: ${msg}`);
|
|
||||||
// output += msg;
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (child.stderr) {
|
|
||||||
// child.stderr.on('data', (data) => {
|
|
||||||
// const msg = data.toString();
|
|
||||||
// logManager.error(`[Task Script Error]: ${msg}`);
|
|
||||||
// errorOutput += msg;
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// child.on('close', (code) => {
|
|
||||||
// logManager.info(`Task script exited with code ${code}`);
|
|
||||||
// if (code === 0) {
|
|
||||||
// resolve({ success: true, output });
|
|
||||||
// } else {
|
|
||||||
// // 如果是因为模块找不到或语法错误退出,这里会捕获
|
|
||||||
// reject(new Error(`Script exited with code ${code}. Error: ${errorOutput}`));
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
// child.on('error', (err) => {
|
|
||||||
// logManager.error('Failed to start task script:', err);
|
|
||||||
// reject(err);
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import { chromium } from 'playwright';
|
import { chromium } from 'playwright';
|
||||||
import log from 'electron-log';
|
import log from 'electron-log';
|
||||||
|
|
||||||
|
|
||||||
const checkLoginStatus = async (page) => {
|
const checkLoginStatus = async (page) => {
|
||||||
try {
|
try {
|
||||||
const currentUrl = await page.url();
|
const currentUrl = await page.url();
|
||||||
@@ -33,7 +32,7 @@ const checkLoginStatus = async (page) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const browser = await chromium.connectOverCDP('http://localhost:9222');
|
const browser = await chromium.connectOverCDP(`http://localhost:9222`);
|
||||||
const context = browser.contexts()[0];
|
const context = browser.contexts()[0];
|
||||||
|
|
||||||
await context.addInitScript(() => {
|
await context.addInitScript(() => {
|
||||||
@@ -127,4 +126,4 @@ const checkLoginStatus = async (page) => {
|
|||||||
// await page.locator('div:nth-child(7) > .boardRow___p2ZiO > div:nth-child(2) > div:nth-child(2) > .ant-spin-nested-loading > .ant-spin-container > div > div > .error___IM8Yw > .bar___i4k4r').click();
|
// await page.locator('div:nth-child(7) > .boardRow___p2ZiO > div:nth-child(2) > div:nth-child(2) > .ant-spin-nested-loading > .ant-spin-container > div > div > .error___IM8Yw > .bar___i4k4r').click();
|
||||||
// await page.locator('div:nth-child(7) > .boardRow___p2ZiO > div:nth-child(2) > div:nth-child(3) > .ant-spin-nested-loading > .ant-spin-container > div > div > .error___IM8Yw > .bar___i4k4r').click();
|
// await page.locator('div:nth-child(7) > .boardRow___p2ZiO > div:nth-child(2) > div:nth-child(3) > .ant-spin-nested-loading > .ant-spin-container > div > div > .error___IM8Yw > .bar___i4k4r').click();
|
||||||
// await page.locator('div:nth-child(7) > .boardRow___p2ZiO > div:nth-child(2) > div:nth-child(4) > .ant-spin-nested-loading > .ant-spin-container > div > div > .error___IM8Yw > .bar___i4k4r').click();
|
// await page.locator('div:nth-child(7) > .boardRow___p2ZiO > div:nth-child(2) > div:nth-child(4) > .ant-spin-nested-loading > .ant-spin-container > div > div > .error___IM8Yw > .bar___i4k4r').click();
|
||||||
})();
|
})();
|
||||||
@@ -5,34 +5,73 @@ import log from 'electron-log';
|
|||||||
|
|
||||||
export class executeScriptService extends EventEmitter {
|
export class executeScriptService extends EventEmitter {
|
||||||
// 执行脚本
|
// 执行脚本
|
||||||
async executeScript(scriptPath: string, options: object): Promise<{ success: boolean; message?: string; error?: string }> {
|
async executeScript(
|
||||||
try {
|
scriptPath: string,
|
||||||
const child = spawn('node', [scriptPath], {
|
options: Record<string, any>,
|
||||||
env: {
|
): Promise<{ success: boolean; exitCode: number | null; stdoutTail: string; stderrTail: string; error?: string }> {
|
||||||
...process.env,
|
const MAX_TAIL = 32 * 1024;
|
||||||
...options,
|
|
||||||
}
|
const appendTail = (current: string, chunk: string) => {
|
||||||
});
|
const next = current + chunk;
|
||||||
|
return next.length > MAX_TAIL ? next.slice(next.length - MAX_TAIL) : next;
|
||||||
child.stdout.on('data', (data: Buffer) => {
|
};
|
||||||
log.info(`stdout: ${data.toString()}`);
|
|
||||||
});
|
return await new Promise((resolve) => {
|
||||||
|
try {
|
||||||
child.stderr.on('data', (data: Buffer) => {
|
const child = spawn('node', [scriptPath], {
|
||||||
log.info(`stderr: ${data.toString()}`);
|
env: {
|
||||||
});
|
...process.env,
|
||||||
|
...options,
|
||||||
child.on('close', (code: number) => {
|
}
|
||||||
log.info(`子进程退出,退出码 ${code}`);
|
});
|
||||||
});
|
|
||||||
|
let stdoutTail = '';
|
||||||
return { success: true, message: 'Node 脚本执行中' };
|
let stderrTail = '';
|
||||||
} catch (error) {
|
|
||||||
return { success: false, message: '运行 Node 脚本时出错' };
|
child.stdout?.on('data', (data: Buffer) => {
|
||||||
}
|
const text = data.toString();
|
||||||
|
stdoutTail = appendTail(stdoutTail, text);
|
||||||
|
log.info(`stdout: ${text}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
child.stderr?.on('data', (data: Buffer) => {
|
||||||
|
const text = data.toString();
|
||||||
|
stderrTail = appendTail(stderrTail, text);
|
||||||
|
log.info(`stderr: ${text}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
child.on('error', (err: any) => {
|
||||||
|
resolve({
|
||||||
|
success: false,
|
||||||
|
exitCode: null,
|
||||||
|
stdoutTail,
|
||||||
|
stderrTail,
|
||||||
|
error: err?.message || 'Failed to start script process',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
child.on('close', (code: number | null) => {
|
||||||
|
log.info(`子进程退出,退出码 ${code}`);
|
||||||
|
resolve({
|
||||||
|
success: code === 0,
|
||||||
|
exitCode: code,
|
||||||
|
stdoutTail,
|
||||||
|
stderrTail,
|
||||||
|
...(code === 0 ? {} : { error: `Script exited with code ${code}` }),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} catch (error: any) {
|
||||||
|
resolve({
|
||||||
|
success: false,
|
||||||
|
exitCode: null,
|
||||||
|
stdoutTail: '',
|
||||||
|
stderrTail: '',
|
||||||
|
error: error?.message || '运行 Node 脚本时出错',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ export async function launchLocalChrome (options: any) {
|
|||||||
const chromePath = getChromePath();
|
const chromePath = getChromePath();
|
||||||
|
|
||||||
// 多账号隔离
|
// 多账号隔离
|
||||||
// const profileDir = getProfileDir(accountId);
|
const profileDir = getProfileDir('default');
|
||||||
// log.info(`Launching Chrome with user data dir: ${options}`);
|
log.info(`Launching Chrome with user data dir: ${options}`);
|
||||||
|
|
||||||
// 检查端口是否被占用
|
// 检查端口是否被占用
|
||||||
const portInUse = await isPortInUse(9222);
|
const portInUse = await isPortInUse(9222);
|
||||||
@@ -32,8 +32,8 @@ export async function launchLocalChrome (options: any) {
|
|||||||
'--window-size=1920,1080',
|
'--window-size=1920,1080',
|
||||||
'--window-position=0,0',
|
'--window-position=0,0',
|
||||||
'--no-first-run',
|
'--no-first-run',
|
||||||
|
`--user-data-dir=${profileDir}`,
|
||||||
'--no-default-browser-check'
|
'--no-default-browser-check'
|
||||||
// `--user-data-dir=${profileDir}`,
|
|
||||||
// '--window-maximized',
|
// '--window-maximized',
|
||||||
], {
|
], {
|
||||||
detached: true,
|
detached: true,
|
||||||
|
|||||||
@@ -1,46 +1,27 @@
|
|||||||
import pms from '@assets/images/channel/pms.png'
|
export interface Item {
|
||||||
import xc from '@assets/images/channel/xc.png'
|
|
||||||
import qne from '@assets/images/channel/qne.png'
|
|
||||||
import fz from '@assets/images/channel/fz.png'
|
|
||||||
import mt from '@assets/images/channel/mt.png'
|
|
||||||
import dy from '@assets/images/channel/dy.png'
|
|
||||||
|
|
||||||
// 菜单列表申明
|
|
||||||
interface Item {
|
|
||||||
id: number
|
id: number
|
||||||
name: string
|
channelName: string
|
||||||
icon: any
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const channel: Item[] = [
|
export const channel: Item[] = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'PMS',
|
channelName: 'pms',
|
||||||
icon: pms,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
name: '携程',
|
channelName: 'xc',
|
||||||
icon: xc,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
name: '去哪儿',
|
channelName: 'fz',
|
||||||
icon: qne,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 4,
|
id: 4,
|
||||||
name: '飞猪',
|
channelName: 'mt',
|
||||||
icon: fz,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 5,
|
id: 5,
|
||||||
name: '美团',
|
channelName: 'dy',
|
||||||
icon: mt,
|
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: 6,
|
|
||||||
name: '抖音',
|
|
||||||
icon: dy,
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
@@ -26,6 +26,7 @@ import { taskCenterItem } from '@constant/taskCenterList'
|
|||||||
import { hotelStaffTypeMappingListUsingPost } from '@api/index'
|
import { hotelStaffTypeMappingListUsingPost } from '@api/index'
|
||||||
|
|
||||||
const isVisible = ref(false)
|
const isVisible = ref(false)
|
||||||
|
const roomList: any = ref([])
|
||||||
const title = ref('')
|
const title = ref('')
|
||||||
const formRef = ref()
|
const formRef = ref()
|
||||||
const form = ref({
|
const form = ref({
|
||||||
@@ -83,13 +84,19 @@ const confirm = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
close()
|
close()
|
||||||
console.log(form.value)
|
|
||||||
|
// 将roomList.value数组处理成标准数组
|
||||||
|
const newList = roomList.value.map((item: any) => ({ ...item }))
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
roomType: form.value.roomType,
|
roomType: form.value.roomType,
|
||||||
startTime: form.value.range[0],
|
startTime: form.value.range[0],
|
||||||
endTime: form.value.range[1],
|
endTime: form.value.range[1],
|
||||||
operation: form.value.operation,
|
operation: form.value.operation,
|
||||||
|
roomList: newList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(options)
|
||||||
/**
|
/**
|
||||||
* 坑:传给进程的参数不能是ref包裹的reactive对象
|
* 坑:传给进程的参数不能是ref包裹的reactive对象
|
||||||
*/
|
*/
|
||||||
@@ -100,7 +107,6 @@ const confirm = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取房型列表
|
// 获取房型列表
|
||||||
const roomList: any = ref([])
|
|
||||||
const getRoomTypeList = async () => {
|
const getRoomTypeList = async () => {
|
||||||
const res = await hotelStaffTypeMappingListUsingPost({ body: {} })
|
const res = await hotelStaffTypeMappingListUsingPost({ body: {} })
|
||||||
roomList.value = res.data
|
roomList.value = res.data
|
||||||
@@ -110,4 +116,4 @@ defineExpose({
|
|||||||
open,
|
open,
|
||||||
close,
|
close,
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -48,6 +48,7 @@
|
|||||||
"**/*.ts",
|
"**/*.ts",
|
||||||
"**/*.js",
|
"**/*.js",
|
||||||
"src/renderer/permission.ts",
|
"src/renderer/permission.ts",
|
||||||
"src/main/scripts/*.js"
|
"src/main/scripts/*.js",
|
||||||
|
"src/main/scripts/*.mjs"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user