o
    Ղi8                  
   @   s  d Z ddlmZmZ ddlmZ ddlZddlmZmZm	Z	m
Z
 ddlmZ ddlmZ ddlmZ dd	lmZ dd
lmZmZmZmZmZmZmZmZmZmZ e ZG dd deZG dd deZ e!de
e deefdedefddZ"e!de
e  deefde defddZ#e$ddddeefdee% de&de&defddZ'e$deefdefd d!Z(e!d"d#d$ Z)e$d%eefd&e%defd'd(Z*e+d%eefd&e%defd)d*Z,e!d+eefdefd,d-Z-e!d.eefdefd/d0Z.G d1d2 d2eZ/e!d3e
e/ deefde/defd4d5Z0e!d6eefdefd7d8Z1e!d9eefdefd:d;Z2e!d<eefdefd=d>Z3e!d?eefdefd@dAZ4e!dBdGdDe&fdEdFZ5dS )Hu   任务管理 API    )ListOptional)datetimeN)	APIRouterDependsHTTPExceptionBody)Session)	BaseModel)get_db)DataProcessingTask)
process_category_taskreprocess_from_local_taskfill_content_text_task backfill_attachment_content_task#redownload_missing_attachments_taskbuild_relations_taskfill_superseded_by_taskfix_json_null_taskfill_issuing_authority_taskbackfill_media_taskc                   @   s2   e Zd ZU dZdZeee  ed< dZ	e
ed< dS )FullProcessRequestu   全量处理请求Ncategory_idsFforce)__name__
__module____qualname____doc__r   r   r   int__annotations__r   bool r!   r!   >/lsinfo/ai/hellotax_ai/data_center/backend/app/api/v1/tasks.pyr      s   
 r   c                   @   s&   e Zd ZU dZdZeee  ed< dS )IncrementalUpdateRequestu   增量更新请求Nr   )	r   r   r   r   r   r   r   r   r   r!   r!   r!   r"   r#   #   s   
 r#   z/full-process)defaultrequestdbc                    s  dt  jdd  }| jrWg }| jD ]}t| d| |dddd}|| ||j q|  t	| jD ]\}}t
|| |d| j q:d	t| d
|| jdS t|ddddd}|| |  tddD ]}| d| }t
||d| j qnd|ddS )u   启动全量处理任务full_N   _catfullpendingr   task_idcategory_idmodestatusprogress
   已创建 u    个全量处理任务messagetask_ids
categories   	   u4   全量处理任务已启动，正在处理8个分类allr4   r-   r6   )uuiduuid4hexr   r   addappendr-   commit	enumerater   delayr   lenranger%   r&   r-   tasksr.   taskidxcat_task_idr!   r!   r"   start_full_process(   sJ   


rJ   z/incremental-updatec                    s  dt  jdd  }| jrUg }| jD ]}t| d| |dddd}|| ||j q|  t	| jD ]\}}t
|| |d q:d	t| d
|| jdS t|ddddd}|| |  tddD ]}| d| }t
||d qld|ddS )u   启动增量更新任务incr_Nr(   r)   incrementalr+   r   r,   r2   u    个增量更新任务r3   r7   r8   u4   增量更新任务已启动，正在处理8个分类r9   r:   )r;   r<   r=   r   r   r>   r?   r-   r@   rA   r   rB   rC   rD   rE   r!   r!   r"   start_incremental_updatea   sJ   


rM       r0   skiplimitc                    s\   | t}| r|tj| k}| }|tj |	|
 }|dd |D dS )u   获取任务列表c                 S   s   g | ]=}|j |j|j|j|j|j|j|j|j|j	|j
r |j
 nd |jr)|j nd |jr2|j nd |jr;|j nd dqS )Nidr-   r.   r/   r0   r1   total_countsuccess_countfailed_counterror_message
started_atcompleted_at
created_at
updated_at)rS   r-   r.   r/   r0   r1   rT   rU   rV   rW   rX   	isoformatrY   rZ   r[   ).0rG   r!   r!   r"   
<listcomp>   s$    zlist_tasks.<locals>.<listcomp>)totalitems)queryr   filterr0   countorder_byrZ   descoffsetrQ   r9   )r0   rP   rQ   r&   ra   r_   rF   r!   r!   r"   
list_tasks   s   
 rg   z/scan-missing-attachmentsc                    s   ddl }ddlm} | |j|j|j|jd	 }g }|D ]1\}}}|s*q"t
|D ]$\}}	|	dd}
|
rR|j|
sR|||||	d|
|	dd q.q"t||d	S )
u3   扫描全库，找出本地文件不存在的附件r   NTaxDocumentpathrN   nameurl)doc_idtitleattachment_idxrk   rj   rl   )total_missingr`   )osapp.models.tax_datari   ra   rS   rn   attachmentsrb   isnotr9   rA   getrj   isfiler?   rC   )r&   rq   ri   docsmissingrm   rn   rs   rH   attrj   r!   r!   r"   scan_missing_attachments   s4   
rz   z/redownload-missing-attachmentsc                         t   ddiS )uA   扫描全库缺失附件并重新下载（异步 Celery 任务）r4   uI   redownload_missing_attachments 任务已派发，完成后可查看日志)r   rB   r!   r!   r!   r"   redownload_missing_attachments   s   r|   z
/{task_id}r-   c                    s   | ttj| k }|stddd|j|j|j|j|j	|j
|j|j|j|j|jr2|j nd|jr;|j nd|jrD|j nd|jrO|j dS ddS )u   获取任务详情     任务不存在status_codedetailNrR   )ra   r   rb   r-   firstr   rS   r.   r/   r0   r1   rT   rU   rV   rW   rX   r\   rY   rZ   r[   r-   r&   rG   r!   r!   r"   get_task_detail   s*   r   c                    sd   | ttj| k }|stddd|jdv r!tdddd|_t |_	|
  d| d	S )
u   取消任务r}   r~   r   )	completedfailed	cancelledi  u   任务已结束，无法取消r   u   任务已取消r4   r-   )ra   r   rb   r-   r   r   r0   r   utcnowrY   r@   r   r!   r!   r"   cancel_task  s   

r   z/fill-content-textc                    s   |  ttjdktjdk }|rd|jdS dtd j	dd  }t|ddd	d
d}| 
| |   t  d|dS )u   
    全库扫描，为 content_text 为空的文档填充 RAG 纯文本。
    异步派发 Celery 任务，立即返回。
    fill_content_textrunning   任务已在运行中r   fill_ct_r;   Nr(   r+   r   r,   u!   fill_content_text 任务已派发)ra   r   rb   r/   r0   r   r-   
__import__r<   r=   r>   r@   r   rB   )r&   r+   r-   rG   r!   r!   r"   r     s&   

r   z/backfill-attachment-contentc                    s   ddl m} | ttjdktjdk }|r d|jdS | ||j	
d|jd }d	t jdd
  }t|dddd|d}| | |   t  d| d||dS )u   
    对有附件但 content_markdown 缺少附件内容的文档（约367条），
    直接读取本地已下载的附件文件转换并追加到 content_markdown / content_text。
    不重新抓取网页，不重新下载附件。
    r   rh   backfill_attachmentr   r   r   Nu   %## 附件%backfill_att_r(   r+   )r-   r.   r/   r0   r1   rT   u9   backfill_attachment 任务已派发，待处理文档约 u    条)r4   r-   pending_count)rr   ri   ra   r   rb   r/   r0   r   r-   rs   rt   content_markdownnotlikerc   r;   r<   r=   r>   r@   r   rB   )r&   ri   r+   rc   r-   rG   r!   r!   r"   backfill_attachment_content:  sD   

	

r   c                   @   s"   e Zd ZU dZeee  ed< dS )ReprocessCategoryRequestNr   )r   r   r   r   r   r   r   r   r!   r!   r!   r"   r   l  s   
 r   z/reprocess-categoryc                    s   ddl m} ddlm} dd |  D }| jr| jn|}g }|D ])}dd ||j|j	|k
 D }|D ]}	t|	 q:||t|d q#tdd	 |D }
d
|
 d|dS )u   
    批量从本地原始 HTML 重处理文档，无需重新抓取网页。
    适用于调整去噪/解析规则后批量刷新 content_markdown。
    content_html 为空的文档会回退到网络抓取。
    r   rh   )CategoryProcessorc                 S      g | ]}|d  qS )rS   r!   )r]   cr!   r!   r"   r^   }  s    z&reprocess_category.<locals>.<listcomp>c                 S   r   )r   r!   )r]   rowr!   r!   r"   r^     s    )r.   	doc_countc                 s   s    | ]}|d  V  qdS )r   Nr!   )r]   dr!   r!   r"   	<genexpr>  s    z%reprocess_category.<locals>.<genexpr>u
   已派发 u    个本地重处理任务)r4   r6   )rr   ri   2app.services.tax_data_processor.category_processorr   get_all_categoriesr   ra   rS   rb   r.   r9   r   rB   r?   rC   sum)r%   r&   ri   r   all_category_ids
target_ids
dispatchedcat_iddoc_idsrm   r_   r!   r!   r"   reprocess_categoryp  s&   


r   z/fix-json-nullc                    r{   )u{   
    清理 supersedes / references 字段中存储的 JSON null 值（jsonb 'null'），
    将其改为 SQL NULL。
    r4   u   fix_json_null 任务已派发)r   rB   r&   r!   r!   r"   fix_json_null     r   z/fill-issuing-authorityc                    s@   |  ttjdktjdk }|rddiS t  ddiS )ut   
    对 issuing_authority 为 NULL 的文档，从 doc_number / title / content_markdown 推断发文机关。
    fill_issuing_authorityr   r4   r   u&   fill_issuing_authority 任务已派发)ra   r   rb   r/   r0   rc   r   rB   )r&   rc   r!   r!   r"   r     s   r   z/fill-superseded-byc                    r{   )u   
    对 doc_status=obsolete 但无 superseded_by 信息的文档，
    从正文提取废止依据文号并回填 superseded_by_doc_id / superseded_by_doc_number。
    r4   u"   fill_superseded_by 任务已派发)r   rB   r   r!   r!   r"   fill_superseded_by  r   r   z/build-relationsc                    r{   )ud   
    全库重新提取并填充 supersedes / references / superseded_by_doc_id 关联关系。
    r4   u   build_relations 任务已派发)r   rB   r   r!   r!   r"   build_relations  s   r   z/backfill-media2   
batch_sizec                    s   t j| d ddiS )uc   从 content_html 回填存量文档的 inline_images/inline_videos/content_markdown/content_text。r   r4   u   backfill_media 任务已派发)r   rB   r   r!   r!   r"   backfill_media  s   r   )r   )6r   typingr   r   r   r;   fastapir   r   r   r   sqlalchemy.ormr	   pydanticr
   app.databaser   rr   r   app.tasks.processor_tasksr   r   r   r   r   r   r   r   r   r   routerr   r#   postrJ   rM   ru   strr   rg   rz   r|   r   deleter   r   r   r   r   r   r   r   r   r   r!   r!   r!   r"   <module>   s    0
8
6'
1
"		