forked from wangziqi/gongxue-base
1107 lines
31 KiB
TypeScript
1107 lines
31 KiB
TypeScript
import { apiRequest } from './api';
|
|
|
|
export interface TenantDashboard {
|
|
scope?: Record<string, unknown>;
|
|
cards?: {
|
|
students?: Record<string, number>;
|
|
learning?: Record<string, number>;
|
|
content?: Record<string, number>;
|
|
activationCodes?: Record<string, number>;
|
|
feedback?: Record<string, number>;
|
|
};
|
|
paymentStats?: Record<string, number>;
|
|
trends?: Record<string, unknown>[];
|
|
activeHours?: Record<string, unknown>[];
|
|
questionDistribution?: Record<string, unknown>[];
|
|
subjectTop?: Record<string, unknown>[];
|
|
regionStats?: Record<string, unknown>[];
|
|
planSales?: Record<string, unknown>[];
|
|
recentActivities?: Record<string, unknown>[];
|
|
}
|
|
|
|
export interface TenantClassItem {
|
|
id: string;
|
|
name: string;
|
|
code?: string | null;
|
|
status?: string;
|
|
regionName?: string | null;
|
|
studentCount?: number;
|
|
teacherCount?: number;
|
|
}
|
|
|
|
export interface TenantStudentItem {
|
|
membershipId?: string;
|
|
userId: string;
|
|
username?: string | null;
|
|
name?: string | null;
|
|
phone?: string | null;
|
|
email?: string | null;
|
|
status?: string;
|
|
profileId?: string;
|
|
regionId?: string | null;
|
|
regionName?: string | null;
|
|
selectedSchoolId?: string | null;
|
|
selectedSchoolName?: string | null;
|
|
selectedMajorId?: string | null;
|
|
selectedMajorName?: string | null;
|
|
questionsAnsweredToday?: number;
|
|
masteredWordsCount?: number;
|
|
lastSeenAt?: string | null;
|
|
classes?: Record<string, unknown>[];
|
|
}
|
|
|
|
export interface TenantStudentInput {
|
|
userId?: string;
|
|
username?: string | null;
|
|
name?: string | null;
|
|
phone?: string | null;
|
|
email?: string | null;
|
|
regionId?: string | null;
|
|
selectedSchoolId?: string | null;
|
|
selectedMajorId?: string | null;
|
|
status?: string;
|
|
stats?: Record<string, unknown>;
|
|
progress?: Record<string, unknown>;
|
|
moduleSelections?: Record<string, unknown>;
|
|
}
|
|
|
|
export interface TenantTeacherItem {
|
|
membershipId?: string;
|
|
userId: string;
|
|
username?: string | null;
|
|
name?: string | null;
|
|
phone?: string | null;
|
|
email?: string | null;
|
|
status?: string;
|
|
classCount?: number;
|
|
}
|
|
|
|
export interface BulkOperationResult {
|
|
total?: number;
|
|
successCount?: number;
|
|
errorCount?: number;
|
|
items?: Record<string, unknown>[];
|
|
errors?: Array<{
|
|
index?: number;
|
|
code?: string;
|
|
message?: string;
|
|
}>;
|
|
}
|
|
|
|
export interface TenantStudentNoteItem {
|
|
id: string;
|
|
studentUserId?: string;
|
|
noteType?: string;
|
|
content?: string;
|
|
visibility?: string;
|
|
isPinned?: boolean;
|
|
metadata?: Record<string, unknown>;
|
|
createdByName?: string | null;
|
|
createdByUsername?: string | null;
|
|
createdAt?: string;
|
|
updatedAt?: string;
|
|
}
|
|
|
|
export interface TenantStudentFollowupItem {
|
|
id: string;
|
|
studentUserId?: string;
|
|
studentName?: string | null;
|
|
studentPhone?: string | null;
|
|
assignedToUserId?: string | null;
|
|
assignedToName?: string | null;
|
|
classId?: string | null;
|
|
className?: string | null;
|
|
title?: string;
|
|
description?: string | null;
|
|
followupType?: string;
|
|
priority?: string;
|
|
status?: string;
|
|
dueAt?: string | null;
|
|
completedAt?: string | null;
|
|
completedBy?: string | null;
|
|
metadata?: Record<string, unknown>;
|
|
createdAt?: string;
|
|
updatedAt?: string;
|
|
}
|
|
|
|
export interface ImportJobItem {
|
|
id: string;
|
|
importType?: string;
|
|
sourceFormat?: string;
|
|
status?: string;
|
|
sourceName?: string | null;
|
|
totalCount?: number;
|
|
validCount?: number;
|
|
successCount?: number;
|
|
errorCount?: number;
|
|
warningCount?: number;
|
|
insertedCount?: number;
|
|
updatedCount?: number;
|
|
skippedCount?: number;
|
|
executionMode?: string;
|
|
queuedAt?: string | null;
|
|
lockedAt?: string | null;
|
|
lockedBy?: string | null;
|
|
attemptCount?: number;
|
|
maxAttempts?: number;
|
|
nextAttemptAt?: string | null;
|
|
errorMessage?: string | null;
|
|
createdAt?: string;
|
|
updatedAt?: string;
|
|
finishedAt?: string | null;
|
|
summary?: Record<string, unknown>;
|
|
}
|
|
|
|
export interface ImportJobDetail {
|
|
item?: ImportJobItem;
|
|
issueSummary?: Record<string, number>;
|
|
itemStatusSummary?: Record<string, number>;
|
|
recentIssues?: ImportIssueItem[];
|
|
importPostCheck?: Record<string, unknown> | null;
|
|
worker?: {
|
|
executionMode?: string;
|
|
queuedAt?: string | null;
|
|
lockedAt?: string | null;
|
|
lockedBy?: string | null;
|
|
attemptCount?: number;
|
|
maxAttempts?: number;
|
|
nextAttemptAt?: string | null;
|
|
lastWorkerError?: Record<string, unknown>;
|
|
};
|
|
}
|
|
|
|
export interface ContentEntryAdminItem {
|
|
id: string;
|
|
name: string;
|
|
entryType?: string;
|
|
visibility?: string;
|
|
isActive?: boolean;
|
|
regionName?: string | null;
|
|
}
|
|
|
|
export interface PublicQuestionBankItem {
|
|
grantId?: string;
|
|
sourceQuestionBankId?: string;
|
|
sourceQuestionBankName?: string;
|
|
sourceRegionName?: string | null;
|
|
name?: string;
|
|
regionName?: string | null;
|
|
grantScope?: string;
|
|
questionCount?: number;
|
|
adoptedId?: string | null;
|
|
adoptionStatus?: string | null;
|
|
syncStatus?: string | null;
|
|
targetEntryId?: string | null;
|
|
targetCollectionId?: string | null;
|
|
copiedQuestionCount?: number;
|
|
lastSyncedAt?: string | null;
|
|
adoption?: Record<string, unknown> | null;
|
|
}
|
|
|
|
export interface PublicQuestionBankConflictItem {
|
|
sourceQuestionId?: string;
|
|
targetQuestionId?: string | null;
|
|
action?: string;
|
|
sourceHash?: string;
|
|
previousSourceHash?: string | null;
|
|
targetHash?: string | null;
|
|
}
|
|
|
|
export interface PublicQuestionBankConflictsResult {
|
|
adoptionId?: string;
|
|
syncStatus?: string;
|
|
status?: string;
|
|
lastSyncedAt?: string | null;
|
|
updatedAt?: string;
|
|
conflictCount?: number;
|
|
counts?: Record<string, unknown>;
|
|
conflicts?: PublicQuestionBankConflictItem[];
|
|
}
|
|
|
|
export interface TenantContentNotificationItem {
|
|
id: string;
|
|
notificationType?: string;
|
|
status?: 'unread' | 'read' | 'dismissed' | 'resolved';
|
|
severity?: 'info' | 'success' | 'warning' | 'error';
|
|
adoptionId?: string | null;
|
|
sourceQuestionBankId?: string | null;
|
|
title?: string;
|
|
message?: string;
|
|
actionLabel?: string | null;
|
|
actionPath?: string | null;
|
|
metadata?: Record<string, unknown>;
|
|
createdAt?: string;
|
|
updatedAt?: string;
|
|
readAt?: string | null;
|
|
resolvedAt?: string | null;
|
|
}
|
|
|
|
export interface ImportIssueItem {
|
|
id: string;
|
|
rowNo?: number | null;
|
|
severity?: string;
|
|
code?: string;
|
|
fieldPath?: string | null;
|
|
message?: string;
|
|
externalId?: string | null;
|
|
itemStatus?: string | null;
|
|
createdAt?: string;
|
|
}
|
|
|
|
export interface ImportFieldMapping {
|
|
importType?: string;
|
|
title?: string;
|
|
description?: string;
|
|
fields?: Array<{
|
|
field?: string;
|
|
label?: string;
|
|
required?: boolean;
|
|
aliases?: string[];
|
|
description?: string;
|
|
example?: unknown;
|
|
}>;
|
|
requiredFields?: string[];
|
|
}
|
|
|
|
export interface ImportTemplateItem {
|
|
importType?: string;
|
|
format?: string;
|
|
fileName?: string;
|
|
mimeType?: string;
|
|
contentBase64?: string;
|
|
contentPreview?: string;
|
|
fields?: Record<string, unknown>[];
|
|
}
|
|
|
|
export interface MediaAnalyticsSummary {
|
|
scope?: Record<string, unknown>;
|
|
assetAccess?: {
|
|
totalEvents?: number;
|
|
grantedEvents?: number;
|
|
deniedEvents?: number;
|
|
downloads?: number;
|
|
previews?: number;
|
|
uploadSigns?: number;
|
|
uploadConfirms?: number;
|
|
uniqueUsers?: number;
|
|
watermarkEvents?: number;
|
|
};
|
|
videoPlay?: {
|
|
totalEvents?: number;
|
|
issuedEvents?: number;
|
|
startedEvents?: number;
|
|
completedEvents?: number;
|
|
freePlays?: number;
|
|
svipPlays?: number;
|
|
quotaPlays?: number;
|
|
consumedQuota?: number;
|
|
watchedSeconds?: number;
|
|
avgCompletionRate?: number;
|
|
uniqueUsers?: number;
|
|
watermarkEvents?: number;
|
|
};
|
|
assetTop?: Record<string, unknown>[];
|
|
videoTop?: Record<string, unknown>[];
|
|
daily?: Record<string, number | string>[];
|
|
traceMatches?: { assets?: number; videos?: number } | null;
|
|
}
|
|
|
|
export interface MediaAssetAccessEvent {
|
|
id: string;
|
|
assetId?: string | null;
|
|
assetTitle?: string | null;
|
|
assetType?: string | null;
|
|
userId?: string | null;
|
|
userLabel?: string | null;
|
|
actorRole?: string;
|
|
accessType?: string;
|
|
visibility?: string | null;
|
|
storageProvider?: string | null;
|
|
disposition?: string | null;
|
|
expiresInSec?: number | null;
|
|
signatureMode?: string | null;
|
|
result?: string;
|
|
denyCode?: string | null;
|
|
ipAddress?: string | null;
|
|
userAgent?: string | null;
|
|
watermarkTraceId?: string | null;
|
|
watermark?: Record<string, unknown> | null;
|
|
createdAt?: string;
|
|
}
|
|
|
|
export interface MediaVideoPlayEvent {
|
|
id: string;
|
|
videoId?: string;
|
|
videoTitle?: string | null;
|
|
assetId?: string | null;
|
|
questionId?: string | null;
|
|
userId?: string;
|
|
userLabel?: string | null;
|
|
status?: string;
|
|
accessMode?: string;
|
|
consumedQuota?: number;
|
|
playback?: Record<string, unknown> | null;
|
|
signedUrlExpiresAt?: string | null;
|
|
ipAddress?: string | null;
|
|
userAgent?: string | null;
|
|
signatureMode?: string | null;
|
|
provider?: string | null;
|
|
watermarkTraceId?: string | null;
|
|
watermark?: Record<string, unknown> | null;
|
|
createdAt?: string;
|
|
updatedAt?: string;
|
|
}
|
|
|
|
export interface ImportPreviewResult {
|
|
job?: {
|
|
id: string;
|
|
status?: string;
|
|
totalCount?: number;
|
|
validCount?: number;
|
|
errorCount?: number;
|
|
warningCount?: number;
|
|
};
|
|
items?: Array<{
|
|
rowNo?: number;
|
|
status?: string;
|
|
externalId?: string | null;
|
|
normalized?: Record<string, unknown> | null;
|
|
issues?: ImportIssueItem[];
|
|
}>;
|
|
issues?: ImportIssueItem[];
|
|
}
|
|
|
|
export type ImportType = 'questions' | 'vocabulary' | 'handbook' | 'scoreline' | 'videos';
|
|
export type ImportSourceFormat = 'json' | 'csv' | 'excel';
|
|
|
|
export interface ImportRequestBody {
|
|
sourceFormat?: ImportSourceFormat;
|
|
sourceName?: string;
|
|
payload?: unknown;
|
|
items?: unknown[];
|
|
csvText?: string;
|
|
fileBase64?: string;
|
|
allowPartial?: boolean;
|
|
executionMode?: 'sync' | 'async';
|
|
previewJobId?: string;
|
|
jobId?: string;
|
|
regionId?: string;
|
|
entryId?: string;
|
|
contentNodeId?: string;
|
|
collectionId?: string;
|
|
fieldMappingOverrides?: Record<string, string[]>;
|
|
fieldMappingPreset?: ImportFieldMapping | null;
|
|
}
|
|
|
|
export interface CouponItem {
|
|
id: string;
|
|
code: string;
|
|
status?: string;
|
|
discountType?: string | null;
|
|
discountValue?: number | string | null;
|
|
usedCount?: number;
|
|
maxUses?: number | null;
|
|
}
|
|
|
|
export interface CodeBatchItem {
|
|
id: string;
|
|
name?: string | null;
|
|
batchNo?: string | null;
|
|
totalCount?: number;
|
|
usedCount?: number;
|
|
status?: string;
|
|
}
|
|
|
|
export interface CrmConfigItem {
|
|
id?: string;
|
|
enabled?: boolean;
|
|
url?: string | null;
|
|
secretRef?: string | null;
|
|
formName?: string | null;
|
|
examType?: string | null;
|
|
timeoutSec?: number;
|
|
delaySec?: number;
|
|
assignmentMode?: 'none' | 'direct' | 'round_robin' | 'referrer';
|
|
assignmentPool?: string[];
|
|
assignmentCursor?: number;
|
|
assignmentConfig?: Record<string, unknown>;
|
|
updatedAt?: string;
|
|
}
|
|
|
|
export interface CrmQueueItem {
|
|
id: string;
|
|
recordId?: string | null;
|
|
status?: string;
|
|
scheduledAt?: string | null;
|
|
attempts?: number;
|
|
nextAttemptAt?: string | null;
|
|
lastError?: string | null;
|
|
lastHttpCode?: number | null;
|
|
leadId?: string | null;
|
|
sentAt?: string | null;
|
|
source?: string | null;
|
|
targetUrl?: string | null;
|
|
payload?: Record<string, unknown>;
|
|
createdAt?: string;
|
|
}
|
|
|
|
export interface CommissionSettingsItem {
|
|
defaultRate?: number | string;
|
|
minSettlementCents?: number;
|
|
settlementCycle?: string;
|
|
config?: Record<string, unknown>;
|
|
updatedAt?: string;
|
|
}
|
|
|
|
export interface CommissionSummaryItem {
|
|
tenantId?: string;
|
|
startDate?: string;
|
|
endDate?: string;
|
|
referrerUserId?: string | null;
|
|
defaultRate?: number | string;
|
|
minSettlementCents?: number;
|
|
sourceCount?: number;
|
|
paidUserCount?: number;
|
|
grossAmountCents?: number;
|
|
commissionAmountCents?: number;
|
|
byReferrer?: Record<string, unknown>[];
|
|
}
|
|
|
|
export interface CommissionOrderItem {
|
|
sourceType?: string;
|
|
sourceId?: string;
|
|
sourceNo?: string | null;
|
|
referrerUserId?: string;
|
|
referrerName?: string | null;
|
|
referrerPhone?: string | null;
|
|
studentUserId?: string | null;
|
|
studentName?: string | null;
|
|
grossAmountCents?: number;
|
|
commissionRate?: number | string;
|
|
commissionAmountCents?: number;
|
|
rateSource?: string;
|
|
settlementId?: string | null;
|
|
sourcePaidAt?: string | null;
|
|
}
|
|
|
|
export interface CommissionSettlementItem {
|
|
id: string;
|
|
settlementNo?: string;
|
|
referrerUserId?: string;
|
|
referrerName?: string | null;
|
|
referrerPhone?: string | null;
|
|
status?: string;
|
|
periodStart?: string;
|
|
periodEnd?: string;
|
|
sourceCount?: number;
|
|
paidUserCount?: number;
|
|
grossAmountCents?: number;
|
|
commissionAmountCents?: number;
|
|
defaultRate?: number | string;
|
|
effectiveRate?: number | string;
|
|
reviewedBy?: string | null;
|
|
reviewedAt?: string | null;
|
|
paidBy?: string | null;
|
|
paidAt?: string | null;
|
|
paymentMethod?: string | null;
|
|
paymentAccount?: string | null;
|
|
remark?: string | null;
|
|
metadata?: Record<string, unknown>;
|
|
createdAt?: string;
|
|
updatedAt?: string;
|
|
}
|
|
|
|
export interface TenantOverview {
|
|
id?: string;
|
|
name?: string;
|
|
slug?: string;
|
|
status?: string;
|
|
brandName?: string;
|
|
shortName?: string;
|
|
slogan?: string | null;
|
|
logoUrl?: string | null;
|
|
theme?: Record<string, unknown>;
|
|
featureFlags?: Record<string, unknown>;
|
|
adminFeatureFlags?: Record<string, unknown>;
|
|
publicConfig?: Record<string, unknown>;
|
|
}
|
|
|
|
export interface TenantThemeTemplateItem {
|
|
code: string;
|
|
name: string;
|
|
description?: string | null;
|
|
previewImageUrl?: string | null;
|
|
theme?: Record<string, unknown>;
|
|
publicAssets?: Record<string, unknown>;
|
|
sortOrder?: number;
|
|
status?: string;
|
|
updatedAt?: string;
|
|
}
|
|
|
|
export interface TenantThemeConfigItem {
|
|
tenantId?: string;
|
|
activeTemplateCode?: string | null;
|
|
activeTemplateName?: string | null;
|
|
activeTheme?: Record<string, unknown>;
|
|
activePublicAssets?: Record<string, unknown>;
|
|
draftTemplateCode?: string | null;
|
|
draftTemplateName?: string | null;
|
|
draftTheme?: Record<string, unknown>;
|
|
draftPublicAssets?: Record<string, unknown>;
|
|
status?: string;
|
|
publishedAt?: string | null;
|
|
updatedAt?: string | null;
|
|
brandingTheme?: Record<string, unknown>;
|
|
brandingPublicAssets?: Record<string, unknown>;
|
|
}
|
|
|
|
export interface TenantThemeDraftInput {
|
|
templateCode: string;
|
|
theme?: Record<string, unknown>;
|
|
publicAssets?: Record<string, unknown>;
|
|
}
|
|
|
|
export interface TenantPermissionCatalogItem {
|
|
key: string;
|
|
label?: string;
|
|
description?: string;
|
|
}
|
|
|
|
export interface TenantPermissionsPayload {
|
|
current?: Record<string, unknown>;
|
|
permissions?: TenantPermissionCatalogItem[];
|
|
menuGroups?: TenantPermissionCatalogItem[];
|
|
fieldGroups?: TenantPermissionCatalogItem[];
|
|
fieldCatalog?: TenantPermissionCatalogItem[];
|
|
roleDefaults?: Record<string, string[]>;
|
|
}
|
|
|
|
export interface TenantRoleTemplateItem {
|
|
id: string;
|
|
code: string;
|
|
name: string;
|
|
description?: string | null;
|
|
baseRole?: string;
|
|
status?: string;
|
|
permissions?: Record<string, boolean>;
|
|
menuPermissions?: Record<string, boolean>;
|
|
modulePermissions?: Record<string, boolean>;
|
|
fieldPermissions?: Record<string, boolean>;
|
|
dataScope?: Record<string, unknown>;
|
|
isSystem?: boolean;
|
|
sortOrder?: number;
|
|
createdAt?: string;
|
|
updatedAt?: string;
|
|
}
|
|
|
|
export interface TenantRoleTemplateInput {
|
|
id?: string;
|
|
code: string;
|
|
name: string;
|
|
description?: string | null;
|
|
baseRole: string;
|
|
status?: string;
|
|
permissions?: Record<string, boolean>;
|
|
menuPermissions?: Record<string, boolean>;
|
|
modulePermissions?: Record<string, boolean>;
|
|
fieldPermissions?: Record<string, boolean>;
|
|
dataScope?: Record<string, unknown>;
|
|
sortOrder?: number;
|
|
}
|
|
|
|
export interface TenantMemberItem {
|
|
id: string;
|
|
userId: string;
|
|
username?: string | null;
|
|
name?: string | null;
|
|
phone?: string | null;
|
|
email?: string | null;
|
|
avatarUrl?: string | null;
|
|
primaryRole?: string | null;
|
|
role: string;
|
|
status?: string;
|
|
permissions?: Record<string, boolean>;
|
|
roleTemplateId?: string | null;
|
|
roleTemplateCode?: string | null;
|
|
roleTemplateName?: string | null;
|
|
menuPermissions?: Record<string, boolean>;
|
|
modulePermissions?: Record<string, boolean>;
|
|
fieldPermissions?: Record<string, boolean>;
|
|
dataScope?: Record<string, unknown>;
|
|
legacyRole?: string | null;
|
|
lastSeenAt?: string | null;
|
|
createdAt?: string;
|
|
updatedAt?: string;
|
|
}
|
|
|
|
export interface TenantMemberInput {
|
|
membershipId?: string;
|
|
userId?: string;
|
|
username?: string | null;
|
|
name?: string | null;
|
|
phone?: string | null;
|
|
email?: string | null;
|
|
primaryRole?: string | null;
|
|
role: string;
|
|
status?: string;
|
|
permissions?: Record<string, boolean>;
|
|
roleTemplateId?: string | null;
|
|
}
|
|
|
|
export async function loadTenantDashboard(timeRange = '30d') {
|
|
return apiRequest<{ item?: TenantDashboard }>('/api/tenant-admin/dashboard', { query: { timeRange } });
|
|
}
|
|
|
|
export async function loadTenantOverview() {
|
|
return apiRequest<{ item?: TenantOverview }>('/api/tenant-admin/overview');
|
|
}
|
|
|
|
export async function loadThemeTemplates() {
|
|
return apiRequest<{ items?: TenantThemeTemplateItem[] }>('/api/tenant-admin/theme-templates');
|
|
}
|
|
|
|
export async function loadTenantTheme() {
|
|
return apiRequest<{ item?: TenantThemeConfigItem }>('/api/tenant-admin/theme');
|
|
}
|
|
|
|
export async function previewTenantTheme(input: TenantThemeDraftInput) {
|
|
return apiRequest<{ item?: TenantThemeConfigItem & { templateName?: string | null } }>('/api/tenant-admin/theme/preview', {
|
|
method: 'POST',
|
|
body: input,
|
|
});
|
|
}
|
|
|
|
export async function publishTenantTheme(input: { useDraft?: boolean; templateCode?: string; theme?: Record<string, unknown>; publicAssets?: Record<string, unknown> } = {}) {
|
|
return apiRequest<{ item?: TenantThemeConfigItem & { templateName?: string | null } }>('/api/tenant-admin/theme/publish', {
|
|
method: 'POST',
|
|
body: { useDraft: input.useDraft ?? true, ...input },
|
|
});
|
|
}
|
|
|
|
export async function loadTenantPermissions() {
|
|
return apiRequest<TenantPermissionsPayload>('/api/tenant-admin/permissions');
|
|
}
|
|
|
|
export async function loadTenantClasses(limit = 50) {
|
|
return apiRequest<{ items?: TenantClassItem[]; scoped?: boolean }>('/api/tenant-admin/classes', { query: { limit } });
|
|
}
|
|
|
|
export async function loadTenantStudents(query: { keyword?: string; classId?: string; status?: string; limit?: number } = {}) {
|
|
return apiRequest<{ items?: TenantStudentItem[]; scoped?: boolean }>('/api/tenant-admin/students', {
|
|
query: { ...query, limit: query.limit || 50 },
|
|
});
|
|
}
|
|
|
|
export async function upsertTenantStudent(input: TenantStudentInput) {
|
|
return apiRequest<{ item?: Record<string, unknown> }>('/api/tenant-admin/students', {
|
|
method: 'PUT',
|
|
body: input,
|
|
});
|
|
}
|
|
|
|
export async function bulkUpsertTenantStudents(students: TenantStudentInput[]) {
|
|
return apiRequest<BulkOperationResult>('/api/tenant-admin/students/bulk-upsert', {
|
|
method: 'POST',
|
|
body: { students },
|
|
});
|
|
}
|
|
|
|
export async function updateTenantStudentStatus(input: { userId: string; status: string; reason?: string | null }) {
|
|
return apiRequest<{ item?: Record<string, unknown> }>('/api/tenant-admin/students/status', {
|
|
method: 'POST',
|
|
body: input,
|
|
});
|
|
}
|
|
|
|
export async function bulkAssignTenantClassMembers(input: {
|
|
classId: string;
|
|
assignments: Array<{
|
|
userId?: string;
|
|
username?: string | null;
|
|
name?: string | null;
|
|
phone?: string | null;
|
|
email?: string | null;
|
|
memberType?: string;
|
|
status?: string;
|
|
metadata?: Record<string, unknown>;
|
|
}>;
|
|
}) {
|
|
return apiRequest<BulkOperationResult>('/api/tenant-admin/classes/members/bulk-assign', {
|
|
method: 'POST',
|
|
body: input,
|
|
});
|
|
}
|
|
|
|
export async function loadTenantTeachers(query: { keyword?: string; limit?: number } = {}) {
|
|
return apiRequest<{ items?: TenantTeacherItem[] }>('/api/tenant-admin/teachers', {
|
|
query: { ...query, limit: query.limit || 50 },
|
|
});
|
|
}
|
|
|
|
export async function loadTenantStudentNotes(studentUserId: string, limit = 20) {
|
|
return apiRequest<{ items?: TenantStudentNoteItem[] }>('/api/tenant-admin/students/notes', {
|
|
query: { studentUserId, limit },
|
|
});
|
|
}
|
|
|
|
export async function upsertTenantStudentNote(input: {
|
|
id?: string;
|
|
studentUserId: string;
|
|
noteType?: string;
|
|
content: string;
|
|
visibility?: string;
|
|
isPinned?: boolean;
|
|
metadata?: Record<string, unknown>;
|
|
}) {
|
|
return apiRequest<{ item?: TenantStudentNoteItem }>('/api/tenant-admin/students/notes', {
|
|
method: 'PUT',
|
|
body: input,
|
|
});
|
|
}
|
|
|
|
export async function loadTenantStudentFollowups(query: {
|
|
studentUserId?: string;
|
|
status?: string;
|
|
assignedToUserId?: string;
|
|
limit?: number;
|
|
} = {}) {
|
|
return apiRequest<{ items?: TenantStudentFollowupItem[] }>('/api/tenant-admin/students/followups', {
|
|
query: { ...query, limit: query.limit || 50 },
|
|
});
|
|
}
|
|
|
|
export async function upsertTenantStudentFollowup(input: {
|
|
id?: string;
|
|
studentUserId: string;
|
|
assignedToUserId?: string | null;
|
|
classId?: string | null;
|
|
title: string;
|
|
description?: string | null;
|
|
followupType?: string;
|
|
priority?: string;
|
|
status?: string;
|
|
dueAt?: string | null;
|
|
metadata?: Record<string, unknown>;
|
|
}) {
|
|
return apiRequest<{ item?: TenantStudentFollowupItem }>('/api/tenant-admin/students/followups', {
|
|
method: 'PUT',
|
|
body: input,
|
|
});
|
|
}
|
|
|
|
export async function loadContentEntriesAdmin() {
|
|
return apiRequest<{ items?: ContentEntryAdminItem[] }>('/api/tenant-content/content-entries');
|
|
}
|
|
|
|
export async function loadImportJobs(limit = 20) {
|
|
return apiRequest<{ items?: ImportJobItem[] }>('/api/tenant-content/imports', { query: { limit } });
|
|
}
|
|
|
|
export async function loadImportJobDetail(jobId: string) {
|
|
return apiRequest<ImportJobDetail>('/api/tenant-content/imports/detail', { query: { jobId } });
|
|
}
|
|
|
|
export async function loadImportIssues(jobId: string) {
|
|
return apiRequest<{ items?: ImportIssueItem[] }>('/api/tenant-content/imports/issues', { query: { jobId } });
|
|
}
|
|
|
|
export async function loadPublicQuestionBanks() {
|
|
return apiRequest<{ items?: PublicQuestionBankItem[] }>('/api/tenant-content/public-question-banks');
|
|
}
|
|
|
|
export async function loadTenantContentNotifications(input: {
|
|
status?: 'unread' | 'read' | 'dismissed' | 'resolved';
|
|
notificationType?: string;
|
|
adoptionId?: string;
|
|
limit?: number;
|
|
} = {}) {
|
|
return apiRequest<{ items?: TenantContentNotificationItem[]; summary?: Record<string, number> }>('/api/tenant-content/notifications', {
|
|
query: input,
|
|
});
|
|
}
|
|
|
|
export async function updateTenantContentNotificationStatus(input: {
|
|
notificationIds: string[];
|
|
status: 'read' | 'dismissed' | 'resolved';
|
|
}) {
|
|
return apiRequest<{ item?: Record<string, unknown> }>('/api/tenant-content/notifications/status', {
|
|
method: 'POST',
|
|
body: input,
|
|
});
|
|
}
|
|
|
|
export async function adoptPublicQuestionBank(input: { grantId: string; entryName?: string; collectionName?: string; copyLimit?: number }) {
|
|
return apiRequest<{ item?: Record<string, unknown> }>('/api/tenant-content/public-question-banks/adopt', {
|
|
method: 'POST',
|
|
body: input,
|
|
});
|
|
}
|
|
|
|
export async function syncPublicQuestionBank(input: { adoptionId: string; copyLimit?: number }) {
|
|
return apiRequest<{ item?: Record<string, unknown>; sync?: Record<string, unknown> }>('/api/tenant-content/public-question-banks/sync', {
|
|
method: 'POST',
|
|
body: input,
|
|
});
|
|
}
|
|
|
|
export async function loadPublicQuestionBankConflicts(adoptionId: string) {
|
|
return apiRequest<{ item?: PublicQuestionBankConflictsResult }>('/api/tenant-content/public-question-banks/conflicts', {
|
|
query: { adoptionId },
|
|
});
|
|
}
|
|
|
|
export async function resolvePublicQuestionBankConflict(input: { adoptionId: string; sourceQuestionId: string; resolution: 'accept_platform' | 'keep_local' }) {
|
|
return apiRequest<{ item?: Record<string, unknown> }>('/api/tenant-content/public-question-banks/conflicts/resolve', {
|
|
method: 'POST',
|
|
body: input,
|
|
});
|
|
}
|
|
|
|
export async function resolvePublicQuestionBankConflicts(input: {
|
|
adoptionId: string;
|
|
sourceQuestionIds: string[];
|
|
resolution: 'accept_platform' | 'keep_local';
|
|
limit?: number;
|
|
}) {
|
|
return apiRequest<{ item?: Record<string, unknown> }>('/api/tenant-content/public-question-banks/conflicts/resolve-batch', {
|
|
method: 'POST',
|
|
body: input,
|
|
});
|
|
}
|
|
|
|
export async function runImportPostCheck(jobId: string) {
|
|
return apiRequest<{ item?: Record<string, unknown> }>('/api/tenant-content/imports/post-check', {
|
|
method: 'POST',
|
|
body: { jobId },
|
|
});
|
|
}
|
|
|
|
export async function loadImportPostCheck(jobId: string) {
|
|
return apiRequest<{ item?: Record<string, unknown> }>('/api/tenant-content/imports/post-check', {
|
|
query: { jobId },
|
|
});
|
|
}
|
|
|
|
export async function loadImportFieldMapping(importType: string) {
|
|
return apiRequest<{ item?: ImportFieldMapping }>('/api/tenant-content/imports/field-mapping', {
|
|
query: { importType },
|
|
});
|
|
}
|
|
|
|
export async function loadImportTemplate(importType: string, format: 'json' | 'csv' = 'json') {
|
|
return apiRequest<{ item?: ImportTemplateItem }>('/api/tenant-content/imports/templates', {
|
|
query: { importType, format },
|
|
});
|
|
}
|
|
|
|
export async function loadMediaAnalyticsSummary(query: {
|
|
timeRange?: '7d' | '30d' | '90d';
|
|
assetId?: string;
|
|
videoId?: string;
|
|
userId?: string;
|
|
traceId?: string;
|
|
limit?: number;
|
|
} = {}) {
|
|
return apiRequest<MediaAnalyticsSummary>('/api/tenant-content/media-analytics/summary', { query });
|
|
}
|
|
|
|
export async function loadMediaAssetAccessEvents(query: {
|
|
timeRange?: '7d' | '30d' | '90d';
|
|
assetId?: string;
|
|
userId?: string;
|
|
accessType?: string;
|
|
result?: 'granted' | 'denied';
|
|
traceId?: string;
|
|
limit?: number;
|
|
} = {}) {
|
|
return apiRequest<{ scope?: Record<string, unknown>; items?: MediaAssetAccessEvent[] }>('/api/tenant-content/media-analytics/asset-events', {
|
|
query: { ...query, limit: query.limit || 100 },
|
|
});
|
|
}
|
|
|
|
export async function loadMediaVideoPlayEvents(query: {
|
|
timeRange?: '7d' | '30d' | '90d';
|
|
videoId?: string;
|
|
userId?: string;
|
|
accessMode?: 'free' | 'svip' | 'video_quota';
|
|
status?: 'issued' | 'started' | 'completed' | 'expired' | 'revoked';
|
|
traceId?: string;
|
|
limit?: number;
|
|
} = {}) {
|
|
return apiRequest<{ scope?: Record<string, unknown>; items?: MediaVideoPlayEvent[] }>('/api/tenant-content/media-analytics/video-events', {
|
|
query: { ...query, limit: query.limit || 100 },
|
|
});
|
|
}
|
|
|
|
export async function previewContentImport(importType: ImportType, body: ImportRequestBody) {
|
|
return apiRequest<ImportPreviewResult>(`/api/tenant-content/imports/preview/${importType}`, {
|
|
method: 'POST',
|
|
body,
|
|
});
|
|
}
|
|
|
|
export async function executeContentImport(importType: ImportType, body: ImportRequestBody) {
|
|
return apiRequest<{ item?: Record<string, unknown>; preview?: ImportPreviewResult }>(`/api/tenant-content/imports/${importType}`, {
|
|
method: 'POST',
|
|
body,
|
|
});
|
|
}
|
|
|
|
export async function loadCoupons() {
|
|
return apiRequest<{ items?: CouponItem[] }>('/api/tenant-admin/coupons');
|
|
}
|
|
|
|
export async function loadCodeBatches() {
|
|
return apiRequest<{ items?: CodeBatchItem[] }>('/api/tenant-admin/code-batches');
|
|
}
|
|
|
|
export async function loadActivationCodes(limit = 20) {
|
|
return apiRequest<{ items?: Record<string, unknown>[] }>('/api/tenant-admin/activation-codes', { query: { limit } });
|
|
}
|
|
|
|
export async function loadTenantDomains() {
|
|
return apiRequest<{ items?: Record<string, unknown>[] }>('/api/tenant-admin/domains');
|
|
}
|
|
|
|
export async function loadPaymentAccounts() {
|
|
return apiRequest<{ items?: Record<string, unknown>[] }>('/api/tenant-admin/payment-accounts');
|
|
}
|
|
|
|
export async function loadAuthProviders() {
|
|
return apiRequest<{ items?: Record<string, unknown>[] }>('/api/tenant-admin/auth-providers');
|
|
}
|
|
|
|
export async function loadRoleTemplates() {
|
|
return apiRequest<{ items?: TenantRoleTemplateItem[] }>('/api/tenant-admin/role-templates');
|
|
}
|
|
|
|
export async function upsertRoleTemplate(input: TenantRoleTemplateInput) {
|
|
return apiRequest<{ item?: TenantRoleTemplateItem }>('/api/tenant-admin/role-templates', {
|
|
method: 'PUT',
|
|
body: input,
|
|
});
|
|
}
|
|
|
|
export async function disableRoleTemplate(roleTemplateId: string) {
|
|
return apiRequest<{ item?: TenantRoleTemplateItem }>('/api/tenant-admin/role-templates/disable', {
|
|
method: 'POST',
|
|
body: { roleTemplateId },
|
|
});
|
|
}
|
|
|
|
export async function loadTenantMembers(query: { keyword?: string; role?: string; status?: string; limit?: number } = {}) {
|
|
return apiRequest<{ items?: TenantMemberItem[] }>('/api/tenant-admin/members', {
|
|
query: { ...query, limit: query.limit || 50 },
|
|
});
|
|
}
|
|
|
|
export async function upsertTenantMember(input: TenantMemberInput) {
|
|
return apiRequest<{ item?: TenantMemberItem }>('/api/tenant-admin/members', {
|
|
method: 'PUT',
|
|
body: input,
|
|
});
|
|
}
|
|
|
|
export async function disableTenantMember(membershipId: string) {
|
|
return apiRequest<{ item?: TenantMemberItem }>('/api/tenant-admin/members/disable', {
|
|
method: 'POST',
|
|
body: { membershipId },
|
|
});
|
|
}
|
|
|
|
export async function loadCrmConfig() {
|
|
return apiRequest<{ item?: CrmConfigItem | null }>('/api/crm/config');
|
|
}
|
|
|
|
export async function upsertCrmConfig(input: {
|
|
enabled?: boolean;
|
|
url?: string | null;
|
|
secretRef?: string | null;
|
|
formName?: string | null;
|
|
examType?: string | null;
|
|
timeoutSec?: number;
|
|
delaySec?: number;
|
|
assignmentMode?: 'none' | 'direct' | 'round_robin' | 'referrer';
|
|
assignmentPool?: string[];
|
|
assignmentConfig?: Record<string, unknown>;
|
|
}) {
|
|
return apiRequest<{ item?: CrmConfigItem }>('/api/crm/config', {
|
|
method: 'PUT',
|
|
body: input,
|
|
});
|
|
}
|
|
|
|
export async function loadCrmQueue(limit = 20, status?: string) {
|
|
return apiRequest<{ items?: CrmQueueItem[] }>('/api/crm/queue', { query: { limit, status } });
|
|
}
|
|
|
|
export async function loadCommissionSettings() {
|
|
return apiRequest<{ item?: CommissionSettingsItem }>('/api/commission/settings');
|
|
}
|
|
|
|
export async function updateCommissionSettings(input: {
|
|
defaultRate: number;
|
|
minSettlementCents?: number;
|
|
settlementCycle?: string;
|
|
config?: Record<string, unknown>;
|
|
}) {
|
|
return apiRequest<{ item?: CommissionSettingsItem }>('/api/commission/settings', {
|
|
method: 'PUT',
|
|
body: input,
|
|
});
|
|
}
|
|
|
|
export async function updateMemberCommissionRate(input: {
|
|
userId: string;
|
|
commissionRate?: number | null;
|
|
commissionConfig?: Record<string, unknown>;
|
|
}) {
|
|
return apiRequest<{ item?: Record<string, unknown> }>('/api/commission/member-rate', {
|
|
method: 'PUT',
|
|
body: input,
|
|
});
|
|
}
|
|
|
|
export async function loadCommissionSummary(query: { startDate?: string; endDate?: string; referrerUserId?: string } = {}) {
|
|
return apiRequest<{ item?: CommissionSummaryItem }>('/api/commission/summary', { query });
|
|
}
|
|
|
|
export async function loadCommissionOrders(query: { startDate?: string; endDate?: string; referrerUserId?: string; limit?: number } = {}) {
|
|
return apiRequest<{ items?: CommissionOrderItem[]; scope?: Record<string, unknown> }>('/api/commission/orders', {
|
|
query: { ...query, limit: query.limit || 50 },
|
|
});
|
|
}
|
|
|
|
export async function loadCommissionSettlements(query: { status?: string; referrerUserId?: string; limit?: number } = {}) {
|
|
return apiRequest<{ items?: CommissionSettlementItem[] }>('/api/commission/settlements', {
|
|
query: { ...query, limit: query.limit || 50 },
|
|
});
|
|
}
|
|
|
|
export async function generateCommissionSettlement(input: {
|
|
referrerUserId: string;
|
|
startDate: string;
|
|
endDate: string;
|
|
status?: string;
|
|
remark?: string | null;
|
|
}) {
|
|
return apiRequest<{ item?: CommissionSettlementItem & { items?: CommissionOrderItem[] } }>('/api/commission/settlements/generate', {
|
|
method: 'POST',
|
|
body: input,
|
|
});
|
|
}
|
|
|
|
export async function updateCommissionSettlementStatus(input: {
|
|
settlementId: string;
|
|
status: string;
|
|
paymentMethod?: string | null;
|
|
paymentAccount?: string | null;
|
|
remark?: string | null;
|
|
metadata?: Record<string, unknown>;
|
|
}) {
|
|
return apiRequest<{ item?: CommissionSettlementItem }>('/api/commission/settlements/status', {
|
|
method: 'POST',
|
|
body: input,
|
|
});
|
|
}
|