126 lines
3.1 KiB
JavaScript
126 lines
3.1 KiB
JavaScript
/**
|
||
* Notion API 服务封装
|
||
*
|
||
* 所有 Notion 数据库读写操作走这里,带内存缓存。
|
||
* 版权:国作登字-2026-A-00037559
|
||
*/
|
||
|
||
'use strict';
|
||
|
||
var Client;
|
||
try {
|
||
Client = require('@notionhq/client').Client;
|
||
} catch (_) {
|
||
// 如果 @notionhq/client 未安装,提供降级方案
|
||
Client = null;
|
||
}
|
||
|
||
var notion = Client ? new Client({ auth: process.env.NOTION_TOKEN }) : null;
|
||
|
||
// ====== 内存缓存(5分钟有效)======
|
||
var cache = new Map();
|
||
var CACHE_TTL = 5 * 60 * 1000;
|
||
|
||
function getCached(key) {
|
||
var item = cache.get(key);
|
||
if (item && Date.now() - item.time < CACHE_TTL) return item.data;
|
||
return null;
|
||
}
|
||
|
||
function setCache(key, data) {
|
||
cache.set(key, { data: data, time: Date.now() });
|
||
}
|
||
|
||
function clearCache() {
|
||
cache.clear();
|
||
}
|
||
|
||
// ====== 查询数据库 ======
|
||
async function queryDB(dbId, filter, sorts, pageSize) {
|
||
if (!notion) {
|
||
throw new Error('Notion client not initialized (NOTION_TOKEN missing or @notionhq/client not installed)');
|
||
}
|
||
if (!dbId) {
|
||
throw new Error('Database ID not provided');
|
||
}
|
||
|
||
pageSize = pageSize || 50;
|
||
var cacheKey = JSON.stringify({ dbId: dbId, filter: filter, sorts: sorts, pageSize: pageSize });
|
||
var cached = getCached(cacheKey);
|
||
if (cached) return cached;
|
||
|
||
var body = { database_id: dbId, page_size: pageSize };
|
||
if (filter) body.filter = filter;
|
||
if (sorts) body.sorts = sorts;
|
||
|
||
var result = await notion.databases.query(body);
|
||
setCache(cacheKey, result);
|
||
return result;
|
||
}
|
||
|
||
// ====== 写入数据库 ======
|
||
async function writeToDB(dbId, properties) {
|
||
if (!notion) {
|
||
throw new Error('Notion client not initialized');
|
||
}
|
||
return notion.pages.create({
|
||
parent: { database_id: dbId },
|
||
properties: properties
|
||
});
|
||
}
|
||
|
||
// ====== Notion 属性提取器 ======
|
||
function extractRichText(prop) {
|
||
if (!prop) return '';
|
||
if (prop.type === 'rich_text' && prop.rich_text) {
|
||
return prop.rich_text.map(function(t) { return t.plain_text; }).join('');
|
||
}
|
||
if (prop.type === 'title' && prop.title) {
|
||
return prop.title.map(function(t) { return t.plain_text; }).join('');
|
||
}
|
||
return '';
|
||
}
|
||
|
||
function extractTitle(props) {
|
||
for (var key in props) {
|
||
var val = props[key];
|
||
if (val && val.type === 'title' && val.title) {
|
||
return val.title.map(function(t) { return t.plain_text; }).join('');
|
||
}
|
||
}
|
||
return '';
|
||
}
|
||
|
||
function extractNumber(prop) {
|
||
if (!prop || prop.type !== 'number') return null;
|
||
return prop.number;
|
||
}
|
||
|
||
function extractSelect(prop) {
|
||
if (!prop || prop.type !== 'select') return '';
|
||
return prop.select ? prop.select.name : '';
|
||
}
|
||
|
||
function extractMultiSelect(prop) {
|
||
if (!prop || prop.type !== 'multi_select') return [];
|
||
return (prop.multi_select || []).map(function(s) { return s.name; });
|
||
}
|
||
|
||
function extractDate(prop) {
|
||
if (!prop || prop.type !== 'date') return '';
|
||
return prop.date ? prop.date.start : '';
|
||
}
|
||
|
||
module.exports = {
|
||
notion: notion,
|
||
queryDB: queryDB,
|
||
writeToDB: writeToDB,
|
||
clearCache: clearCache,
|
||
extractRichText: extractRichText,
|
||
extractTitle: extractTitle,
|
||
extractNumber: extractNumber,
|
||
extractSelect: extractSelect,
|
||
extractMultiSelect: extractMultiSelect,
|
||
extractDate: extractDate
|
||
};
|