Files
smart_grid_iot/server/src/dto/customer_visit.dto.ts
T
andyesfly 1c83e09fa1 feat: 完成客户拜访模块全功能开发
本次提交实现了完整的客户拜访业务系统:
1.  新增对象存储上传服务,支持S3兼容协议的RustFS存储
2.  完善客户拜访实体与数据库表结构,补充全业务字段
3.  开发拜访台账与记录的前后端接口,包含增删改查、批量创建、状态流转等能力
4.  新增拜访表单页面与路由,支持父级菜单补全面包屑
5.  新增勘察列表页预填客户ID跳转逻辑
6.  补充UI设计文档与页面截图
7.  调整日志中间件跳过上传路由,优化RBAC权限配置
2026-09-14 11:42:10 +08:00

88 lines
4.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
}