
    in;                       U d Z ddlmZ ddlmZ ddlmZmZ ddlm	Z	m
Z
mZmZ ddlmZmZ ddlmZ ddlmZmZmZmZmZmZ dd	lmZ dd
lmZmZ ddlmZ  ee           Z! e	ddg          Z"d@dZ# e#            Z$de%d<    G d de          Z& G d de          Z' G d de          Z( G d de          Z) G d de          Z* G d de          Z+ G d d e          Z,e"-                    d!e'"          dAd$            Z.e"-                    d%e+"          dBd&            Z/e"0                    d'e1e,         "          dCd)            Z2e"0                    d*e1e(         "          	 	 	 dDdEd5            Z3e"0                    d6e)"          dFd8            Z4e"5                    d9          dGd<            Z6e"0                    d=          dHd>            Z7d?S )Ia	  Mock OA endpoints for integration testing.

These endpoints simulate the OA (Office Automation) system's side of the
integration, letting developers:
  - Generate JWT tokens with custom user context (office/dept/area/roles)
  - Login with predefined test accounts
  - Check task status and list recent tasks
  - List ingested documents in Elasticsearch
  - Delete test documents to clean up

WARNING: These endpoints are for **development / testing only**.
         Disable by setting  MOCK_ENABLED=false  in production.
    )annotations)Path)	AnnotatedAny)	APIRouterDependsHTTPExceptionRequest)	BaseModelField)get_es_client)ensure_prefixPREFIX_USERPREFIX_OFFICEPREFIX_DEPTPREFIX_AREAPREFIX_ROLE)settings)TokenClaimscreate_access_token)
get_loggerz/mockzmock-oa)prefixtagsreturndict[str, dict]c                     ddl } t          t                                                    j        j        j        dz  dz  }t          |d          5 }|                     |          cddd           S # 1 swxY w Y   dS )u*   从配置文件加载测试用户数据。r   Nconfigztest_users.jsonzutf-8)encoding)jsonr   __file__resolveparentopenload)r   _config_pathfs      )D:\work\zm-rag\backend\app\api\v1\mock.py_load_test_usersr(   #   s    KKK>>))++29@8KN__L	lW	-	-	- yy||                 s   A33A7:A7_TEST_USERSc                  |   e Zd ZU dZ edd          Zded<    edd          Zded	<    ed
d          Zded<    edd          Z	ded<    edd          Z
ded<    edd          Zded<    edd          Zded<    edd          Zded<    eed          Zded<    edd          Zded <   d!S )"TokenRequestz-Request body for generating a test JWT token.test_user_001zUser ID (JWT sub claim))defaultdescriptionstruser_id u   用户姓名	user_nameO_01u	   科室 ID	office_idu   科室名称office_nameD_01u	   部门 IDdept_idu   部门名称	dept_nameA_01u	   地区 IDarea_idu   地区名称	area_nameu   角色 ID 列表)default_factoryr.   	list[str]role_idsi  z&Token validity in minutes (default 8h)intexpires_minutesN)__name__
__module____qualname____doc__r   r0   __annotations__r2   r4   r5   r7   r8   r:   r;   listr>   r@        r'   r+   r+   0   s[        775>WXXXGXXXXU2>BBBIBBBBU6{CCCICCCCuR^DDDKDDDD5[AAAGAAAAU2>BBBIBBBB5[AAAGAAAAU2>BBBIBBBB%BTUUUHUUUU 5:bcccOccccccrH   r+   c                  h    e Zd ZU dZded<   dZded<   ded<   ded<   ded	<   ded
<   ded<   ded<   dS )TokenResponsez2Generated JWT token with decoded info for display.r/   access_tokenbearer
token_typer?   
expires_inr0   r4   r7   r:   r=   r>   N)rA   rB   rC   rD   rE   rM   rG   rH   r'   rJ   rJ   ?   sn         <<JOOOLLLNNNLLLLLLrH   rJ   c                      e Zd ZU dZded<   dZded<   dZded<   dZded<   dZded<   dZ	ded	<   dZ
ded
<   dZded<   g Zded<   dZded<   dZded<   dS )DocListItemz'Summary of a document in Elasticsearch.r/   doc_idr1   content_hashtitle
doc_numberissuing_orgdoc_typepublish_dater   r?   chunk_countr=   acl_idsstatus
indexed_atN)rA   rB   rC   rD   rE   rR   rS   rT   rU   rV   rW   rX   rY   rZ   r[   rG   rH   r'   rP   rP   L   s         11KKKLEOOOOJKHLKGFJrH   rP   c                  R    e Zd ZU dZded<   ded<   dZded<   dZd	ed
<   dZded<   dS )TaskStatusResponsezCelery task status.r/   task_idrZ           floatprogressNzdict[str, Any] | Noneresultz
str | Noneerror)rA   rB   rC   rD   rE   ra   rb   rc   rG   rH   r'   r]   r]   \   s]         LLLKKKH$(F((((ErH   r]   c                  (    e Zd ZU dZded<   ded<   dS )LoginRequestz7Username / password login for predefined test accounts.r/   usernamepasswordN)rA   rB   rC   rD   rE   rG   rH   r'   re   re   f   s(         AAMMMMMMMMrH   re   c                      e Zd ZU dZded<   dZded<   ded<   ded<   ded	<   ded
<   ded<   dZded<   ded<   dZded<   ded<   dZded<   ded<   ded<   dS )LoginResponsezSuccessful login response.r/   rK   rL   rM   r?   rN   r0   rf   r2   r4   r1   r5   r7   r8   r:   r;   r=   r>   roleN)	rA   rB   rC   rD   rE   rM   r5   r8   r;   rG   rH   r'   ri   ri   m   s         $$JOOOLLLMMMNNNNNNKLLLILLLIIIIIIrH   ri   c                      e Zd ZU dZded<   ded<   ded<   dZded<   ded<   dZded	<   ded
<   dZded<   ded<   ded<   dS )TestUserInfoz7Public info about a predefined test user (no password).r/   rf   r2   r4   r1   r5   r7   r8   r:   r;   r=   r>   rj   N)rA   rB   rC   rD   rE   r5   r8   r;   rG   rH   r'   rl   rl      s         AAMMMNNNNNNKLLLILLLIIIIIIrH   rl   z/token)response_modelbodyc                  K   t          | j        t                    }t          | j        t                    }t          | j        t                    }t          | j        t                    }d | j	        D             }t          || j        || j        || j        || j        |	  	        }t          || j        d          }t"                              d||||           t'          |j        |j        |||||          S )u   Generate a signed JWT token for testing.

    The token is signed with the same secret as the main application so it
    works with all authenticated endpoints (search, research, etc.).

    No authentication required – **for testing only**.
    c                8    g | ]}t          |t                    S rG   r   r   .0rs     r'   
<listcomp>z'generate_test_token.<locals>.<listcomp>   s"    GGGA-;//GGGrH   	r0   r2   r4   r5   r7   r8   r:   r;   r>   zm-rag-mock)r@   issuermock_token_generated)r0   r4   r7   r:   )rK   rN   r0   r4   r7   r:   r>   )r   r0   r   r4   r   r7   r   r:   r   r>   r   r2   r5   r8   r;   r   r@   loggerinforJ   rK   rN   )rn   	p_user_idp_office_id	p_dept_id	p_area_id
p_role_idsclaimsrb   s           r'   generate_test_tokenr      s      dlK88I>>KdlK88IdlK88IGGGGGJ.$..
 
 
F !9MVcdddF
KK     ($   rH   z/loginc                  K   t                               | j                  }||d         | j        k    rt	          dd          t          |d         t                    }t          |d         t                    }t          |d         t                    }t          |d	         t                    }d
 |d         D             }t          ||d         ||d         ||d         ||d         |	  	        }t          |d          }t                              d| j        |           t          |j        |j        || j        |d         ||d         ||d         ||d         ||d                   S )u  Login with a predefined test account.

    Predefined accounts (原始 ID → 自动加前缀)
    -------------------------------------------
    admin / admin123       – 地区领导 (A_01, D_01, O_01, R_01)
    zhang_san / user123    – 科室用户 (A_01, D_05, O_17)
    li_si / user123        – 科室用户 (A_02, D_08, O_22)
    wang_wu / manager123   – 部门领导 (A_01, D_05, O_01, R_03)
    Nrg   i  u   用户名或密码错误status_codedetailr0   r4   r7   r:   c                8    g | ]}t          |t                    S rG   rq   rr   s     r'   ru   zlogin.<locals>.<listcomp>   s"    JJJA-;//JJJrH   r>   r2   r5   r8   r;   rv   rw   )rx   
mock_login)rf   r0   rj   )rK   rN   r0   rf   r2   r4   r5   r7   r8   r:   r;   r>   rj   )r)   getrf   rg   r	   r   r   r   r   r   r   r   rz   r{   ri   rK   rN   )	rn   userr|   r}   r~   r   r   r   rb   s	            r'   loginr      s{      ??4=))D|tJ'4=884NOOOO d9o{;;I[ 1=AAKd9o{;;Id9o{;;IJJj9IJJJJ{#'{#{#
 
 
F !>>>F
KKt}iKHHH(${#'{#{#&\   rH   z/userslist[TestUserInfo]c                 L   K   d t                                           D             S )zBReturn the list of predefined test users (passwords not included).c                   g | ]\  }}t          ||d          t          |d         t                    |                    dd          t          |d         t                    |                    dd          t          |d         t
                    |                    dd          d |d	         D             |d
         
  
        S )r2   r4   r5   r1   r7   r8   r:   r;   c                8    g | ]}t          |t                    S rG   rq   rr   s     r'   ru   z.list_test_users.<locals>.<listcomp>.<listcomp>  s"    NNNmA{33NNNrH   r>   rj   )
rf   r2   r4   r5   r7   r8   r:   r;   r>   rj   )rl   r   r   r   r   r   )rs   unamer{   s      r'   ru   z#list_test_users.<locals>.<listcomp>   s        E4 	;'#D$5}EE33!$y/;??hh{B//!$y/;??hh{B//NNT*=MNNNf	
 	
 	
  rH   )r)   itemsrG   rH   r'   list_test_usersr      s5        ',,..   rH   z/docs   r1   requestr
   sizer?   from_keywordr/   list[DocListItem]c                  K   t          |           }di i}|                                rd|                                g ddi}	 |j                            t          j        |t          |d          |ddidd	diigd
           d{V }t          |t                    r|n|j	        }|
                    di           
                    dg           }g }	|D ]3}
|

                    di           }|	                    t          |
                    d          p|
d         |
                    d          pd|
                    d          pd|
                    d          pd|
                    d          pd|
                    d          pd|
                    d          pd|
                    d          pd|
                    d          pg |
                    d          pd|
                    d          pd                     5|	S # t          $ r0}t                              d           t!          dd           d}~ww xY w)!zList documents currently indexed in Elasticsearch.

    Returns document-level metadata (from the ``gov_doc_meta`` index).
    No authentication required for testing convenience.
    	match_allmulti_match)rS   rT   rU   )queryfieldsd   _scoredescrW   order)r   r   fromsort)indexrn   Nhits_sourcerQ   _idrR   r1   rS   rT   rU   rV   rX   r   rY   rZ   
created_at)rQ   rR   rS   rT   rU   rV   rW   rX   rY   rZ   r[   mock_list_docs_errori  zInternal server errorr   )r   striprawsearchr   es_meta_indexmin
isinstancedictrn   r   appendrP   	Exceptionrz   	exceptionr	   )r   r   r   r   esr   respr   r   resultshitsrcexcs                r'   	list_docsr     s      
w		B("-E}} 
 @@@ 
#MV]](D#"F+nw>O-PQ	  # 
 
 
 
 
 
 
 
 !t,,;dd$)wwvr""&&vr22 	 	C'')R((CNN778,,:E
!$!8!8!>B'''**0b"ww|44: # 6 6 <" WWZ006B!$!8!8!>B # 6 6 ;!GGI..4"778,,2"ww|44:       M M M/0004KLLLLMs   G	H 
I
+II
z/task/{task_id}r^   c           	     F  K   ddl m} ddlm}  || |j                  }|j        dk    rt          | dd          S |j        dv rF|j        pi }t          | d	t          |t                    r|
                    d
d          nd          S |j        dk    rD|j        pi }d}t          | |dt          |t                    r|ndt          |          i          S |j        dk    r%t          | ddt          |j                            S t          | |j        d          S )zfPoll the status of a Celery ingest task.

    No authentication required for testing convenience.
    r   )AsyncResult)ingest_document_task)appPENDINGr_   )r^   rZ   ra   )STARTED
PROCESSINGr   ra   g?SUCCESS	COMPLETEDg      ?r   )r^   rZ   ra   rb   FAILUREFAILED)r^   rZ   ra   rc   )celery.resultr   app.tasks.ingest_taskr   r   stater]   r{   r   r   r   rb   r/   )r^   r   r   rb   r{   
status_strs         r'   get_task_statusr   H  sy      *)))))::::::[&:&>???F|y  !')cRRRR	2	2	2{ b!2<T42H2HQTXXj#...c
 
 
 	

 
	"	"}" 
!%dD11I44s4yy7I	
 
 
 	
 
	"	"!fm$$	
 
 
 	
 "'&,QTUUUUrH   z/doc/{doc_id}rQ   dict[str, Any]c                (  K   t          |          }|                    |            d{V }	 |j        j        j        }|j                            t          j                  4 d{V }|	                    d|            d{V  ddd          d{V  n# 1 d{V swxY w Y   d|d<   nF# t          $ r9}t                              d| t          |                     d	|d<   Y d}~nd}~ww xY wt          j        dd| i| d| i|ddiS )a8  Delete a test document from ES (handles shared content) and Neo4j.

    Uses ESClient.delete_document() which correctly handles shared content:
    - If last reference: deletes chunks + meta
    - If shared content: removes meta, recomputes chunk ACL

    No authentication required for testing convenience.
    Ndatabasez4MATCH (d:Document {doc_id: $doc_id}) DETACH DELETE d)rQ      neo4jmock_delete_neo4j_error)rQ   rc   r   mock_doc_deletedrQ   rZ   ok)r   )r   delete_documentr   r   neo4j_clientdriversessionr   neo4j_databaserunr   rz   warningr/   r{   )rQ   r   r   rb   r   r   r   s          r'   delete_test_docr   r  s      
w		B%%f--------F
!.<''1H'II 	 	 	 	 	 	 	W++F          	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	
 w   0s3xxPPPw K<<6<V<<<f77$777s;   <B1 *BB1 
B$$B1 'B$(B1 1
C4;/C//C4z/statsc                l  K   i }	 t          |           }|j                            t          j                   d{V }|j                            t          j                   d{V }t          |t                    r|n|j        }t          |t                    r|n|j        }|	                    dd          |	                    dd          d|d<   n+# t          $ r}dt          |          i|d<   Y d}~nd}~ww xY w	 | j        j        j        }|                                 d{V  dd	i|d
<   n,# t          $ r}dt          |          d|d
<   Y d}~nd}~ww xY w	 | j        j        j        }	|	j                            t          j                  4 d{V }
|
                    d           d{V }|                                 d{V }d |D             |d<   ddd          d{V  n# 1 d{V swxY w Y   n+# t          $ r}dt          |          i|d<   Y d}~nd}~ww xY w|S )zsReturn basic system stats: ES doc counts, Redis info.

    No authentication required for testing convenience.
    )r   Ncountr   )chunks	documentsr   rc   rZ   r   redis)rZ   rc   r   z7MATCH (n) RETURN labels(n)[0] AS label, count(*) AS cntc                <    i | ]}|d          
|d          |d         S )labelcntrG   rr   s     r'   
<dictcomp>z system_stats.<locals>.<dictcomp>  s+    RRRqqzRaj!E(RRRrH   r   )r   r   r   r   es_chunk_indexr   r   r   rn   r   r   r/   r   r   redis_clientpingr   r   r   r   r   data)r   statsr   chunks_resp	meta_respcrmrr   r   r   r   rb   recordss                r'   system_statsr     sC      E*7##FLLx/FLGGGGGGGG&,,X-C,DDDDDDDD	&{D99O[[{?O$Y55IYY9>ffWa((++
 
d  * * *C)d*@{(5!!!!!!!!!"D)g @ @ @$+c#hh??g@-!.<''1H'II 	S 	S 	S 	S 	S 	S 	SW";;'`aaaaaaaaF"KKMM))))))GRRGRRRE'N	S 	S 	S 	S 	S 	S 	S 	S 	S 	S 	S 	S 	S 	S 	S 	S 	S 	S 	S 	S 	S 	S 	S 	S 	S 	S 	S  - - -!3s88,g- Lsm   CC 
D "C;;D 2D7 7
E EE $<H	  AG7%H	 7
HH	 HH	 	
H1H,,H1N)r   r   )rn   r+   r   rJ   )rn   re   r   ri   )r   r   )r   r   r1   )
r   r
   r   r?   r   r?   r   r/   r   r   )r^   r/   r   r]   )rQ   r/   r   r
   r   r   )r   r
   r   r   )8rD   
__future__r   pathlibr   typingr   r   fastapir   r   r	   r
   pydanticr   r   app.api.depsr   app.api.v1.authr   r   r   r   r   r   
app.configr   app.core.securityr   r   app.utils.loggerr   rA   rz   routerr(   r)   rE   r+   rJ   rP   r]   re   ri   rl   postr   r   r   rF   r   r   r   deleter   r   rG   rH   r'   <module>r      s     # " " " " "       ! ! ! ! ! ! ! ! > > > > > > > > > > > > % % % % % % % % & & & & & & l l l l l l l l l l l l l l l l       > > > > > > > > ' ' ' ' ' '	H			'	4	4	4     0/11 1 1 1 1
d d d d d9 d d d
 
 
 
 
I 
 
 
    )               9       I   &    9   $ Xm44, , , 54,^ Xm441 1 1 541h HT,%788   98& GD$566 	9M 9M 9M 9M 769Mx .@AA&V &V &V BA&VR 8 8 8  8@ H' ' ' ' ' 'rH   