feat(aihr): finalize practice growth and access boundaries
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import axios from 'axios';
|
||||
import request, { globalHeaders, reconcileTenantContextBlobResponse } from '@/utils/request';
|
||||
import request from '@/utils/request';
|
||||
|
||||
type ApiResult<T> = {
|
||||
code: number;
|
||||
@@ -7,123 +6,6 @@ type ApiResult<T> = {
|
||||
data: T;
|
||||
};
|
||||
|
||||
export type ReviewRecord = {
|
||||
id: number;
|
||||
sessionId: string;
|
||||
time: string;
|
||||
trainee: string;
|
||||
scene: string;
|
||||
score: number;
|
||||
status: string;
|
||||
summary: string;
|
||||
extPartyId: string;
|
||||
};
|
||||
|
||||
export type ReviewDetail = ReviewRecord & {
|
||||
mentorRewrite: string;
|
||||
aiComment: string;
|
||||
reviewAdvice: string;
|
||||
incentivePoint: number | null;
|
||||
scoreItems: Array<{ label: string; value: number | string; note: string }>;
|
||||
dialogue: Array<{ role: string; label: string; text: string; audioUrl?: string; audioOssId?: number | string }>;
|
||||
annotations: Array<{ turnIndex: number; sourceText: string; issue: string; rewrite: string; sopPoint: string }>;
|
||||
};
|
||||
|
||||
export type ReviewAdviceRequest = {
|
||||
reviewer?: string;
|
||||
advice?: string;
|
||||
incentivePoint?: number;
|
||||
nextScenarioId?: string;
|
||||
nextReason?: string;
|
||||
};
|
||||
|
||||
export type CompetencyProfile = {
|
||||
score: number;
|
||||
completed: number;
|
||||
pendingReview: number;
|
||||
trainingMinutes: number;
|
||||
contribution: number;
|
||||
assessmentScore: number;
|
||||
aiLevel: string;
|
||||
dimensions: Array<{ label: string; value: number | string | null; note: string }>;
|
||||
growthPath: Array<{ level: string; progress: number; status: string; evidence: string }>;
|
||||
};
|
||||
|
||||
export type PracticeAlert = {
|
||||
type: string;
|
||||
title: string;
|
||||
desc: string;
|
||||
severity: string;
|
||||
extPartyId: string;
|
||||
};
|
||||
|
||||
export type PracticeAssignment = {
|
||||
id: number;
|
||||
extPartyId: string;
|
||||
traineeName: string;
|
||||
scenarioId: string;
|
||||
scenarioName: string;
|
||||
source: string;
|
||||
reason: string;
|
||||
status: string;
|
||||
createTime: string;
|
||||
question?: string;
|
||||
referenceAnswer?: string;
|
||||
userAnswer?: string;
|
||||
feedback?: string;
|
||||
correct?: boolean;
|
||||
};
|
||||
|
||||
export type PracticeAssignmentRequest = {
|
||||
extPartyId: string;
|
||||
traineeName?: string;
|
||||
scenarioId: string;
|
||||
scenarioName?: string;
|
||||
source: string;
|
||||
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;
|
||||
assessmentScore: number;
|
||||
trainingMinutes: number;
|
||||
contribution: number;
|
||||
completed: number;
|
||||
pendingReview: number;
|
||||
dimensions: Array<{ label: string; value: number | string | null; note: string }>;
|
||||
latestRecords: ReviewRecord[];
|
||||
assignments: PracticeAssignment[];
|
||||
evidenceItems: string[];
|
||||
humanAssessmentScore?: number | null;
|
||||
finalAdoptedScore?: number | null;
|
||||
calibrationCount?: number;
|
||||
};
|
||||
|
||||
export type OrgSyncRequest = {
|
||||
dryRun?: boolean;
|
||||
replaceExisting?: boolean;
|
||||
@@ -188,127 +70,6 @@ export type OrgSnapshotResponse = {
|
||||
rows: OrgPersonRow[];
|
||||
};
|
||||
|
||||
export type ReviewListParams = {
|
||||
projectCode?: string;
|
||||
extPartyId?: string;
|
||||
limit?: number;
|
||||
};
|
||||
|
||||
export function listPracticeReviews(params?: ReviewListParams): Promise<ApiResult<ReviewRecord[]>> {
|
||||
return request({
|
||||
url: '/api/aihr/mobile/practice/reviews',
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
}
|
||||
|
||||
export function getPracticeReview(id: number): Promise<ApiResult<ReviewDetail>> {
|
||||
return request({
|
||||
url: `/api/aihr/mobile/practice/reviews/${id}`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
export async function getPracticeAudioBlobUrl(ossId: number | string): Promise<string> {
|
||||
try {
|
||||
const response = await axios({
|
||||
method: 'get',
|
||||
url: `${import.meta.env.VITE_APP_BASE_API}/api/aihr/mobile/oss/${ossId}`,
|
||||
responseType: 'blob',
|
||||
headers: globalHeaders()
|
||||
});
|
||||
const contentType = String(response.headers['content-type'] || '');
|
||||
if (contentType.includes('application/json') && (await reconcileTenantContextBlobResponse(response.data))) {
|
||||
throw new Error('租户上下文已变更,请刷新后重试');
|
||||
}
|
||||
return URL.createObjectURL(response.data);
|
||||
} catch (error) {
|
||||
await reconcileTenantContextBlobResponse(axios.isAxiosError(error) ? error.response?.data : undefined);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export function markPracticeReviewed(id: number, data: ReviewAdviceRequest): Promise<ApiResult<null>> {
|
||||
return request({
|
||||
url: `/api/aihr/mobile/practice/reviews/${id}/reviewed`,
|
||||
method: 'post',
|
||||
data,
|
||||
headers: { repeatSubmit: false }
|
||||
});
|
||||
}
|
||||
|
||||
export function getCompetencyProfile(extPartyId?: string): Promise<ApiResult<CompetencyProfile>> {
|
||||
return request({
|
||||
url: '/api/aihr/mobile/profile',
|
||||
method: 'get',
|
||||
params: { extPartyId }
|
||||
});
|
||||
}
|
||||
|
||||
export function getPromotionEvidence(extPartyId?: string): Promise<ApiResult<PromotionEvidence>> {
|
||||
return request({
|
||||
url: '/api/aihr/mobile/profile/promotion-evidence',
|
||||
method: 'get',
|
||||
params: { extPartyId }
|
||||
});
|
||||
}
|
||||
|
||||
export function listPracticeAlerts(): Promise<ApiResult<PracticeAlert[]>> {
|
||||
return request({
|
||||
url: '/api/aihr/mobile/practice/alerts',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
export function listPracticeAssignments(extPartyId?: string): Promise<ApiResult<PracticeAssignment[]>> {
|
||||
return request({
|
||||
url: '/api/aihr/mobile/practice/assignments',
|
||||
method: 'get',
|
||||
params: { extPartyId }
|
||||
});
|
||||
}
|
||||
|
||||
export function createPracticeAssignment(data: PracticeAssignmentRequest): Promise<ApiResult<PracticeAssignment>> {
|
||||
return request({
|
||||
url: '/api/aihr/mobile/practice/assignments',
|
||||
method: 'post',
|
||||
data,
|
||||
headers: { repeatSubmit: false }
|
||||
});
|
||||
}
|
||||
|
||||
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',
|
||||
|
||||
@@ -18,6 +18,17 @@ export type PracticeScenarioResponse = {
|
||||
position: string;
|
||||
scenarioType: string;
|
||||
projectType: string;
|
||||
growthLevel: string;
|
||||
competencyCode: string;
|
||||
collaborationPositions: string;
|
||||
reviewStatus: string;
|
||||
riskLevel: '待评估' | '常规' | '高风险';
|
||||
requiresDualReview: boolean;
|
||||
curriculumVersion: string;
|
||||
reviewer?: string;
|
||||
reviewedTime?: string;
|
||||
secondReviewer?: string;
|
||||
secondReviewedTime?: string;
|
||||
difficulty: number;
|
||||
customerPersona: string;
|
||||
openingText: string;
|
||||
@@ -58,6 +69,11 @@ export type PracticeScenarioSaveRequest = {
|
||||
position: string;
|
||||
scenarioType: string;
|
||||
projectType: string;
|
||||
growthLevel: string;
|
||||
competencyCode: string;
|
||||
collaborationPositions: string;
|
||||
riskLevel: '待评估' | '常规' | '高风险';
|
||||
curriculumVersion: string;
|
||||
difficulty: number;
|
||||
customerPersona: string;
|
||||
openingText: string;
|
||||
@@ -73,6 +89,35 @@ export type PracticeScenarioSaveRequest = {
|
||||
rounds: PracticeScenarioRound[];
|
||||
};
|
||||
|
||||
export type PracticeScenarioReviewStatusRequest = {
|
||||
reviewStatus: '草稿' | '待业务审核' | '已发布' | '已下线';
|
||||
};
|
||||
|
||||
export type PracticeScenarioReviewStatusResponse = {
|
||||
reviewStatus: '草稿' | '待业务审核' | '已发布' | '已下线';
|
||||
awaitingSecondReview: boolean;
|
||||
message: string;
|
||||
};
|
||||
|
||||
export type PracticeCurriculumCapability = {
|
||||
position: string;
|
||||
growthLevel: string;
|
||||
code: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type PracticeCurriculumMatrixRow = {
|
||||
position: string;
|
||||
growthLevel: string;
|
||||
competencyCode: string;
|
||||
competencyName: string;
|
||||
publishedCount: number;
|
||||
pendingReviewCount: number;
|
||||
draftCount: number;
|
||||
offlineCount: number;
|
||||
coverageStatus: string;
|
||||
};
|
||||
|
||||
export type PracticeRubricDimension = {
|
||||
code: string;
|
||||
name: string;
|
||||
@@ -289,6 +334,30 @@ export function updatePracticeScenarioEnabled(id: string, data: PracticeScenario
|
||||
});
|
||||
}
|
||||
|
||||
export function updatePracticeScenarioReviewStatus(
|
||||
id: string,
|
||||
data: PracticeScenarioReviewStatusRequest
|
||||
): Promise<ApiResult<PracticeScenarioReviewStatusResponse>> {
|
||||
return request({
|
||||
url: `/api/train/practice/scenarios/${encodeURIComponent(id)}/review-status`,
|
||||
method: 'patch',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
export function listPracticeCurriculumCapabilities(params?: {
|
||||
position?: string;
|
||||
growthLevel?: string;
|
||||
}): Promise<ApiResult<PracticeCurriculumCapability[]>> {
|
||||
return request({ url: '/api/train/practice/curriculum/capabilities', method: 'get', params });
|
||||
}
|
||||
|
||||
export function listPracticeCurriculumMatrix(params?: {
|
||||
position?: string;
|
||||
}): Promise<ApiResult<PracticeCurriculumMatrixRow[]>> {
|
||||
return request({ url: '/api/train/practice/curriculum/matrix', method: 'get', params });
|
||||
}
|
||||
|
||||
export function savePracticeScenario(data: PracticeScenarioSaveRequest): Promise<ApiResult<PracticeScenarioResponse>> {
|
||||
return request({
|
||||
url: '/api/train/practice/scenarios',
|
||||
|
||||
Reference in New Issue
Block a user