Files
prop-ai-hr/frontend/src/api/aihr/questions.ts
T
admin 4e7c1a2e80 feat(frontend): promote best answers to experience candidates
- questions admin: 转为经验候选 action on published best answers,
  moderator-gated, confirm dialog, idempotent feedback
2026-08-05 02:46:07 +08:00

99 lines
3.9 KiB
TypeScript

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 type PromoteCaseCandidateResult = { caseId: number; created: boolean };
export const promoteAnswerToCaseCandidate = (answerId: number): Promise<ApiResult<PromoteCaseCandidateResult>> => request({
url: `/api/aihr/question-admin/answers/${answerId}/to-case-candidate`, method: 'post', 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 }
});