unit uWorkOrderRecordUnit; interface uses System.SysUtils, System.Classes, Data.DB, System.DateUtils, System.JSON, System.JSON.Types, System.JSON.Readers, System.JSON.Writers; type { 必须字段验证异常 } ERequiredFieldException = class(Exception); { JSON转换异常 } EJSONConversionException = class(Exception); { 工单记录结构 - 对应数据库工单主表 } TWorkOrderRecord = record id: Int64; // id - 主键 number: string; // 订单编号 - 必须字段 priority: Integer; // 优先级 annex: string; // 附件 name: string; // 工单名称 source_type: string; // 来源类型 source_system: string; // 来源系统 source_info: string; // 来源信息 batch_number: string; // 批次号 material_id: Int64; // 产品ID - 必须字段 material_number: string; // 产品编号 material_name: string; // 产品名称 - 必须字段 specification: string; // 规格 material_unit_id: Int64; // 单位ID - 必须字段 material_unit_name: string; // 单位名称 - 必须字段 quantity: Double; // 生产数量 - 必须字段 begin_pro_date: TDate; // 开始生产日期 plan_finish_date: TDate; // 计划完成日期 real_finish_date: TDate; // 实际完成日期 pro_status: string; // 生产状态 remark: string; // 备注 create_by: string; // 创建人 - 必须字段 create_time: TDateTime; // 创建时间 - 必须字段 update_by: string; // 更新人 update_time: TDateTime; // 更新时间 status: string; // 状态 - 必须字段,默认'A' is_postpone: string; // 是否超期 - 必须字段,默认'N' repair_flag: string; // 返修标记,默认'N' extend_field: string; // 扩展字段 custom_sort: Integer; // 自定义排序 route_id: string; // 路由ID { 初始化记录为默认值 } procedure Initialize; { 检查记录是否为空(新记录) } function IsEmpty: Boolean; { 将记录转换为JSON对象 } function ToJSONObject: TJSONObject; { 将记录转换为JSON字符串 } function ToJSONString: string; end; { 工单映射器类 - 负责数据转换和验证 } TWorkOrderMapper = class public { 创建并返回一个具有默认值的新记录 } class function CreateDefaultRecord: TWorkOrderRecord; { 从数据集转换为记录 } class function DataSetToRecord(DataSet: TDataSet): TWorkOrderRecord; { 从JSON对象转换为记录 } class function JSONToRecord(JSON: TJSONObject): TWorkOrderRecord; overload; { 从JSON字符串转换为记录 } class function JSONToRecord(const JSONString: string): TWorkOrderRecord; overload; { 将记录转换为参数,用于数据库操作 } class procedure RecordToParams(const ARecord: TWorkOrderRecord; Params: TParams); { 验证记录必须字段 } class function ValidateRequiredFields(const ARecord: TWorkOrderRecord): Boolean; { 获取验证错误信息 } class function GetValidationErrors(const ARecord: TWorkOrderRecord): string; end; implementation { TWorkOrderRecord } procedure TWorkOrderRecord.Initialize; begin id := 0; number := ''; priority := 0; annex := ''; name := ''; source_type := ''; source_system := ''; source_info := ''; batch_number := ''; material_id := 0; material_number := ''; material_name := ''; specification := ''; material_unit_id := 0; material_unit_name := ''; quantity := 0.0; begin_pro_date := 0; plan_finish_date := 0; real_finish_date := 0; pro_status := ''; remark := ''; create_by := ''; create_time := 0; update_by := ''; update_time := 0; status := 'A'; // 默认状态为'A' is_postpone := 'N'; // 默认不超期 repair_flag := 'N'; // 默认不返修 extend_field := ''; custom_sort := 0; route_id := ''; end; function TWorkOrderRecord.IsEmpty: Boolean; begin Result := (id = 0) and (number = '') and (material_id = 0); end; function TWorkOrderRecord.ToJSONObject: TJSONObject; begin Result := TJSONObject.Create; try Result.AddPair('id', TJSONNumber.Create(id)); Result.AddPair('number', number); if priority <> 0 then Result.AddPair('priority', TJSONNumber.Create(priority)) else Result.AddPair('priority', TJSONNull.Create); Result.AddPair('annex', annex); Result.AddPair('name', name); Result.AddPair('source_type', source_type); Result.AddPair('source_system', source_system); Result.AddPair('source_info', source_info); Result.AddPair('batch_number', batch_number); Result.AddPair('material_id', TJSONNumber.Create(material_id)); Result.AddPair('material_number', material_number); Result.AddPair('material_name', material_name); Result.AddPair('specification', specification); Result.AddPair('material_unit_id', TJSONNumber.Create(material_unit_id)); Result.AddPair('material_unit_name', material_unit_name); Result.AddPair('quantity', TJSONNumber.Create(quantity)); if begin_pro_date > 0 then Result.AddPair('begin_pro_date', FormatDateTime('yyyy-mm-dd', begin_pro_date)) else Result.AddPair('begin_pro_date', TJSONNull.Create); if plan_finish_date > 0 then Result.AddPair('plan_finish_date', FormatDateTime('yyyy-mm-dd', plan_finish_date)) else Result.AddPair('plan_finish_date', TJSONNull.Create); if real_finish_date > 0 then Result.AddPair('real_finish_date', FormatDateTime('yyyy-mm-dd', real_finish_date)) else Result.AddPair('real_finish_date', TJSONNull.Create); Result.AddPair('pro_status', pro_status); Result.AddPair('remark', remark); Result.AddPair('create_by', create_by); Result.AddPair('create_time', FormatDateTime('yyyy-mm-dd hh:nn:ss', create_time)); if update_time > 0 then Result.AddPair('update_time', FormatDateTime('yyyy-mm-dd hh:nn:ss', update_time)) else Result.AddPair('update_time', TJSONNull.Create); Result.AddPair('update_by', update_by); Result.AddPair('status', status); Result.AddPair('is_postpone', is_postpone); Result.AddPair('repair_flag', repair_flag); Result.AddPair('extend_field', extend_field); if custom_sort <> 0 then Result.AddPair('custom_sort', TJSONNumber.Create(custom_sort)) else Result.AddPair('custom_sort', TJSONNull.Create); Result.AddPair('route_id', route_id); except Result.Free; raise; end; end; function TWorkOrderRecord.ToJSONString: string; var JSONObj: TJSONObject; begin JSONObj := ToJSONObject; try Result := JSONObj.ToString; finally JSONObj.Free; end; end; { TWorkOrderMapper } class function TWorkOrderMapper.CreateDefaultRecord: TWorkOrderRecord; begin Result.Initialize; // 设置创建时间和创建人(通常在业务层设置) Result.create_time := Now; // 其他业务相关的默认值可以在这里设置 end; class function TWorkOrderMapper.DataSetToRecord(DataSet: TDataSet): TWorkOrderRecord; begin if not DataSet.Active then raise Exception.Create('数据集未激活'); if DataSet.IsEmpty then begin Result.Initialize; Exit; end; // 初始化记录 Result.Initialize; // 填充字段值 Result.id := DataSet.FieldByName('id').AsLargeInt; Result.number := DataSet.FieldByName('number').AsString; // 处理可能为空的字段 if not DataSet.FieldByName('priority').IsNull then Result.priority := DataSet.FieldByName('priority').AsInteger; if not DataSet.FieldByName('annex').IsNull then Result.annex := DataSet.FieldByName('annex').AsString; if not DataSet.FieldByName('name').IsNull then Result.name := DataSet.FieldByName('name').AsString; if not DataSet.FieldByName('source_type').IsNull then Result.source_type := DataSet.FieldByName('source_type').AsString; if not DataSet.FieldByName('source_system').IsNull then Result.source_system := DataSet.FieldByName('source_system').AsString; if not DataSet.FieldByName('source_info').IsNull then Result.source_info := DataSet.FieldByName('source_info').AsString; if not DataSet.FieldByName('batch_number').IsNull then Result.batch_number := DataSet.FieldByName('batch_number').AsString; Result.material_id := DataSet.FieldByName('material_id').AsLargeInt; if not DataSet.FieldByName('material_number').IsNull then Result.material_number := DataSet.FieldByName('material_number').AsString; Result.material_name := DataSet.FieldByName('material_name').AsString; if not DataSet.FieldByName('specification').IsNull then Result.specification := DataSet.FieldByName('specification').AsString; Result.material_unit_id := DataSet.FieldByName('material_unit_id').AsLargeInt; Result.material_unit_name := DataSet.FieldByName('material_unit_name').AsString; Result.quantity := DataSet.FieldByName('quantity').AsFloat; // 日期字段处理 if not DataSet.FieldByName('begin_pro_date').IsNull then Result.begin_pro_date := DataSet.FieldByName('begin_pro_date').AsDateTime; if not DataSet.FieldByName('plan_finish_date').IsNull then Result.plan_finish_date := DataSet.FieldByName('plan_finish_date').AsDateTime; if not DataSet.FieldByName('real_finish_date').IsNull then Result.real_finish_date := DataSet.FieldByName('real_finish_date').AsDateTime; if not DataSet.FieldByName('pro_status').IsNull then Result.pro_status := DataSet.FieldByName('pro_status').AsString; if not DataSet.FieldByName('remark').IsNull then Result.remark := DataSet.FieldByName('remark').AsString; Result.create_by := DataSet.FieldByName('create_by').AsString; Result.create_time := DataSet.FieldByName('create_time').AsDateTime; if not DataSet.FieldByName('update_by').IsNull then Result.update_by := DataSet.FieldByName('update_by').AsString; if not DataSet.FieldByName('update_time').IsNull then Result.update_time := DataSet.FieldByName('update_time').AsDateTime; // 设置默认值 if not DataSet.FieldByName('status').IsNull then Result.status := DataSet.FieldByName('status').AsString; if not DataSet.FieldByName('is_postpone').IsNull then Result.is_postpone := DataSet.FieldByName('is_postpone').AsString; if not DataSet.FieldByName('repair_flag').IsNull then Result.repair_flag := DataSet.FieldByName('repair_flag').AsString; if not DataSet.FieldByName('extend_field').IsNull then Result.extend_field := DataSet.FieldByName('extend_field').AsString; if not DataSet.FieldByName('custom_sort').IsNull then Result.custom_sort := DataSet.FieldByName('custom_sort').AsInteger; if not DataSet.FieldByName('route_id').IsNull then Result.route_id := DataSet.FieldByName('route_id').AsString; end; class function TWorkOrderMapper.JSONToRecord(JSON: TJSONObject): TWorkOrderRecord; var JSONValue: TJSONValue; begin if not Assigned(JSON) then raise EJSONConversionException.Create('JSON对象不能为空'); Result.Initialize; try // 解析JSON字段 JSONValue := JSON.GetValue('id'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.id := (JSONValue as TJSONNumber).AsInt64; JSONValue := JSON.GetValue('number'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.number := JSONValue.Value; JSONValue := JSON.GetValue('priority'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.priority := (JSONValue as TJSONNumber).AsInt; JSONValue := JSON.GetValue('annex'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.annex := JSONValue.Value; JSONValue := JSON.GetValue('name'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.name := JSONValue.Value; JSONValue := JSON.GetValue('source_type'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.source_type := JSONValue.Value; JSONValue := JSON.GetValue('source_system'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.source_system := JSONValue.Value; JSONValue := JSON.GetValue('source_info'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.source_info := JSONValue.Value; JSONValue := JSON.GetValue('batch_number'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.batch_number := JSONValue.Value; JSONValue := JSON.GetValue('material_id'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.material_id := (JSONValue as TJSONNumber).AsInt64; JSONValue := JSON.GetValue('material_number'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.material_number := JSONValue.Value; JSONValue := JSON.GetValue('material_name'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.material_name := JSONValue.Value; JSONValue := JSON.GetValue('specification'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.specification := JSONValue.Value; JSONValue := JSON.GetValue('material_unit_id'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.material_unit_id := (JSONValue as TJSONNumber).AsInt64; JSONValue := JSON.GetValue('material_unit_name'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.material_unit_name := JSONValue.Value; JSONValue := JSON.GetValue('quantity'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.quantity := (JSONValue as TJSONNumber).AsDouble; // 日期字段处理 JSONValue := JSON.GetValue('begin_pro_date'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.begin_pro_date := StrToDateDef(JSONValue.Value, 0); JSONValue := JSON.GetValue('plan_finish_date'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.plan_finish_date := StrToDateDef(JSONValue.Value, 0); JSONValue := JSON.GetValue('real_finish_date'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.real_finish_date := StrToDateDef(JSONValue.Value, 0); JSONValue := JSON.GetValue('pro_status'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.pro_status := JSONValue.Value; JSONValue := JSON.GetValue('remark'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.remark := JSONValue.Value; JSONValue := JSON.GetValue('create_by'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.create_by := JSONValue.Value; JSONValue := JSON.GetValue('create_time'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.create_time := StrToDateTimeDef(JSONValue.Value, 0); JSONValue := JSON.GetValue('update_by'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.update_by := JSONValue.Value; JSONValue := JSON.GetValue('update_time'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.update_time := StrToDateTimeDef(JSONValue.Value, 0); JSONValue := JSON.GetValue('status'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.status := JSONValue.Value else Result.status := 'A'; JSONValue := JSON.GetValue('is_postpone'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.is_postpone := JSONValue.Value else Result.is_postpone := 'N'; JSONValue := JSON.GetValue('repair_flag'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.repair_flag := JSONValue.Value else Result.repair_flag := 'N'; JSONValue := JSON.GetValue('extend_field'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.extend_field := JSONValue.Value; JSONValue := JSON.GetValue('custom_sort'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.custom_sort := (JSONValue as TJSONNumber).AsInt; JSONValue := JSON.GetValue('route_id'); if Assigned(JSONValue) and not (JSONValue is TJSONNull) then Result.route_id := JSONValue.Value; except on E: Exception do raise EJSONConversionException.Create('JSON转换错误: ' + E.Message); end; end; class function TWorkOrderMapper.JSONToRecord(const JSONString: string): TWorkOrderRecord; var JSONObj: TJSONObject; begin if Trim(JSONString) = '' then begin Result.Initialize; Exit; end; JSONObj := TJSONObject.ParseJSONValue(JSONString) as TJSONObject; try if not Assigned(JSONObj) then raise EJSONConversionException.Create('无效的JSON字符串'); Result := JSONToRecord(JSONObj); finally if Assigned(JSONObj) then JSONObj.Free; end; end; class procedure TWorkOrderMapper.RecordToParams(const ARecord: TWorkOrderRecord; Params: TParams); begin if not Assigned(Params) then raise Exception.Create('参数集不能为空'); // 设置参数值 Params.ParamByName('id').AsLargeInt := ARecord.id; Params.ParamByName('number').AsString := ARecord.number; if ARecord.priority <> 0 then Params.ParamByName('priority').AsInteger := ARecord.priority else Params.ParamByName('priority').Clear; if ARecord.annex <> '' then Params.ParamByName('annex').AsString := ARecord.annex else Params.ParamByName('annex').Clear; if ARecord.name <> '' then Params.ParamByName('name').AsString := ARecord.name else Params.ParamByName('name').Clear; if ARecord.source_type <> '' then Params.ParamByName('source_type').AsString := ARecord.source_type else Params.ParamByName('source_type').Clear; if ARecord.source_system <> '' then Params.ParamByName('source_system').AsString := ARecord.source_system else Params.ParamByName('source_system').Clear; if ARecord.source_info <> '' then Params.ParamByName('source_info').AsString := ARecord.source_info else Params.ParamByName('source_info').Clear; if ARecord.batch_number <> '' then Params.ParamByName('batch_number').AsString := ARecord.batch_number else Params.ParamByName('batch_number').Clear; Params.ParamByName('material_id').AsLargeInt := ARecord.material_id; if ARecord.material_number <> '' then Params.ParamByName('material_number').AsString := ARecord.material_number else Params.ParamByName('material_number').Clear; Params.ParamByName('material_name').AsString := ARecord.material_name; if ARecord.specification <> '' then Params.ParamByName('specification').AsString := ARecord.specification else Params.ParamByName('specification').Clear; Params.ParamByName('material_unit_id').AsLargeInt := ARecord.material_unit_id; Params.ParamByName('material_unit_name').AsString := ARecord.material_unit_name; Params.ParamByName('quantity').AsFloat := ARecord.quantity; // 日期字段处理 if ARecord.begin_pro_date > 0 then Params.ParamByName('begin_pro_date').AsDate := ARecord.begin_pro_date else Params.ParamByName('begin_pro_date').Clear; if ARecord.plan_finish_date > 0 then Params.ParamByName('plan_finish_date').AsDate := ARecord.plan_finish_date else Params.ParamByName('plan_finish_date').Clear; if ARecord.real_finish_date > 0 then Params.ParamByName('real_finish_date').AsDate := ARecord.real_finish_date else Params.ParamByName('real_finish_date').Clear; if ARecord.pro_status <> '' then Params.ParamByName('pro_status').AsString := ARecord.pro_status else Params.ParamByName('pro_status').Clear; if ARecord.remark <> '' then Params.ParamByName('remark').AsString := ARecord.remark else Params.ParamByName('remark').Clear; Params.ParamByName('create_by').AsString := ARecord.create_by; Params.ParamByName('create_time').AsDateTime := ARecord.create_time; if ARecord.update_by <> '' then Params.ParamByName('update_by').AsString := ARecord.update_by else Params.ParamByName('update_by').Clear; if ARecord.update_time > 0 then Params.ParamByName('update_time').AsDateTime := ARecord.update_time else Params.ParamByName('update_time').Clear; Params.ParamByName('status').AsString := ARecord.status; Params.ParamByName('is_postpone').AsString := ARecord.is_postpone; Params.ParamByName('repair_flag').AsString := ARecord.repair_flag; if ARecord.extend_field <> '' then Params.ParamByName('extend_field').AsString := ARecord.extend_field else Params.ParamByName('extend_field').Clear; if ARecord.custom_sort <> 0 then Params.ParamByName('custom_sort').AsInteger := ARecord.custom_sort else Params.ParamByName('custom_sort').Clear; if ARecord.route_id <> '' then Params.ParamByName('route_id').AsString := ARecord.route_id else Params.ParamByName('route_id').Clear; end; class function TWorkOrderMapper.ValidateRequiredFields(const ARecord: TWorkOrderRecord): Boolean; begin Result := True; // 检查必须字段 if Trim(ARecord.number) = '' then raise ERequiredFieldException.Create('编号不能为空'); if ARecord.material_id <= 0 then raise ERequiredFieldException.Create('产品ID必须大于0'); if Trim(ARecord.material_name) = '' then raise ERequiredFieldException.Create('产品名称不能为空'); if ARecord.material_unit_id <= 0 then raise ERequiredFieldException.Create('单位ID必须大于0'); if Trim(ARecord.material_unit_name) = '' then raise ERequiredFieldException.Create('单位名称不能为空'); if ARecord.quantity <= 0 then raise ERequiredFieldException.Create('生产数量必须大于0'); if Trim(ARecord.create_by) = '' then raise ERequiredFieldException.Create('创建人不能为空'); if ARecord.create_time = 0 then raise ERequiredFieldException.Create('创建时间不能为空'); if Trim(ARecord.status) = '' then raise ERequiredFieldException.Create('状态不能为空'); if Trim(ARecord.is_postpone) = '' then raise ERequiredFieldException.Create('是否超期标记不能为空'); end; class function TWorkOrderMapper.GetValidationErrors(const ARecord: TWorkOrderRecord): string; var ErrorMsg: string; begin ErrorMsg := ''; try ValidateRequiredFields(ARecord); except on E: ERequiredFieldException do ErrorMsg := E.Message; on E: Exception do ErrorMsg := '未知验证错误: ' + E.Message; end; Result := ErrorMsg; end; end.