This commit is contained in:
cheney 2026-07-13 21:21:05 +08:00
parent 99c33220b8
commit 01b4a000bd

171
duizhang.js Normal file
View File

@ -0,0 +1,171 @@
const https = require('https');
const XLSX = require('xlsx');
const path = require('path');
// ======================== 配置 ========================
const EXCEL_FILE = '新建 XLSX 工作表.xlsx';
const SHEET_NAME = '2026';
const BASE_HOST = 'superstar.geelytravel.com';
const API_BASE = '/api/tmc/tmc';
const STEP_DELAY_MS = 3000;
const API_HEADERS = {
'accept': 'application/json, text/plain, */*',
'accept-language': 'zh-CN,zh;q=0.9',
'authorization': 'Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKV1QtRC1TRVFfVVNFUl9UTUNfMDAwMDAxODMiLCJ1c2VyX2NvZGUiOiJTRVFfVVNFUl9UTUNfMDAwMDAxODMiLCJyb2xlcyI6WyLnu5PnrpfkuJPlkZgiLCLnu5PnrpfkuJPlkZgiXSwiZXhwIjoxNzg2NTMzNTUxLCJpYXQiOjE3ODM5NDE1NTF9.1Mcmf5IM07Scrry4FzPs6TAcfUu0Nqtjre_2iJlNd5c',
'cache-control': 'no-cache',
'pragma': 'no-cache',
'requestsource': 'D',
'usercode': 'SEQ_USER_TMC_00000183',
'cookie': '_c_WBKFRo=CefecIUfnnuqejh60diYtytjDPwI9Qptslcwakc4; acw_tc=731dc87617839414337367659e81455072769ade5ba31e7a9d3e4d5d817123',
'Referer': 'https://superstar.geelytravel.com/new-tmc/finance/account/saleBill/list',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
};
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function httpRequest(method, path, headers, body) {
return new Promise((resolve, reject) => {
const opts = {
hostname: BASE_HOST,
path,
method,
headers: headers || API_HEADERS,
rejectUnauthorized: false,
};
const req = https.request(opts, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => {
try {
resolve({ status: res.statusCode, data: JSON.parse(data) });
} catch {
resolve({ status: res.statusCode, data });
}
});
});
req.on('error', reject);
if (body) req.write(body);
req.end();
});
}
// ======================== 1. 读取 Excel ========================
function readTeamIds() {
const workbook = XLSX.readFile(path.join(__dirname, EXCEL_FILE));
const sheet = workbook.Sheets[SHEET_NAME];
if (!sheet) { console.error('Sheet not found'); process.exit(1); }
const rows = XLSX.utils.sheet_to_json(sheet, { defval: null });
const teamIds = [];
for (const row of rows) {
const val = row['团号'];
if (val && /^TEAM-\d+$/.test(String(val).trim())) teamIds.push(String(val).trim());
}
return teamIds;
}
// ======================== 2. 获取 settlementAccountIds ========================
async function querySettlementAccountIds(teamId) {
const path = `${API_BASE}/customerAccount/settlement/querySettlementAccountIds?customerSettlementAccountName=${encodeURIComponent(teamId)}`;
console.log(` [GET] ${path}`);
const res = await httpRequest('GET', path);
console.log(` [OK]`, JSON.stringify(res.data));
return (res.data && res.data.code === 'Z000' && Array.isArray(res.data.result)) ? res.data.result : [];
}
// ======================== 3. 获取结算单列表 ========================
async function listReceiveStatements(settlementAccountId) {
const path = `${API_BASE}/settlement/receiveStatements/listPageReceiveStatements?settlementAccountId=${settlementAccountId}&sortType=1&pageIndex=1&pageSize=10`;
console.log(` [GET] ${path}`);
const res = await httpRequest('GET', path);
console.log(` [OK]`, JSON.stringify(res.data));
return (res.data && res.data.code === 'Z000' && res.data.result && res.data.result.list) ? res.data.result.list : [];
}
// ======================== 4. 自动对账POST ========================
async function autoReconciliation(statementSeq, id) {
const path = '/api/tmc/finance/frond/receiveStatements/autoReconciliation';
const headers = {
...API_HEADERS,
'content-type': 'application/json;charset=UTF-8',
'Referer': `https://superstar.geelytravel.com/new-tmc/finance/account/saleBill/detail/${id}/${statementSeq}`,
};
const body = JSON.stringify({ statementSeq });
console.log(` [POST] ${path} body=${body}`);
const res = await httpRequest('POST', path, headers, body);
console.log(` [OK]`, JSON.stringify(res.data));
return res.data;
}
// ======================== 5. 确认对账POST ========================
async function confirmReconciliation(statementSeq, id) {
const path = `${API_BASE}/settlement/receiveStatementsGroup/confirmCompleteReceiveReconciliation?statementSeq=${statementSeq}`;
const headers = {
...API_HEADERS,
'Referer': `https://superstar.geelytravel.com/new-tmc/finance/account/saleBill/detail/${id}/${statementSeq}`,
};
console.log(` [POST] ${path}`);
const res = await httpRequest('POST', path, headers, null);
console.log(` [OK]`, JSON.stringify(res.data));
return res.data;
}
// ======================== 主流程 ========================
async function main() {
const teamIds = readTeamIds();
console.log(`\nTEAM IDs: ${teamIds.join(', ')}\n`);
const allResults = [];
for (const teamId of teamIds) {
console.log(`${'='.repeat(50)}\n 处理 ${teamId}\n${'='.repeat(50)}`);
const accounts = await querySettlementAccountIds(teamId);
if (!accounts.length) { console.log(' ⚠ 未找到'); continue; }
for (const account of accounts) {
const accountId = account.id;
console.log(`\n --> 结算单列表 settlementAccountId: ${accountId}`);
const statements = await listReceiveStatements(accountId);
console.log(` --> 共 ${statements.length}`);
for (const st of statements) {
const { id, statementSeq, statementName, receivableTotalPrice, statementStatus } = st;
console.log(`\n --- [${statementSeq}] ${statementName}`);
console.log(` 应收: \u00a5${receivableTotalPrice} | 状态: ${statementStatus}`);
if (statementStatus === '5') {
console.log(` \u26a0 已完成,跳过`);
allResults.push({ teamId, settlementAccountId: accountId, statementSeq, statementName, receivableTotalPrice, autoReconResult: { code: 'SKIP' }, confirmResult: { code: 'SKIP' } });
continue;
}
const autoResult = await autoReconciliation(statementSeq, id);
console.log(` \u23f3 等待 ${STEP_DELAY_MS / 1000}s...`);
await sleep(STEP_DELAY_MS);
const confirmResult = await confirmReconciliation(statementSeq, id);
allResults.push({ teamId, settlementAccountId: accountId, statementSeq, statementName, receivableTotalPrice, autoReconResult: autoResult, confirmResult });
}
}
}
// ======================== 汇总 ========================
console.log('\n\n' + '='.repeat(70));
console.log(' 最终汇总');
console.log('='.repeat(70));
for (const item of allResults) {
const aOk = item.autoReconResult && (item.autoReconResult.code === 'Z000' || item.autoReconResult.code === 'SKIP');
const cOk = item.confirmResult && (item.confirmResult.code === 'Z000' || item.confirmResult.code === 'SKIP');
console.log(`\n${aOk ? '\u2705' : '\u274c'} 自动 | ${cOk ? '\u2705' : '\u274c'} 确认 | ${item.teamId} | ${item.statementSeq}`);
console.log(` ${item.statementName} \u00a5${item.receivableTotalPrice}`);
console.log(` 自动: ${JSON.stringify(item.autoReconResult)}`);
console.log(` 确认: ${JSON.stringify(item.confirmResult)}`);
}
}
main().catch(err => { console.error('Error:', err); process.exit(1); });