Files
gmv-analytics/__pycache__/project_bitable_common.cpython-312.pyc
T

398 lines
48 KiB
Plaintext
Raw Normal View History

Ë
ò‡³i£‹ãó¬—ddlZddlZddlZddlmZddlmZddlmZmZm Z ddl
m Z ddl m
Z
ddlmZddlmZddlZddlZdd lmZe
e«j/«j0Zed
z Zed z Zd Zd Zd
Zgd¢Zgd¢Z ed¬«Gd„d««Z!e!dddd¬«e!dddd¬«dœZ"ed¬«Gd„d««Z#ed¬«Gd„d ««Z$ed¬«Gd!„d"««Z%d#e&d$dfd%„Z'dfd&e(ed'e)d$e&fd(„Z*efd)e
d$dfd*„Z+d$e&fd+„Z,d$e
fd,„Z-d-e(e.e&efd.e&d$e&fd/„Z/d0e&d$efd1„Z0d0e&d$e&fd2„Z1d3e&d$e&fd4„Z2d5e&d$e(e&fd6„Z3d7e
d$e.e&e&ffd8„Z4d9e&d:e.e&e&fd$e&fd;„Z5d$ejljnfd<„Z8d=ed>ed?e!d$e(e#fd@„Z9dAe(e#d:e.e&e&fd?e!d$e.e&e.e:e)e)fe.ee.e&e fffffdB„Z;d=ed>edCe.e&e.e:e)e)fe.ee.e&e ffffd$e.e&e.e:e)e)fe.ee.e&e fffffdD„Z<d$e&fdE„Z=GdF„dG«Z>dHe&d$e.e&e&ffdI„Z? dgdJe(e$d5e&dKe)dLe)dMe.e&e&fdzd$e$f dN„Z@d0e d$eAfdO„ZBdPed?e!d$e&fdQ„ZCd0ed$eAdzfdR„ZDd=ed>ed$e(efdS„ZEdTe>dUe$dVe&dWe.ee.e&e ffdXeFd?e!d$e%fdY„ZGdZe&d=ed>ed[e&d\e
dVe&d]e&dXeFd^eFd$dfd_„ZHdPed$e&fd`„ZIdPed$e&fda„ZJdPed$e&fdb„ZKdTe>dUe$dPedce&dXeFd$df dd„ZLd=ed>ed[e&dVe&dXeFd^eFd$dfde„ZMy)héN©Ú defaultdict)Ú dataclass)ÚdateÚdatetimeÚ timedelta)ÚDecimal)ÚPath)ÚAny)Úurlparse)Ú
load_workbookz.envu店铺映射表.xlsxz
2026-03-01õ业务店铺å��ç§°)ru 店铺å��ç§°u 门店å��ç§°u店铺uå��ç§°©õ 大眼ç�›õ 羽心堂ÚMMMJÚEMõ 春回ç›�ú
BROKEN ZOOT)Úfrozencó6—eZdZUeed<eed<eed<eed<y)Ú MetricConfigÚnameÚ descriptionÚ sql_templateÚ field_suffixN©Ú__name__Ú
__module__Ú __qualname__ÚstrÚ__annotations__©óõ8/Users/idss/YXT_Project/临时/project_bitable_common.pyrrs…à
ƒIØÓØÓØÔr$rÚsalesu 销售é¢�u 总销售é¢�uŠ
SET TIMEZONE = 'Asia/Shanghai';
WITH base_data AS (
SELECT
f.支付时间::DATE AS 统计日期,
f.店铺主键,
s.店铺�称 AS 原始店铺�称,
p.�牌�称,
f.业务模�,
f.实付分摊金� AS 指标金�
FROM analytics_layer.fact_sales f
LEFT JOIN analytics_layer.dim_shop s
ON f.店铺主键 = s.店铺主键
LEFT JOIN analytics_layer.dim_product p
ON f.商�主键 = p.商�主键
AND f.统一规格编� = p.统一规格编�
WHERE f.支付时间 >= %(start_ts)s
AND f.支付时间 < %(end_ts_exclusive)s
),
brand_split AS (
SELECT
统计日期,
店铺主键,
原始店铺�称,
业务模�,
CASE
WHEN 店铺主键 IN (1,28,29) AND �牌�称 LIKE '羽心堂'
THEN 原始店铺�称 || '_羽心堂'
WHEN 店铺主键 IN (1,28,29) AND �牌�称 LIKE '春回�'
THEN 原始店铺�称 || '_春回�'
WHEN 店铺主键 IN (1,28,29) AND �牌�称 LIKE 'Mama Mujer'
THEN 原始店铺�称 || '_Mama Mujer'
WHEN 店铺主键 IN (1,28,29) AND �牌�称 LIKE 'ERBA MIGINA'
THEN 原始店铺�称 || '_ERBA MIGINA'
WHEN 店铺主键 IN (1,28,29) AND �牌�称 LIKE 'BROKEN ZOO'
THEN 原始店铺�称 || '_BROKEN ZOO'
WHEN 店铺主键 IN (1,28,29)
THEN 原始店铺�称 || '_其他'
WHEN 店铺主键 IN (3,9) AND �牌�称 LIKE '羽心堂'
THEN 原始店铺�称 || '_羽心堂'
WHEN 店铺主键 IN (3,9) AND �牌�称 LIKE 'Mama Mujer'
THEN 原始店铺�称 || '_Mama Mujer'
WHEN 店铺主键 IN (3,9)
THEN 原始店铺�称 || '_其他'
WHEN 店铺主键 IS NULL OR 原始店铺�称 IS NULL
THEN '分销/SEEDING'
ELSE 原始店铺�称
END AS 店铺�称,
指标金�
FROM base_data
),
shop_brand_detail AS (
SELECT
统计日期,
店铺主键,
店铺�称,
SUM(指标金�) AS 指标值
FROM brand_split
GROUP BY 1,2,3
)
SELECT
统计日期,
店铺�称,
指标值
FROM shop_brand_detail
WHERE NOT (
店铺主键 IN (1,9,28,29)
AND 店铺�称 LIKE '%%_其他'
)
UNION ALL
SELECT
统计日期,
原始店铺�称 AS 店铺�称,
SUM(指标金�)
FROM base_data
WHERE 店铺主键 IN (1,28,29,3)
GROUP BY 1,2
UNION ALL
SELECT
统计日期,
CASE
WHEN 业务模� IN ('三方计算','三方分销')
THEN 原始店铺�称 || '_三方'
ELSE 原始店铺�称 || '_自�'
END AS 店铺�称,
SUM(指标金�)
FROM base_data
WHERE 店铺主键 = 1
GROUP BY 1,2
ORDER BY 统计日期 DESC, 指标值 DESC;
)rrrrÚrefundu 退款é¢�u-
SET TIMEZONE = 'Asia/Shanghai';
WITH base_data AS (
SELECT
f.完结时间::DATE AS 统计日期,
f.店铺主键,
s.店铺�称 AS 原始店铺�称,
p.�牌�称,
f.退款金� AS 指标金�
FROM analytics_layer.fact_returns f
LEFT JOIN analytics_layer.dim_shop s
ON f.店铺主键 = s.店铺主键
LEFT JOIN analytics_layer.dim_product p
ON f.商�主键 = p.商�主键
AND f.统一规格编� = p.统一规格编�
WHERE f.完结时间 >= %(start_ts)s
AND f.完结时间 < %(end_ts_exclusive)s
),
brand_split AS (
SELECT
统计日期,
店铺主键,
原始店铺�称,
CASE
WHEN 店铺主键 IN (1,28,29) AND �牌�称 LIKE '羽心堂'
THEN 原始店铺�称 || '_羽心堂'
WHEN 店铺主键 IN (1,28,29) AND �牌�称 LIKE '春回�'
THEN 原始店铺�称 || '_春回�'
WHEN 店铺主键 IN (1,28,29) AND �牌�称 LIKE 'Mama Mujer'
THEN 原始店铺�称 || '_Mama Mujer'
WHEN 店铺主键 IN (1,28,29) AND �牌�称 LIKE 'ERBA MIGINA'
THEN 原始店铺�称 || '_ERBA MIGINA'
WHEN 店铺主键 IN (1,28,29) AND �牌�称 LIKE 'BROKEN ZOO'
THEN 原始店铺�称 || '_BROKEN ZOO'
WHEN 店铺主键 IN (1,28,29)
THEN 原始店铺�称 || '_其他'
WHEN 店铺主键 IN (3,9) AND �牌�称 LIKE '羽心堂'
THEN 原始店铺�称 || '_羽心堂'
WHEN 店铺主键 IN (3,9) AND �牌�称 LIKE 'Mama Mujer'
THEN 原始店铺�称 || '_Mama Mujer'
WHEN 店铺主键 IN (3,9)
THEN 原始店铺�称 || '_其他'
WHEN 店铺主键 IS NULL OR 原始店铺�称 IS NULL
THEN '分销/SEEDING'
ELSE 原始店铺�称
END AS 店铺�称,
指标金�
FROM base_data
),
shop_brand_detail AS (
SELECT
统计日期,
店铺主键,
店铺�称,
SUM(指标金�) AS 指标值
FROM brand_split
GROUP BY 1,2,3
)
SELECT
统计日期,
店铺�称,
指标值
FROM shop_brand_detail
WHERE NOT (
店铺主键 IN (1,9,28,29)
AND 店铺�称 LIKE '%%_其他'
)
UNION ALL
SELECT
统计日期,
原始店铺�称 AS 店铺�称,
SUM(指标金�)
FROM base_data
WHERE 店铺主键 IN (1,28,29,3)
GROUP BY 1,2
ORDER BY 统计日期 DESC, 指标值 DESC;
)r&r'có,—eZdZUeed<eed<eed<y)Ú MetricRowÚ stat_dateÚ
bi_store_nameÚamountN)rrr rr"r!r r#r$r%r)r)ãs…àƒOØÓØ „Or$r)có"—eZdZUeed<eed<y)Ú TableInfoÚtable_idrNrr#r$r%r.r.ês …àƒMØ
„Ir$r.có.—eZdZUeeed<eeed<y)Ú
SyncResultÚsql_only_storesÚfeishu_only_storesN)rrr Úlistr!r"r#r$r%r1r1ðs…à˜#‘YÓØ˜S™ Ô!r$r1ÚmessageÚreturncóp—tj«jd«}td|›d|›�d¬«y)Nz%Y-%m-%d %H:%M:%SÚ[z] T)Úflush)rÚnowÚstrftimeÚprint)r5r:s r%Úlog_stepr=ös0€Ü
�,‰,‹.×
!Ñ
!Ð"5Ó
6€CÜ ˆAˆcˆU�"�W�IÐ
 dÖ+r$ÚvaluesÚlimitcó\—t|«|kr t|«S|d|›dt|«›d�S)Nu ... å…±u项)Úlenr!)r>r?s r%Ú preview_listrBûs6€Ü
ˆ6ƒ{�eÒÜ�6‹{ÐØ�V�eˆnÐ
˜X¤c¨&£k ]°#Ð 6Ð6r$Úenv_filecóš—|j«std|›�«ytd|›�«|jd¬«j«D]{}|j «}|r|j d«sd|vrŒ+|j
dd«\}}tjj|j «|j ««Œ}y)Nu&未找到 .env 文件,跳过加载: u加载环境å�˜é‡�: úutf-8©ÚencodingÚ#Ú=é)
Úexistsr=Ú read_textÚ
splitlinesÚstripÚ
startswithÚsplitÚosÚenvironÚ
setdefault)rCÚraw_lineÚlineÚkeyÚvalues r%Ú load_dotenvrXs¨€Ø �?‰?Ô ÜÐ9¸(¸ÐDÔEØÜ Ð# H :Ð
.Ô/Ø×&Ñ&°Ð&Ó8×CÑCÖEˆØ�~‰~ÓˆÙ�t—‘ sÔ+¨s¸$©Ø Ø—Z‘Z  QÓ'‰
ˆˆUÜ
�
‰
×јcŸi™i›k¨5¯;©;«=Õ9ñ Fr$cóJ—tjd«}|s td«‚|S)NÚFEISHU_BASE_URLu"缺少环境å�˜é‡� FEISHU_BASE_URL)rQÚgetenvÚ
ValueError)Úbase_urls r%Ú get_base_urlr^s%€Ü�y‰yÐ*Ó+€HÙ ÜÐ=Ó>Ð>Ø €Or$cóZ—ttjdtt«««S)NÚPROJECT_MAPPING_FILE)r
rQr[r!ÚDEFAULT_MAPPING_FILEr#r$r%Úget_mapping_filerbs€Ü ”— ‘ Ð0´#Ô6JÓ2KÓLÓ MÐMr$ÚfieldsÚpreferred_field_namecó¶—|D�cgc] }t|jd«xsd«‘Œ"}}||vr|StD]
}||vsŒ|cStd|›d|›�«‚cc}w)NÚ
field_nameÚu'未找到店铺å��称字段: preferred=u, å�¯ç”¨å­—段=)r!ÚgetÚSTORE_FIELD_CANDIDATESr\)rcrdÚitemÚ field_namesÚ candidates r%Úresolve_store_field_namermsu€ÙAGÓHÁ¸”3�t—x‘x  Ó-Ò3°Õ4À€KÐHؘ{Ñ*Ø#Ð#ß+ˆ Ø ˜ Ò #ØÒ ð,ô Ð>Ð?SÐ>TÐTcÐdoÐcpÐqÓ
rÐrùò
Is…%ArWcóJ—tj|d«j«S)Nz%Y-%m-%d)rÚstrptimer©rWs r%Ú
validate_daterq#s€Ü × Ñ ˜U JÓ /× 4Ñ 4Ó 6Ð6r$cóT—tjdd|xsd«j«S)Nu[\s_\-()()]+rg)ÚreÚsubÚlowerrps r%Únormalize_textrv's#€Ü
�6‰6Ð% r¨5ª;°BÓ 7× =Ñ =Ó ?Ð?r$Ú
store_namec󘇗|xsdŠdgd¢fddgfdddgfddgfddgfg}|D]\}}tˆfd „|D««sŒ|cSd
‰vsd ‰vry
y) Nrgr)ÚMamaÚmmmjrrrú ERBA MIGINArrc3ó&•K—|]}|‰v–—Œ
y­w©Nr#)Ú.0Úkeywordrs €r%Ú <genexpr>z#classify_project.<locals>.<genexpr>5søèø€Ð7©h 7ˆw˜$Œ©hùóƒru大西瓜籽妈妈)Úany)rwÚprioritized_rulesÚprojectÚkeywordsrs @r%Úclassify_projectr†+s…ø€Ø Ò ˜€Dà Ò)Ð*Ø �{�mÐ$Ø
��mÐ$Ð%Ø �{�mÐ$Ø ˜ �~Ð&ð Ðó/ш�Ü Ó7©hÓ7Õ 7ØŠNð/ð�dÑÐ2°dÑ:ØØ
r$r„cóN—ddgdggd¢ddgdggd¢d œ}|j||g«S)
Nru大眼�买买买r)rz
Mama Mujerz
MAMA MUJERrr{r)rÚ BROKENZOOu 葆ç¦�颂r©rh)r„Úaliasess r%Úget_project_table_aliasesr‹<s?€à!Ð#7Ð8Ø!�]Ú4Ø�]Ð#Ø!�]Ú>ñ
€Gð �;‰;�w   Ó *Ð*r$Úpathcó.—td|›�«t|d¬«}|j}i}|jdd¬«D]=\}}|r|sŒ t |«j «|t |«j «<Œ?tdt
|«›d�«|S)Nu加载店铺映射表: T)Ú data_onlyé)Úmin_rowÚ values_onlyu店铺映射加载完æˆ�: õ æ�¡)r=r
ÚactiveÚ iter_rowsr!rNrA)rŒÚwbÚwsÚmappingÚ
business_nameÚbi_names r%Úload_store_mappingršHs“€Ü Ð& t fÐ
-Ô.Ü �t tÔ ,€BØ �‰€BØ €GØ"$§,¡,°qÀd ,Ö"Kш
�wÙ¡GØ Ü(+¨MÓ(:×(@Ñ(@Ó(Bˆ”�G“ ×"Ñ"Ó$Ò%ð#Lô
Ð)¬#¨g«,¨°tÐ
<Ô=Ø €Nr$r+Ú
store_mappingcó&—|j|d«S©Nrgr‰)r+r›s r%Úresolve_business_store_nameržUs€Ø × Ñ ˜]¨BÓ /Ð/r$c ó—gd¢}|D�cgc]}tj|«rŒ|‘Œ}}|rtddj|«›�«‚t dtj
d›dtj
d›dtj
d ›�«t
jtj
dtj
dtj
d
tj
d tj
d ¬ «Scc}w)
N)ÚANALYTICS_DB_HOSTÚANALYTICS_DB_PORTÚANALYTICS_DB_USERÚANALYTICS_DB_PASSWORDÚANALYTICS_DB_NAMEu缺少数æ�®åº“环境å�˜é‡�: ú, u连接 PostgreSQL: host=r z port=r¡z db=r¤r¢r£)ÚhostÚportÚuserÚpasswordÚdbname)rQr[r\Újoinr=rRÚpsycopg2Úconnect)ÚrequiredrVÚmissings r%Úget_db_connectionr°Ysö€ò€Hñ'Ó=™h�s¬b¯i©i¸­nŠs˜h€GÐ=ÙÜÐ8¸¿¹À7Ó9KÐ8LÐMÓNÐNÜ Ø
"¤2§:¡:Ð.AÑ#BÐ"CÀ6Ì"Ï*É*ÐUhÑJiÐIjÐjnÔoq×oyÑoyð{NñpOðoPð Qôô × Ñ Ü
�Z‰ZÐ+Ñ
,Ü
�Z‰ZÐ+Ñ
,Ü
�Z‰ZÐ+Ñ
,Ü—‘Ð3Ñ4Ü�z‰zÐ-Ñ.ô  ðùò
>s
‰C?¤C?Ú
start_dateÚend_dateÚ
metric_configc
óf—|j«›d�}|td¬«zj«›d�}td|j›d|›d|›d�«t «5}|j «5}|j
|j||dœ«|j«}ddd«ddd«t|j›d t«›d
�«|D�cgc]8}t|d |dxsd j«|d
xs td«¬«‘Œ:c}S#1swYŒyxYw#1swYŒ}xYwcc}w)Nz 00:00:00rJ©Údaysu 开始查询z: ú -> u (å�³å¼€åŒºé—´))Ústart_tsÚend_ts_exclusiveu查询完æˆ�: u 行rrgr�Ú0)r*r+r,)
Ú isoformatrr=rr°ÚcursorÚexecuterÚfetchallrAr)rNr ) r±r²r³r¸r¹ÚconnÚcurÚrowsÚrows r%Úfetch_metric_rowsrÃps,€Ø×&Ñ&Ó(Ð)¨Ð3€HØ#¤i°QÔ&7Ñ7×BÑBÓDÐEÀYÐOÐÜ ˆ|˜M×5Ñ5Ð6°b¸¸
À$ÐGWÐFXÐXgÐ
hÔiÜ Ô  Ø
�[‰[Œ]˜cØ �K‰K˜
×2Ñ2ÀÐ_oÑ4pÔ qØ—<‘<“>ˆD÷÷
ô
�
×)Ñ)Ð*¨.¼¸T»¸ À4Ð
HÔIñó áˆCô ˜C ™F°3°q±6²<¸R×2FÑ2FÓ2HÐQTÐUVÑQWÒQgÔ[bÐcfÓ[gÖhØñ ð÷ ˆ]ú÷
Ð üò
 s*ÁD"Á.0DÂD"Ã=D.ÄD ÄD"Ä"D+rÁcó8—td|j›�«td„«}|D]¨}t|j|«}|sŒt |«}|sŒ*|j j|j jf}||||j j|td««}||jz||||j |<Œªt|«} td„|j«D««}
t|j›d| ›d|
›�«|S)Nu 开始按项目/月/日期è�šå�ˆcó—td„«S)Ncó —tt«Sr})rÚdictr#r$r%Ú<lambda>z9build_project_metrics.<locals>.<lambda>.<locals>.<lambda>†s €¤K´Ô$5r$rr#r$r%rÈz'build_project_metrics.<locals>.<lambda>†s
€” Ñ5Ô6r$rºc3ó2K—|]}t|«–—Œy­wr})rA)r~Úmonthss r%r€z(build_project_metrics.<locals>.<genexpr>“sèø€ÐAÑ0@ f”c˜&—kÑ0@ùó‚uè�šå�ˆå®Œæˆ�: 项目=u,项目月份组å�ˆ=)r=rrržr+r†r*ÚyearÚmonthrhr r,rAÚsumr>) rÁr›r³ÚgroupedrÂÚbusiness_store_namer„Ú month_keyÚcurrent_amountÚ
project_countÚ month_counts r%Úbuild_project_metricsrÕs€ô

Ð/°
×0IÑ0IÐ/JÐ
KÔLÜP[Ù6óQ€GóˆÜ9¸#×:KÑ:KÈ]Ó[ÐÙ"Ø Ü"Ð#6Ó7ˆÙØ Ø—]‘]×'Ñ'¨¯©×)<Ñ)<Ð=ˆ Ø  Ñ)¨)Ñ4°S·]±]ÑC×GÑGÐH[Ô]dÐehÓ]iÓjˆØJXÐ[^×[eÑ[eÑJeˆ�јÑ# C§M¡MÑ2Ð3FÒGðô˜“L€MÜÑA°·±Ô0@ÓAÓA€KÜ �
×)Ñ)Ð*Ð*?À
¸ÐNdÐepÐdqÐ
rÔsØ €Nr$Úgrouped_metricscó°—|j«D����cic]3\}}||j«D��cic]\}}|t|«“Œc}}“Œ5}}}}}|}||krn|j|jf}tD]8}|j |i«} | j |i«}
|
j |i«Œ:|t
d¬«z
}||krŒn|Scc}}wcc}}}}w)NrJrµ)ÚitemsrÇrÌrÍÚSUPPORTED_PROJECTSrSr) r±r²rÖr„Ú
month_dictrÑÚdate_mapÚresultÚcurrentÚproject_monthsÚmetrics_by_dates r%Úbuild_target_project_monthsrà˜së€ð$3×#8Ñ#8Ô#:öá#:Ñ ˆG�Zð Àz×GWÑGWÔGYÔZÑGYÑ0C° ¸8�)œT (›^Ñ+ÐGYÒZÑZØ#:ð óð€GØ
�XÒ
Ø—\‘\ 7§=¡=Ð1ˆ ß)ˆGØ#×.Ñ.¨w¸Ó;ˆNØ,×7Ñ7¸ À2ÓFˆOØ × &Ñ & w°Õ 3ð*ð ”9 !Ô$Ñ$ˆð
�XÓ
ð €Mùó[ùõs–C
²C
ÁC
Ã
C
cód—tjd«}tjd«}|r|s td«‚td«t j
d||dœd¬«}|j
«|j«}|jd «d
k7rtd |›�«‚td «|d
S)NÚ
FEISHU_APP_IDÚFEISHU_APP_SECRETu:缺少飞书环境å�˜é‡� FEISHU_APP_ID / FEISHU_APP_SECRETu&开始获å�–飞书 tenant_access_tokenzEhttps://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal)Úapp_idÚ
app_secreté)ÚjsonÚtimeoutÚcoderu获å�–飞书 token 失败: u'飞书 tenant_access_token 获å�–æˆ�功Útenant_access_token)
rQr[r\r=ÚrequestsÚpostÚraise_for_statusrçrhÚ RuntimeError)räråÚrespÚpayloads r%Úget_feishu_tokenrñ¬s§€Ü
�Y‰Y�Ó
'€FÜ—‘Ð.Ó/€JÙ ™ÜÐUÓVÐVÜ Ð
5Ô6Ü �=‰=ØOبjÑ
9Øô €Dð
 ×ÑÔØ�i‰i‹k€G؇{�{�6Ó˜aÒÜÐ8¸¸ ÐBÓCÐCÜ Ð
6Ô7Ø Ð(Ñ )Ð)r$c
óà—eZdZdeddfd„Zededefd„«Zdedededeeeffd „Z de
e fd
„Z d ede
eeeffd „Z
d ed
eddfd„Zd ede
eeeffd„Zd ededeeefddfd„Zy)ÚFeishuBitableClientr]r6Ncó6—||_|j|«|_td|j›�«t «|_i|_tj«|_ |jjjd|j
›�ddœ«y)Nu(è§£æž�飞书 Base URL æˆ�功: app_token=zBearer zapplication/json; charset=utf-8)Ú
Authorizationz Content-Type) r]Úextract_app_tokenÚ app_tokenr=rñÚtokenÚ
_fields_cacherëÚSessionÚsessionÚheadersÚupdate)Úselfr]s r%Ú__init__zFeishuBitableClient.__init__Às€€Ø ˆŒ
Ø×/Ñ/°Ó9ˆŒÜÐ;¸D¿N¹NÐ;KÐLÔMÜ%Ó'ˆŒ
Ø>@ˆÔÜ×'Ñ'Ó)ˆŒ Ø � ‰ ×Ñ×#Ñ#à#*¨4¯:©:¨,Ð!7Ø Añ
õ
r$có¸—t|«jjd«}tjd|«}|st d|›�«‚|j
d«S)NÚ/z base/([^/]+)u$无法从链接中解æž� app_token: rJ)r rŒrNrsÚsearchr\Úgroup)r]rŒÚmatchs r%röz%FeishuBitableClient.extract_app_tokenÎsQ€ä˜Ó!×&Ñ&×,Ñ,¨SÓ1ˆÜ— ‘ ˜/¨4Ó0ˆÙÜÐCÀHÀ:ÐNÓOÐ OØ�{‰{˜1‹~Ðr$ÚmethodrŒÚkwargsc óh—d|›�}|jd«}|rtd|›d|›d|›�«ntd|›d|›�«|jj||fddi|¤Ž} |j «}|jd k\rj|jd «xsi}td
|j›d|›d|jd«›d|jd«›d|jdd«›d|›� «‚|jd«dk7r]|jd «xsi}td|›d|jd«›d|jd«›d|jdd«›d|›�
«‚|jd«xsiS#t
$r9|j
«td|j›d |jdd
›�«‚wxYw)Nzhttps://open.feishu.cnÚparamsu请求飞书接å�£: Ú z params=rèé<u-飞书接å�£è¿”回了é�ž JSON å“�应: status=z, body=éôi�Úerroru飞书接å�£ HTTP z: code=réz msg=Úmsgz log_id=Úlog_idrgz payload=ru飞书接å�£å¤±è´¥ Údata)
rhr=rûÚrequestrçr\rírîÚ status_codeÚtext) rþrrŒrÚurlrrïrðr s r%rzFeishuBitableClient.requestÖsö€Ø& t fÐ-ˆØ—‘˜HÓ%ˆÙ Ü Ð+¨F¨8°1°T°F¸(À6À(ÐKÕ Lä Ð+¨F¨8°1°T°FÐ;Ô <Ø#ˆt�|‰|×#Ñ# F¨CÑF¸ÐF¸vÑFˆð {Ø—i‘i“kˆGð × Ñ ˜sÒ "Ø—K‘K Ó(Ò.¨BˆEÜØ$ T×%5Ñ%5Ð$6°a¸°v¸WÀWÇ[Á[ÐQWÓEXÐDYÐY^Ð_f×_jÑ_jÐkpÓ_qÐ^rðsØŸ)™) H¨bÓ1Ð2°)¸G¸9ðFóð
ð �;‰;�vÓ  !Ò #Ø—K‘K Ó(Ò.¨BˆEÜØ% d V¨7°7·;±;¸vÓ3FÐ2GÀuÈWÏ[É[ÐY^ÓM_ÐL`ðaØŸ)™) H¨bÓ1Ð2°)¸G¸9ðFóð
ð�{‰{˜6Ó"Ò( bÐ(øôò {Ø × !Ñ !Ô #ÜÐ!NÈt×O_ÑO_ÐN`Ð`gÐhl×hqÑhqÐrvÐsvÐhwÐgxÐyÓzÐ zð {ús
Á E/Å/AF1c óä—td«g}d} i}|r||d<|jdd|j›d�|¬«}|jd«xsgD]$}|j t |d |d
¬ ««Œ&|jd d
«sn|jd«xsd}|snŒ“tdt
|D�cgc]}|j‘Œc}«›�«tdt|«›d�«|Scc}w)Nu'开始获å�–多维表格数æ�®è¡¨åˆ—表rgÚ
page_tokenÚGETú/open-apis/bitable/v1/apps/z/tables©rrØr/r)r/rÚhas_moreFu 飞书返回的数æ�®è¡¨å��ç§°: uæ•°æ�®è¡¨åˆ—表获å�–完æˆ�: u 个) r=rr÷rhÚappendr.rBrrA)rþrØrrrrjs r%Ú list_tableszFeishuBitableClient.list_tablesñs€ÜÐ:Ô;Ø!#ˆØˆ
ØØ%'ˆFÙØ'1��|Ñ$Ø—<‘< Ð)DÀTÇ^Á^ÐDTÐT[Ð'\Ðek�<ÓlˆDØŸ™ Ó)Ò/¨RÒ/�Ø— ‘ œY°°ZÑ0@ÀtÈFÁ|ÔTÕUð0à—8‘8˜J¨Ô.ØØŸ™ ,Ó/Ò5°2ˆJÙØðô Ð3´LÑX]ÓA^ÑX]ÐPTÀ$Ç)Ã)ÐX]ÑA^Ó4_Ð3`ÐaÔbÜÐ0´°U³° ¸DÐAÔB؈ ùòB_sÂ3C-r/cóΗ||jvr6td|›dt|j|«›�«|j|Std|›�«g}d}t«}d} |dz
}ddi}|r+||d <||vrt d
|›d |›�«‚|j |«td |›d
|›d |xsd›�«|j
dd|j›d|›d�|¬«}|j|jd«xsg«|jdd«}|jd «xsd} td|›d
|›dt|«›d|›d| xsd›�
«|sn3| st d|›dt|«›�«‚| }|dk\rt d|›�«‚�Œ||j|<td|›d t|D�
cgc]}
|
jd!d«‘Œc}
«›�«td"|›dt|«›�«|Scc}
w)#Nu命中字段缓存: table_id=u , 字段数=u#开始获å�–字段列表: table_id=rgrrJÚ page_sizeédru@字段列表分页出现é‡�å¤� page_token,已中断: table_id=z
, page_token=u#获å�–字段列表分页: table_id=z, page=z<first>rrú/tables/ú/fieldsrrØrFu#字段列表分页完æˆ�: table_id=u, 累计字段数=z , has_more=z, next_page_token=z<empty>uG字段列表分页返回 has_more=True 但 page_token 为空: table_id=u, 已获å�–字段数=u7字段列表分页超过 100 页,已中断: table_id=u&飞书返回的字段å��ç§°: table_id=ú , fields=rfu#字段列表获å�–完æˆ�: table_id=) rùr=rAÚsetrîÚaddrr÷ÚextendrhrB) rþr/rØrÚ seen_tokensÚ
page_indexrrrÚnext_page_tokenrjs r%Ú list_fieldszFeishuBitableClient.list_fieldss¼€Ø �t×)Ñ)Ñ )Ü Ð4°X°J¸lÌ3Èt×OaÑOaÐbjÑOkÓKlÐJmÐnÔ oØ×%Ñ% hÑ/Ð /ÜÐ6°x°jÐAÔBØ&(ˆØˆ
Ü #£ˆ ؈
ØØ ˜!‰OˆJØ! 3Ð'ˆFÙØ'1��|Ñ$Ø Ñ,Ü&Ð)iÐjrÐisðtAðBLðAMð(NóOðOØ—‘ 
Ô+Ü Ð:¸8¸*ÀGÈJÈ<ÐWdÐeoÒe|Ðs|Ðd}Ð~Ô Ø—<‘<ØØ-¨d¯n©nÐ-=¸XÀhÀZÈwÐWØð óˆDð
�L‰L˜Ÿ™ 'Ó*Ò0¨bÔ 1Ø—x‘x 
¨EÓ2ˆHØ"Ÿh™h |Ó4Ò:¸ˆOÜ Ø5°h°Z¸wÀzÀlÐRdÔehÐinÓeoÐdpðqØ$˜:Ð%7¸Ò8TÈ9Ð7UðWô
ñØÙ"Ü"Ð%lÐmuÐlvðwLôMPðQVóMWðLXð$YóZðZØ(ˆJؘSÒ Ü"Ð%\Ð]eÐ\fÐ#gÓhÐhñ7ð8(-ˆ×ј8Ñ$ÜÐ9¸(¸À9Ì\ñCHó[IñCHÐz~Ð[_×[cÑ[cÐdpÐrtÕ[uðCHñ[IóNJðMKðLô MÜÐ6°x°jÀ ÌSÐQVËZÈLÐYÔZ؈ ùò[IsÆ G"rfcób‡—td|›d‰›�«|j|«}tˆfd„|D««rtd|›d‰›�«ytd|›d‰›�«|jdd|j›d|›d �‰d
d œ¬ «|j
j
|d«td
|›d‰›�«y)Nu#检查字段是å�¦å­˜åœ¨: table_id=ú, field=c3óF•K—|]}|jd«‰k(–—Œy­w)rfNr‰)r~rjrfs €r%r€z:FeishuBitableClient.ensure_number_field.<locals>.<genexpr>2s øèø€ÐGÁ¸ˆt�x‰x˜ Ó%¨Õ3Áùsƒ!u)字段已存在,跳过创建: table_id=u#开始创建数字字段: table_id=ÚPOSTrrr r�)rfÚtype©rçu字段创建完æˆ�: table_id=)r=r(r‚rr÷rùÚpop)rþr/rfrcs ` r%Úensure_number_fieldz'FeishuBitableClient.ensure_number_field/sÉø€ÜÐ6°x°jÀÈÈ ÐUÔVØ×!Ñ! (Ó+ˆÜ ÓGÁÓGÔ GÜ Ð@ÀÀ
È(ÐS]ÐR^Ð_Ô `Ø ÜÐ6°x°jÀÈÈ ÐUÔVØ � ‰ Ø Ø)¨$¯.©.Ð)9¸À(ÀÈ7Ð SØ *°AÑ6ð ô
ð
×Ñ×јx¨Ô.ÜÐ0°°
¸(À:À,ÐOÕPr$có:—td|›�«g}d} ddi}|r||d<|jdd|j›d|›d �|¬
«}|j|j d «xsg«|j d«xsd}|snŒmtd |›d
t |«›�«|S)Nu#开始获å�–记录列表: table_id=rgrr rrrrz/recordsrrØu#记录列表获å�–完æˆ�: table_id=u , 记录数=)r=rr÷r$rhrA)rþr/rØrrrs r%Ú list_recordsz FeishuBitableClient.list_records>s€ÜÐ6°x°jÐAÔBØ&(ˆØˆ
ØØ! 3Ð'ˆFÙØ'1��|Ñ$Ø—<‘<ØØ-¨d¯n©nÐ-=¸XÀhÀZÈxÐXØð óˆDð
�L‰L˜Ÿ™ 'Ó*Ò0¨bÔ 1ØŸ™ ,Ó/Ò5°2ˆJÙØðô Ð6°x°jÀ ÌSÐQVËZÈLÐYÔZ؈ r$Ú record_idrcc ó¬—td|›d|›dt|j««›�«|jdd|j›d|›d|›�d|i¬ «y)
Nu更新记录: table_id=z , record_id=r!ÚPUTrrz /records/rcr.)r=r4Úkeysrr÷)rþr/r3rcs r%Ú
update_recordz!FeishuBitableClient.update_recordRsh€ÜÐ*¨8¨*°LÀÀ È9ÔUYÐZ`×ZeÑZeÓZgÓUhÐTiÐjÔkØ � ‰ Ø Ø)¨$¯.©.Ð)9¸À(ÀÈ9ÐU^ÐT_Ð `ؘFÐ#ð õ
r$)rrr r!rÿÚ staticmethodrör rÇrr4r.rr(r0r2r7r#r$r%róró¿sù„ð 
 ð 
¨ó 
ðð Cð¨Còóðð)˜cð)¨ð)¸ð)ÀÀSÈ#ÀXÁó)ð6˜T )™_óð(( Cð(¨D°°c¸3°h±Ñ,@ó(ðT
Q¨Cð
Q¸Sð
QÀTó
Qð Sð¨T°$°s¸C°x±.Ñ-Aóð(
 cð
°cð
À4ÈÈSÈÁ>ð
ÐVZô
r$róÚ path_textcó—|siSt|«}|j«std|›�«‚tj|j d¬««}|j
«D��cic]\}}t|«t|«“Œc}}Scc}}w)Nu未找到表å��映射文件: rErF)r
rKÚFileNotFoundErrorrçÚloadsrLrØr!)r9rŒrðrVrWs r%Úload_table_name_overridesr=[sw€Ù ؈ Ü � ‹?€DØ �;‰;Œ=ÜÐ"?À¸vÐ FÓGÐGÜ�j‰j˜Ÿ™°˜Ó9Ó:€GØ3:·=±=´?Ô C±?¡Z S¨%ŒC�‹H”c˜%“jÑ °?Ò CÐCùÓ CsÁ&B ÚtablesrÌrÍÚ overridesc
ó,‡—td|›d|d›d|d›�«|xsi}|›d|d›d|d›�}||vrI||}|D]0}|j|k(sŒtd|›d|j›�«|cStd |›d