feat(api): 添加帮助中心反馈系统和VIP服务功能

- 在ApiClient中新增submitFeedback、myFeedback、helpArticles、helpArticleDetail、
  siteArticles、promotions、vipPackages、createVipOrder、vipOrders等方法
- 添加帮助文章和站点资讯的参数验证逻辑
- 更新测试文件添加新的API方法测试用例
- 在HTML页面中添加反馈、帮助和VIP服务相关页面的脚本引用
- 更新加入家谱页面为完整的申请流程界面
- 修改资讯详情页面为站点资讯展示页面
- 更新AxiosRequestUtil中认证处理逻辑
- 添加世系树渲染的HTML生成函数用于页面复用
- 更新文档中的API契约说明和页面规划
This commit is contained in:
fizzleaf
2026-07-30 16:04:48 +08:00
parent fb1743aa2a
commit 309598bfa6
53 changed files with 6740 additions and 282 deletions
+62
View File
@@ -2,6 +2,7 @@ const assert = require('node:assert/strict');
const test = require('node:test');
const GenealogyApi = require('../utils/ApiClient.js');
const AxiosRequestUtil = require('../utils/AxiosRequestUtil.js');
function createFailingClient(status) {
let storedToken = 'access-token';
@@ -96,3 +97,64 @@ test('business response code 401 clears the stored login token', async () => {
assert.equal(client.getToken(), '');
});
test('public HTTP 401 neither sends nor clears the stored login token', async () => {
let storedToken = 'access-token';
let seenRequest;
const requester = AxiosRequestUtil.createRequester({
baseUrl: 'https://api.example.test',
clientId: 'web-pc',
getToken() {
return storedToken;
},
onUnauthorized() {
storedToken = '';
},
axiosInstance: {
request(config) {
seenRequest = config;
return Promise.reject({
message: 'Request failed',
response: {
status: 401,
data: {
code: 401,
msg: '认证失败'
}
}
});
}
}
});
await assert.rejects(requester('GET', '/public', { auth: false }));
assert.equal(seenRequest.headers.Authorization, undefined);
assert.equal(storedToken, 'access-token');
});
test('public business 401 preserves the stored login token', async () => {
let storedToken = 'access-token';
const requester = AxiosRequestUtil.createRequester({
baseUrl: 'https://api.example.test',
clientId: 'web-pc',
getToken() {
return storedToken;
},
onUnauthorized() {
storedToken = '';
},
axiosInstance: {
request() {
return Promise.resolve({
data: {
code: 401,
msg: '认证失败'
}
});
}
}
});
await assert.rejects(requester('GET', '/public', { auth: false }));
assert.equal(storedToken, 'access-token');
});