forked from wangziqi/gongxue-base
1714 lines
48 KiB
TypeScript
1714 lines
48 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 TenantStudentCrmPushInput {
|
|
studentUserIds: string[];
|
|
source?: string;
|
|
title: string;
|
|
message?: string | null;
|
|
followupType?: string;
|
|
priority?: string;
|
|
assignedToUserId?: string | null;
|
|
classId?: string | null;
|
|
dueAt?: string | null;
|
|
metadata?: Record<string, unknown>;
|
|
idempotencyKey?: string;
|
|
}
|
|
|
|
export interface TenantStudentFollowupReport {
|
|
item?: {
|
|
range?: Record<string, unknown>;
|
|
filters?: Record<string, unknown>;
|
|
summary?: {
|
|
total?: number;
|
|
open?: number;
|
|
inProgress?: number;
|
|
done?: number;
|
|
cancelled?: number;
|
|
highPriority?: number;
|
|
crmPushTasks?: number;
|
|
openBacklog?: number;
|
|
overdueBacklog?: number;
|
|
completedInRange?: number;
|
|
completionRate?: number;
|
|
completedInRangeRate?: number;
|
|
avgCompleteHours?: number;
|
|
};
|
|
crmQueue?: {
|
|
total?: number;
|
|
pending?: number;
|
|
sent?: number;
|
|
failed?: number;
|
|
studentCount?: number;
|
|
};
|
|
byStatus?: Record<string, unknown>[];
|
|
byType?: Record<string, unknown>[];
|
|
byPriority?: Record<string, unknown>[];
|
|
byAssignee?: Record<string, unknown>[];
|
|
byClass?: Record<string, unknown>[];
|
|
dailyTrend?: Record<string, unknown>[];
|
|
overdueItems?: TenantStudentFollowupItem[];
|
|
};
|
|
}
|
|
|
|
export interface TenantStudentSupervisionRules {
|
|
windowDays?: number;
|
|
inactivityDays?: number;
|
|
minAnswers?: number;
|
|
lowAccuracyThreshold?: number;
|
|
wrongQuestionThreshold?: number;
|
|
vocabularyDueThreshold?: number;
|
|
staleSessionDays?: number;
|
|
}
|
|
|
|
export interface TenantStudentSupervisionCandidate {
|
|
studentUserId: string;
|
|
studentName?: string | null;
|
|
studentPhone?: string | null;
|
|
assignedToUserId?: string | null;
|
|
classId?: string | null;
|
|
title?: string;
|
|
description?: string;
|
|
followupType?: string;
|
|
priority?: string;
|
|
riskScore?: number;
|
|
reasons?: Record<string, unknown>[];
|
|
evidence?: Record<string, unknown>;
|
|
}
|
|
|
|
export interface TenantStudentSupervisionPreview {
|
|
item?: {
|
|
rules?: TenantStudentSupervisionRules;
|
|
filters?: Record<string, unknown>;
|
|
totalCandidates?: number;
|
|
candidates?: TenantStudentSupervisionCandidate[];
|
|
};
|
|
}
|
|
|
|
export interface TenantStudentSupervisionSchedule {
|
|
enabled?: boolean;
|
|
frequency?: 'manual' | 'daily' | 'weekly';
|
|
hour?: number;
|
|
minute?: number;
|
|
timezone?: string;
|
|
weekdays?: number[];
|
|
}
|
|
|
|
export interface TenantStudentSupervisionRuleItem {
|
|
id: string;
|
|
name: string;
|
|
status?: string;
|
|
rules?: TenantStudentSupervisionRules;
|
|
schedule?: TenantStudentSupervisionSchedule;
|
|
classId?: string | null;
|
|
className?: string | null;
|
|
assignedToUserId?: string | null;
|
|
assignedToName?: string | null;
|
|
limit?: number;
|
|
metadata?: Record<string, unknown>;
|
|
lastRunAt?: string | null;
|
|
nextRunAt?: string | null;
|
|
lastResult?: Record<string, unknown>;
|
|
createdAt?: string;
|
|
updatedAt?: string;
|
|
}
|
|
|
|
export interface TenantStudentSupervisionRulesPayload {
|
|
items?: TenantStudentSupervisionRuleItem[];
|
|
scoped?: boolean;
|
|
}
|
|
|
|
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 UserNotificationAdminItem {
|
|
id: string;
|
|
userId?: string;
|
|
userName?: string | null;
|
|
userPhone?: string | null;
|
|
userAvatarUrl?: string | null;
|
|
notificationType?: string;
|
|
status?: 'unread' | 'read' | 'dismissed' | 'archived';
|
|
severity?: 'info' | 'success' | 'warning' | 'error';
|
|
title?: string;
|
|
message?: string;
|
|
actionLabel?: string | null;
|
|
actionPath?: string | null;
|
|
sourceType?: string | null;
|
|
sourceId?: string | null;
|
|
metadata?: Record<string, unknown>;
|
|
createdBy?: string | null;
|
|
createdByName?: string | null;
|
|
readAt?: string | null;
|
|
createdAt?: string;
|
|
updatedAt?: string;
|
|
}
|
|
|
|
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;
|
|
campaignName?: string | null;
|
|
planId?: string | null;
|
|
discountType?: string | null;
|
|
discountValue?: number | string | null;
|
|
validFrom?: string | null;
|
|
validTo?: string | null;
|
|
usedCount?: number;
|
|
maxUses?: number | null;
|
|
minOrderAmountCents?: number;
|
|
maxDiscountCents?: number | null;
|
|
perUserLimit?: number;
|
|
firstOrderOnly?: boolean;
|
|
allowedPlanIds?: string[];
|
|
allowedRegionIds?: string[];
|
|
metadata?: Record<string, unknown>;
|
|
source?: string | null;
|
|
remark?: string | null;
|
|
}
|
|
|
|
export interface CouponInput {
|
|
id?: string;
|
|
code: string;
|
|
planId?: string | null;
|
|
discountType?: 'fixed' | 'percent' | null;
|
|
discountValue?: number | string | null;
|
|
validFrom?: string | null;
|
|
validTo?: string | null;
|
|
maxUses?: number | null;
|
|
status?: 'active' | 'disabled' | 'archived';
|
|
campaignName?: string | null;
|
|
minOrderAmountCents?: number;
|
|
maxDiscountCents?: number | null;
|
|
perUserLimit?: number;
|
|
firstOrderOnly?: boolean;
|
|
allowedPlanIds?: string[];
|
|
allowedRegionIds?: string[];
|
|
metadata?: Record<string, unknown>;
|
|
source?: string | null;
|
|
remark?: string | null;
|
|
}
|
|
|
|
export interface CouponRedemptionItem {
|
|
id: string;
|
|
couponId?: string | null;
|
|
couponCode?: string | null;
|
|
campaignName?: string | null;
|
|
userId?: string | null;
|
|
userName?: string | null;
|
|
userPhone?: string | null;
|
|
planId?: string | null;
|
|
planName?: string | null;
|
|
orderId?: string | null;
|
|
orderNo?: string | null;
|
|
status?: string;
|
|
discountAppliedCents?: number;
|
|
regionId?: string | null;
|
|
regionName?: string | null;
|
|
source?: string | null;
|
|
remark?: string | null;
|
|
claimedAt?: string | null;
|
|
usedAt?: string | null;
|
|
createdAt?: string;
|
|
}
|
|
|
|
export interface CouponReport {
|
|
startDate?: string;
|
|
endDate?: string;
|
|
claimCount?: number;
|
|
usedCount?: number;
|
|
discountCents?: number;
|
|
paidAmountCents?: number;
|
|
conversionRate?: number;
|
|
byCoupon?: Array<Record<string, unknown>>;
|
|
byCampaign?: Array<Record<string, unknown>>;
|
|
daily?: Array<Record<string, unknown>>;
|
|
}
|
|
|
|
export interface PointActivityTaskItem {
|
|
id: string;
|
|
legacyId?: string | null;
|
|
code?: string;
|
|
title?: string;
|
|
description?: string | null;
|
|
taskType?: 'daily_check_in' | 'feedback_submit' | 'feedback_resolved' | 'practice_complete' | 'vocabulary_review' | 'mock_exam_submit' | 'manual';
|
|
rewardPoints?: number;
|
|
claimLimitPerUser?: number;
|
|
periodType?: 'once' | 'daily' | 'weekly' | 'monthly' | 'unlimited';
|
|
validFrom?: string | null;
|
|
validTo?: string | null;
|
|
status?: 'active' | 'disabled' | 'archived';
|
|
order?: number;
|
|
metadata?: Record<string, unknown>;
|
|
claimCount?: number;
|
|
claimUserCount?: number;
|
|
createdAt?: string;
|
|
updatedAt?: string;
|
|
}
|
|
|
|
export interface PointActivityTaskInput {
|
|
id?: string;
|
|
legacyId?: string | null;
|
|
code: string;
|
|
title: string;
|
|
description?: string | null;
|
|
taskType?: PointActivityTaskItem['taskType'];
|
|
rewardPoints: number;
|
|
claimLimitPerUser?: number;
|
|
periodType?: PointActivityTaskItem['periodType'];
|
|
validFrom?: string | null;
|
|
validTo?: string | null;
|
|
status?: PointActivityTaskItem['status'];
|
|
order?: number;
|
|
metadata?: Record<string, unknown>;
|
|
}
|
|
|
|
export interface PointExchangeItem {
|
|
id: string;
|
|
legacyId?: string | null;
|
|
code?: string;
|
|
title?: string;
|
|
description?: string | null;
|
|
costPoints?: number;
|
|
itemType?: 'coupon' | 'manual' | 'asset' | 'custom';
|
|
couponId?: string | null;
|
|
couponCode?: string | null;
|
|
assetId?: string | null;
|
|
stockTotal?: number | null;
|
|
stockUsed?: number;
|
|
stockRemaining?: number | null;
|
|
perUserLimit?: number;
|
|
validFrom?: string | null;
|
|
validTo?: string | null;
|
|
status?: 'active' | 'disabled' | 'archived';
|
|
order?: number;
|
|
metadata?: Record<string, unknown>;
|
|
orderCount?: number;
|
|
createdAt?: string;
|
|
updatedAt?: string;
|
|
}
|
|
|
|
export interface PointExchangeItemInput {
|
|
id?: string;
|
|
legacyId?: string | null;
|
|
code: string;
|
|
title: string;
|
|
description?: string | null;
|
|
costPoints: number;
|
|
itemType?: PointExchangeItem['itemType'];
|
|
couponId?: string | null;
|
|
assetId?: string | null;
|
|
stockTotal?: number | null;
|
|
perUserLimit?: number;
|
|
validFrom?: string | null;
|
|
validTo?: string | null;
|
|
status?: PointExchangeItem['status'];
|
|
order?: number;
|
|
metadata?: Record<string, unknown>;
|
|
}
|
|
|
|
export interface PointsRiskReport {
|
|
range?: {
|
|
timeRange?: '7d' | '30d' | '90d';
|
|
startDate?: string;
|
|
endDate?: string;
|
|
};
|
|
thresholds?: Record<string, number>;
|
|
summary?: {
|
|
eventCount?: number;
|
|
affectedUsers?: number;
|
|
earnedPoints?: number;
|
|
spentPoints?: number;
|
|
netPoints?: number;
|
|
maxEarnedEvent?: number;
|
|
maxSpentEvent?: number;
|
|
suspiciousUserCount?: number;
|
|
suspiciousEventCount?: number;
|
|
riskScore?: number;
|
|
};
|
|
eventBreakdown?: Array<Record<string, unknown>>;
|
|
suspiciousUsers?: Array<Record<string, unknown>>;
|
|
suspiciousEvents?: Array<Record<string, unknown>>;
|
|
suspiciousTasks?: Array<Record<string, unknown>>;
|
|
suspiciousExchanges?: Array<Record<string, unknown>>;
|
|
dailyTrend?: Array<Record<string, unknown>>;
|
|
}
|
|
|
|
export interface FeedbackReport {
|
|
range?: {
|
|
timeRange?: '7d' | '30d' | '90d';
|
|
startDate?: string;
|
|
endDate?: string;
|
|
};
|
|
summary?: {
|
|
total?: number;
|
|
newInRange?: number;
|
|
pending?: number;
|
|
accepted?: number;
|
|
rejected?: number;
|
|
resolved?: number;
|
|
closed?: number;
|
|
highPriority?: number;
|
|
pendingBacklog?: number;
|
|
handledInRange?: number;
|
|
rewardedReports?: number;
|
|
rewardPoints?: number;
|
|
resolutionRate?: number;
|
|
handledRate?: number;
|
|
rejectionRate?: number;
|
|
avgHandleHours?: number;
|
|
};
|
|
byStatus?: Array<Record<string, unknown>>;
|
|
byType?: Array<Record<string, unknown>>;
|
|
byCategory?: Array<Record<string, unknown>>;
|
|
byPriority?: Array<Record<string, unknown>>;
|
|
dailyTrend?: Array<Record<string, unknown>>;
|
|
topQuestions?: Array<Record<string, unknown>>;
|
|
topHandlers?: Array<Record<string, unknown>>;
|
|
recentUnhandled?: Array<Record<string, unknown>>;
|
|
}
|
|
|
|
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 CommissionSettlementExportItem {
|
|
settlementId?: string;
|
|
filename?: string;
|
|
format?: 'csv' | 'json';
|
|
mimeType?: string;
|
|
rowCount?: number;
|
|
contentBase64?: string;
|
|
sha256?: string;
|
|
sizeBytes?: number;
|
|
}
|
|
|
|
export interface CommissionSettlementProofItem {
|
|
id: string;
|
|
settlementId?: string;
|
|
proofType?: string;
|
|
status?: string;
|
|
title?: string | null;
|
|
description?: string | null;
|
|
assetId?: string | null;
|
|
externalUrl?: string | null;
|
|
amountCents?: number | null;
|
|
paymentMethod?: string | null;
|
|
paymentAccount?: string | null;
|
|
paidAt?: string | null;
|
|
submittedBy?: string | null;
|
|
submittedByName?: string | null;
|
|
reviewedBy?: string | null;
|
|
reviewedByName?: string | null;
|
|
reviewedAt?: string | null;
|
|
reviewNote?: 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 pushTenantStudentsToCrm(input: TenantStudentCrmPushInput) {
|
|
return apiRequest<BulkOperationResult & { requestId?: string }>('/api/tenant-admin/students/crm-push', {
|
|
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 loadTenantStudentFollowupReport(query: {
|
|
timeRange?: '7d' | '30d' | '90d';
|
|
startDate?: string;
|
|
endDate?: string;
|
|
assignedToUserId?: string;
|
|
followupType?: string;
|
|
classId?: string;
|
|
limit?: number;
|
|
} = {}) {
|
|
return apiRequest<TenantStudentFollowupReport>('/api/tenant-admin/students/followups/report', {
|
|
query: { ...query, limit: query.limit || 10 },
|
|
});
|
|
}
|
|
|
|
export async function previewTenantStudentSupervision(query: TenantStudentSupervisionRules & {
|
|
classId?: string;
|
|
assignedToUserId?: string;
|
|
limit?: number;
|
|
} = {}) {
|
|
return apiRequest<TenantStudentSupervisionPreview>('/api/tenant-admin/students/supervision/preview', {
|
|
query: { ...query, limit: query.limit || 20 },
|
|
});
|
|
}
|
|
|
|
export async function loadTenantStudentSupervisionRules(query: {
|
|
status?: string;
|
|
limit?: number;
|
|
} = {}) {
|
|
return apiRequest<TenantStudentSupervisionRulesPayload>('/api/tenant-admin/students/supervision/rules', {
|
|
query: { ...query, limit: query.limit || 50 },
|
|
});
|
|
}
|
|
|
|
export async function upsertTenantStudentSupervisionRule(input: {
|
|
id?: string;
|
|
name: string;
|
|
status?: string;
|
|
rules?: TenantStudentSupervisionRules;
|
|
schedule?: TenantStudentSupervisionSchedule;
|
|
classId?: string | null;
|
|
assignedToUserId?: string | null;
|
|
limit?: number;
|
|
metadata?: Record<string, unknown>;
|
|
}) {
|
|
return apiRequest<{ item?: TenantStudentSupervisionRuleItem }>('/api/tenant-admin/students/supervision/rules', {
|
|
method: 'PUT',
|
|
body: input,
|
|
});
|
|
}
|
|
|
|
export async function generateTenantStudentSupervision(input: {
|
|
rules?: TenantStudentSupervisionRules;
|
|
classId?: string | null;
|
|
assignedToUserId?: string | null;
|
|
studentUserIds?: string[];
|
|
dueAt?: string | null;
|
|
batchKey?: string;
|
|
limit?: number;
|
|
metadata?: Record<string, unknown>;
|
|
}) {
|
|
return apiRequest<BulkOperationResult>('/api/tenant-admin/students/supervision/generate', {
|
|
method: 'POST',
|
|
body: input,
|
|
});
|
|
}
|
|
|
|
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 loadUserNotifications(input: {
|
|
userId?: string;
|
|
status?: UserNotificationAdminItem['status'];
|
|
notificationType?: string;
|
|
limit?: number;
|
|
} = {}) {
|
|
return apiRequest<{ items?: UserNotificationAdminItem[]; summary?: Record<string, number> }>('/api/tenant-admin/user-notifications', {
|
|
query: { ...input, limit: input.limit || 100 },
|
|
});
|
|
}
|
|
|
|
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(query: { status?: string; campaignName?: string; limit?: number } = {}) {
|
|
return apiRequest<{ items?: CouponItem[] }>('/api/tenant-admin/coupons', { query });
|
|
}
|
|
|
|
export async function upsertCoupon(input: CouponInput) {
|
|
return apiRequest<{ item?: CouponItem }>('/api/tenant-admin/coupons', {
|
|
method: 'PUT',
|
|
body: input,
|
|
});
|
|
}
|
|
|
|
export async function loadCouponRedemptions(query: {
|
|
couponId?: string;
|
|
status?: 'claimed' | 'pending' | 'used' | 'cancelled' | 'expired';
|
|
limit?: number;
|
|
} = {}) {
|
|
return apiRequest<{ items?: CouponRedemptionItem[] }>('/api/tenant-admin/coupons/redemptions', {
|
|
query: { ...query, limit: query.limit || 50 },
|
|
});
|
|
}
|
|
|
|
export async function loadCouponReport(query: {
|
|
startDate?: string;
|
|
endDate?: string;
|
|
couponId?: string;
|
|
campaignName?: string;
|
|
} = {}) {
|
|
return apiRequest<{ item?: CouponReport }>('/api/tenant-admin/coupons/report', { query });
|
|
}
|
|
|
|
export async function loadPointActivityTasks(query: { status?: string; taskType?: string; limit?: number } = {}) {
|
|
return apiRequest<{ items?: PointActivityTaskItem[] }>('/api/tenant-admin/point-activity-tasks', {
|
|
query: { ...query, limit: query.limit || 100 },
|
|
});
|
|
}
|
|
|
|
export async function upsertPointActivityTask(input: PointActivityTaskInput) {
|
|
return apiRequest<{ item?: PointActivityTaskItem }>('/api/tenant-admin/point-activity-tasks', {
|
|
method: 'PUT',
|
|
body: input,
|
|
});
|
|
}
|
|
|
|
export async function loadPointActivityClaims(query: { taskId?: string; userId?: string; limit?: number } = {}) {
|
|
return apiRequest<{ items?: Record<string, unknown>[] }>('/api/tenant-admin/point-activity-claims', {
|
|
query: { ...query, limit: query.limit || 100 },
|
|
});
|
|
}
|
|
|
|
export async function loadPointExchangeItems(query: { status?: string; itemType?: string; limit?: number } = {}) {
|
|
return apiRequest<{ items?: PointExchangeItem[] }>('/api/tenant-admin/point-exchange-items', {
|
|
query: { ...query, limit: query.limit || 100 },
|
|
});
|
|
}
|
|
|
|
export async function upsertPointExchangeItem(input: PointExchangeItemInput) {
|
|
return apiRequest<{ item?: PointExchangeItem }>('/api/tenant-admin/point-exchange-items', {
|
|
method: 'PUT',
|
|
body: input,
|
|
});
|
|
}
|
|
|
|
export async function loadPointExchangeOrders(query: {
|
|
itemId?: string;
|
|
userId?: string;
|
|
status?: 'completed' | 'pending_fulfillment' | 'cancelled';
|
|
limit?: number;
|
|
} = {}) {
|
|
return apiRequest<{ items?: Record<string, unknown>[] }>('/api/tenant-admin/point-exchange-orders', {
|
|
query: { ...query, limit: query.limit || 100 },
|
|
});
|
|
}
|
|
|
|
export async function loadPointsRiskReport(query: {
|
|
timeRange?: '7d' | '30d' | '90d';
|
|
highEarnedThreshold?: number;
|
|
highClaimThreshold?: number;
|
|
highEventPointsThreshold?: number;
|
|
highRedeemThreshold?: number;
|
|
limit?: number;
|
|
} = {}) {
|
|
return apiRequest<{ item?: PointsRiskReport }>('/api/tenant-admin/points-risk-report', {
|
|
query: { ...query, limit: query.limit || 20 },
|
|
});
|
|
}
|
|
|
|
export async function loadFeedbackReport(query: {
|
|
timeRange?: '7d' | '30d' | '90d';
|
|
limit?: number;
|
|
} = {}) {
|
|
return apiRequest<{ item?: FeedbackReport }>('/api/tenant-admin/feedbacks/report', {
|
|
query: { ...query, limit: query.limit || 20 },
|
|
});
|
|
}
|
|
|
|
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 exportCommissionSettlement(settlementId: string, format: 'csv' | 'json' = 'csv') {
|
|
return apiRequest<{ item?: CommissionSettlementExportItem }>('/api/commission/settlements/export', {
|
|
query: { settlementId, format },
|
|
});
|
|
}
|
|
|
|
export async function loadCommissionSettlementProofs(settlementId: string) {
|
|
return apiRequest<{ items?: CommissionSettlementProofItem[] }>('/api/commission/settlements/proofs', {
|
|
query: { settlementId },
|
|
});
|
|
}
|
|
|
|
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,
|
|
});
|
|
}
|
|
|
|
export async function createCommissionSettlementProof(input: {
|
|
settlementId: string;
|
|
proofType?: string;
|
|
title?: string | null;
|
|
description?: string | null;
|
|
assetId?: string | null;
|
|
externalUrl?: string | null;
|
|
amountCents?: number | null;
|
|
paymentMethod?: string | null;
|
|
paymentAccount?: string | null;
|
|
paidAt?: string | null;
|
|
metadata?: Record<string, unknown>;
|
|
}) {
|
|
return apiRequest<{ item?: CommissionSettlementProofItem }>('/api/commission/settlements/proofs', {
|
|
method: 'POST',
|
|
body: input,
|
|
});
|
|
}
|
|
|
|
export async function updateCommissionSettlementProofStatus(input: {
|
|
proofId: string;
|
|
status: string;
|
|
reviewNote?: string | null;
|
|
metadata?: Record<string, unknown>;
|
|
}) {
|
|
return apiRequest<{ item?: CommissionSettlementProofItem }>('/api/commission/settlements/proofs/status', {
|
|
method: 'POST',
|
|
body: input,
|
|
});
|
|
}
|