Files
aliyunApsSkill/aliyun-sync/aliyun-aps-sync/src/config.js
2026-05-07 10:08:56 +08:00

248 lines
11 KiB
JavaScript

import fs from 'node:fs';
import path from 'node:path';
import process from 'node:process';
import dotenv from 'dotenv';
const rootDir = path.resolve(process.cwd());
dotenv.config({ path: path.join(rootDir, '.env') });
const toBool = (value, fallback) => {
if (value == null) return fallback;
return ['1', 'true', 'yes', 'y', 'on'].includes(String(value).trim().toLowerCase());
};
const ensureDir = (dirPath) => {
fs.mkdirSync(dirPath, { recursive: true });
return dirPath;
};
export const config = {
rootDir,
baseUrl: process.env.ALIYUN_APS_BASE_URL || 'https://aps.aliyun.com',
headless: toBool(process.env.ALIYUN_APS_HEADLESS, false),
browserMode: (process.env.ALIYUN_APS_BROWSER_MODE || 'launch').trim().toLowerCase(),
browserChannel: (process.env.ALIYUN_APS_BROWSER_CHANNEL || '').trim(),
browserExecutablePath: (process.env.ALIYUN_APS_BROWSER_EXECUTABLE_PATH || '').trim(),
cdpUrl: (process.env.ALIYUN_APS_CDP_URL || 'http://127.0.0.1:9222').trim(),
timezone: process.env.ALIYUN_APS_TIMEZONE || 'Asia/Shanghai',
cron: process.env.ALIYUN_APS_CRON || '0 6 * * *',
orderStartDate: process.env.ALIYUN_APS_ORDER_START_DATE || '2024-01-01',
incrementalOrderStartDate: process.env.ALIYUN_APS_INCREMENTAL_ORDER_START_DATE || '',
billStartMonth: process.env.ALIYUN_APS_BILL_START_MONTH || '2024-01',
orderIncrementalOverlapDays: Math.max(0, Number.parseInt(process.env.ALIYUN_APS_ORDER_INCREMENTAL_OVERLAP_DAYS || '2', 10) || 2),
billIncrementalOverlapDays: Math.max(0, Number.parseInt(process.env.ALIYUN_APS_BILL_INCREMENTAL_OVERLAP_DAYS || '7', 10) || 7),
messageIncrementalOverlapDays: Math.max(0, Number.parseInt(process.env.ALIYUN_APS_MESSAGE_INCREMENTAL_OVERLAP_DAYS || '7', 10) || 7),
scheduleMode: process.env.ALIYUN_APS_SCHEDULE_MODE || 'incremental',
smtp: {
host: process.env.ALIYUN_APS_SMTP_HOST || 'smtp.qq.com',
port: parseInt(process.env.ALIYUN_APS_SMTP_PORT || '465', 10),
secure: toBool(process.env.ALIYUN_APS_SMTP_SECURE, true),
user: process.env.ALIYUN_APS_SMTP_USER || '',
pass: process.env.ALIYUN_APS_SMTP_PASS || '',
},
notifyEmail: process.env.ALIYUN_APS_NOTIFY_EMAIL || '',
closeBrowser: toBool(process.env.ALIYUN_APS_CLOSE_BROWSER, true),
fullSync: toBool(process.env.ALIYUN_APS_FULL_SYNC, true),
resumeBillMonth: process.env.ALIYUN_APS_RESUME_BILL_MONTH || '',
resumeBillPage: Math.max(1, Number.parseInt(process.env.ALIYUN_APS_RESUME_BILL_PAGE || '1', 10) || 1),
userDataDir: ensureDir(path.join(rootDir, '.browser')),
storageStateFile: path.join(rootDir, '.browser', 'storage-state.json'),
dataDir: ensureDir(path.join(rootDir, 'data')),
downloadDir: ensureDir(path.join(rootDir, 'downloads')),
errorDir: ensureDir(path.join(rootDir, 'data', 'errors')),
db: {
host: process.env.ALIYUN_APS_DB_HOST || '',
port: parseInt(process.env.ALIYUN_APS_DB_PORT || '3306', 10),
user: process.env.ALIYUN_APS_DB_USER || '',
password: process.env.ALIYUN_APS_DB_PASSWORD || '',
database: process.env.ALIYUN_APS_DB_NAME || '',
charset: process.env.ALIYUN_APS_DB_CHARSET || 'utf8mb4',
connectionLimit: Math.max(1, Number.parseInt(process.env.ALIYUN_APS_DB_CONNECTION_LIMIT || '5', 10) || 5),
connectTimeout: Math.max(1000, Number.parseInt(process.env.ALIYUN_APS_DB_CONNECT_TIMEOUT || '20000', 10) || 20000),
},
};
export const datasets = {
customers: {
name: 'customers',
url: `${config.baseUrl}/#/detail/my_customer/~/customer/list`,
heading: '我的客户',
pageSize: 20,
uniqueKey: (record) => record.accountId || record.loginName || record.__hash,
normalize: (record) => {
const loginAndUid = record['登录名称/账号ID'] || '';
const [loginName = '', accountId = ''] = splitLines(loginAndUid);
return {
loginName: loginName.replace(/\s+/g, ''),
accountId,
listPageNum: context.pageNum || '',
realName: record['UID实名认证名称'] || '',
reportSource: record['报备来源'] || '',
reportType: record['报备类型'] || '',
tradeMode: record['交易模式'] || '',
authStatus: record['实名认证状态'] || '',
relationTime: record['关联日期'] || '',
owner: record['跟进员工'] || '',
cashBalanceCny: record['账户现金余额(CNY)'] || '',
invoicePendingCny: record['待开票金额(CNY)'] || '',
lastMonthConsumptionCny: record['上月消费金额(CNY)'] || '',
thisMonthConsumptionCny: record['本月消费金额(CNY)'] || '',
paymentNoticeStatus: record['代为支付告知状态'] || '',
inviteType: record['邀约注册类型'] || '',
isNewCustomer: record['是否新客户'] || '',
isPerformanceQualified: record['是否达成业绩起算点'] || '',
customerCategory: record['客户分类'] || '',
remark: record['备注'] || '',
inactiveMonths: record['客户无消费月数'] || '',
releasePlanTime: record['计划释放时间'] || '',
releasePlanReason: record['计划释放原因'] || '',
};
},
},
orders: {
name: 'orders',
url: `${config.baseUrl}/#/detail/order/~/costCenter/order`,
heading: '订单查询',
pageSize: 20,
uniqueKey: (record) => record.orderId || record.__hash,
normalize: (record, context) => ({
orderId: record['订单号'] || '',
customerAccount: (record['客户账号'] || '').replace(/\s+/g, ''),
customerCategory: record['客户分类'] || '',
orderType: record['订单类型'] || '',
orderOriginalPriceCny: record['订单原价 (CNY)'] || '',
actualPaidCny: record['实付金额 (CNY)'] || '',
orderStatus: record['订单状态'] || '',
createdAt: record['下单时间'] || '',
windowStart: context.windowStart || '',
windowEnd: context.windowEnd || '',
}),
},
orderDetails: {
name: 'orderDetails',
url: `${config.baseUrl}/#/detail/order/~/costCenter/order`,
heading: '订单详情',
pageSize: 100,
uniqueKey: (record) => record.orderId || record.__hash,
normalize: (record, context) => ({
orderId: record.orderId || '',
orderType: record.orderType || '',
status: record.status || '',
tradeType: record.tradeType || '',
customerCategory: record.customerCategory || '',
dealerName: record.dealerName || '',
dealerUid: record.dealerUid || '',
customerType: record.customerType || '',
opportunityId: record.opportunityId || '',
paymentTime: record.paymentTime || '',
orderTime: record.orderTime || '',
productName: record.productName || '',
productCode: record.productCode || '',
originalPriceCny: record.originalPriceCny || '',
paidAmountCny: record.paidAmountCny || '',
discount: record.discount || '',
payableAmountCny: record.payableAmountCny || '',
couponAmountCny: record.couponAmountCny || '',
windowStart: context.windowStart || '',
windowEnd: context.windowEnd || '',
}),
},
customerDetails: {
name: 'customerDetails',
url: `${config.baseUrl}/#/detail/my_customer/~/customer/list`,
heading: '详情',
pageSize: 100,
uniqueKey: (record) => record.accountId || record.__hash,
normalize: (record, context) => ({
accountId: context.accountId || '',
customerAccount: record.customerAccount || '',
customerName: record.customerName || '',
customerType: record.customerType || '',
tradeMode: record.tradeMode || '',
customerSource: record.customerSource || '',
realNameStatus: record.realNameStatus || '',
email: record.email || '',
relationDate: record.relationDate || '',
phone: record.phone || '',
remark: record.remark || '',
paymentNoticeStatus: record.paymentNoticeStatus || '',
department: record.department || '',
lastMonthPayableTotalCny: record.lastMonthPayableTotalCny || '',
lastMonthPrepayCny: record.lastMonthPrepayCny || '',
lastMonthPostpayCny: record.lastMonthPostpayCny || '',
currentMonthPayableTotalCny: record.currentMonthPayableTotalCny || '',
currentMonthPrepayCny: record.currentMonthPrepayCny || '',
currentMonthPostpayCny: record.currentMonthPostpayCny || '',
}),
},
bills: {
name: 'bills',
url: `${config.baseUrl}/#/detail/bill/~/costCenter/bill`,
heading: '账单查询',
pageSize: 20,
uniqueKey: (record) => record.__hash,
normalize: (record, context) => ({
billingMonth: record['账期'] || '',
consumeDate: record['消费时间'] || '',
customerAccount: (record['客户账号'] || '').replace(/\s+/g, ''),
customerCategory: record['客户分类'] || '',
productCategory: record['产品分类'] || '',
productName: record['产品名称'] || '',
originalPriceCny: record['原价 (CNY)'] || '',
customerPayableCny: record['客户应付金额 (CNY)'] || '',
billType: record['账单类型'] || '',
countsForPerformance: record['是否计入业绩'] || '',
commissionable: record['是否返佣'] || '',
commissionMonth: record['佣金月份'] || context.month || '',
inviteType: record['邀约注册类型'] || '',
serviceStartAt: record['服务开始时间'] || '',
serviceEndAt: record['服务结束时间'] || '',
}),
},
messages: {
name: 'messages',
url: `${config.baseUrl}/#/message`,
heading: '消息',
pageSize: 20,
uniqueKey: (record) => record.msgId || record.__hash,
normalize: (record) => ({
msgId: pickFirst(record, ['消息ID', 'msg_id', '消息id', 'ID', 'id']),
title: pickFirst(record, ['消息标题', '标题', 'title']),
content: pickFirst(record, ['消息内容', '内容', 'content']),
msgType: pickFirst(record, ['消息类型', 'type', 'msg_type']),
fromApp: pickFirst(record, ['来源应用', 'from_app', '应用']),
bizCode: pickFirst(record, ['业务编码', 'biz_code']),
msgChannel: pickFirst(record, ['消息通道', 'msg_channel']),
categoryId: pickFirst(record, ['分类ID', 'category_id']),
categoryName: pickFirst(record, ['分类名称', 'category_name']),
lv1CategoryId: pickFirst(record, ['一级分类ID', 'lv1_category_id']),
lv2CategoryId: pickFirst(record, ['二级分类ID', 'lv2_category_id']),
lv3CategoryId: pickFirst(record, ['三级分类ID', 'lv3_category_id']),
messageClassification: pickFirst(record, ['归类结果', 'message_classification']),
customerName: pickFirst(record, ['客户名称', 'customer_name']),
orderNo: pickFirst(record, ['订单号', 'order_no']),
status: pickFirst(record, ['消息状态', '状态', 'status']),
gmtCreated: pickFirst(record, ['消息创建时间', '创建时间', 'gmt_created']),
gmtModified: pickFirst(record, ['消息修改时间', '修改时间', 'gmt_modified']),
extraData: record,
}),
},
};
function splitLines(value) {
return String(value)
.split('\n')
.map((part) => part.trim())
.filter(Boolean);
}
function pickFirst(record, keys) {
for (const key of keys) {
const value = record[key];
if (value != null && String(value).trim()) {
return String(value).trim();
}
}
return '';
}