| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234 |
- const fs = require('fs');
- const path = require('path');
- const { categoryDir } = require('./output-paths');
- function customersDir() {
- return categoryDir('customers');
- }
- function brokersDir() {
- return categoryDir('brokers');
- }
- function customerFilePath(customerId) {
- return path.join(customersDir(), `${customerId}.json`);
- }
- function brokerFilePath(brokerId) {
- return path.join(brokersDir(), `${brokerId}.json`);
- }
- function phoneIndexPath() {
- return path.join(customersDir(), 'index-phone.json');
- }
- function externalUserIdIndexPath() {
- return path.join(customersDir(), 'index-external-user-id.json');
- }
- function safeReadJson(filePath, fallback = null) {
- try {
- if (!filePath || !fs.existsSync(filePath)) return fallback;
- return JSON.parse(fs.readFileSync(filePath, 'utf8'));
- } catch {
- return fallback;
- }
- }
- function atomicWriteJson(filePath, data) {
- const dir = path.dirname(filePath);
- fs.mkdirSync(dir, { recursive: true });
- const tmpPath = `${filePath}.tmp.${Date.now()}.${Math.random().toString(36).slice(2, 8)}`;
- try {
- fs.writeFileSync(tmpPath, JSON.stringify(data, null, 2), 'utf8');
- fs.renameSync(tmpPath, filePath);
- } catch (err) {
- try { fs.unlinkSync(tmpPath); } catch {}
- throw err;
- }
- return filePath;
- }
- function normalizePhone(raw) {
- if (!raw) return '';
- let value = String(raw).trim().replace(/[^\d]/g, '');
- if (value.length >= 12 && /^(?:86)?1[3-9]\d{9}$/.test(value)) value = value.replace(/^86/, '');
- return value;
- }
- function buildCustomerId(input = {}) {
- if (input.customerId) return String(input.customerId).trim();
- if (input.externalUserId) return String(input.externalUserId).trim();
- const phone = normalizePhone(input.phone);
- if (phone) return phone;
- return `cust-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
- }
- function readCustomerById(customerId) {
- if (!customerId) return null;
- return safeReadJson(customerFilePath(customerId));
- }
- function writeCustomer(customer) {
- if (!customer || !customer.customerId) throw new Error('customer.customerId 必填');
- const filePath = customerFilePath(customer.customerId);
- const existing = safeReadJson(filePath) || {};
- const now = new Date().toISOString();
- const record = {
- ...existing,
- ...customer,
- customerId: customer.customerId,
- createdAt: existing.createdAt || now,
- updatedAt: now
- };
- atomicWriteJson(filePath, record);
- rebuildCustomerIndexIfNeeded();
- return record;
- }
- function readPhoneIndex() {
- return safeReadJson(phoneIndexPath(), {});
- }
- function readExternalUserIdIndex() {
- return safeReadJson(externalUserIdIndexPath(), {});
- }
- function writePhoneIndex(index) {
- atomicWriteJson(phoneIndexPath(), index);
- }
- function writeExternalUserIdIndex(index) {
- atomicWriteJson(externalUserIdIndexPath(), index);
- }
- function rebuildCustomerIndexIfNeeded() {
- const dir = customersDir();
- if (!fs.existsSync(dir)) return;
- const phoneIndex = {};
- const externalUserIdIndex = {};
- for (const file of fs.readdirSync(dir)) {
- if (!file.endsWith('.json') || file.startsWith('index-')) continue;
- const customerId = file.replace(/\.json$/, '');
- const customer = safeReadJson(path.join(dir, file));
- if (!customer) continue;
- const phone = normalizePhone(customer.phone);
- if (phone) phoneIndex[phone] = customer.customerId || customerId;
- if (customer.externalUserId) externalUserIdIndex[customer.externalUserId] = customer.customerId || customerId;
- }
- writePhoneIndex(phoneIndex);
- writeExternalUserIdIndex(externalUserIdIndex);
- }
- function readCustomerByExternalUserId(externalUserId) {
- if (!externalUserId) return null;
- const index = readExternalUserIdIndex();
- const customerId = index[externalUserId];
- if (customerId) {
- const customer = readCustomerById(customerId);
- if (customer) return customer;
- }
- // fallback: 扫描文件
- const dir = customersDir();
- if (!fs.existsSync(dir)) return null;
- for (const file of fs.readdirSync(dir)) {
- if (!file.endsWith('.json') || file.startsWith('index-')) continue;
- const customer = safeReadJson(path.join(dir, file));
- if (customer && customer.externalUserId === externalUserId) return customer;
- }
- return null;
- }
- function readCustomerByPhone(phone) {
- const normalized = normalizePhone(phone);
- if (!normalized) return null;
- const index = readPhoneIndex();
- const customerId = index[normalized];
- if (customerId) {
- const customer = readCustomerById(customerId);
- if (customer) return customer;
- }
- // fallback: 扫描文件
- const dir = customersDir();
- if (!fs.existsSync(dir)) return null;
- for (const file of fs.readdirSync(dir)) {
- if (!file.endsWith('.json') || file.startsWith('index-')) continue;
- const customer = safeReadJson(path.join(dir, file));
- if (customer && normalizePhone(customer.phone) === normalized) return customer;
- }
- return null;
- }
- function readBrokerById(brokerId) {
- if (!brokerId) return null;
- return safeReadJson(brokerFilePath(brokerId));
- }
- function writeBroker(broker) {
- if (!broker || !broker.brokerId) throw new Error('broker.brokerId 必填');
- const filePath = brokerFilePath(broker.brokerId);
- const existing = safeReadJson(filePath) || {};
- const now = new Date().toISOString();
- const guidList = Array.from(new Set([
- ...(existing.guidList || []),
- ...(broker.guidList || [])
- ].filter(Boolean)));
- const record = {
- ...existing,
- ...broker,
- brokerId: broker.brokerId,
- guidList,
- createdAt: existing.createdAt || now,
- updatedAt: now
- };
- atomicWriteJson(filePath, record);
- return record;
- }
- function readBrokerByWecomUserId(wecomUserId) {
- if (!wecomUserId) return null;
- const dir = brokersDir();
- if (!fs.existsSync(dir)) return null;
- for (const file of fs.readdirSync(dir)) {
- if (!file.endsWith('.json')) continue;
- const broker = safeReadJson(path.join(dir, file));
- if (broker && broker.wecomUserId === wecomUserId) return broker;
- }
- return null;
- }
- function bindBrokerToDevice(brokerId, guid) {
- if (!brokerId || !guid) return null;
- const broker = readBrokerById(brokerId) || { brokerId };
- broker.guidList = Array.from(new Set([...(broker.guidList || []), guid].filter(Boolean)));
- return writeBroker(broker);
- }
- function buildCustomerPhoneIndex() {
- rebuildCustomerIndexIfNeeded();
- return readPhoneIndex();
- }
- function buildCustomerExternalUserIdIndex() {
- rebuildCustomerIndexIfNeeded();
- return readExternalUserIdIndex();
- }
- module.exports = {
- normalizePhone,
- buildCustomerId,
- readCustomerById,
- writeCustomer,
- readCustomerByExternalUserId,
- readCustomerByPhone,
- readBrokerById,
- writeBroker,
- readBrokerByWecomUserId,
- bindBrokerToDevice,
- buildCustomerPhoneIndex,
- buildCustomerExternalUserIdIndex,
- customerFilePath,
- brokerFilePath,
- phoneIndexPath,
- externalUserIdIndexPath
- };
|