feat(api): 添加帮助中心反馈系统和VIP服务功能
- 在ApiClient中新增submitFeedback、myFeedback、helpArticles、helpArticleDetail、 siteArticles、promotions、vipPackages、createVipOrder、vipOrders等方法 - 添加帮助文章和站点资讯的参数验证逻辑 - 更新测试文件添加新的API方法测试用例 - 在HTML页面中添加反馈、帮助和VIP服务相关页面的脚本引用 - 更新加入家谱页面为完整的申请流程界面 - 修改资讯详情页面为站点资讯展示页面 - 更新AxiosRequestUtil中认证处理逻辑 - 添加世系树渲染的HTML生成函数用于页面复用 - 更新文档中的API契约说明和页面规划
This commit is contained in:
@@ -32,6 +32,10 @@ test('API client exposes latest document-defined PC operations', () => {
|
||||
'genealogiesMine', 'genealogyOptions', 'genealogyDetail', 'genealogyOverview', 'genealogyQuota',
|
||||
'createGenealogy', 'applyToGenealogy', 'myGenealogyJoinApplies',
|
||||
'pendingGenealogyJoinApplies', 'auditGenealogyJoinApply', 'cancelGenealogyJoinApply',
|
||||
'submitFeedback', 'myFeedback',
|
||||
'helpArticles', 'helpArticleDetail', 'siteArticles',
|
||||
'promotions',
|
||||
'vipPackages', 'createVipOrder', 'vipOrders',
|
||||
'notifications', 'notificationDetail', 'unreadNotificationCount', 'markNotificationRead', 'markAllNotificationsRead',
|
||||
'feeds', 'feedsPage', 'feedDetail', 'createFeed', 'updateFeed', 'deleteFeed',
|
||||
'likeFeed', 'unlikeFeed', 'feedComments', 'feedCommentsPage', 'createFeedComment',
|
||||
@@ -506,6 +510,110 @@ test('genealogy quota uses the current PC path without manufacturing a genealogy
|
||||
await client.genealogyQuota();
|
||||
});
|
||||
|
||||
test('help article methods use authenticated PC paths and whitelist the optional category', async () => {
|
||||
const calls = [];
|
||||
const client = GenealogyApi.createClient({
|
||||
baseUrl: 'https://api.example.test',
|
||||
tokenStore: { getItem() { return 'access-token'; }, setItem() {}, removeItem() {} },
|
||||
axiosInstance: {
|
||||
request(config) {
|
||||
calls.push(config);
|
||||
return Promise.resolve({ data: { code: 200, data: [] } });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await client.helpArticles({ helpCategory: ' account ', keyword: 'must-drop' });
|
||||
await client.helpArticleDetail('2062179707935264769');
|
||||
|
||||
assert.deepEqual(calls.map((config) => [
|
||||
config.method,
|
||||
config.url,
|
||||
config.params,
|
||||
config.headers.Authorization
|
||||
]), [
|
||||
['get', '/genealogy/pc/help-articles', { helpCategory: ' account ' }, 'Bearer access-token'],
|
||||
['get', '/genealogy/pc/help-articles/2062179707935264769', undefined, 'Bearer access-token']
|
||||
]);
|
||||
});
|
||||
|
||||
test('promotion method uses the authenticated PC path and whitelists platform query', async () => {
|
||||
const calls = [];
|
||||
const client = GenealogyApi.createClient({
|
||||
baseUrl: 'https://api.example.test',
|
||||
tokenStore: { getItem() { return 'access-token'; }, setItem() {}, removeItem() {} },
|
||||
axiosInstance: {
|
||||
request(config) {
|
||||
calls.push(config);
|
||||
return Promise.resolve({ data: { code: 200, data: [] } });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await client.promotions({
|
||||
platform: 'pc',
|
||||
keyword: 'must-drop'
|
||||
});
|
||||
|
||||
assert.deepEqual(calls.map((config) => [
|
||||
config.method,
|
||||
config.url,
|
||||
config.params,
|
||||
config.headers.Authorization
|
||||
]), [
|
||||
['get', '/genealogy/pc/promotions', { platform: 'pc' }, 'Bearer access-token']
|
||||
]);
|
||||
assert.throws(
|
||||
() => client.promotions({ platform: 'desktop' }),
|
||||
/不支持的推广平台/
|
||||
);
|
||||
});
|
||||
|
||||
test('site article method uses the public PC path and validates SQL query values', async () => {
|
||||
const calls = [];
|
||||
const client = GenealogyApi.createClient({
|
||||
baseUrl: 'https://api.example.test',
|
||||
tokenStore: {
|
||||
getItem() { return 'access-token'; },
|
||||
setItem() {},
|
||||
removeItem() {}
|
||||
},
|
||||
axiosInstance: {
|
||||
request(config) {
|
||||
calls.push(config);
|
||||
return Promise.resolve({ data: { code: 200, data: [] } });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await client.siteArticles({
|
||||
articleType: 'notice',
|
||||
limit: 100,
|
||||
keyword: 'must-drop'
|
||||
});
|
||||
await client.siteArticles({ limit: 100 });
|
||||
|
||||
assert.deepEqual(calls.map((config) => [
|
||||
config.method,
|
||||
config.url,
|
||||
config.params,
|
||||
config.headers.Authorization
|
||||
]), [
|
||||
['get', '/genealogy/pc/site/articles', { articleType: 'notice', limit: 100 }, undefined],
|
||||
['get', '/genealogy/pc/site/articles', { limit: 100 }, undefined]
|
||||
]);
|
||||
assert.throws(
|
||||
() => client.siteArticles({ articleType: 'culture', limit: 100 }),
|
||||
/不支持的资讯类型/
|
||||
);
|
||||
[0, -1, 1.5, 101, '100'].forEach((limit) => {
|
||||
assert.throws(
|
||||
() => client.siteArticles({ limit }),
|
||||
/资讯数量限制/
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('genealogy context methods obtain IDs from the real PC list and detail paths', async () => {
|
||||
const calls = [];
|
||||
const client = GenealogyApi.createClient({
|
||||
@@ -613,6 +721,72 @@ test('genealogy lifecycle methods use PC paths and strict request bodies', async
|
||||
);
|
||||
});
|
||||
|
||||
test('feedback methods use the PC collection path and AppFeedbackBody whitelist', async () => {
|
||||
const calls = [];
|
||||
const client = GenealogyApi.createClient({
|
||||
baseUrl: 'https://api.example.test',
|
||||
tokenStore: { getItem() { return 'access-token'; }, setItem() {}, removeItem() {} },
|
||||
axiosInstance: {
|
||||
request(config) {
|
||||
calls.push(config);
|
||||
return Promise.resolve({ data: { code: 200, data: [] } });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await client.submitFeedback({
|
||||
feedbackType: 'bug',
|
||||
feedbackContent: '页面按钮无响应',
|
||||
contactInfo: '19181970173',
|
||||
feedbackTitle: 'must-drop',
|
||||
appUserId: 'must-drop'
|
||||
});
|
||||
await client.myFeedback();
|
||||
|
||||
assert.deepEqual(calls.map((config) => [config.method, config.url, config.data]), [
|
||||
['post', '/genealogy/pc/feedback', {
|
||||
feedbackType: 'bug',
|
||||
feedbackContent: '页面按钮无响应',
|
||||
contactInfo: '19181970173'
|
||||
}],
|
||||
['get', '/genealogy/pc/feedback', undefined]
|
||||
]);
|
||||
});
|
||||
|
||||
test('VIP methods use PC package and order paths with AppVipOrderBody whitelist', async () => {
|
||||
const calls = [];
|
||||
const client = GenealogyApi.createClient({
|
||||
baseUrl: 'https://api.example.test',
|
||||
tokenStore: { getItem() { return 'access-token'; }, setItem() {}, removeItem() {} },
|
||||
axiosInstance: {
|
||||
request(config) {
|
||||
calls.push(config);
|
||||
return Promise.resolve({ data: { code: 200, data: [] } });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await client.vipPackages();
|
||||
await client.createVipOrder({
|
||||
packageId: '2062179707935264769',
|
||||
genealogyId: '2062179707935264770',
|
||||
payType: 'wechat',
|
||||
appUserId: 'must-drop',
|
||||
payStatus: 'must-drop'
|
||||
});
|
||||
await client.vipOrders();
|
||||
|
||||
assert.deepEqual(calls.map((config) => [config.method, config.url, config.data]), [
|
||||
['get', '/genealogy/pc/vip/packages', undefined],
|
||||
['post', '/genealogy/pc/vip/orders', {
|
||||
packageId: '2062179707935264769',
|
||||
genealogyId: '2062179707935264770',
|
||||
payType: 'wechat'
|
||||
}],
|
||||
['get', '/genealogy/pc/vip/orders', undefined]
|
||||
]);
|
||||
});
|
||||
|
||||
test('notification methods use the current PC list, unread and read paths', async () => {
|
||||
const calls = [];
|
||||
const client = GenealogyApi.createClient({
|
||||
|
||||
Reference in New Issue
Block a user