This commit is contained in:
2025-10-29 18:44:21 +08:00
parent 98210ef15c
commit da867e258e
10 changed files with 168 additions and 8 deletions

3
.gitignore vendored
View File

@@ -1,5 +1,8 @@
# 目标代码 # 目标代码
dist/ dist/
# 依赖包
node_modules/
# 编辑器 # 编辑器
.vscode/ .vscode/

View File

@@ -0,0 +1,37 @@
import type { ICredentialTestRequest, ICredentialType, INodeProperties, Icon } from 'n8n-workflow';
export class GwezzChangdunovelApi implements ICredentialType {
name = 'gwezzChangdunovelApi';
displayName = 'Gwezz 常读分销 API';
icon = { light: 'file:../icons/gwezz.svg', dark: 'file:../icons/gwezz.dark.svg' } as Icon;
documentationUrl = 'https://github.com/org/-gwezz-changdunovel?tab=readme-ov-file#credentials';
properties: INodeProperties[] = [
{
displayName: 'distributor_id',
name: 'distributor_id',
type: 'string',
required: true,
default: '',
description: '分销商 ID可与常读商务同学沟通获取',
},
{
displayName: 'secret_key',
name: 'secret_key',
type: 'string',
typeOptions: { password: true },
required: true,
default: '',
description: '签名密钥,可与常读商务同学沟通获取',
},
];
test: ICredentialTestRequest = {
request: {
url: 'https://www.changdunovel.com/novelsale/openap',
},
};
}

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

18
node_modules/.package-lock.json generated vendored
View File

@@ -1286,6 +1286,16 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/node": {
"version": "24.9.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.2.tgz",
"integrity": "sha512-uWN8YqxXxqFMX2RqGOrumsKeti4LlmIMIyV0lgut4jx7KQBcBiW6vkDtIBvHnHIquwNfJhk8v2OtmO8zXWHfPA==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~7.16.0"
}
},
"node_modules/@types/parse-path": { "node_modules/@types/parse-path": {
"version": "7.0.3", "version": "7.0.3",
"resolved": "https://registry.npmmirror.com/@types/parse-path/-/parse-path-7.0.3.tgz", "resolved": "https://registry.npmmirror.com/@types/parse-path/-/parse-path-7.0.3.tgz",
@@ -1800,7 +1810,6 @@
"x64" "x64"
], ],
"dev": true, "dev": true,
"ideallyInert": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -6822,6 +6831,13 @@
"node": ">=18.17" "node": ">=18.17"
} }
}, },
"node_modules/undici-types": {
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
"dev": true,
"license": "MIT"
},
"node_modules/universal-user-agent": { "node_modules/universal-user-agent": {
"version": "7.0.3", "version": "7.0.3",
"resolved": "https://registry.npmmirror.com/universal-user-agent/-/universal-user-agent-7.0.3.tgz", "resolved": "https://registry.npmmirror.com/universal-user-agent/-/universal-user-agent-7.0.3.tgz",

View File

@@ -1,11 +1,45 @@
import { type INodeType, type INodeTypeDescription } from 'n8n-workflow'; import { createHash } from 'crypto';
import {
type INodeType,
type INodeTypeDescription,
IExecuteFunctions,
INodeExecutionData,
} from 'n8n-workflow';
import { distributionDescription } from './resources/distribution'; import { distributionDescription } from './resources/distribution';
/**
* 生成 MD5 签名
* @param params 参数数组
* @returns MD5 签名字符串
*/
function getSign(params: (string | number)[]): string {
const paramStr = params.join('');
return createHash('md5').update(paramStr).digest('hex');
}
/**
* 创建认证参数
* @param distributorId 分销商 ID
* @param secretKey 签名密钥
* @returns
*/
function createAuthParams(
distributorId: string,
secretKey: string,
): Record<string, string | number> {
const timeStamp = Math.floor(Date.now() / 1000);
return {
distributor_id: distributorId,
ts: timeStamp,
sign: getSign([distributorId, secretKey, timeStamp]),
};
}
export class GwezzChangdunovel implements INodeType { export class GwezzChangdunovel implements INodeType {
description: INodeTypeDescription = { description: INodeTypeDescription = {
displayName: 'Gwezz 常读分销', displayName: 'Gwezz 常读分销',
name: 'gwezzChangdunovel', name: 'gwezzChangdunovel',
icon: { light: 'file:gwezz.svg', dark: 'file:gwezz.dark.svg' }, icon: { light: 'file:../../icons/gwezz.svg', dark: 'file:../../icons/gwezz.dark.svg' },
group: ['transform'], group: ['transform'],
version: 1, version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
@@ -16,7 +50,7 @@ export class GwezzChangdunovel implements INodeType {
usableAsTool: true, usableAsTool: true,
inputs: ['main'], inputs: ['main'],
outputs: ['main'], outputs: ['main'],
credentials: [], credentials: [{ name: 'gwezzChangdunovelApi', required: true }],
requestDefaults: { requestDefaults: {
baseURL: 'https://www.changdunovel.com/novelsale/openapi', baseURL: 'https://www.changdunovel.com/novelsale/openapi',
headers: { headers: {
@@ -41,4 +75,53 @@ export class GwezzChangdunovel implements INodeType {
...distributionDescription, ...distributionDescription,
], ],
}; };
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
const returnData: INodeExecutionData[] = [];
const resource = this.getNodeParameter('resource', 0);
const operation = this.getNodeParameter('operation', 0);
const credentials = await this.getCredentials('gwezzChangdunovelApi');
const distributorId = credentials.distributor_id as string;
const secretKey = credentials.secret_key as string;
let responseData;
for (let i = 0; i < items.length; i++) {
try {
if (resource === 'distribution') {
if (operation === 'contentBookMetaV1') {
const bookId = this.getNodeParameter('book_id', i) as string;
const authParams = createAuthParams(distributorId, secretKey);
const qs = {
...authParams,
book_id: bookId,
};
responseData = await this.helpers.httpRequest({
method: 'GET',
url: '/v1/content/book/meta',
qs,
});
}
}
returnData.push({ json: responseData, itemIndex: i });
} catch (error) {
if (this.continueOnFail()) {
returnData.push({
json: {
error: (error as Error).message,
},
itemIndex: i,
});
continue;
}
throw error;
}
}
return [this.helpers.returnJsonArray(returnData)];
}
} }

View File

@@ -8,7 +8,7 @@ const showOnlyForDistributionContentBookMetaV1 = {
export const contentBookMetaV1Description: INodeProperties[] = [ export const contentBookMetaV1Description: INodeProperties[] = [
{ {
displayName: '书籍 ID', displayName: '书籍 ID',
name: 'bookId', name: 'book_id',
type: 'string', type: 'string',
required: true, required: true,
default: '', default: '',

View File

@@ -18,7 +18,7 @@ export const distributionDescription: INodeProperties[] = [
{ {
name: '获取书籍信息', name: '获取书籍信息',
value: 'contentBookMetaV1', value: 'contentBookMetaV1',
action: 'Get content book metadata', action: '获取书籍信息',
routing: { routing: {
request: { request: {
method: 'GET', method: 'GET',

18
package-lock.json generated
View File

@@ -10,6 +10,7 @@
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@n8n/node-cli": "*", "@n8n/node-cli": "*",
"@types/node": "^24.9.2",
"eslint": "9.32.0", "eslint": "9.32.0",
"prettier": "3.6.2", "prettier": "3.6.2",
"release-it": "^19.0.4", "release-it": "^19.0.4",
@@ -1296,6 +1297,16 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/node": {
"version": "24.9.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.2.tgz",
"integrity": "sha512-uWN8YqxXxqFMX2RqGOrumsKeti4LlmIMIyV0lgut4jx7KQBcBiW6vkDtIBvHnHIquwNfJhk8v2OtmO8zXWHfPA==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~7.16.0"
}
},
"node_modules/@types/parse-path": { "node_modules/@types/parse-path": {
"version": "7.0.3", "version": "7.0.3",
"resolved": "https://registry.npmmirror.com/@types/parse-path/-/parse-path-7.0.3.tgz", "resolved": "https://registry.npmmirror.com/@types/parse-path/-/parse-path-7.0.3.tgz",
@@ -6814,6 +6825,13 @@
"node": ">=18.17" "node": ">=18.17"
} }
}, },
"node_modules/undici-types": {
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
"dev": true,
"license": "MIT"
},
"node_modules/universal-user-agent": { "node_modules/universal-user-agent": {
"version": "7.0.3", "version": "7.0.3",
"resolved": "https://registry.npmmirror.com/universal-user-agent/-/universal-user-agent-7.0.3.tgz", "resolved": "https://registry.npmmirror.com/universal-user-agent/-/universal-user-agent-7.0.3.tgz",

View File

@@ -30,13 +30,16 @@
"n8n": { "n8n": {
"n8nNodesApiVersion": 1, "n8nNodesApiVersion": 1,
"strict": true, "strict": true,
"credentials": [], "credentials": [
"dist/credentials/GwezzChangdunovelApi.credentials.js"
],
"nodes": [ "nodes": [
"dist/nodes/GwezzChangdunovel/GwezzChangdunovel.node.js" "dist/nodes/GwezzChangdunovel/GwezzChangdunovel.node.js"
] ]
}, },
"devDependencies": { "devDependencies": {
"@n8n/node-cli": "*", "@n8n/node-cli": "*",
"@types/node": "^24.9.2",
"eslint": "9.32.0", "eslint": "9.32.0",
"prettier": "3.6.2", "prettier": "3.6.2",
"release-it": "^19.0.4", "release-it": "^19.0.4",
@@ -45,4 +48,4 @@
"peerDependencies": { "peerDependencies": {
"n8n-workflow": "*" "n8n-workflow": "*"
} }
} }