import random from typing import Union, Optional, Callable import pandas as pd from sqlalchemy import select, delete from tornado_swagger.model import register_swagger_model from wtforms import StringField, TextAreaField, IntegerField, FloatField from wtforms.validators import Length import models from models.common_model import CommonModel from models.db_models import TD3iDcmTaskFormDatum from paste.core.logging import echo_log from paste.util.pagination import Pagination from paste.web.form import ModelForm class DcmTaskFormDatumForm(ModelForm): """ 企业待办表单数据验证类(完全映射 TD3iDcmTaskFormDatum 字段)。 用于验证和处理数字化城市管理信息系统中企业待办的表单数据。 字段完全对应数据库表 t_d3i_dcm_task_form_data 的结构。 """ # 基础信息 id = IntegerField('主键ID') rec_id = IntegerField('记录ID') rec_disp_num = StringField('显示编号', validators=[Length(max=50, message='显示编号长度不能超过50字符')]) rec_type_id = IntegerField('类型ID') rec_type_name = StringField('案件类型', validators=[Length(max=100, message='案件类型长度不能超过100字符')]) # 任务信息 task_num = StringField('任务号', validators=[Length(max=50, message='任务号长度不能超过50字符')]) other_task_num = StringField('第三方任务号', validators=[Length(max=100, message='第三方任务号长度不能超过100字符')]) act_property_id = IntegerField('任务属性ID') # 业务信息 biz_id = IntegerField('业务ID') biz_name = StringField('业务名称', validators=[Length(max=200, message='业务名称长度不能超过200字符')]) sys_id = IntegerField('系统ID') # 地址与坐标 address = TextAreaField('地址描述', validators=[Length(max=65535, message='地址描述长度不能超过65535字符')]) district_name = StringField('所属区域', validators=[Length(max=50, message='所属区域长度不能超过50字符')]) coordinate_x = FloatField('经度') coordinate_y = FloatField('纬度') lonlat_x = FloatField('经纬度X') lonlat_y = FloatField('经纬度Y') # 事件信息 event_type_id = IntegerField('问题类型ID') event_type_name = StringField('问题类型', validators=[Length(max=100, message='问题类型长度不能超过100字符')]) event_src_id = IntegerField('问题来源ID') event_src_name = StringField('问题来源', validators=[Length(max=100, message='问题来源长度不能超过100字符')]) event_desc = TextAreaField('问题描述', validators=[Length(max=65535, message='问题描述长度不能超过65535字符')]) max_event_type_id = IntegerField('最大事件类型ID') max_event_type_name = StringField('最大事件类型名称', validators=[Length(max=200, message='最大事件类型名称长度不能超过200字符')]) # 分类信息 main_type_id = IntegerField('大类ID') main_type_name = StringField('大类名称', validators=[Length(max=100, message='大类名称长度不能超过100字符')]) sub_type_id = IntegerField('小类ID') sub_type_name = StringField('小类名称', validators=[Length(max=100, message='小类名称长度不能超过100字符')]) third_type_id = IntegerField('第三级类型ID') third_type_name = StringField('第三级类型名称', validators=[Length(max=100, message='第三级类型名称长度不能超过100字符')]) forth_type_id = IntegerField('第四级类型ID') forth_type_name = StringField('第四级类型名称', validators=[Length(max=100, message='第四级类型名称长度不能超过100字符')]) fifth_type_id = IntegerField('第五级类型ID') fifth_type_name = StringField('第五级类型名称', validators=[Length(max=100, message='第五级类型名称长度不能超过100字符')]) sixth_type_id = IntegerField('第六级类型ID') sixth_type_name = StringField('第六级类型名称', validators=[Length(max=100, message='第六级类型名称长度不能超过100字符')]) seventh_type_id = IntegerField('第七级类型ID') seventh_type_name = StringField('第七级类型名称', validators=[Length(max=100, message='第七级类型名称长度不能超过100字符')]) # 时间与状态 create_time = IntegerField('创建时间戳') update_time = IntegerField('更新时间戳') deadline_time = IntegerField('处理截止时间戳') warning_time = IntegerField('处理预警时间戳') occur_time = IntegerField('发生时间戳') dispatch_time = IntegerField('派遣时间戳') archive_time = IntegerField('归档时间戳') cancel_time = IntegerField('取消时间戳') refresh_time = IntegerField('刷新时间戳') refresh_start_time = IntegerField('刷新开始时间戳') check_send_time = IntegerField('核查发送时间戳') check_reply_time = IntegerField('核查回复时间戳') func_deadline = IntegerField('职能部门截止时间戳') func_deal_time = IntegerField('职能部门处理时间戳') proc_start_time = IntegerField('处理开始时间戳') custom_deadline = IntegerField('自定义截止时间戳') patroltask_deadline_time = IntegerField('巡查任务截止时间戳') # 时限描述 deadline_char = StringField('时限描述', validators=[Length(max=50, message='时限描述长度不能超过50字符')]) func_limit_char = StringField('职能部门时限描述', validators=[Length(max=50, message='职能部门时限描述长度不能超过50字符')]) rec_remain_char = StringField('记录剩余时间描述', validators=[Length(max=50, message='记录剩余时间描述长度不能超过50字符')]) rec_used_char = StringField('记录已用时间描述', validators=[Length(max=50, message='记录已用时间描述长度不能超过50字符')]) # 数值型字段 rec_remain = FloatField('记录剩余时间') rec_used = FloatField('记录已用时间') rec_warning = FloatField('记录预警时间') rec_deadline = FloatField('记录时限') # 部门与网格 func_part_id = IntegerField('职能部门ID') func_part_name = StringField('职能部门名称', validators=[Length(max=200, message='职能部门名称长度不能超过200字符')]) func_part_list_id = StringField('职能部门列表ID', validators=[Length(max=100, message='职能部门列表ID长度不能超过100字符')]) func_part_list_name = StringField('职能部门列表名称', validators=[Length(max=200, message='职能部门列表名称长度不能超过200字符')]) specify_func_id = IntegerField('指定职能部门ID') specify_func_name = StringField('指定职能部门名称', validators=[Length(max=200, message='指定职能部门名称长度不能超过200字符')]) specify_competent_func_id = IntegerField('指定主管职能部门ID') specify_competent_func_name = StringField('指定主管职能部门名称', validators=[Length(max=200, message='指定主管职能部门名称长度不能超过200字符')]) first_depart_name = StringField('一级专业部门', validators=[Length(max=100, message='一级专业部门长度不能超过100字符')]) second_depart_name = StringField('二级专业部门', validators=[Length(max=100, message='二级专业部门长度不能超过100字符')]) # 地理信息 district_id = IntegerField('区域ID') street_id = IntegerField('街道ID') street_name = StringField('街道名称', validators=[Length(max=200, message='街道名称长度不能超过200字符')]) community_id = IntegerField('社区ID') community_name = StringField('社区名称', validators=[Length(max=200, message='社区名称长度不能超过200字符')]) duty_grid_id = IntegerField('责任网格ID') duty_grid_name = StringField('责任网格名称', validators=[Length(max=200, message='责任网格名称长度不能超过200字符')]) duty_region_id = IntegerField('责任区域ID') duty_region_name = StringField('责任区域名称', validators=[Length(max=200, message='责任区域名称长度不能超过200字符')]) duty_district_id = IntegerField('责任区域ID') duty_district_name = StringField('责任区域名称', validators=[Length(max=200, message='责任区域名称长度不能超过200字符')]) duty_street_id = IntegerField('责任街道ID') duty_street_name = StringField('责任街道名称', validators=[Length(max=200, message='责任街道名称长度不能超过200字符')]) duty_community_id = IntegerField('责任社区ID') duty_community_name = StringField('责任社区名称', validators=[Length(max=200, message='责任社区名称长度不能超过200字符')]) law_duty_grid_id = IntegerField('法律责任网格ID') law_duty_grid_name = StringField('法律责任网格名称', validators=[Length(max=200, message='法律责任网格名称长度不能超过200字符')]) deal_duty_grid_id = IntegerField('处置责任网格ID') deal_duty_grid_name = StringField('处置责任网格名称', validators=[Length(max=200, message='处置责任网格名称长度不能超过200字符')]) # 人员信息 patrol_id = IntegerField('巡查员ID') patrol_name = StringField('巡查员名称', validators=[Length(max=200, message='巡查员名称长度不能超过200字符')]) accepter_id = IntegerField('受理人ID') accepter_name = StringField('受理人姓名', validators=[Length(max=100, message='受理人姓名长度不能超过100字符')]) human_id = IntegerField('操作人ID') human_name = StringField('操作人名称', validators=[Length(max=255, message='操作人名称长度不能超过255字符')]) reporter_name = StringField('举报人姓名', validators=[Length(max=100, message='举报人姓名长度不能超过100字符')]) reporter_contact = StringField('举报电话', validators=[Length(max=50, message='举报电话长度不能超过50字符')]) tell_num = StringField('联系电话', validators=[Length(max=50, message='联系电话长度不能超过50字符')]) # 状态与标识 read_flag = IntegerField('是否已读(0未读,1已读)') reply_intime = IntegerField('是否两小时回复(0无需回复,1待回复,2已回复,3超时,4无需回复已恢复)') return_visit_flag = IntegerField('回访标识(0无需,1待回访,2已回访)') urgency_level = IntegerField('紧急程度(0正常,1紧急)') urgent_flag = IntegerField('紧急标识') func_forbid_reporter_info_flag = IntegerField('是否禁止举报人信息') public_flag = IntegerField('公开标志') locked_flag = IntegerField('锁定标识') transited_flag = IntegerField('转交标识') split_rec_flag = IntegerField('拆分记录标识') enable_check_msg = IntegerField('启用核查消息') no_return_visit_flag = IntegerField('无需回访标识') common_rec_type_flag = StringField('通用记录类型标识', validators=[Length(max=50, message='通用记录类型标识长度不能超过50字符')]) common_rec_attr_flag = StringField('通用记录属性标识', validators=[Length(max=50, message='通用记录属性标识长度不能超过50字符')]) send_pub_check_task_flag = IntegerField('发送公共核查任务标识') reply_flag = StringField('回复标识', validators=[Length(max=50, message='回复标识长度不能超过50字符')]) whistle_flag = StringField('吹哨标识', validators=[Length(max=50, message='吹哨标识长度不能超过50字符')]) repeat_state = StringField('重复状态', validators=[Length(max=50, message='重复状态长度不能超过50字符')]) report_state = StringField('上报状态', validators=[Length(max=50, message='上报状态长度不能超过50字符')]) dispose_state = IntegerField('处置状态') pre_dispose_state = StringField('预处置状态', validators=[Length(max=50, message='预处置状态长度不能超过50字符')]) undertake_user_name = StringField('承办人员', validators=[Length(max=50, message='承办人员长度不能超过50字符')]) undertake_phone = StringField('联系电话', validators=[Length(max=50, message='联系电话长度不能超过50字符')]) deal_person_org = StringField('承办部门', validators=[Length(max=50, message='承办部门长度不能超过50字符')]) # 媒体信息 media_upload_num = IntegerField('媒体上传数量') media_upload_total_num = IntegerField('媒体上传总数') media_upload_state = StringField('媒体上传状态', validators=[Length(max=50, message='媒体上传状态长度不能超过50字符')]) media_check_num = IntegerField('媒体核查数量') media_check_total_num = IntegerField('媒体核查总数') media_verify_num = IntegerField('媒体核实数量') media_verify_total_num = IntegerField('媒体核实总数') media_self_deal_num = IntegerField('自行处置媒体数量') media_self_deal_total_num = IntegerField('自行处置媒体总数') media_review_num = IntegerField('复核媒体数量') media_review_total_num = IntegerField('复核媒体总数') report_pic_num = IntegerField('上报图片数量') report_pic_total_num = IntegerField('上报图片总数') report_video_num = IntegerField('上报视频数量') report_video_total_num = IntegerField('上报视频总数') report_wav_num = IntegerField('上报音频数量') report_wav_total_num = IntegerField('上报音频总数') check_pic_num = IntegerField('核查图片数量') check_pic_total_num = IntegerField('核查图片总数') check_video_num = IntegerField('核查视频数量') check_video_total_num = IntegerField('核查视频总数') check_wav_num = IntegerField('核查音频数量') check_wav_total_num = IntegerField('核查音频总数') verify_pic_num = IntegerField('核实图片数量') verify_pic_total_num = IntegerField('核实图片总数') verify_video_num = IntegerField('核实视频数量') verify_video_total_num = IntegerField('核实视频总数') verify_wav_num = IntegerField('核实音频数量') verify_wav_total_num = IntegerField('核实音频总数') self_deal_pic_num = IntegerField('自行处置图片数量') self_deal_pic_total_num = IntegerField('自行处置图片总数') self_deal_video_num = IntegerField('自行处置视频数量') self_deal_video_total_num = IntegerField('自行处置视频总数') self_deal_wav_num = IntegerField('自行处置音频数量') self_deal_wav_total_num = IntegerField('自行处置音频总数') review_pic_num = IntegerField('复核图片数量') review_video_total_num = IntegerField('复核视频总数') review_wav_num = IntegerField('复核音频数量') review_wav_total_num = IntegerField('复核音频总数') # 媒体路径与属性 media_url = StringField('内部访问URL', validators=[Length(max=512, message='内部访问URL长度不能超过512字符')]) mms_pic_path = StringField('彩信图片路径', validators=[Length(max=500, message='彩信图片路径长度不能超过500字符')]) media_path = StringField('服务器存储路径', validators=[Length(max=512, message='服务器存储路径长度不能超过512字符')]) media_type = StringField('媒体类型', validators=[Length(max=50, message='媒体类型长度不能超过50字符')]) media_usage = StringField('使用场景', validators=[Length(max=100, message='使用场景长度不能超过100字符')]) media_server_name = StringField('媒体服务器名称', validators=[Length(max=100, message='媒体服务器名称长度不能超过100字符')]) media_property = IntegerField('媒体属性') media_uploaded_name = StringField('上传时的原始文件名', validators=[Length(max=255, message='上传时的原始文件名长度不能超过255字符')]) media_shot = StringField('截图标识或路径', validators=[Length(max=255, message='截图标识或路径长度不能超过255字符')]) media_label_type_id = IntegerField('标签类型ID') media_default_url = StringField('外部可访问URL', validators=[Length(max=512, message='外部可访问URL长度不能超过512字符')]) display_order = IntegerField('显示顺序') store_type_id = IntegerField('存储类型ID') special_item_image_type = StringField('特殊图片类型', validators=[Length(max=100, message='特殊图片类型长度不能超过100字符')]) height = IntegerField('图片高度') width = IntegerField('图片宽度') send_flag = IntegerField('发送标志') public_flag = IntegerField('公开标志') gen_thumb = IntegerField('是否生成缩略图') can_delete = IntegerField('是否可删除') delete_flag = IntegerField('删除标记') delete_reason = TextAreaField('删除原因', validators=[Length(max=65535, message='删除原因长度不能超过65535字符')]) # 地理与位置 pos_type = StringField('位置类型', validators=[Length(max=50, message='位置类型长度不能超过50字符')]) view_angle = StringField('视角', validators=[Length(max=100, message='视角长度不能超过100字符')]) view_image_name = StringField('视图图片名称', validators=[Length(max=200, message='视图图片名称长度不能超过200字符')]) view_image_x = FloatField('视图图片X坐标') view_image_y = FloatField('视图图片Y坐标') view_pos_x = FloatField('视图位置X坐标') view_pos_y = FloatField('视图位置Y坐标') # 附件与标识 attach_rec_flag = StringField('附件记录标识', validators=[Length(max=50, message='附件记录标识长度不能超过50字符')]) gather_flag = StringField('汇总标识', validators=[Length(max=50, message='汇总标识长度不能超过50字符')]) link_field_value = StringField('关联字段值', validators=[Length(max=500, message='关联字段值长度不能超过500字符')]) link_field_display_value = StringField('关联字段显示值', validators=[Length(max=500, message='关联字段显示值长度不能超过500字符')]) unique_id = StringField('唯一标识', validators=[Length(max=100, message='唯一标识长度不能超过100字符')]) third_unique_id = StringField('第三方唯一标识', validators=[Length(max=100, message='第三方唯一标识长度不能超过100字符')]) equal_group_id = IntegerField('等值组ID') rec_category_id = IntegerField('记录类别ID') # 处置与审核 dispatch_opinion = StringField('派遣意见', validators=[Length(max=500, message='派遣意见长度不能超过500字符')]) revise_opinion = StringField('修订意见', validators=[Length(max=500, message='修订意见长度不能超过500字符')]) reply_opinion = StringField('回复意见', validators=[Length(max=500, message='回复意见长度不能超过500字符')]) new_inst_advise = StringField('立案建议', validators=[Length(max=500, message='立案建议长度不能超过500字符')]) new_inst_cond_id = IntegerField('立案条件ID') new_inst_cond_name = StringField('立案条件', validators=[Length(max=200, message='立案条件长度不能超过200字符')]) case_closure_condition = StringField('结案条件', validators=[Length(max=200, message='结案条件长度不能超过200字符')]) # 特殊字段 event_marks = StringField('事件标记', validators=[Length(max=500, message='事件标记长度不能超过500字符')]) deduction = StringField('扣减', validators=[Length(max=100, message='扣减长度不能超过100字符')]) event_property_id = IntegerField('事件属性ID') event_property_name = StringField('事件属性名称', validators=[Length(max=200, message='事件属性名称长度不能超过200字符')]) city_village_flag = StringField('城乡标识', validators=[Length(max=50, message='城乡标识长度不能超过50字符')]) force_handle_flag = StringField('强制处理标识', validators=[Length(max=50, message='强制处理标识长度不能超过50字符')]) auto_check_count = IntegerField('自动核查次数') deal_evaluate_ids = StringField('处置评价ID列表', validators=[Length(max=200, message='处置评价ID列表长度不能超过200字符')]) newinst_no_transit = StringField('立案不转交', validators=[Length(max=50, message='立案不转交长度不能超过50字符')]) super_rec_id = IntegerField('上级记录ID') site_num = StringField('站点编号', validators=[Length(max=50, message='站点编号长度不能超过50字符')]) difficult_type_id = IntegerField('困难类型ID') event_district_grade_id = IntegerField('事件区域等级ID') event_district_grade_name = StringField('事件区域等级名称', validators=[Length(max=100, message='事件区域等级名称长度不能超过100字符')]) cus_grid_code = StringField('自定义网格编码', validators=[Length(max=100, message='自定义网格编码长度不能超过100字符')]) site_id = IntegerField('站点ID') shop_id = IntegerField('商铺ID') shop_name = StringField('商铺名称', validators=[Length(max=200, message='商铺名称长度不能超过200字符')]) spec_type_id = IntegerField('特殊类型ID') spec_type_name = StringField('特殊类型名称', validators=[Length(max=100, message='特殊类型名称长度不能超过100字符')]) proc_account_state_id = IntegerField('处理账户状态ID') check_type_id = IntegerField('核查类型ID') rec_analysis_type_id = IntegerField('记录分析类型ID') proc_time_state_id = IntegerField('处理流程状态ID') proc_ard_state_id = IntegerField('处理仲裁状态ID') proc_enq_state_id = IntegerField('处理询问状态ID') proc_sup_state_id = IntegerField('处理监督状态ID') func_time_state_id = IntegerField('职能部门时间状态ID') check_msg_state_id = IntegerField('核查消息状态ID') verify_msg_state_id = IntegerField('核实消息状态ID') regather_msg_state_id = IntegerField('重新采集消息状态ID') supervision_check_state_id = IntegerField('监督核查状态ID') self_deal_msg_state_id = IntegerField('自行处置消息状态ID') review_msg_state_id = IntegerField('复核消息状态ID') proc_press_state_id = IntegerField('处理压力状态ID') hot_area = StringField('热点区域', validators=[Length(max=100, message='热点区域长度不能超过100字符')]) cg_area = StringField('城管区域', validators=[Length(max=100, message='城管区域长度不能超过100字符')]) hw_area = StringField('环卫区域', validators=[Length(max=100, message='环卫区域长度不能超过100字符')]) sz_area = StringField('市政区域', validators=[Length(max=100, message='市政区域长度不能超过100字符')]) device_guid = StringField('设备GUID', validators=[Length(max=100, message='设备GUID长度不能超过100字符')]) jx_id = IntegerField('警讯ID') jx_jxmc = StringField('警讯名称', validators=[Length(max=200, message='警讯名称长度不能超过200字符')]) jx_design_type = StringField('警讯设计类型', validators=[Length(max=100, message='警讯设计类型长度不能超过100字符')]) report_time_segment_id = IntegerField('上报时段ID') archive_cond_id = IntegerField('归档条件ID') archive_cond = StringField('归档条件', validators=[Length(max=100, message='归档条件长度不能超过100字符')]) archive_type_id = IntegerField('归档类型ID') road_type_id = IntegerField('道路类型ID') road_name = StringField('道路名称', validators=[Length(max=200, message='道路名称长度不能超过200字符')]) road_id = IntegerField('道路ID') road_type_name = StringField('道路类型名称', validators=[Length(max=100, message='道路类型名称长度不能超过100字符')]) area_type_id = IntegerField('区域类型ID') duty_grid_type_id = IntegerField('责任网格类型ID') deal_duty_grid_type_id = IntegerField('处置责任网格类型ID') time_area_id = IntegerField('时段ID') time_area_name = StringField('时段名称', validators=[Length(max=100, message='时段名称长度不能超过100字符')]) card_num = StringField('证件号码', validators=[Length(max=100, message='证件号码长度不能超过100字符')]) cell_id = IntegerField('单元格ID') cell_name = StringField('单元格名称', validators=[Length(max=200, message='单元格名称长度不能超过200字符')]) damage_grade_id = IntegerField('损毁等级ID') damage_grade_name = StringField('损毁等级名称', validators=[Length(max=100, message='损毁等级名称长度不能超过100字符')]) event_grade_id = IntegerField('事件等级ID') event_grade_name = StringField('事件等级名称', validators=[Length(max=100, message='事件等级名称长度不能超过100字符')]) event_level_id = IntegerField('事件级别ID') event_level_name = StringField('事件级别名称', validators=[Length(max=100, message='事件级别名称长度不能超过100字符')]) event_district_id = IntegerField('事件区域ID') event_district_name = StringField('事件区域名称', validators=[Length(max=100, message='事件区域名称长度不能超过100字符')]) display_property = StringField('显示属性', validators=[Length(max=200, message='显示属性长度不能超过200字符')]) display_style_id = IntegerField('显示样式ID') refresh_flag = IntegerField('刷新标识') video_device_id = IntegerField('视频设备ID') video_param = StringField('视频参数', validators=[Length(max=500, message='视频参数长度不能超过500字符')]) video_device_id = IntegerField('视频设备ID') video_param = StringField('视频参数', validators=[Length(max=500, message='视频参数长度不能超过500字符')]) patrol_deal_flag = IntegerField('巡查处置标识') send_from_type = StringField('发送来源类型', validators=[Length(max=50, message='发送来源类型长度不能超过50字符')]) reply_intime_deadline = IntegerField('两小时回复截止时间戳') accept_status = StringField('受理状态', validators=[Length(max=50, message='受理状态长度不能超过50字符')]) squadron_id = IntegerField('中队ID') squadron_name = StringField('中队名称', validators=[Length(max=200, message='中队名称长度不能超过200字符')]) property_company_id = IntegerField('物业公司ID') act_record_id = IntegerField('操作记录ID') main_rec_id = IntegerField('主记录ID') force_handle_flag = StringField('强制处理标识', validators=[Length(max=50, message='强制处理标识长度不能超过50字符')]) func_custom_limit = StringField('职能部门自定义时限', validators=[Length(max=50, message='职能部门自定义时限长度不能超过50字符')]) def process(self, formdata=None, obj=None, **kwargs): """ 处理表单数据,在数据绑定前进行预处理。 主要功能: - 遍历所有表单字段 - 对字符串类型的值去除两端空白字符 - 调用父类的process方法继续处理 """ if formdata: for name, values in formdata.items(): if isinstance(values, list) and values: formdata[name] = [v.strip() if isinstance(v, str) else v for v in values] elif isinstance(values, str): formdata[name] = values.strip() super().process(formdata, obj, **kwargs) class DcmTaskFormDatumBase(TD3iDcmTaskFormDatum, CommonModel): """ 企业待办表单数据基础类(完全映射 TD3iDcmTaskFormDatum 字段)。 封装所有与企业待办表单数据相关的通用操作方法。 """ FieldMapping = { 'id': 'id', 'rec_id': 'rec_id', 'rec_disp_num': 'rec_disp_num', 'rec_type_id': 'rec_type_id', 'rec_type_name': 'rec_type_name', 'task_num': 'task_num', 'other_task_num': 'other_task_num', 'act_property_id': 'act_property_id', 'biz_id': 'biz_id', 'biz_name': 'biz_name', 'sys_id': 'sys_id', 'address': 'address', 'district_name': 'district_name', 'coordinate_x': 'coordinate_x', 'coordinate_y': 'coordinate_y', 'lonlat_x': 'lonlat_x', 'lonlat_y': 'lonlat_y', 'event_type_id': 'event_type_id', 'event_type_name': 'event_type_name', 'event_src_id': 'event_src_id', 'event_src_name': 'event_src_name', 'event_desc': 'event_desc', 'max_event_type_id': 'max_event_type_id', 'max_event_type_name': 'max_event_type_name', 'main_type_id': 'main_type_id', 'main_type_name': 'main_type_name', 'sub_type_id': 'sub_type_id', 'sub_type_name': 'sub_type_name', 'third_type_id': 'third_type_id', 'third_type_name': 'third_type_name', 'forth_type_id': 'forth_type_id', 'forth_type_name': 'forth_type_name', 'fifth_type_id': 'fifth_type_id', 'fifth_type_name': 'fifth_type_name', 'sixth_type_id': 'sixth_type_id', 'sixth_type_name': 'sixth_type_name', 'seventh_type_id': 'seventh_type_id', 'seventh_type_name': 'seventh_type_name', 'create_time': 'create_time', 'update_time': 'update_time', 'deadline_time': 'deadline_time', 'warning_time': 'warning_time', 'occur_time': 'occur_time', 'dispatch_time': 'dispatch_time', 'archive_time': 'archive_time', 'cancel_time': 'cancel_time', 'refresh_time': 'refresh_time', 'refresh_start_time': 'refresh_start_time', 'check_send_time': 'check_send_time', 'check_reply_time': 'check_reply_time', 'func_deadline': 'func_deadline', 'func_deal_time': 'func_deal_time', 'proc_start_time': 'proc_start_time', 'custom_deadline': 'custom_deadline', 'patroltask_deadline_time': 'patroltask_deadline_time', 'deadline_char': 'deadline_char', 'func_limit_char': 'func_limit_char', 'rec_remain_char': 'rec_remain_char', 'rec_used_char': 'rec_used_char', 'rec_remain': 'rec_remain', 'rec_used': 'rec_used', 'rec_warning': 'rec_warning', 'rec_deadline': 'rec_deadline', 'func_part_id': 'func_part_id', 'func_part_name': 'func_part_name', 'func_part_list_id': 'func_part_list_id', 'func_part_list_name': 'func_part_list_name', 'specify_func_id': 'specify_func_id', 'specify_func_name': 'specify_func_name', 'specify_competent_func_id': 'specify_competent_func_id', 'specify_competent_func_name': 'specify_competent_func_name', 'first_depart_name': 'first_depart_name', 'second_depart_name': 'second_depart_name', 'district_id': 'district_id', 'street_id': 'street_id', 'street_name': 'street_name', 'community_id': 'community_id', 'community_name': 'community_name', 'duty_grid_id': 'duty_grid_id', 'duty_grid_name': 'duty_grid_name', 'duty_region_id': 'duty_region_id', 'duty_region_name': 'duty_region_name', 'duty_district_id': 'duty_district_id', 'duty_district_name': 'duty_district_name', 'duty_street_id': 'duty_street_id', 'duty_street_name': 'duty_street_name', 'duty_community_id': 'duty_community_id', 'duty_community_name': 'duty_community_name', 'law_duty_grid_id': 'law_duty_grid_id', 'law_duty_grid_name': 'law_duty_grid_name', 'deal_duty_grid_id': 'deal_duty_grid_id', 'deal_duty_grid_name': 'deal_duty_grid_name', 'patrol_id': 'patrol_id', 'patrol_name': 'patrol_name', 'accepter_id': 'accepter_id', 'accepter_name': 'accepter_name', 'human_id': 'human_id', 'human_name': 'human_name', 'reporter_name': 'reporter_name', 'reporter_contact': 'reporter_contact', 'tell_num': 'tell_num', 'read_flag': 'read_flag', 'reply_intime': 'reply_intime', 'return_visit_flag': 'return_visit_flag', 'urgency_level': 'urgency_level', 'urgent_flag': 'urgent_flag', 'func_forbid_reporter_info_flag': 'func_forbid_reporter_info_flag', 'public_flag': 'public_flag', 'locked_flag': 'locked_flag', 'transited_flag': 'transited_flag', 'split_rec_flag': 'split_rec_flag', 'enable_check_msg': 'enable_check_msg', 'no_return_visit_flag': 'no_return_visit_flag', 'common_rec_type_flag': 'common_rec_type_flag', 'common_rec_attr_flag': 'common_rec_attr_flag', 'send_pub_check_task_flag': 'send_pub_check_task_flag', 'reply_flag': 'reply_flag', 'whistle_flag': 'whistle_flag', 'repeat_state': 'repeat_state', 'report_state': 'report_state', 'dispose_state': 'dispose_state', 'pre_dispose_state': 'pre_dispose_state', 'undertake_user_name': 'undertake_user_name', 'undertake_phone': 'undertake_phone', 'deal_person_org': 'deal_person_org', 'media_upload_num': 'media_upload_num', 'media_upload_total_num': 'media_upload_total_num', 'media_upload_state': 'media_upload_state', 'media_check_num': 'media_check_num', 'media_check_total_num': 'media_check_total_num', 'media_verify_num': 'media_verify_num', 'media_verify_total_num': 'media_verify_total_num', 'media_self_deal_num': 'media_self_deal_num', 'media_self_deal_total_num': 'media_self_deal_total_num', 'media_review_num': 'media_review_num', 'media_review_total_num': 'media_review_total_num', 'report_pic_num': 'report_pic_num', 'report_pic_total_num': 'report_pic_total_num', 'report_video_num': 'report_video_num', 'report_video_total_num': 'report_video_total_num', 'report_wav_num': 'report_wav_num', 'report_wav_total_num': 'report_wav_total_num', 'check_pic_num': 'check_pic_num', 'check_pic_total_num': 'check_pic_total_num', 'check_video_num': 'check_video_num', 'check_video_total_num': 'check_video_total_num', 'check_wav_num': 'check_wav_num', 'check_wav_total_num': 'check_wav_total_num', 'verify_pic_num': 'verify_pic_num', 'verify_pic_total_num': 'verify_pic_total_num', 'verify_video_num': 'verify_video_num', 'verify_video_total_num': 'verify_video_total_num', 'verify_wav_num': 'verify_wav_num', 'verify_wav_total_num': 'verify_wav_total_num', 'self_deal_pic_num': 'self_deal_pic_num', 'self_deal_pic_total_num': 'self_deal_pic_total_num', 'self_deal_video_num': 'self_deal_video_num', 'self_deal_video_total_num': 'self_deal_video_total_num', 'self_deal_wav_num': 'self_deal_wav_num', 'self_deal_wav_total_num': 'self_deal_wav_total_num', 'review_pic_num': 'review_pic_num', 'review_pic_total_num': 'review_pic_total_num', 'review_video_num': 'review_video_num', 'review_video_total_num': 'review_video_total_num', 'review_wav_num': 'review_wav_num', 'review_wav_total_num': 'review_wav_total_num', 'media_url': 'media_url', 'mms_pic_path': 'mms_pic_path', 'media_path': 'media_path', 'media_type': 'media_type', 'media_usage': 'media_usage', 'media_server_name': 'media_server_name', 'media_property': 'media_property', 'media_uploaded_name': 'media_uploaded_name', 'media_shot': 'media_shot', 'media_label_type_id': 'media_label_type_id', 'media_default_url': 'media_default_url', 'display_order': 'display_order', 'store_type_id': 'store_type_id', 'special_item_image_type': 'special_item_image_type', 'height': 'height', 'width': 'width', 'send_flag': 'send_flag', 'public_flag': 'public_flag', 'gen_thumb': 'gen_thumb', 'can_delete': 'can_delete', 'delete_flag': 'delete_flag', 'delete_reason': 'delete_reason', 'pos_type': 'pos_type', 'view_angle': 'view_angle', 'view_image_name': 'view_image_name', 'view_image_x': 'view_image_x', 'view_image_y': 'view_image_y', 'view_pos_x': 'view_pos_x', 'view_pos_y': 'view_pos_y', 'attach_rec_flag': 'attach_rec_flag', 'gather_flag': 'gather_flag', 'link_field_value': 'link_field_value', 'link_field_display_value': 'link_field_display_value', 'unique_id': 'unique_id', 'third_unique_id': 'third_unique_id', 'equal_group_id': 'equal_group_id', 'rec_category_id': 'rec_category_id', 'dispatch_opinion': 'dispatch_opinion', 'revise_opinion': 'revise_opinion', 'reply_opinion': 'reply_opinion', 'new_inst_advise': 'new_inst_advise', 'new_inst_cond_id': 'new_inst_cond_id', 'new_inst_cond_name': 'new_inst_cond_name', 'case_closure_condition': 'case_closure_condition', 'event_marks': 'event_marks', 'deduction': 'deduction', 'event_property_id': 'event_property_id', 'event_property_name': 'event_property_name', 'city_village_flag': 'city_village_flag', 'force_handle_flag': 'force_handle_flag', 'auto_check_count': 'auto_check_count', 'deal_evaluate_ids': 'deal_evaluate_ids', 'newinst_no_transit': 'newinst_no_transit', 'super_rec_id': 'super_rec_id', 'site_num': 'site_num', 'difficult_type_id': 'difficult_type_id', 'event_district_grade_id': 'event_district_grade_id', 'event_district_grade_name': 'event_district_grade_name', 'cus_grid_code': 'cus_grid_code', 'site_id': 'site_id', 'shop_id': 'shop_id', 'shop_name': 'shop_name', 'spec_type_id': 'spec_type_id', 'spec_type_name': 'spec_type_name', 'proc_account_state_id': 'proc_account_state_id', 'check_type_id': 'check_type_id', 'rec_analysis_type_id': 'rec_analysis_type_id', 'proc_time_state_id': 'proc_time_state_id', 'proc_ard_state_id': 'proc_ard_state_id', 'proc_enq_state_id': 'proc_enq_state_id', 'proc_sup_state_id': 'proc_sup_state_id', 'func_time_state_id': 'func_time_state_id', 'check_msg_state_id': 'check_msg_state_id', 'verify_msg_state_id': 'verify_msg_state_id', 'regather_msg_state_id': 'regather_msg_state_id', 'supervision_check_state_id': 'supervision_check_state_id', 'self_deal_msg_state_id': 'self_deal_msg_state_id', 'review_msg_state_id': 'review_msg_state_id', 'proc_press_state_id': 'proc_press_state_id', 'hot_area': 'hot_area', 'cg_area': 'cg_area', 'hw_area': 'hw_area', 'sz_area': 'sz_area', 'device_guid': 'device_guid', 'jx_id': 'jx_id', 'jx_jxmc': 'jx_jxmc', 'jx_design_type': 'jx_design_type', 'report_time_segment_id': 'report_time_segment_id', 'archive_cond_id': 'archive_cond_id', 'archive_cond': 'archive_cond', 'archive_type_id': 'archive_type_id', 'road_type_id': 'road_type_id', 'road_name': 'road_name', 'road_id': 'road_id', 'road_type_name': 'road_type_name', 'area_type_id': 'area_type_id', 'duty_grid_type_id': 'duty_grid_type_id', 'deal_duty_grid_type_id': 'deal_duty_grid_type_id', 'time_area_id': 'time_area_id', 'time_area_name': 'time_area_name', 'card_num': 'card_num', 'cell_id': 'cell_id', 'cell_name': 'cell_name', 'damage_grade_id': 'damage_grade_id', 'damage_grade_name': 'damage_grade_name', 'event_grade_id': 'event_grade_id', 'event_grade_name': 'event_grade_name', 'event_level_id': 'event_level_id', 'event_level_name': 'event_level_name', 'event_district_id': 'event_district_id', 'event_district_name': 'event_district_name', 'display_property': 'display_property', 'display_style_id': 'display_style_id', 'refresh_flag': 'refresh_flag', 'video_device_id': 'video_device_id', 'video_param': 'video_param', 'patrol_deal_flag': 'patrol_deal_flag', 'send_from_type': 'send_from_type', 'reply_intime_deadline': 'reply_intime_deadline', 'accept_status': 'accept_status', 'squadron_id': 'squadron_id', 'squadron_name': 'squadron_name', 'property_company_id': 'property_company_id', 'act_record_id': 'act_record_id', 'main_rec_id': 'main_rec_id', 'func_custom_limit': 'func_custom_limit', } @classmethod async def exist_other(cls, id: Union[str, int], rec_id: Union[str, int]): """ 检查是否存在除当前任务外的其他同记录ID的任务。 :param id: 当前任务ID :param rec_id: 记录ID :return: 存在返回任务对象,不存在返回None """ _query = select(cls).where(cls.id != id, cls.rec_id == rec_id) _task: cls = await cls.query_first(_query) return _task @classmethod async def find_by_ids(cls, ids: list[Union[str, int]]): """ 根据ID列表批量查找任务数据。 """ _query = select(cls).where(cls.id.in_(ids)) _task_list: list[cls] = (await cls.orm_execute_scalars(_query)).all() return _task_list @classmethod async def is_exist(cls, rec_id: Union[str, int]): """ 检查任务是否已经存在(根据记录ID)。 """ _query = select(cls).where(cls.rec_id == rec_id) _task: cls = await cls.query_first(_query) return _task @classmethod async def search_base(cls, is_paging=True, **kwargs): """ 按参数搜索任务数据的基础方法。 支持字段: - task_num, rec_disp_num, event_type_name, district_name, urgency_level, read_flag 等 - 支持模糊匹配:event_type_name, rec_type_name, event_src_name, first_depart_name, second_depart_name - 支持精确匹配:biz_id, sys_id, urgency_level, read_flag, rec_type_id, deadline_time 等 :param is_paging: 是否分页 :param kwargs: 查询参数 :key int page_number: 页码(缺省随机1~100) :key int page_size: 每页数量(缺省20) :key dict sort_clause: 排序配置,如 {'task_num': 'asc'} :key str task_num: 精确匹配任务号 :key str rec_disp_num: 精确匹配显示编号 :key str event_type_name: 模糊匹配问题类型 :key str district_name: 精确匹配区域 :key int urgency_level: 精确匹配紧急程度 :key int read_flag: 精确匹配是否已读 :key int biz_id: 精确匹配业务ID :key int sys_id: 精确匹配系统ID :key int rec_type_id: 精确匹配类型ID :key int deadline_time: 精确匹配处理截止时间戳 :return: (DataFrame, Pagination) """ page_number = kwargs.get('page_number', random.randint(1, 100)) page_size = kwargs.get('page_size', 20) kwargs.update({'page_number': page_number, 'page_size': page_size}) # 模糊查询字段 _name_likes = { cls.event_type_name.key: '%{}%', cls.rec_type_name.key: '%{}%', cls.event_src_name.key: '%{}%', cls.first_depart_name.key: '%{}%', cls.second_depart_name.key: '%{}%', cls.district_name.key: '%{}%', cls.street_name.key: '%{}%', cls.community_name.key: '%{}%', cls.func_part_name.key: '%{}%', cls.specify_func_name.key: '%{}%', cls.specify_competent_func_name.key: '%{}%', cls.main_type_name.key: '%{}%', cls.sub_type_name.key: '%{}%', cls.third_type_name.key: '%{}%', cls.forth_type_name.key: '%{}%', cls.fifth_type_name.key: '%{}%', cls.sixth_type_name.key: '%{}%', cls.seventh_type_name.key: '%{}%', cls.duty_grid_name.key: '%{}%', cls.duty_region_name.key: '%{}%', cls.duty_district_name.key: '%{}%', cls.duty_street_name.key: '%{}%', cls.duty_community_name.key: '%{}%', cls.law_duty_grid_name.key: '%{}%', cls.deal_duty_grid_name.key: '%{}%', cls.patrol_name.key: '%{}%', cls.accepter_name.key: '%{}%', cls.human_name.key: '%{}%', cls.reporter_name.key: '%{}%', cls.shop_name.key: '%{}%', cls.spec_type_name.key: '%{}%', cls.squadron_name.key: '%{}%', cls.road_name.key: '%{}%', cls.time_area_name.key: '%{}%', cls.hot_area.key: '%{}%', cls.cg_area.key: '%{}%', cls.hw_area.key: '%{}%', cls.sz_area.key: '%{}%', cls.event_district_grade_name.key: '%{}%', } _query = select(cls).where( *cls.search_wheres(likes=_name_likes, **kwargs) ).group_by(cls.id) _paging = None if is_paging: _row_count = await cls.query_count(_query) _paging = Pagination(_row_count).paging(page_number, page_size) _data_query = _query.limit(page_size).offset(_paging.offset_size) else: _data_query = _query.where() _sort_clause = cls.sort_clauses(kwargs.get('sort_clause', {})) if _sort_clause: _data_query = _data_query.order_by(*_sort_clause) else: _data_query = _data_query.order_by(cls.task_num, cls.rec_disp_num) _task_df = await cls.query_as_df(_data_query) if not _task_df.empty: _task_df.replace(models.EmptyInDF + models.EmptyDatetimeInDF, '', inplace=True) _task_df[cls.id.key] = _task_df[cls.id.key].astype(str) return _task_df, _paging @classmethod async def search(cls, **kwargs): """ 按参数搜索任务数据,返回分页格式数据。 """ _task_df, _paging = await cls.search_base(**kwargs) return { 'total': _paging.row_count, 'rows': _task_df.to_dict('records'), 'pagination': { 'page_number': _paging.page_number, 'page_count': _paging.page_count, 'page_size': _paging.page_size, }, } @classmethod async def exists_rec_id(cls, data_df: pd.DataFrame): """ 查找 data_df 中在数据库中已存在和不存在的记录。根据 rec_id 字段判断。 :param data_df: 输入的数据框架,必须包含 rec_id 列 :return: (exists_df: pd.DataFrame, latest_df: pd.DataFrame) - exists_df: 在数据库中存在的记录 - latest_df: 在数据库中不存在的记录 """ if data_df.empty: return pd.DataFrame(), pd.DataFrame() # 获取待查询的 rec_id 列表(去重) rec_ids = data_df[cls.rec_id.key].unique().tolist() if not rec_ids: return pd.DataFrame(), data_df.copy() # 查询数据库中已存在的 rec_id _query = select(cls.id, cls.rec_id).where(cls.rec_id.in_(rec_ids)) rec_ids_df = await cls.query_as_df(_query) if rec_ids_df.empty: return pd.DataFrame(), data_df.copy() # 构建 rec_id -> id 的映射字典 rec_id_to_id_map = dict(zip(rec_ids_df[cls.rec_id.key], rec_ids_df[cls.id.key])) # 根据 rec_id 是否在数据库中,划分数据 mask_exists = data_df[cls.rec_id.key].isin(rec_ids_df[cls.rec_id.key]) # 数据库已经有的记录 exists_df = data_df[mask_exists].copy() # 自动补充从数据库查到的 id 字段 exists_df[cls.id.key] = exists_df[cls.rec_id.key].map(rec_id_to_id_map) # 新的数据 latest_df = data_df[~mask_exists].copy() return exists_df, latest_df @classmethod async def fill_form_datum(cls, data_df: pd.DataFrame, index_field: str = 'id', column_name: str = 'datums', preprocessing: Optional[Callable] = None): """ 填充详细数据到数据框架。 用于在查询结果中添加关联的详细信息。 :param pandas.DataFrame data_df: 待填充的数据框架 :param str index_field: 索引字段,一般是任务ID :param str column_name: 填充时,新增加的列名称,默认为`datums` :param preprocessing: 预处理,注意预处理必须要返回处理后的结果 :return: 详细数据框架(已填充) :rtype: pandas.DataFrame """ if data_df.empty: return pd.DataFrame() _task_ids = list(set(data_df[index_field].unique().tolist())) if not _task_ids: return pd.DataFrame() _query = select(cls).where(cls.dcm_task_id.in_(_task_ids)) _datum_df: pd.DataFrame = await cls.query_as_df(_query) if not _datum_df.empty: _datum_df.replace(models.EmptyInDF+models.EmptyDatetimeInDF, '', inplace=True) # 整理输出数据类型 _datum_df[cls.id.key] = _datum_df[cls.id.key].astype(str) _datum_df[cls.dcm_task_id.key] = _datum_df[cls.dcm_task_id.key].astype(str) # 设置索引 _datum_df['index_id'] = _datum_df[cls.id.key] _datum_df.set_index(['index_id'], inplace=True) # 对数据进行预处理 if isinstance(preprocessing, Callable): _datum_df = preprocessing(_datum_df) # 增加数据填充列 data_df[column_name] = data_df[index_field].apply( lambda x: _datum_df.query(f"{cls.dcm_task_id.key}=='{x}'").to_dict('records') ) else: data_df[column_name] = [[] for _ in range(len(data_df))] return _datum_df @register_swagger_model class DcmTaskFormDatum(DcmTaskFormDatumBase): """ 企业待办表单数据主业务类(完全继承 TD3iDcmTaskFormDatum 字段)。 --- description: 数字化城市管理信息系统企业待办表单数据 type: object properties: id: description: 主键ID type: integer example: 1001 readOnly: true rec_id: description: 记录ID type: integer example: 2001 rec_disp_num: description: 显示编号 type: string example: "D20240501001" maxLength: 50 rec_type_id: description: 类型ID type: integer example: 101 rec_type_name: description: 案件类型 type: string example: "市容环境" maxLength: 100 task_num: description: 任务号 type: string example: "TASK20240501001" maxLength: 50 other_task_num: description: 第三方任务号 type: string example: "THIRD-2024-001" maxLength: 100 act_property_id: description: 任务属性ID type: integer example: 5 biz_id: description: 业务ID type: integer example: 10 biz_name: description: 业务名称 type: string example: "市容巡查" maxLength: 200 sys_id: description: 系统ID type: integer example: 1 address: description: 地址描述 type: string example: "中山路与解放路交叉口" maxLength: 65535 district_name: description: 所属区域 type: string example: "鼓楼区" maxLength: 50 coordinate_x: description: 经度 type: number format: decimal example: 118.789012 coordinate_y: description: 纬度 type: number format: decimal example: 32.045678 lonlat_x: description: 经纬度X type: number format: decimal example: 118.789012 lonlat_y: description: 经纬度Y type: number format: decimal example: 32.045678 event_type_id: description: 问题类型ID type: integer example: 1001 event_type_name: description: 问题类型 type: string example: "道路破损" maxLength: 100 event_src_id: description: 问题来源ID type: integer example: 101 event_src_name: description: 问题来源 type: string example: "市民举报" maxLength: 100 event_desc: description: 问题描述 type: string example: "中山路与解放路交叉口路面大面积破损" maxLength: 65535 max_event_type_id: description: 最大事件类型ID type: integer example: 1002 max_event_type_name: description: 最大事件类型名称 type: string example: "市政设施" maxLength: 200 main_type_id: description: 大类ID type: integer example: 101 main_type_name: description: 大类名称 type: string example: "市容环境" maxLength: 100 sub_type_id: description: 小类ID type: integer example: 10101 sub_type_name: description: 小类名称 type: string example: "道路破损" maxLength: 100 third_type_id: description: 第三级类型ID type: integer example: 1010101 third_type_name: description: 第三级类型名称 type: string example: "人行道破损" maxLength: 100 forth_type_id: description: 第四级类型ID type: integer example: 101010101 forth_type_name: description: 第四级类型名称 type: string example: "沥青路面破损" maxLength: 100 fifth_type_id: description: 第五级类型ID type: integer example: 10101010101 fifth_type_name: description: 第五级类型名称 type: string example: "裂缝" maxLength: 100 sixth_type_id: description: 第六级类型ID type: integer example: 1010101010101 sixth_type_name: description: 第六级类型名称 type: string example: "横向裂缝" maxLength: 100 seventh_type_id: description: 第七级类型ID type: integer example: 101010101010101 seventh_type_name: description: 第七级类型名称 type: string example: "细小横向裂缝" maxLength: 100 create_time: description: 创建时间戳 type: integer example: 1714567890000 update_time: description: 更新时间戳 type: integer example: 1714578000000 deadline_time: description: 处理截止时间戳 type: integer example: 1714578000000 warning_time: description: 处理预警时间戳 type: integer example: 1714570000000 occur_time: description: 发生时间戳 type: integer example: 1714567800000 dispatch_time: description: 派遣时间戳 type: integer example: 1714568000000 archive_time: description: 归档时间戳 type: integer example: 1714580000000 cancel_time: description: 取消时间戳 type: integer example: 1714579000000 refresh_time: description: 刷新时间戳 type: integer example: 1714572000000 refresh_start_time: description: 刷新开始时间戳 type: integer example: 1714571000000 check_send_time: description: 核查发送时间戳 type: integer example: 1714570000000 check_reply_time: description: 核查回复时间戳 type: integer example: 1714571000000 func_deadline: description: 职能部门截止时间戳 type: integer example: 1714578000000 func_deal_time: description: 职能部门处理时间戳 type: integer example: 1714576000000 proc_start_time: description: 处理开始时间戳 type: integer example: 1714572000000 custom_deadline: description: 自定义截止时间戳 type: integer example: 1714579000000 patroltask_deadline_time: description: 巡查任务截止时间戳 type: integer example: 1714575000000 deadline_char: description: 时限描述 type: string example: "24小时" maxLength: 50 func_limit_char: description: 职能部门时限描述 type: string example: "48小时" maxLength: 50 rec_remain_char: description: 记录剩余时间描述 type: string example: "3天" maxLength: 50 rec_used_char: description: 记录已用时间描述 type: string example: "1天" maxLength: 50 rec_remain: description: 记录剩余时间 type: number format: decimal example: 3.5 rec_used: description: 记录已用时间 type: number format: decimal example: 1.2 rec_warning: description: 记录预警时间 type: number format: decimal example: 0.5 rec_deadline: description: 记录时限 type: number format: decimal example: 5.0 func_part_id: description: 职能部门ID type: integer example: 101 func_part_name: description: 职能部门名称 type: string example: "市政工程处" maxLength: 200 func_part_list_id: description: 职能部门列表ID type: string example: "LIST-001" maxLength: 100 func_part_list_name: description: 职能部门列表名称 type: string example: "市政处置组" maxLength: 200 specify_func_id: description: 指定职能部门ID type: integer example: 102 specify_func_name: description: 指定职能部门名称 type: string example: "城市管理局" maxLength: 200 specify_competent_func_id: description: 指定主管职能部门ID type: integer example: 103 specify_competent_func_name: description: 指定主管职能部门名称 type: string example: "市城管委" maxLength: 200 first_depart_name: description: 一级专业部门 type: string example: "市政工程处" maxLength: 100 second_depart_name: description: 二级专业部门 type: string example: "道路养护科" maxLength: 100 district_id: description: 区域ID type: integer example: 1001 street_id: description: 街道ID type: integer example: 1002 street_name: description: 街道名称 type: string example: "中山路" maxLength: 200 community_id: description: 社区ID type: integer example: 1003 community_name: description: 社区名称 type: string example: "鼓楼社区" maxLength: 200 duty_grid_id: description: 责任网格ID type: integer example: 1004 duty_grid_name: description: 责任网格名称 type: string example: "鼓楼网格01" maxLength: 200 duty_region_id: description: 责任区域ID type: integer example: 1005 duty_region_name: description: 责任区域名称 type: string example: "鼓楼区" maxLength: 200 duty_district_id: description: 责任区域ID type: integer example: 1005 duty_district_name: description: 责任区域名称 type: string example: "鼓楼区" maxLength: 200 duty_street_id: description: 责任街道ID type: integer example: 1006 duty_street_name: description: 责任街道名称 type: string example: "中山路" maxLength: 200 duty_community_id: description: 责任社区ID type: integer example: 1007 duty_community_name: description: 责任社区名称 type: string example: "鼓楼社区" maxLength: 200 law_duty_grid_id: description: 法律责任网格ID type: integer example: 1008 law_duty_grid_name: description: 法律责任网格名称 type: string example: "执法网格01" maxLength: 200 deal_duty_grid_id: description: 处置责任网格ID type: integer example: 1009 deal_duty_grid_name: description: 处置责任网格名称 type: string example: "处置网格01" maxLength: 200 patrol_id: description: 巡查员ID type: integer example: 2001 patrol_name: description: 巡查员名称 type: string example: "张三" maxLength: 200 accepter_id: description: 受理人ID type: integer example: 2002 accepter_name: description: 受理人姓名 type: string example: "李四" maxLength: 100 human_id: description: 操作人ID type: integer example: 2003 human_name: description: 操作人名称 type: string example: "王五" maxLength: 255 reporter_name: description: 举报人姓名 type: string example: "张三" maxLength: 100 reporter_contact: description: 举报电话 type: string example: "13800138000" maxLength: 50 tell_num: description: 联系电话 type: string example: "13800138000" maxLength: 50 read_flag: description: 是否已读(0未读,1已读) type: integer example: 1 reply_intime: description: 是否两小时回复(0无需回复,1待回复,2已回复,3超时,4无需回复已恢复) type: integer example: 2 return_visit_flag: description: 回访标识(0无需,1待回访,2已回访) type: integer example: 1 urgency_level: description: 紧急程度(0正常,1紧急) type: integer example: 1 urgent_flag: description: 紧急标识 type: integer example: 1 func_forbid_reporter_info_flag: description: 是否禁止举报人信息 type: integer example: 0 public_flag: description: 公开标志 type: integer example: 1 locked_flag: description: 锁定标识 type: integer example: 0 transited_flag: description: 转交标识 type: integer example: 1 split_rec_flag: description: 拆分记录标识 type: integer example: 0 enable_check_msg: description: 启用核查消息 type: integer example: 1 no_return_visit_flag: description: 无需回访标识 type: integer example: 0 common_rec_type_flag: description: 通用记录类型标识 type: string example: "COMMON" maxLength: 50 common_rec_attr_flag: description: 通用记录属性标识 type: string example: "AUTO" maxLength: 50 send_pub_check_task_flag: description: 发送公共核查任务标识 type: integer example: 1 reply_flag: description: 回复标识 type: string example: "REPLIED" maxLength: 50 whistle_flag: description: 吹哨标识 type: string example: "WHISTLE" maxLength: 50 repeat_state: description: 重复状态 type: string example: "NOT_REPEAT" maxLength: 50 report_state: description: 上报状态 type: string example: "SUBMITTED" maxLength: 50 dispose_state: description: 处置状态 type: integer example: 1 pre_dispose_state: description: 预处置状态 type: string example: "PENDING" maxLength: 50 undertake_user_name: description: 承办人员 type: string example: "张三" maxLength: 50 undertake_phone: description: 联系电话 type: string example: "13800138000" maxLength: 50 deal_person_org: description: 承办部门 type: string example: "部门名称" maxLength: 50 media_upload_num: description: 媒体上传数量 type: integer example: 3 media_upload_total_num: description: 媒体上传总数 type: integer example: 5 media_upload_state: description: 媒体上传状态 type: string example: "SUCCESS" maxLength: 50 media_check_num: description: 媒体核查数量 type: integer example: 2 media_check_total_num: description: 媒体核查总数 type: integer example: 5 media_verify_num: description: 媒体核实数量 type: integer example: 1 media_verify_total_num: description: 媒体核实总数 type: integer example: 5 media_self_deal_num: description: 自行处置媒体数量 type: integer example: 1 media_self_deal_total_num: description: 自行处置媒体总数 type: integer example: 3 media_review_num: description: 复核媒体数量 type: integer example: 1 media_review_total_num: description: 复核媒体总数 type: integer example: 3 report_pic_num: description: 上报图片数量 type: integer example: 2 report_pic_total_num: description: 上报图片总数 type: integer example: 3 report_video_num: description: 上报视频数量 type: integer example: 1 report_video_total_num: description: 上报视频总数 type: integer example: 1 report_wav_num: description: 上报音频数量 type: integer example: 0 report_wav_total_num: description: 上报音频总数 type: integer example: 0 check_pic_num: description: 核查图片数量 type: integer example: 2 check_pic_total_num: description: 核查图片总数 type: integer example: 3 check_video_num: description: 核查视频数量 type: integer example: 1 check_video_total_num: description: 核查视频总数 type: integer example: 1 check_wav_num: description: 核查音频数量 type: integer example: 0 check_wav_total_num: description: 核查音频总数 type: integer example: 0 verify_pic_num: description: 核实图片数量 type: integer example: 1 verify_pic_total_num: description: 核实图片总数 type: integer example: 1 verify_video_num: description: 核实视频数量 type: integer example: 0 verify_video_total_num: description: 核实视频总数 type: integer example: 0 verify_wav_num: description: 核实音频数量 type: integer example: 0 verify_wav_total_num: description: 核实音频总数 type: integer example: 0 self_deal_pic_num: description: 自行处置图片数量 type: integer example: 1 self_deal_pic_total_num: description: 自行处置图片总数 type: integer example: 2 self_deal_video_num: description: 自行处置视频数量 type: integer example: 0 self_deal_video_total_num: description: 自行处置视频总数 type: integer example: 1 self_deal_wav_num: description: 自行处置音频数量 type: integer example: 0 self_deal_wav_total_num: description: 自行处置音频总数 type: integer example: 0 review_pic_num: description: 复核图片数量 type: integer example: 1 review_pic_total_num: description: 复核图片总数 type: integer example: 1 review_video_num: description: 复核视频数量 type: integer example: 0 review_video_total_num: description: 复核视频总数 type: integer example: 0 review_wav_num: description: 复核音频数量 type: integer example: 0 review_wav_total_num: description: 复核音频总数 type: integer example: 0 media_url: description: 内部访问URL type: string example: "http://internal/media/123.jpg" maxLength: 512 mms_pic_path: description: 彩信图片路径 type: string example: "/mms/123.jpg" maxLength: 500 media_path: description: 服务器存储路径 type: string example: "/storage/media/123.jpg" maxLength: 512 media_type: description: 媒体类型 type: string example: "IMAGE" maxLength: 50 media_usage: description: 使用场景 type: string example: "上报" maxLength: 100 media_server_name: description: 媒体服务器名称 type: string example: "media-server-01" maxLength: 100 media_property: description: 媒体属性 type: integer example: 1 media_uploaded_name: description: 上传时的原始文件名 type: string example: "IMG_20240501.jpg" maxLength: 255 media_shot: description: 截图标识或路径 type: string example: "/shots/123.jpg" maxLength: 255 media_label_type_id: description: 标签类型ID type: integer example: 101 media_default_url: description: 外部可访问URL type: string example: "https://external/media/123.jpg" maxLength: 512 display_order: description: 显示顺序 type: integer example: 1 store_type_id: description: 存储类型ID type: integer example: 1 special_item_image_type: description: 特殊图片类型 type: string example: "SIGNATURE" maxLength: 100 height: description: 图片高度 type: integer example: 1080 width: description: 图片宽度 type: integer example: 1920 send_flag: description: 发送标志 type: integer example: 1 gen_thumb: description: 是否生成缩略图 type: integer example: 1 can_delete: description: 是否可删除 type: integer example: 1 delete_flag: description: 删除标记 type: integer example: 0 delete_reason: description: 删除原因 type: string example: "数据重复" maxLength: 65535 pos_type: description: 位置类型 type: string example: "GPS" maxLength: 50 view_angle: description: 视角 type: string example: "FRONT" maxLength: 100 view_image_name: description: 视图图片名称 type: string example: "view_123.jpg" maxLength: 200 view_image_x: description: 视图图片X坐标 type: number format: decimal example: 0.5 view_image_y: description: 视图图片Y坐标 type: number format: decimal example: 0.5 view_pos_x: description: 视图位置X坐标 type: number format: decimal example: 0.5 view_pos_y: description: 视图位置Y坐标 type: number format: decimal example: 0.5 attach_rec_flag: description: 附件记录标识 type: string example: "ATTACH" maxLength: 50 gather_flag: description: 汇总标识 type: string example: "GATHERED" maxLength: 50 link_field_value: description: 关联字段值 type: string example: "LINK-123" maxLength: 500 link_field_display_value: description: 关联字段显示值 type: string example: "关联值显示" maxLength: 500 unique_id: description: 唯一标识 type: string example: "UNIQ-20240501-001" maxLength: 100 third_unique_id: description: 第三方唯一标识 type: string example: "THIRD-2024-001" maxLength: 100 equal_group_id: description: 等值组ID type: integer example: 1001 rec_category_id: description: 记录类别ID type: integer example: 101 dispatch_opinion: description: 派遣意见 type: string example: "请尽快处理" maxLength: 500 revise_opinion: description: 修订意见 type: string example: "建议补充图片" maxLength: 500 reply_opinion: description: 回复意见 type: string example: "已处理完毕" maxLength: 500 new_inst_advise: description: 立案建议 type: string example: "建议立案处理" maxLength: 500 new_inst_cond_id: description: 立案条件ID type: integer example: 101 new_inst_cond_name: description: 立案条件 type: string example: "破损面积大于0.5㎡" maxLength: 200 case_closure_condition: description: 结案条件 type: string example: "修复完成并验收" maxLength: 200 event_marks: description: 事件标记 type: string example: "HIGH_PRIORITY" maxLength: 500 deduction: description: 扣减 type: string example: "扣2分" maxLength: 100 event_property_id: description: 事件属性ID type: integer example: 101 event_property_name: description: 事件属性名称 type: string example: "公共设施" maxLength: 200 city_village_flag: description: 城乡标识 type: string example: "CITY" maxLength: 50 force_handle_flag: description: 强制处理标识 type: string example: "FORCE" maxLength: 50 auto_check_count: description: 自动核查次数 type: integer example: 2 deal_evaluate_ids: description: 处置评价ID列表 type: string example: "101,102,103" maxLength: 200 newinst_no_transit: description: 立案不转交 type: string example: "NO_TRANSIT" maxLength: 50 super_rec_id: description: 上级记录ID type: integer example: 2001 site_num: description: 站点编号 type: string example: "SITE-001" maxLength: 50 difficult_type_id: description: 困难类型ID type: integer example: 101 event_district_grade_id: description: 事件区域等级ID type: integer example: 101 event_district_grade_name: description: 事件区域等级名称 type: string example: "重点区域" maxLength: 100 cus_grid_code: description: 自定义网格编码 type: string example: "CUST-GRID-001" maxLength: 100 site_id: description: 站点ID type: integer example: 101 shop_id: description: 商铺ID type: integer example: 101 shop_name: description: 商铺名称 type: string example: "幸福便利店" maxLength: 200 spec_type_id: description: 特殊类型ID type: integer example: 101 spec_type_name: description: 特殊类型名称 type: string example: "紧急事件" maxLength: 100 proc_account_state_id: description: 处理账户状态ID type: integer example: 1 check_type_id: description: 核查类型ID type: integer example: 1 rec_analysis_type_id: description: 记录分析类型ID type: integer example: 1 proc_time_state_id: description: 处理流程状态ID type: integer example: 1 proc_ard_state_id: description: 处理仲裁状态ID type: integer example: 1 proc_enq_state_id: description: 处理询问状态ID type: integer example: 1 proc_sup_state_id: description: 处理监督状态ID type: integer example: 1 func_time_state_id: description: 职能部门时间状态ID type: integer example: 1 check_msg_state_id: description: 核查消息状态ID type: integer example: 1 verify_msg_state_id: description: 核实消息状态ID type: integer example: 1 regather_msg_state_id: description: 重新采集消息状态ID type: integer example: 1 supervision_check_state_id: description: 监督核查状态ID type: integer example: 1 self_deal_msg_state_id: description: 自行处置消息状态ID type: integer example: 1 review_msg_state_id: description: 复核消息状态ID type: integer example: 1 proc_press_state_id: description: 处理压力状态ID type: integer example: 1 hot_area: description: 热点区域 type: string example: "市中心" maxLength: 100 cg_area: description: 城管区域 type: string example: "鼓楼区" maxLength: 100 hw_area: description: 环卫区域 type: string example: "鼓楼区" maxLength: 100 sz_area: description: 市政区域 type: string example: "鼓楼区" maxLength: 100 device_guid: description: 设备GUID type: string example: "A1B2-C3D4-E5F6" maxLength: 100 jx_id: description: 警讯ID type: integer example: 1001 jx_jxmc: description: 警讯名称 type: string example: "道路塌陷警讯" maxLength: 200 jx_design_type: description: 警讯设计类型 type: string example: "自动触发" maxLength: 100 report_time_segment_id: description: 上报时段ID type: integer example: 101 archive_cond_id: description: 归档条件ID type: integer example: 101 archive_cond: description: 归档条件 type: string example: "处理完成" maxLength: 100 archive_type_id: description: 归档类型ID type: integer example: 101 road_type_id: description: 道路类型ID type: integer example: 101 road_name: description: 道路名称 type: string example: "中山路" maxLength: 200 road_id: description: 道路ID type: integer example: 101 road_type_name: description: 道路类型名称 type: string example: "主干道" maxLength: 100 area_type_id: description: 区域类型ID type: integer example: 101 duty_grid_type_id: description: 责任网格类型ID type: integer example: 101 deal_duty_grid_type_id: description: 处置责任网格类型ID type: integer example: 101 time_area_id: description: 时段ID type: integer example: 101 time_area_name: description: 时段名称 type: string example: "早高峰" maxLength: 100 card_num: description: 证件号码 type: string example: "110101199001012345" maxLength: 100 cell_id: description: 单元格ID type: integer example: 101 cell_name: description: 单元格名称 type: string example: "A01单元" maxLength: 200 damage_grade_id: description: 损毁等级ID type: integer example: 101 damage_grade_name: description: 损毁等级名称 type: string example: "严重" maxLength: 100 event_grade_id: description: 事件等级ID type: integer example: 101 event_grade_name: description: 事件等级名称 type: string example: "重大" maxLength: 100 event_level_id: description: 事件级别ID type: integer example: 101 event_level_name: description: 事件级别名称 type: string example: "一级" maxLength: 100 event_district_id: description: 事件区域ID type: integer example: 101 event_district_name: description: 事件区域名称 type: string example: "鼓楼区" maxLength: 100 display_property: description: 显示属性 type: string example: "高亮显示" maxLength: 200 display_style_id: description: 显示样式ID type: integer example: 1 refresh_flag: description: 刷新标识 type: integer example: 1 video_device_id: description: 视频设备ID type: integer example: 101 video_param: description: 视频参数 type: string example: "1080p,30fps" maxLength: 500 patrol_deal_flag: description: 巡查处置标识 type: integer example: 1 send_from_type: description: 发送来源类型 type: string example: "APP" maxLength: 50 reply_intime_deadline: description: 两小时回复截止时间戳 type: integer example: 1714568000000 accept_status: description: 受理状态 type: string example: "ACCEPTED" maxLength: 50 squadron_id: description: 中队ID type: integer example: 101 squadron_name: description: 中队名称 type: string example: "第一中队" maxLength: 200 property_company_id: description: 物业公司ID type: integer example: 101 act_record_id: description: 操作记录ID type: integer example: 1001 main_rec_id: description: 主记录ID type: integer example: 2001 func_custom_limit: description: 职能部门自定义时限 type: string example: "72小时" maxLength: 50 created_at: description: 创建时间,ISO格式的日期时间字符串 type: string format: date-time example: "2024-01-15 10:30:00" readOnly: true created_by: description: 创建者用户名 type: string example: "admin" readOnly: true updated_at: description: 修改时间,ISO格式的日期时间字符串 type: string format: date-time example: "2024-01-16 14:25:00" readOnly: true updated_by: description: 修改者用户名 type: string example: "editor" readOnly: true """ @classmethod async def create(cls, **kwargs): """ 创建新的任务表单数据。 业务流程: 1. 使用 HumanTaskFormDatumForm 验证表单数据完整性 2. 检查任务是否已存在(根据 rec_id) 3. 创建新任务对象 4. 设置创建者和更新者为当前用户 5. 保存到数据库 6. 返回创建的任务对象 :param kwargs: 任务参数字典 :return: 新建任务对象 :rtype: DcmTaskFormDatum :raises AssertionError: 当任务已存在时抛出 :raises ValidationError: 当表单验证失败时抛出 """ # 处理字符串字段去除空格 for _k, _v in kwargs.items(): if isinstance(_v, str): kwargs[_k] = _v.strip() _task_form = DcmTaskFormDatumForm(formdata=kwargs) _task_form.validate_form() # 检查是否存在同记录ID的任务 _task: cls = await cls.is_exist(_task_form.rec_id.data) assert _task is None, "记录ID已存在,不能重复创建。" # 创建任务对象 _task = cls().copy_from_dict(_task_form.data, skip_none=True).before_save() await _task.async_save() return _task @classmethod async def delete(cls, task_id: Union[str, int]): """ 删除任务表单数据。 业务流程: 1. 根据ID查找任务 2. 验证任务存在性 3. 执行删除操作 :param task_id: 要删除的任务ID :return: 删除的任务对象 :rtype: DcmTaskFormDatum :raises AssertionError: 当任务不存在时抛出 """ _task: cls = await cls.async_find_by_id(task_id) assert _task, f"根据 ID {task_id} 未找到任务。" # 执行删除 _del_query = delete(cls).where(cls.id == _task.id) _del_count = (await cls.raw_execute(_del_query)).rowcount echo_log(f'已删除任务表单数据(记录ID:{_task.rec_id},ID:{_task.id}).') return _task @classmethod async def modify(cls, task_id: Union[str, int], **kwargs): """ 修改已有任务表单数据。 业务流程: 1. 将 task_id 添加到参数中 2. 处理字符串字段去除首尾空格 3. 使用 HumanTaskFormDatumForm 验证表单数据 4. 检查是否有其他任务使用了相同的 rec_id 5. 查询原任务对象 6. 验证任务存在性 7. 更新字段并设置更新者 8. 保存到数据库 9. 返回更新后的任务对象 :param task_id: 要修改的任务ID :param kwargs: 需要更新的字段 :return: 修改后的任务对象 :rtype: DcmTaskFormDatum :raises AssertionError: 当任务不存在或信息重复时抛出 :raises ValidationError: 当表单验证失败时抛出 """ # 处理字符串字段去除空格 for _k, _v in kwargs.items(): if isinstance(_v, str): kwargs[_k] = _v.strip() # 表单验证 _task_form = DcmTaskFormDatumForm(formdata=kwargs) _task_form.validate_form() # 检查是否与其他任务重复(排除自身) _other = await cls.exist_other(task_id, _task_form.rec_id.data) assert _other is None, "记录ID已存在,不能重复修改。" # 查询原任务 _task: cls = await cls.async_find_by_id(task_id) assert _task, f'查无此任务信息。' # 更新字段 _task.copy_from_dict(_task_form.data, skip_none=True).before_save() await _task.async_save() return _task @classmethod async def create_batch(cls, data_df: pd.DataFrame): """ 批量创建新任务表单数据(传入数据应为全新记录,无需校验是否存在)。 :param data_df: 包含任务数据的 DataFrame,字段需与模型属性匹配(如 rec_id, task_num 等) :return: 成功创建的任务数量 :rtype: int """ if data_df.empty: return 0 # 一次性转为字典列表(C 层高效) records = data_df.to_dict('records') # 用列表推导式构造对象 tasks = [cls().copy_from_dict(record, skip_none=True).before_save() for record in records] # 批量插入 session = cls.get_aio_session() try: session.add_all(tasks) await session.commit() except Exception as e: await session.rollback() raise e finally: await session.close() echo_log(f"批量创建成功:创建 {len(tasks)} 条新任务表单数据。") return len(tasks) @classmethod async def modify_batch(cls, data_df: pd.DataFrame): """ 批量修改已有任务表单数据。 :param data_df: 包含任务数据的 DataFrame :return: 成功更新的任务数量 :rtype: int """ if data_df.empty: return 0 # 必须包含 id 列 if 'id' not in data_df.columns: echo_log(f"错误:modify_batch 要求输入数据必须包含 '{cls.id.key}' 列(主键)") return 0 # 转换为字典列表 update_data = data_df.to_dict('records') # 使用 bulk_update_mappings session = cls.get_aio_session() try: await session.run_sync( lambda sync_session: sync_session.bulk_update_mappings(cls, update_data) ) await session.commit() updated_count = len(update_data) except Exception as e: await session.rollback() raise e finally: await session.close() echo_log(f"批量修改成功:更新 {updated_count} 条任务表单数据。") return updated_count @classmethod async def save_batch(cls, data_df: pd.DataFrame): """ 批量保存数据,自动处理新建和更新。 :param data_df: 要保存的数据框架 :return: 新建和更新的数量 """ # 筛选数据状态 _exists_df, _latest_df = await DcmTaskFormDatum.exists_rec_id(data_df) # 保存到数据库 _created_count = await DcmTaskFormDatum.create_batch(_latest_df) _updated_count = await DcmTaskFormDatum.modify_batch(_exists_df) return _created_count, _updated_count