Files
d3i-szct/models/govs_create_return.py
T
2026-06-02 17:46:38 +08:00

354 lines
14 KiB
Python
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.
# coding: utf-8
import datetime
import random
from typing import Union
import pandas as pd
from sqlalchemy import select, delete
from tornado_swagger.model import register_swagger_model
from wtforms import StringField, TextAreaField, IntegerField, DateTimeField
from wtforms.validators import Length
import models
from models.db_models import TD3iGovsWorkOrderReturnFormal
from models.common_model import CommonModel
from paste.core.logging import echo_log
from paste.rbac.rbac_user import RbacUser
from paste.util.pagination import Pagination
from paste.web.form import ModelForm
class GovsWorkOrderReturnFormalForm(ModelForm):
"""工单退回表单验证类"""
id = IntegerField('主键')
master_id = IntegerField('主表ID')
master_id = StringField('代签收唯一标志', validators=[Length(max=64)])
return_reason = StringField('退回原因', validators=[Length(max=255)])
return_reason_name = StringField('退回原因名称', validators=[Length(max=255)])
return_auditor_name = StringField('退回审核人', validators=[Length(max=100)])
return_auditor_id = StringField('退回审核人ID', validators=[Length(max=64)])
deal_opinion = TextAreaField('处理意见')
reason = TextAreaField('处理意见2')
remark = StringField('备注', validators=[Length(max=500)])
file_id_str = TextAreaField('OA文件id')
process_instance_id = StringField('流程实例ID', validators=[Length(max=64)])
action_name = StringField('操作名称', validators=[Length(max=255)])
order_id = StringField('工单ID', validators=[Length(max=64)])
task_id = StringField('任务ID', validators=[Length(max=64)])
order_no = StringField('工单号', validators=[Length(max=64)])
case_accord_type_one_name = StringField('诉求归口一级名称', validators=[Length(max=255)])
case_accord_type_two_name = StringField('诉求归口二级名称', validators=[Length(max=255)])
case_accord_type_three_name = StringField('诉求归口三级名称', validators=[Length(max=255)])
save_status = IntegerField('提交状态')
oa_feedback_status = IntegerField('OA反馈状态')
flow_token = StringField('流令牌', validators=[Length(max=256)])
created_at = DateTimeField('创建时间')
created_by = StringField('创建者', validators=[Length(max=64)])
updated_at = DateTimeField('更新时间')
updated_by = StringField('更新者', validators=[Length(max=64)])
def process(self, formdata=None, obj=None, **kwargs):
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 GovsWorkOrderReturnFormalBase(TD3iGovsWorkOrderReturnFormal, CommonModel):
"""工单退回业务基类"""
FieldMapping = {
# 主键 & 关联ID
'id': 'id',
'master_id': 'gdId',
'gd_id': 'gdId',
'order_id': 'orderId',
'order_no': 'orderNo',
'process_instance_id': 'processInstanceId',
'task_id': 'taskId',
# 退回核心信息
'return_reason': 'returnReason',
'return_reason_name': 'returnReasonName',
'return_auditor_name': 'returnAuditorName',
'return_auditor_id': 'returnAuditorId',
# 处理意见 & 备注
'deal_opinion': 'dealOpinion',
'reason': 'reason',
'remark': 'remark',
'file_id_str': 'fileIdStr',
# 流程 & 分类信息
'action_name': 'actionName',
'case_accord_type_one_name': 'caseAccordTypeOneName',
'case_accord_type_two_name': 'caseAccordTypeTwoName',
'case_accord_type_three_name': 'caseAccordTypeThreeName',
# 状态 & 令牌
'flow_token': 'flowToken',
}
@classmethod
async def exist_other(cls, id: Union[str, int], master_id: Union[str, int] = None, order_id: str = None,
order_no: str = None):
"""检查是否存在除当前记录外的同唯一标识工单退回记录"""
_query = select(cls).where(cls.id != id)
if master_id:
_query = _query.where(cls.master_id == master_id)
if order_id:
_query = _query.where(cls.order_id == order_id)
if order_no:
_query = _query.where(cls.order_no == order_no)
_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))
_list: list[cls] = (await cls.orm_execute_scalars(_query)).all()
return _list
@classmethod
async def is_exist(cls, master_id: Union[str, int] = None, order_id: str = None, order_no: str = None):
"""检查工单退回记录是否已存在"""
_query = select(cls)
if master_id:
_query = _query.where(cls.master == master_id)
if order_id:
_query = _query.where(cls.order_id == order_id)
if order_no:
_query = _query.where(cls.order_no == order_no)
_task: cls = await cls.query_first(_query)
return _task
@classmethod
async def search_base(cls, is_paging=True, **kwargs):
"""工单退回基础搜索(分页/不分页)"""
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.master_id.key: '%{}%',
cls.order_no.key: '%{}%',
cls.return_reason.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.created_at.desc())
_df = await cls.query_as_df(_data_query)
if not _df.empty:
_df.replace(models.EmptyInDF + models.EmptyDatetimeInDF, '', inplace=True)
_df[cls.id.key] = _df[cls.id.key].astype(str)
return _df, _paging
@classmethod
async def search(cls, **kwargs):
"""分页搜索工单退回记录,返回标准分页结构"""
_df, _paging = await cls.search_base(**kwargs)
return {
'total': _paging.row_count,
'rows': _df.to_dict('records'),
'pagination': {
'page_number': _paging.page_number,
'page_count': _paging.page_count,
'page_size': _paging.page_size,
},
}
@classmethod
async def exists_master_id(cls, data_df: pd.DataFrame):
"""根据 master_id 区分已有数据/新增数据(批量保存用)"""
if data_df.empty:
return pd.DataFrame(), pd.DataFrame()
master_ids = data_df[cls.master_id.key].unique().tolist()
if not master_ids:
return pd.DataFrame(), data_df.copy()
_query = select(cls.id, cls.master_id).where(cls.master_id.in_(master_ids))
existing_df = await cls.query_as_df(_query)
if existing_df.empty:
return pd.DataFrame(), data_df.copy()
master_id_to_id_map = dict(zip(existing_df[cls.master_id.key], existing_df[cls.id.key]))
mask_exists = data_df[cls.master_id.key].isin(existing_df[cls.master_id.key])
exists_df = data_df[mask_exists].copy()
exists_df[cls.id.key] = exists_df[cls.master_id.key].map(master_id_to_id_map)
latest_df = data_df[~mask_exists].copy()
return exists_df, latest_df
@classmethod
async def find_by_master_id(cls, master_id: Union[str, int]):
"""根据主表ID查询单条退回记录"""
_query = select(cls).where(cls.master_id == master_id)
return await cls.query_first(_query)
@classmethod
async def find_by_master_id(cls, master_id: str):
"""根据代签收唯一标志查询退回记录"""
_query = select(cls).where(cls.master_id == master_id)
return await cls.query_first(_query)
@classmethod
async def find_by_order_id(cls, order_id: str):
"""根据工单ID查询退回记录"""
_query = select(cls).where(cls.order_id == order_id)
return await cls.query_first(_query)
@classmethod
async def find_by_master_ids(cls, master_ids: list[Union[str, int]]):
"""根据主表ID列表批量查询退回记录"""
_query = select(cls).where(cls.master_id.in_(master_ids))
_list: list[cls] = (await cls.orm_execute_scalars(_query)).all()
return _list
@register_swagger_model
class GovsWorkOrderReturnFormal(GovsWorkOrderReturnFormalBase):
"""工单退回业务操作类"""
@classmethod
async def create(cls, user: RbacUser = None, **kwargs):
"""新增工单退回记录"""
for _k, _v in kwargs.items():
if isinstance(_v, str):
kwargs[_k] = _v.strip()
_form = GovsWorkOrderReturnFormalForm(formdata=kwargs)
_form.validate_form()
_existing = await cls.is_exist(
master_id=_form.master_id.data,
order_id=_form.order_id.data,
order_no=_form.order_no.data
)
assert _existing is None, "该任务已存在工单退回记录,无法重复创建。"
_return_info = cls().copy_from_dict(_form.data, skip_none=True).before_save()
if user:
_return_info.created_by = user.username
_return_info.updated_by = user.username
await _return_info.async_save()
return _return_info
@classmethod
async def delete(cls, return_id: Union[str, int]):
"""删除工单退回记录"""
_return_info: cls = await cls.async_find_by_id(return_id)
assert _return_info, f"根据 ID {return_id} 未找到工单退回记录。"
_del_query = delete(cls).where(cls.id == _return_info.id)
await cls.raw_execute(_del_query)
echo_log(f'已删除工单退回记录(工单ID{_return_info.master_id}ID{_return_info.id}.')
return _return_info
@classmethod
async def modify(cls, return_id: Union[str, int], user: RbacUser = None, **kwargs):
"""修改工单退回记录"""
for _k, _v in kwargs.items():
if isinstance(_v, str):
kwargs[_k] = _v.strip()
_form = GovsWorkOrderReturnFormalForm(formdata=kwargs)
_form.validate_form()
_return_info: cls = await cls.async_find_by_id(return_id)
assert _return_info, f'查无此工单退回记录。'
_return_info.copy_from_dict(_form.data, skip_none=True).before_save()
if user:
_return_info.updated_by = user.username
await _return_info.async_save()
return _return_info
@classmethod
async def create_batch(cls, data_df: pd.DataFrame, user: RbacUser = None):
"""批量新增工单退回记录"""
if data_df.empty:
return 0
if user:
data_df['created_by'] = user.username
data_df['updated_by'] = user.username
records = data_df.to_dict('records')
return_list = [cls().copy_from_dict(record, skip_none=True).before_save() for record in records]
session = cls.get_aio_session()
try:
session.add_all(return_list)
await session.commit()
except Exception as e:
await session.rollback()
raise e
finally:
await session.close()
echo_log(f"批量创建成功:创建 {len(return_list)} 条工单退回记录。")
return len(return_list)
@classmethod
async def modify_batch(cls, data_df: pd.DataFrame, user: RbacUser = None):
"""批量修改工单退回记录"""
if data_df.empty:
return 0
if 'id' not in data_df.columns:
echo_log(f"错误:modify_batch 要求输入数据必须包含 '{cls.id.key}'")
return 0
data_df['updated_at'] = datetime.datetime.now()
if user:
data_df['updated_by'] = user.username
update_data = data_df.to_dict('records')
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, user: RbacUser = None):
"""批量保存(自动区分新增/更新)"""
_exists_df, _latest_df = await cls.exists_master_id(data_df)
_created_count = await cls.create_batch(_latest_df, user)
_updated_count = await cls.modify_batch(_exists_df, user)
return _created_count, _updated_count