feat: restore learning feedback and knowledge capabilities
This commit is contained in:
@@ -83,6 +83,30 @@ export type PracticeAssignmentRequest = {
|
||||
reason: string;
|
||||
};
|
||||
|
||||
export type PracticeDifficulty = {
|
||||
scenarioId: string;
|
||||
scenarioName: string;
|
||||
cause: string;
|
||||
count: number;
|
||||
affectedPeople: number;
|
||||
averageScore: number;
|
||||
latestTime: string;
|
||||
position?: string;
|
||||
assignmentStatus?: string;
|
||||
attempts?: number;
|
||||
mistakeCount?: number;
|
||||
difficultyScore?: number;
|
||||
};
|
||||
|
||||
export type PracticeAssignmentBatchResponse = {
|
||||
requestId: string;
|
||||
total: number;
|
||||
created: number;
|
||||
duplicate: number;
|
||||
failed: number;
|
||||
results: Array<{ extPartyId: string; traineeName: string; status: 'CREATED' | 'DUPLICATE' | 'FAILED'; message: string }>;
|
||||
};
|
||||
|
||||
export type PromotionEvidence = {
|
||||
extPartyId: string;
|
||||
currentLevel: string;
|
||||
@@ -244,6 +268,38 @@ export function createPracticeAssignment(data: PracticeAssignmentRequest): Promi
|
||||
});
|
||||
}
|
||||
|
||||
export function listPracticeDifficulties(params?: { windowDays?: number; position?: string }): Promise<ApiResult<PracticeDifficulty[]>> {
|
||||
type RawDifficulty = Omit<PracticeDifficulty, 'cause' | 'count'> & { attempts: number; mistakeCount: number };
|
||||
return request<unknown, ApiResult<RawDifficulty[]>>({
|
||||
url: '/api/aihr/mobile/practice/difficulties',
|
||||
method: 'get',
|
||||
params
|
||||
}).then((result) => ({
|
||||
...result,
|
||||
data: (result.data || []).map((item) => ({
|
||||
...item,
|
||||
cause: item.mistakeCount >= Math.max(2, Math.ceil(item.attempts / 2)) ? '高频错题或低分' : '近期低分记录',
|
||||
count: item.mistakeCount
|
||||
}))
|
||||
}));
|
||||
}
|
||||
|
||||
export function assignPracticeDifficulty(data: {
|
||||
scenarioId: string;
|
||||
cause: string;
|
||||
dueDate: string;
|
||||
requestId: string;
|
||||
windowDays: number;
|
||||
position?: string;
|
||||
}): Promise<ApiResult<PracticeAssignmentBatchResponse>> {
|
||||
return request({
|
||||
url: '/api/aihr/mobile/practice/difficulties/assign',
|
||||
method: 'post',
|
||||
data,
|
||||
headers: { repeatSubmit: false }
|
||||
});
|
||||
}
|
||||
|
||||
export function syncOrgSnapshot(data: OrgSyncRequest): Promise<ApiResult<OrgSyncResponse>> {
|
||||
return request({
|
||||
url: '/api/aihr/org/sync',
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
import request from '@/utils/request';
|
||||
|
||||
type ApiResult<T> = { code: number; msg: string; data: T };
|
||||
|
||||
export type AdminQuestionAnswer = {
|
||||
id: number;
|
||||
questionId: number;
|
||||
content: string;
|
||||
authorName: string;
|
||||
authorPosition?: string;
|
||||
status: string;
|
||||
best: boolean;
|
||||
recommended?: boolean;
|
||||
createTime: string;
|
||||
};
|
||||
|
||||
export type AdminQuestion = {
|
||||
id: number;
|
||||
type: 'HELP' | 'BOUNTY' | string;
|
||||
title: string;
|
||||
content: string;
|
||||
category: string;
|
||||
authorName: string;
|
||||
status: string;
|
||||
rewardPoints: number;
|
||||
rewardAccountType: 'POINTS' | 'STUDY_CREDIT' | string;
|
||||
answerCount: number;
|
||||
deadline?: string | null;
|
||||
createTime: string;
|
||||
bestAnswerId?: number | null;
|
||||
answers?: AdminQuestionAnswer[];
|
||||
};
|
||||
|
||||
export type IncentiveRule = {
|
||||
id?: number;
|
||||
ruleCode: string;
|
||||
displayName: string;
|
||||
accountType: 'POINTS' | 'STUDY_CREDIT';
|
||||
value: number;
|
||||
dailyCap?: number | null;
|
||||
enabled: boolean;
|
||||
};
|
||||
|
||||
type RawAdminQuestionAnswer = Omit<AdminQuestionAnswer, 'createTime'> & { createdAt: string };
|
||||
type RawAdminQuestion = Omit<AdminQuestion, 'createTime' | 'answers'> & {
|
||||
createdAt: string;
|
||||
answers?: RawAdminQuestionAnswer[];
|
||||
};
|
||||
|
||||
const normalizeAnswer = (item: RawAdminQuestionAnswer): AdminQuestionAnswer => ({
|
||||
...item,
|
||||
createTime: item.createdAt
|
||||
});
|
||||
|
||||
const normalizeQuestion = (item: RawAdminQuestion): AdminQuestion => ({
|
||||
...item,
|
||||
createTime: item.createdAt,
|
||||
answers: (item.answers || []).map(normalizeAnswer)
|
||||
});
|
||||
|
||||
export const listAdminQuestions = (status?: string): Promise<ApiResult<AdminQuestion[]>> =>
|
||||
request<unknown, ApiResult<RawAdminQuestion[]>>({
|
||||
url: '/api/aihr/question-admin/questions', method: 'get', params: { status }
|
||||
}).then((result) => ({ ...result, data: (result.data || []).map(normalizeQuestion) }));
|
||||
|
||||
export const createBountyQuestion = (data: { title: string; content: string; category: string; rewardPoints: number; deadline: string }): Promise<ApiResult<AdminQuestion>> =>
|
||||
request<unknown, ApiResult<RawAdminQuestion>>({
|
||||
url: '/api/aihr/question-admin/questions', method: 'post', data: { ...data, ruleCode: 'BEST_ANSWER' }, headers: { repeatSubmit: false }
|
||||
}).then((result) => ({ ...result, data: normalizeQuestion(result.data) }));
|
||||
|
||||
export const reviewQuestion = (id: number, decision: 'APPROVE' | 'REJECT', note = ''): Promise<ApiResult<AdminQuestion>> =>
|
||||
request<unknown, ApiResult<RawAdminQuestion>>({
|
||||
url: `/api/aihr/question-admin/questions/${id}/review`, method: 'post', data: { decision, note }, headers: { repeatSubmit: false }
|
||||
}).then((result) => ({ ...result, data: normalizeQuestion(result.data) }));
|
||||
|
||||
export const reviewQuestionAnswer = (id: number, decision: 'APPROVE' | 'REJECT', note = ''): Promise<ApiResult<AdminQuestionAnswer>> =>
|
||||
request<unknown, ApiResult<RawAdminQuestionAnswer>>({
|
||||
url: `/api/aihr/question-admin/answers/${id}/review`, method: 'post', data: { decision, note }, headers: { repeatSubmit: false }
|
||||
}).then((result) => ({ ...result, data: normalizeAnswer(result.data) }));
|
||||
|
||||
export const selectBestAnswer = (questionId: number, answerId: number): Promise<ApiResult<unknown>> => request({
|
||||
url: `/api/aihr/question-admin/questions/${questionId}/best-answer`, method: 'post', data: { answerId }, headers: { repeatSubmit: false }
|
||||
});
|
||||
|
||||
export const listIncentiveRules = (): Promise<ApiResult<IncentiveRule[]>> => request({
|
||||
url: '/api/aihr/incentive-admin/rules', method: 'get'
|
||||
});
|
||||
|
||||
export const saveIncentiveRule = (data: IncentiveRule): Promise<ApiResult<IncentiveRule>> => request({
|
||||
url: '/api/aihr/incentive-admin/rules',
|
||||
method: data.id ? 'put' : 'post', data, headers: { repeatSubmit: false }
|
||||
});
|
||||
@@ -16,6 +16,9 @@ export type SopDoc = {
|
||||
export type SopSnippet = {
|
||||
title: string;
|
||||
text: string;
|
||||
fragmentId?: number | null;
|
||||
spaceCode?: string | null;
|
||||
sourceType?: string;
|
||||
};
|
||||
|
||||
export type SopRecord = {
|
||||
@@ -29,6 +32,7 @@ export type SopRecord = {
|
||||
};
|
||||
|
||||
export type SopSearchResponse = {
|
||||
requestId?: string;
|
||||
queryText: string;
|
||||
category: string;
|
||||
answer: string;
|
||||
@@ -41,6 +45,31 @@ export type SopSearchResponse = {
|
||||
cautions?: string[];
|
||||
promptVersion?: string | null;
|
||||
reviewId?: number;
|
||||
citations?: SopCitation[];
|
||||
usedSpaceCodes?: string[];
|
||||
noEvidence?: boolean;
|
||||
data?: unknown;
|
||||
};
|
||||
|
||||
export type SopCitation = {
|
||||
spaceCode?: string | null;
|
||||
sourceType: string;
|
||||
docId?: string | null;
|
||||
title: string;
|
||||
snippet: string;
|
||||
fragmentId?: number | null;
|
||||
};
|
||||
|
||||
type UnifiedQueryResponse = {
|
||||
requestId: string;
|
||||
queryText: string;
|
||||
answer: string;
|
||||
citations: SopCitation[];
|
||||
usedSpaceCodes: string[];
|
||||
noEvidence: boolean;
|
||||
promptVersion?: string | null;
|
||||
legacy?: SopSearchResponse | null;
|
||||
data?: unknown;
|
||||
};
|
||||
|
||||
export type SopReviewResponse = {
|
||||
@@ -92,12 +121,46 @@ export function searchKnowledgeSop(data: {
|
||||
position?: string;
|
||||
source?: string;
|
||||
limit: number;
|
||||
spaceCodes?: string[];
|
||||
toolCode?: 'MY_PRACTICE_SUMMARY' | 'TEAM_PRACTICE_SUMMARY';
|
||||
}): Promise<ApiResult<SopSearchResponse>> {
|
||||
return request({
|
||||
url: '/api/knowledge/search',
|
||||
return request<unknown, ApiResult<UnifiedQueryResponse>>({
|
||||
url: '/api/knowledge/query',
|
||||
method: 'post',
|
||||
data,
|
||||
headers: { repeatSubmit: false }
|
||||
}).then((result) => {
|
||||
const response = result.data;
|
||||
const legacy = response.legacy || ({} as SopSearchResponse);
|
||||
const snippets = legacy.snippets?.length
|
||||
? legacy.snippets
|
||||
: (response.citations || []).map((item) => ({
|
||||
title: item.title,
|
||||
text: item.snippet,
|
||||
fragmentId: item.fragmentId,
|
||||
spaceCode: item.spaceCode,
|
||||
sourceType: item.sourceType
|
||||
}));
|
||||
return {
|
||||
...result,
|
||||
data: {
|
||||
...legacy,
|
||||
requestId: response.requestId,
|
||||
queryText: response.queryText,
|
||||
category: legacy.category || data.category || 'sop',
|
||||
answer: response.answer,
|
||||
reference: legacy.reference || response.citations.map((item) => item.title).join('、'),
|
||||
docs: legacy.docs || [],
|
||||
snippets,
|
||||
training: legacy.training || [],
|
||||
records: legacy.records || [],
|
||||
citations: response.citations || [],
|
||||
usedSpaceCodes: response.usedSpaceCodes || [],
|
||||
noEvidence: response.noEvidence,
|
||||
promptVersion: response.promptVersion,
|
||||
data: response.data
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -149,6 +212,7 @@ export type UploadItem = {
|
||||
batchId: string;
|
||||
fileName: string;
|
||||
category: string;
|
||||
spaceCodes: string[];
|
||||
status: number;
|
||||
statusLabel: string;
|
||||
error: string | null;
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
import request from '@/utils/request';
|
||||
|
||||
type ApiResult<T> = { code: number; msg: string; data: T };
|
||||
|
||||
export type KnowledgeSpace = {
|
||||
id: number;
|
||||
code: string;
|
||||
name: string;
|
||||
description: string;
|
||||
spaceType: 'PUBLIC' | 'BUSINESS' | 'MANAGEMENT' | 'EXTERNAL';
|
||||
sensitivityLevel: 'PUBLIC' | 'INTERNAL' | 'CONFIDENTIAL';
|
||||
status: 'DRAFT' | 'ACTIVE' | 'DISABLED';
|
||||
documentCount: number;
|
||||
fragmentCount: number;
|
||||
};
|
||||
|
||||
export type SpaceForm = Pick<KnowledgeSpace, 'code' | 'name' | 'description' | 'spaceType' | 'sensitivityLevel' | 'status'>;
|
||||
|
||||
export type SpaceGrant = {
|
||||
principalType: 'ROLE' | 'USER';
|
||||
principalValue: string;
|
||||
permission: 'READ' | 'MANAGE';
|
||||
status: 'ACTIVE' | 'DISABLED';
|
||||
};
|
||||
|
||||
export type KnowledgeDocument = {
|
||||
attachId: number;
|
||||
docId: string;
|
||||
name: string;
|
||||
type: string;
|
||||
status: string;
|
||||
ossId?: number | null;
|
||||
fragmentCount: number;
|
||||
updateTime?: string | null;
|
||||
};
|
||||
|
||||
export type UnbindDocumentResult = {
|
||||
attachId: number;
|
||||
spaceCode: string;
|
||||
ossId?: number | null;
|
||||
ossDeleted: boolean;
|
||||
};
|
||||
|
||||
export type KnowledgeApp = {
|
||||
id: number;
|
||||
appCode: string;
|
||||
appName: string;
|
||||
authType: 'SESSION' | 'API_TOKEN';
|
||||
internalClientKey?: string | null;
|
||||
status: 'ACTIVE' | 'DISABLED';
|
||||
rateLimitPerMinute: number;
|
||||
expiresTime?: string | null;
|
||||
lastUsedTime?: string | null;
|
||||
spaceCodes: string[];
|
||||
};
|
||||
|
||||
export type AppForm = {
|
||||
appCode: string;
|
||||
appName: string;
|
||||
authType: 'SESSION' | 'API_TOKEN';
|
||||
internalClientKey?: string | null;
|
||||
status: 'ACTIVE' | 'DISABLED';
|
||||
rateLimitPerMinute: number;
|
||||
expiresTime?: string | null;
|
||||
spaceCodes: string[];
|
||||
};
|
||||
|
||||
export type IssuedToken = { appCode: string; token: string; expiresTime?: string | null };
|
||||
export type CreatedApp = { app: KnowledgeApp; token?: string | null };
|
||||
|
||||
export const listKnowledgeSpaces = (): Promise<ApiResult<KnowledgeSpace[]>> =>
|
||||
request({ url: '/api/knowledge/admin/spaces', method: 'get' });
|
||||
|
||||
export const createKnowledgeSpace = (data: SpaceForm): Promise<ApiResult<KnowledgeSpace>> =>
|
||||
request({ url: '/api/knowledge/admin/spaces', method: 'post', data });
|
||||
|
||||
export const updateKnowledgeSpace = (id: number, data: SpaceForm): Promise<ApiResult<KnowledgeSpace>> =>
|
||||
request({ url: `/api/knowledge/admin/spaces/${id}`, method: 'put', data });
|
||||
|
||||
export const listSpaceDocuments = (id: number): Promise<ApiResult<KnowledgeDocument[]>> =>
|
||||
request({ url: `/api/knowledge/admin/spaces/${id}/documents`, method: 'get' });
|
||||
|
||||
export const unbindSpaceDocument = (id: number, attachId: number): Promise<ApiResult<UnbindDocumentResult>> =>
|
||||
request({ url: `/api/knowledge/admin/spaces/${id}/documents/${attachId}`, method: 'delete' });
|
||||
|
||||
export const listSpaceGrants = (id: number): Promise<ApiResult<SpaceGrant[]>> =>
|
||||
request({ url: `/api/knowledge/admin/spaces/${id}/grants`, method: 'get' });
|
||||
|
||||
export const replaceSpaceGrants = (id: number, grants: SpaceGrant[]): Promise<ApiResult<SpaceGrant[]>> =>
|
||||
request({ url: `/api/knowledge/admin/spaces/${id}/grants`, method: 'put', data: { grants } });
|
||||
|
||||
export const listKnowledgeApps = (): Promise<ApiResult<KnowledgeApp[]>> =>
|
||||
request({ url: '/api/knowledge/admin/apps', method: 'get' });
|
||||
|
||||
export const createKnowledgeApp = (data: AppForm): Promise<ApiResult<CreatedApp>> =>
|
||||
request({ url: '/api/knowledge/admin/apps', method: 'post', data });
|
||||
|
||||
export const updateKnowledgeApp = (
|
||||
id: number,
|
||||
data: Pick<AppForm, 'appName' | 'status' | 'rateLimitPerMinute' | 'expiresTime'>
|
||||
): Promise<ApiResult<KnowledgeApp>> => request({ url: `/api/knowledge/admin/apps/${id}`, method: 'put', data });
|
||||
|
||||
export const replaceKnowledgeAppSpaces = (id: number, spaceCodes: string[]): Promise<ApiResult<KnowledgeApp>> =>
|
||||
request({ url: `/api/knowledge/admin/apps/${id}/spaces`, method: 'put', data: { spaceCodes } });
|
||||
|
||||
export const rotateKnowledgeAppToken = (id: number, expiresTime?: string | null): Promise<ApiResult<IssuedToken>> =>
|
||||
request({ url: `/api/knowledge/admin/apps/${id}/rotate-token`, method: 'post', data: { expiresTime } });
|
||||
@@ -0,0 +1,47 @@
|
||||
import request from '@/utils/request';
|
||||
|
||||
type ApiResult<T> = { code: number; msg: string; data: T };
|
||||
|
||||
export interface WebSearchProvider {
|
||||
id: number;
|
||||
name: string;
|
||||
providerCode: 'tavily' | string;
|
||||
endpoint: string;
|
||||
configured: boolean;
|
||||
enabled: boolean;
|
||||
lastTestOk: boolean;
|
||||
lastTestTime?: string | null;
|
||||
}
|
||||
|
||||
export interface WebSearchProviderForm {
|
||||
id?: number;
|
||||
name: string;
|
||||
providerCode: 'tavily';
|
||||
endpoint: string;
|
||||
apiKey: string;
|
||||
}
|
||||
|
||||
export const listWebSearchProviders = (): Promise<ApiResult<WebSearchProvider[]>> => request({
|
||||
url: '/api/aihr/web-search/providers',
|
||||
method: 'get'
|
||||
});
|
||||
|
||||
export const saveWebSearchProvider = (data: WebSearchProviderForm): Promise<ApiResult<WebSearchProvider>> => request({
|
||||
url: '/api/aihr/web-search/providers',
|
||||
method: data.id ? 'put' : 'post',
|
||||
data,
|
||||
headers: { repeatSubmit: false }
|
||||
});
|
||||
|
||||
export const toggleWebSearchProvider = (id: number, enabled: boolean): Promise<ApiResult<WebSearchProvider>> => request({
|
||||
url: `/api/aihr/web-search/providers/${id}/enabled`,
|
||||
method: 'patch',
|
||||
data: { enabled },
|
||||
headers: { repeatSubmit: false }
|
||||
});
|
||||
|
||||
export const testWebSearchProvider = (id: number): Promise<ApiResult<{ ok: boolean; sourceCount: number; message: string }>> => request({
|
||||
url: `/api/aihr/web-search/providers/${id}/test`,
|
||||
method: 'post',
|
||||
headers: { repeatSubmit: false }
|
||||
});
|
||||
Reference in New Issue
Block a user