feat: 新增现场勘察派单全流程业务能力

1.  新增共享电工管理模块:包含实体、DTO、控制器、服务以及前后端API
2.  重构勘察工单实体与查询逻辑,完善工单全状态流程与字段
3.  新增勘察SOP模板与工单回执业务体系
4.  更新数据库字典与初始化脚本,补充业务数据
5.  新增勘察派单增删改查、指派、复核、催办等全流程接口
6.  修复用户服务角色关联SQL生成问题
7.  新增勘察派单前后端完整交互链路
This commit is contained in:
andyesfly committed 2026-09-15 08:50:55 +08:00
1 parent 1c83e09fa1
commit 6b00f61550
21 files changed
+4739 -388

No files matched your search

+83 -13
View File
@@ -1,27 +1,97 @@
import { IsNotEmpty, IsOptional, IsString, IsInt, Matches, Min, Max, MaxLength } from "class-validator";
import {
IsNotEmpty,
IsOptional,
IsString,
IsInt,
IsNumber,
IsArray,
IsDateString,
IsIn,
ArrayNotEmpty,
Matches,
Min,
Max,
MaxLength
} from "class-validator";
export class SurveyOrderQueryDTO {
@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() status?: number;
@IsOptional() @IsInt() @Min(0) @Max(5) status?: number;
@IsOptional() @IsInt() @Min(1) @Max(3) priority?: number;
@IsOptional() @IsInt() workerId?: number;
@IsOptional() @IsInt() salesmanId?: number;
// 计划到场日期窗口(YYYY-MM-DD)
@IsOptional() @IsString() @MaxLength(10) planStartDate?: string;
@IsOptional() @IsString() @MaxLength(10) planEndDate?: string;
// 是否包含草稿:字符串或数字,服务层统一转 number 判定
@IsOptional() includeDraft?: string | number;
}
export class SurveyOrderCreateDTO {
@IsNotEmpty({ message: '所属客户不能为空' }) @IsInt() customerId!: number;
@IsOptional() @IsString() @MaxLength(64) @Matches(/^[^<>]*$/) surveyor?: string;
@IsOptional() @IsString() @MaxLength(128) @Matches(/^[^<>]*$/) taskTitle?: string;
@IsOptional() @IsString() @MaxLength(255) @Matches(/^[^<>]*$/) siteLocation?: string;
@IsOptional() @IsInt() @Min(1) @Max(3) priority?: number;
@IsOptional() @IsString() @MaxLength(128) @Matches(/^[^<>]*$/) loopSpec?: string;
@IsOptional() @IsInt() @Min(0) loopCount?: number;
@IsOptional() @IsString() @MaxLength(128) @Matches(/^[^<>]*$/) techContact?: string;
@IsOptional() @IsDateString() planStart?: string;
@IsOptional() @IsDateString() planEnd?: string;
@IsOptional() @IsInt() @Min(0) expectMinutes?: number;
@IsOptional() @IsString() @MaxLength(32) @Matches(/^[^<>]*$/) slaLevel?: string;
@IsOptional() @IsInt() workerId?: number;
@IsOptional() @IsString() surveyTime?: string;
@IsOptional() @IsInt() salesmanId?: number;
@IsOptional() @IsNumber() estimatedFee?: number;
@IsOptional() @IsString() @MaxLength(2000) @Matches(/^[^<>]*$/) remark?: string;
@IsOptional() @IsInt() @Min(0) @Max(2) status?: number;
@IsOptional() @IsString() @MaxLength(32) @Matches(/^[^<>]*$/) source?: string;
@IsOptional() @IsInt() @Min(0) @Max(1) isDraft?: number;
// 向导选用的 SOP 模板编码,用于生成回执记录(非持久化列)
@IsOptional() @IsString() @MaxLength(64) @Matches(/^[^<>]*$/) templateCode?: string;
}
export class SurveyOrderUpdateDTO {
@IsOptional() @IsInt() customerId?: number;
@IsOptional() @IsString() @MaxLength(64) @Matches(/^[^<>]*$/) surveyor?: string;
@IsOptional() @IsInt() workerId?: number;
@IsOptional() @IsString() surveyTime?: string;
@IsOptional() @IsString() @MaxLength(2000) @Matches(/^[^<>]*$/) remark?: string;
@IsOptional() @IsInt() @Min(0) @Max(2) status?: number;
}
export class SurveyOrderUpdateDTO extends SurveyOrderCreateDTO {
@IsOptional() @IsInt() declare customerId: number;
@IsOptional() @IsString() @MaxLength(128) @Matches(/^[^<>]*$/) declare taskTitle?: string;
@IsOptional() @IsString() @MaxLength(255) @Matches(/^[^<>]*$/) declare siteLocation?: string;
@IsOptional() @IsInt() @Min(1) @Max(3) declare priority?: number;
@IsOptional() @IsString() @MaxLength(128) @Matches(/^[^<>]*$/) declare loopSpec?: string;
@IsOptional() @IsInt() @Min(0) declare loopCount?: number;
@IsOptional() @IsString() @MaxLength(128) @Matches(/^[^<>]*$/) declare techContact?: string;
@IsOptional() @IsDateString() declare planStart?: string;
@IsOptional() @IsDateString() declare planEnd?: string;
@IsOptional() @IsInt() @Min(0) declare expectMinutes?: number;
@IsOptional() @IsString() @MaxLength(32) @Matches(/^[^<>]*$/) declare slaLevel?: string;
@IsOptional() @IsInt() declare workerId?: number;
@IsOptional() @IsInt() declare salesmanId?: number;
@IsOptional() @IsNumber() declare estimatedFee?: number;
@IsOptional() @IsString() @MaxLength(2000) @Matches(/^[^<>]*$/) declare remark?: string;
@IsOptional() @IsString() @MaxLength(32) @Matches(/^[^<>]*$/) declare source?: string;
@IsOptional() @IsInt() @Min(0) @Max(1) declare isDraft?: number;
@IsOptional() @IsString() @MaxLength(64) @Matches(/^[^<>]*$/) declare templateCode?: string;
}
// 指派 / 改派(0待指派→指派,1现场勘察中→改派)
export class SurveyOrderAssignDTO {
@IsNotEmpty({ message: '请选择共享电工' }) @IsInt() workerId!: number;
@IsOptional() @IsNumber() estimatedFee?: number;
}
// 批量指派
export class SurveyOrderBatchAssignDTO {
@IsArray() @ArrayNotEmpty({ message: '请至少选择一条工单' }) @IsInt({ each: true }) ids!: number[];
@IsNotEmpty({ message: '请选择共享电工' }) @IsInt() workerId!: number;
}
// 技术复核
export class SurveyOrderReviewDTO {
@IsIn(['pass', 'reject'], { message: '复核结果不合法' }) result!: 'pass' | 'reject';
@IsOptional() @IsString() @MaxLength(500) @Matches(/^[^<>]*$/) remark?: string;
}
// 取消工单
export class SurveyOrderCancelDTO {
@IsOptional() @IsString() @MaxLength(255) @Matches(/^[^<>]*$/) reason?: string;
}