import { IsNotEmpty, IsOptional, IsString, IsInt, IsArray, IsDateString, ArrayNotEmpty, Matches, Min, Max, MaxLength } from "class-validator"; export class CustomerVisitQueryDTO { @IsOptional() @IsInt() @Min(1) page?: number = 1; @IsOptional() @IsInt() @Min(1) @Max(200) pageSize?: number = 10; @IsOptional() @IsString() @MaxLength(64) keyword?: string; @IsOptional() @IsInt() customerId?: number; @IsOptional() @IsInt() salesmanId?: number; @IsOptional() @IsInt() @Min(1) @Max(5) visitType?: number; @IsOptional() @IsInt() @Min(0) @Max(5) status?: number; @IsOptional() @IsInt() @Min(1) @Max(3) priority?: number; // 计划拜访日期窗口(YYYY-MM-DD) @IsOptional() @IsString() @MaxLength(10) planStartDate?: string; @IsOptional() @IsString() @MaxLength(10) planEndDate?: string; } export class CustomerVisitCreateDTO { @IsNotEmpty({ message: '所属客户不能为空' }) @IsInt() customerId!: number; @IsOptional() @IsString() @MaxLength(64) @Matches(/^[^<>]*$/) visitPerson?: string; @IsOptional() @Matches(/^1[3-9]\d{9}$/, { message: '手机号格式不正确' }) visitPhone?: string; @IsOptional() @IsString() @MaxLength(2000) @Matches(/^[^<>]*$/) visitContent?: string; @IsOptional() @IsString() @MaxLength(10) visitTime?: string; @IsOptional() @IsInt() salesmanId?: number; @IsOptional() @IsInt() @Min(0) @Max(1) willPlan?: number; @IsOptional() @IsInt() @Min(1) @Max(5) visitType?: number; @IsOptional() @IsInt() @Min(1) @Max(4) visitMode?: number; @IsOptional() @IsInt() @Min(1) @Max(3) priority?: number; @IsOptional() @IsString() @MaxLength(128) @Matches(/^[^<>]*$/) subject?: string; @IsOptional() @IsDateString() planTime?: string; @IsOptional() @IsDateString() planEndTime?: string; @IsOptional() @IsString() @MaxLength(128) @Matches(/^[^<>]*$/) spaceDesc?: string; @IsOptional() @IsInt() @Min(1) @Max(3) intentLevel?: number; @IsOptional() @IsString() @MaxLength(64) @Matches(/^[^<>]*$/) intentDesc?: string; @IsOptional() @IsString() @MaxLength(512) @Matches(/^[^<>]*$/) demands?: string; @IsOptional() @IsString() @MaxLength(128) @Matches(/^[^<>]*$/) companion?: string; @IsOptional() @IsString() @MaxLength(64) @Matches(/^[^<>]*$/) receptionPerson?: string; @IsOptional() @IsString() @MaxLength(64) @Matches(/^[^<>]*$/) receptionTitle?: string; @IsOptional() @Matches(/^1[3-9]\d{9}$/, { message: '接待人联系方式格式不正确' }) receptionPhone?: string; @IsOptional() @IsString() @MaxLength(64) @Matches(/^[^<>]*$/) resultRating?: string; @IsOptional() @IsInt() @Min(0) @Max(5) status?: number; @IsOptional() @IsString() @MaxLength(10) nextVisitTime?: string; @IsOptional() @IsString() @MaxLength(128) @Matches(/^[^<>]*$/) nextAction?: string; // 现场照片对象 key 列表(对象存储),最多 2 张 @IsOptional() @IsArray() @MaxLength(2) photos?: string[]; } export class CustomerVisitUpdateDTO extends CustomerVisitCreateDTO { @IsOptional() @IsInt() declare customerId: number; @IsOptional() @IsInt() @Min(0) @Max(5) declare status?: number; @IsOptional() @IsInt() @Min(0) @Max(1) declare checkinStatus?: number; @IsOptional() @IsDateString() declare checkinTime?: string; @IsOptional() declare checkinLng?: number; @IsOptional() declare checkinLat?: number; } // 批量安排拜访:多选客户 + 统一计划信息,批量生成待执行(status=0)记录 export class CustomerVisitBatchCreateDTO { @IsArray() @ArrayNotEmpty({ message: '请至少选择一个客户' }) @IsInt({ each: true }) customerIds!: number[]; @IsNotEmpty({ message: '计划拜访时间不能为空' }) @IsDateString() planTime!: string; @IsOptional() @IsDateString() planEndTime?: string; @IsOptional() @IsInt() salesmanId?: number; @IsOptional() @IsInt() @Min(1) @Max(5) visitType?: number; @IsOptional() @IsInt() @Min(1) @Max(4) visitMode?: number; @IsOptional() @IsInt() @Min(1) @Max(3) priority?: number; @IsOptional() @IsString() @MaxLength(128) @Matches(/^[^<>]*$/) subject?: string; } // 状态流转(执行跟进 / 审核现场取证 / 取消 / 归档) export class CustomerVisitStatusDTO { @IsNotEmpty({ message: '目标状态不能为空' }) @IsInt() @Min(0) @Max(5) status!: number; @IsOptional() @IsDateString() checkinTime?: string; @IsOptional() @IsString() @MaxLength(64) @Matches(/^[^<>]*$/) resultRating?: string; @IsOptional() @IsInt() surveyOrderId?: number; @IsOptional() @IsString() @MaxLength(64) @Matches(/^[^<>]*$/) projectNo?: string; }