Files
lehuo/packages/contracts/src/case-management.ts
T
2026-08-12 14:35:12 +08:00

309 lines
7.5 KiB
TypeScript

import type { ServiceApplicationStatus, ServiceCategory } from './service-applications.js';
export const BENEFICIARY_GENDERS = ['FEMALE', 'MALE', 'OTHER', 'UNDISCLOSED'] as const;
export type BeneficiaryGender = (typeof BENEFICIARY_GENDERS)[number];
export const BENEFICIARY_STATUSES = ['ACTIVE', 'INACTIVE', 'ARCHIVED'] as const;
export type BeneficiaryStatus = (typeof BENEFICIARY_STATUSES)[number];
export const SERVICE_CASE_PRIORITIES = ['LOW', 'NORMAL', 'HIGH', 'URGENT'] as const;
export type ServiceCasePriority = (typeof SERVICE_CASE_PRIORITIES)[number];
export const SERVICE_CASE_STATUSES = [
'ASSIGNED',
'IN_SERVICE',
'SERVICE_COMPLETED',
'SUSPENDED',
'CANCELLED',
] as const;
export type ServiceCaseStatus = (typeof SERVICE_CASE_STATUSES)[number];
export const CASE_ASSIGNEE_TYPES = ['STAFF', 'VOLUNTEER'] as const;
export type CaseAssigneeType = (typeof CASE_ASSIGNEE_TYPES)[number];
export const SERVICE_RECORD_TYPES = [
'HOME_VISIT',
'PHONE_CONTACT',
'ESCORT',
'MATERIAL_SUPPORT',
'ACTIVITY',
'OTHER',
] as const;
export type ServiceRecordType = (typeof SERVICE_RECORD_TYPES)[number];
export const BENEFICIARY_CONFIRM_STATUSES = [
'NOT_REQUESTED',
'CONFIRMED',
'PARTIALLY_CONFIRMED',
'DISPUTED',
] as const;
export type BeneficiaryConfirmStatus = (typeof BENEFICIARY_CONFIRM_STATUSES)[number];
export const SERVICE_CASE_STATUS_LABELS: Record<ServiceCaseStatus, string> = {
ASSIGNED: '已指派',
IN_SERVICE: '服务中',
SERVICE_COMPLETED: '服务已完成',
SUSPENDED: '服务暂缓',
CANCELLED: '已取消',
};
export const SERVICE_CASE_PRIORITY_LABELS: Record<ServiceCasePriority, string> = {
LOW: '低',
NORMAL: '一般',
HIGH: '较高',
URGENT: '紧急',
};
export const SERVICE_RECORD_TYPE_LABELS: Record<ServiceRecordType, string> = {
HOME_VISIT: '上门探访',
PHONE_CONTACT: '电话关怀',
ESCORT: '陪同协助',
MATERIAL_SUPPORT: '物资支持',
ACTIVITY: '公益活动',
OTHER: '其他服务',
};
export interface BeneficiaryContactInput {
name: string;
relation: string;
mobile: string;
isGuardian: boolean;
isEmergencyContact: boolean;
}
export interface NewBeneficiaryInput {
displayName: string;
gender?: BeneficiaryGender;
birthYear?: number;
mobile?: string;
regionCode: string;
addressDetail?: string;
keyCareFlag: boolean;
isMinor: boolean;
notes?: string;
contacts: BeneficiaryContactInput[];
tagCodes: string[];
guardianConsentConfirmed?: true;
}
export interface AcceptServiceApplicationRequest {
clientRequestId: string;
beneficiary:
| { mode: 'CREATE'; profile: NewBeneficiaryInput }
| {
mode: 'LINK_EXISTING';
beneficiaryPublicId: string;
manualMatchConfirmed: true;
};
ownerStaffPublicId: string;
collaboratorUserPublicIds: string[];
priority: ServiceCasePriority;
plannedStartAt?: string;
plannedEndAt?: string;
serviceLocation?: string;
summary: string;
}
export interface TransitionServiceApplicationRequest {
targetStatus: Extract<ServiceApplicationStatus, 'PENDING_CONTACT' | 'ASSESSING'>;
reasonCode: string;
}
export interface BeneficiaryContactDetail {
publicId: string;
name: string;
relation: string;
mobile: string;
mobileMasked: string;
isGuardian: boolean;
isEmergencyContact: boolean;
hasConsentRecord: boolean;
}
export interface BeneficiaryTagOption {
code: string;
name: string;
description: string | null;
}
export interface BeneficiaryListItem {
publicId: string;
displayName: string;
gender: BeneficiaryGender | null;
birthYear: number | null;
mobileMasked: string | null;
regionCode: string;
primaryCategory: ServiceCategory;
keyCareFlag: boolean;
isMinor: boolean;
status: BeneficiaryStatus;
tags: BeneficiaryTagOption[];
createdAt: string;
}
export interface BeneficiaryDetail extends BeneficiaryListItem {
mobile: string | null;
addressDetail: string | null;
notes: string | null;
contacts: BeneficiaryContactDetail[];
serviceCaseCount: number;
}
export interface PaginatedBeneficiariesResponse {
items: BeneficiaryListItem[];
page: number;
pageSize: number;
total: number;
}
export interface BeneficiaryDetailResponse {
beneficiary: BeneficiaryDetail;
}
export interface UpdateBeneficiaryRequest extends NewBeneficiaryInput {
status: BeneficiaryStatus;
}
export interface AssigneeOption {
userPublicId: string;
displayName: string;
type: CaseAssigneeType;
department: string | null;
}
export interface CaseAssigneeItem extends AssigneeOption {
role: 'OWNER' | 'COLLABORATOR';
assignedAt: string;
}
export interface ServiceCaseHistoryItem {
status: ServiceCaseStatus;
statusLabel: string;
reasonLabel: string;
createdAt: string;
}
export interface ServiceRecordAttachment {
publicId: string;
filename: string;
mimeType: string;
size: number;
}
export interface ServiceRecordItem {
publicId: string;
serviceStartAt: string;
serviceEndAt: string | null;
serviceType: ServiceRecordType;
serviceTypeLabel: string;
content: string;
result: string;
followUpSuggestion: string | null;
beneficiaryConfirmStatus: BeneficiaryConfirmStatus;
createdByDisplayName: string;
revision: number;
attachments: ServiceRecordAttachment[];
createdAt: string;
updatedAt: string;
}
export interface ServiceCaseListItem {
publicId: string;
caseNo: string;
applicationPublicId: string;
applicationNo: string;
beneficiaryPublicId: string;
beneficiaryDisplayName: string;
category: ServiceCategory;
priority: ServiceCasePriority;
priorityLabel: string;
status: ServiceCaseStatus;
statusLabel: string;
ownerDisplayName: string;
plannedStartAt: string | null;
createdAt: string;
}
export interface ServiceCaseDetail extends ServiceCaseListItem {
regionCode: string;
beneficiaryMobile: string | null;
beneficiaryMobileMasked: string | null;
serviceLocation: string | null;
summary: string;
resultSummary: string | null;
followUpPlan: string | null;
completedAt: string | null;
assignees: CaseAssigneeItem[];
history: ServiceCaseHistoryItem[];
records: ServiceRecordItem[];
}
export interface PaginatedServiceCasesResponse {
items: ServiceCaseListItem[];
page: number;
pageSize: number;
total: number;
}
export interface ServiceCaseDetailResponse {
serviceCase: ServiceCaseDetail;
}
export interface AcceptServiceApplicationResponse {
serviceCase: ServiceCaseDetail;
idempotentReplay: boolean;
}
export interface ReplaceCaseAssigneesRequest {
collaboratorUserPublicIds: string[];
}
export interface CreateServiceRecordRequest {
clientRequestId: string;
serviceStartAt: string;
serviceEndAt?: string;
serviceType: ServiceRecordType;
content: string;
result: string;
followUpSuggestion?: string;
beneficiaryConfirmStatus: BeneficiaryConfirmStatus;
attachmentPublicIds: string[];
}
export interface UpdateServiceRecordRequest extends Omit<
CreateServiceRecordRequest,
'clientRequestId'
> {
expectedRevision: number;
}
export interface ServiceRecordResponse {
record: ServiceRecordItem;
idempotentReplay?: boolean;
}
export interface CreateUploadIntentRequest {
clientRequestId: string;
filename: string;
mimeType: 'image/jpeg' | 'image/png' | 'image/webp';
size: number;
sha256?: string;
}
export interface CreateUploadIntentResponse {
file: ServiceRecordAttachment;
uploadUrl: string;
uploadHeaders: Record<string, string>;
expiresAt: string;
idempotentReplay: boolean;
}
export interface ConfirmUploadResponse {
file: ServiceRecordAttachment;
}
export interface DownloadUrlResponse {
downloadUrl: string;
expiresAt: string;
}