# AI-intelligent-operation-data **Repository Path**: elfbobo_admin_admin/ai-intelligent-operation-data ## Basic Information - **Project Name**: AI-intelligent-operation-data - **Description**: AI智能运营-数据项目 - **Primary Language**: Python - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2026-08-10 - **Last Updated**: 2026-08-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # supplier-data-sync — 供应商数据定时同步服务 基于 Flask + APScheduler + SQLAlchemy 的通用供应商数据定时同步服务。 支持多供应商接口、多数据表同步,扩展新数据源无需修改框架代码。 --- ## 目录结构 ``` supplier-data-sync/ ├── run.py Flask 应用入口 ├── .env 本地环境变量(不提交 git) ├── .env.example 环境变量模板 ├── requirements.txt ├── config/ │ └── settings.py 全局配置(读取 .env) ├── models/ │ ├── __init__.py SQLAlchemy 引擎、Base、SessionLocal │ └── sync_table_models.py 数据同步表 ORM 模型(HotfundRecord / AntPostRecord 等 36 张表) ├── services/ │ ├── datasources.py 数据源配置注册表(DATASOURCE_REGISTRY) │ ├── providers/ 供应商接口适配器层 │ │ ├── base.py BaseProvider 抽象基类 │ │ ├── sidi/ │ │ │ ├── __init__.py Sidi Provider 包入口 │ │ │ └── client.py SidiProvider(分页、鉴权、重试) │ │ ├── yangjibao/ │ │ │ ├── __init__.py YangJiBao Provider 包入口 │ │ │ └── client.py YangJiBaoProvider(AES+HMAC 鉴权、两接口分步拉取) │ │ ├── radar_newbanker/ (NTB:牛头帮供应商) │ │ │ ├── __init__.py NTB Provider 包入口 │ │ │ └── client.py NTB Provider(login/refresh + filter 分页,按 meta.filter_count 终止) │ │ ├── daole/ │ │ │ ├── __init__.py Daole Provider 包入口 │ │ │ ├── client.py DaoleProvider(货架 / 讨论区 / 京东达人 / 支付宝 KOL 文件) │ │ │ └── zfb_kol_mappers.py 支付宝 KOL 六接口行映射 │ │ ├── boyan/ │ │ │ ├── __init__.py BoYan Provider 包入口 │ │ │ └── client.py BoYanProvider(报蛋 key+detail 双接口) │ │ ├── caiyue/ │ │ │ ├── __init__.py Caiyue Provider 包入口 │ │ │ └── client.py CaiyueProvider(电报列表 all + 详情 Bearer) │ │ └── sidi_mms/ │ │ ├── __init__.py SidiMms Provider 包入口 │ │ └── client.py SidiMmsProvider(Login 获取 token;蚂蚁/腾安发布记录) │ └── jobs/ │ ├── __init__.py Job 包入口 │ ├── base.py BaseJob 模板方法(日志、upsert、类型转换;写入策略 ignore/update;含 hourly cron / cron_trigger / should_run_on_startup 扩展点) │ ├── registry.py Job 注册表(JOB_REGISTRY) │ ├── sidi_hotfund_sync.py 思迪·重点与竞品池同步 Job │ ├── sidi_hotsearch_sync.py 思迪·热搜基金同步 Job │ ├── yjb_sync.py 养基宝板块榜单同步 Job(每小时 :27 / :57) │ ├── radar_hotfund_stat_sync.py NTB 同步 Job(每天四次;filter 与批次时间由 Job 内置映射) │ ├── daole_ant_shelf_sync.py 道乐蚂蚁货架同步 Job │ ├── daole_ant_forum_articles_sync.py 道乐讨论区发帖同步 Job │ ├── daole_jd_expert_sync.py 道乐京东达人持仓+列表+操作 Job(每日 10:20,date=昨天) │ ├── daole_jd_jjdrb_sync.py 道乐京东基金达人榜单 Job(每日 10:20,date=当天) │ ├── daole_zfb_expert_operation_sync.py 道乐支付宝重点大V操作记录 Job(每小时 :30) │ ├── daole_zfb_kol_sync.py 道乐支付宝 KOL 六文件接口 Job(每日 10:00) │ ├── boyan_gaineggs_sync.py 博彦报蛋同步 Job(每日 08:00,蚂蚁+腾讯) │ ├── boyan_dongbo_sync.py 博彦东博指数/基金估值 Job(每小时 :27/:57;batch_time 快照覆盖) │ ├── caiyue_telegram_sync.py 财跃电报解读详情 Job(每 15 分钟;列表 all + 详情落库) │ ├── sidi_ant_post_operate_sync.py 思迪·蚂蚁发帖记录(操作行为)Job(每小时 :02/:32) │ ├── sidi_ant_post_sync.py 思迪·蚂蚁帖子(讨论区)Job(每小时 :02/:32) │ ├── sidi_ant_shelf_sync.py 思迪·蚂蚁货架旧版(FG_03)+ 指数(FG_03_A)Job(23:30) │ ├── sidi_ant_shelf_new_sync.py 思迪·蚂蚁货架新版(FG_ED11)Job(02:45,昨日) │ ├── sidi_ant_shelf_jxhj_sync.py 思迪·蚂蚁货架精选好基(FG_ED12)Job(02:45,昨日) │ ├── sidi_ant_shelf_yeb_sync.py 思迪·蚂蚁余额宝货架(FG_ED13)Job(02:45,昨日) │ ├── sidi_ant_shelf_jx_v2_sync.py 思迪·蚂蚁金选货架 V2(FG_ED14)Job(02:45,昨日) │ ├── sidi_ant_shelf_wj_sync.py 思迪·蚂蚁稳健货架(FG_ED20)Job(02:45,昨日) │ ├── sidi_ant_high_end_product_master_sync.py 思迪·蚂蚁高端产品主数据(FG_ED16)Job(02:30,昨日) │ ├── sidi_ant_high_end_nav_history_sync.py 思迪·蚂蚁高端产品历史净值(FG_ED17)Job(02:30;接口昨日 updateTime 区间) │ ├── sidi_ant_weekly_treasure_sync.py 思迪·蚂蚁周周宝数据(FG_ED18)Job(02:30,昨日) │ ├── sidi_ant_app_post_interact_sync.py 思迪·蚂蚁 APP 发帖互动(FG_ED19)Job(`.env`:`JOB_ANT_APP_POST_INTERACT_CRON`,默认 `fri 13:00`;当日 `dataDate`) │ ├── sidi_ant_all_fund_sync.py 思迪·蚂蚁全量基金(FG_01)Job(02:45,昨日) │ ├── sidi_tencent_kol_sync.py 思迪·腾安 KOL + KOL 持仓 Job(08:30) │ ├── sidi_tencent_post_sync.py 思迪·腾安帖子(讨论区)Job(FG_ED05 → tencent_discuss_post_FG_ED05_sd;每小时 :02/:32;isdate=False,滚动前 24h12min ~ 后 12min) │ ├── sidi_tencent_post_reply_sync.py 思迪·腾安帖子评论 Job(FG_ED22 → tencent_discuss_post_reply_FG_ED22_sd;与 ED05 同 cron/传参/策略) │ ├── sidi_tencent_hqj_sync.py 思迪·腾安理财通活期+(FG_ED03)Job(02:45,昨日) │ ├── sidi_tencent_shelf_sync.py 思迪·腾安理财通货架(wjlc)Job(23:30) │ ├── sidi_tencent_traffic_map_sync.py 思迪·腾安流量地图(FG_ED15)Job(02:45,昨日) │ ├── sidi_tiantian_fund_hot_sync.py 思迪·天天基金热门(FG_TT01)Job(02:45,昨日) │ ├── sidi_ant_kol_expert_operation_fg10_sync.py 思迪·蚂蚁达人操作(FG_10)Job(OrTrigger 8 时点,当日 dataDate) │ ├── sidi_ant_discuss_post_report_sync.py 思迪·FG_ED21 讨论区竞品/举报 Job(job_mms_report,每天 00:00/06:00/12:00/18:00) │ ├── sidi_mms_publish_records_sync.py 思迪 MMS·蚂蚁/腾安 发布+回复 Job(蚂蚁含小红书 TOPIC_REPLY,每小时 :00/:30) ├── scheduler/ │ └── scheduler.py APScheduler 注册与管理(支持 JOB_YJB_CRON_MINUTES / cron_trigger;启动时是否立即跑由各 Job.should_run_on_startup() 决定) ├── api/ │ └── management.py Flask 蓝图:/health /jobs /jobs//trigger ├── utils/ │ ├── logger.py 日志初始化(控制台 + 滚动文件) │ └── time_utils.py 时间工具(DayBegin / DayEnd / GetBatchTime) └── logs/ └── sidi_sync.log 运行日志(自动生成) ``` --- ## 架构说明 ``` ┌─────────────┐ ┌──────────────────┐ ┌───────────────────┐ │ Scheduler │────▶│ BaseJob.run() │────▶│ BaseProvider │ │ (APScheduler│ │ (模板方法) │ │ .fetch() │ │ cron 触发) │ │ 1. 时间范围计算 │ │ │ └─────────────┘ │ 2. 调用 provider │ │ SidiProvider │ │ 3. 字段补齐 │ │ (HTTP 分页拉取) │ │ 4. MySQL upsert │ └───────────────────┘ └──────────────────┘ │ ┌──────▼──────┐ │ MySQL 目标表│ └─────────────┘ ``` - **DataSource 注册表**(`services/datasources.py`):声明每张表对应的接口、供应商、ORM 模型 - **Provider 注册表**(`services/providers/__init__.py`):管理所有供应商 HTTP 客户端实例 - **Job 注册表**(`services/jobs/registry.py`):声明所有激活的同步 Job,调度器自动读取 ### 供应商、数据源、Job 与目标表(全量对照) 下列表格与 `services/datasources.py` 中 `DATASOURCE_REGISTRY`、`services/jobs/registry.py` 中 `JOB_REGISTRY` 一致,便于运维查 **物理表名**、**`job_id`**(手动触发)、**数据源键**(代码与配置)。同一 `job_id` 可能对应多张表(如养基宝一次任务写两张表;博彦每个渠道各写 key + compete 两张表)。 #### 思迪(`provider_name='sidi'`) | 数据源键 `datasource_name` | `job_id` | ORM 模型 | MySQL 表名 | 接口/业务简称 | |---------------------------|----------|----------|------------|---------------| | `hotfund` | `job_hotfund` | `HotfundRecord` | `tencent_fund_hot_FG_ED04_sd` | 重点与竞品池 | | `hotsearch` | `job_hotsearch` | `HotsearchRecord` | `tencent_search_hot_FG_ED10_sd` | 热搜基金 | | `ant_post_operate` | `job_ant_post_operate` | `AntPostOperateRecord` | `ant_discuss_post_operate_FG_ED09_sd` | 蚂蚁发帖记录(操作行为),FG | | `ant_post` | `job_ant_post` | `AntPostRecord` | `ant_discuss_post_FG_ED06_sd` | 蚂蚁帖子(讨论区) | | `ant_shelf` | `job_ant_shelf` | `AntShelfRecord` | `ant_shelf_FG_03_sd` | 蚂蚁货架旧版 FG_03 | | `ant_shelf_index` | `job_ant_shelf_index` | `AntShelfIndexRecord` | `ant_shelf_index_FG_03_A_sd` | 蚂蚁货架指数 FG_03_A | | `ant_shelf_new` | `job_ant_shelf_new` | `AntShelfNewRecord` | `ant_shelf_FG_ED11_sd` | 蚂蚁货架新版 FG_ED11 | | `ant_shelf_jxhj` | `job_ant_shelf_jxhj` | `AntShelfJXHJRecord` | `ant_shelf_jxhj_FG_ED12_sd` | 精选好基 FG_ED12 | | `ant_shelf_yeb` | `job_ant_shelf_yeb` | `AntShelfYEBRecord` | `ant_shelf_yeb_FG_ED13_sd` | 余额宝货架 FG_ED13 | | `ant_shelf_jx_v2` | `job_ant_shelf_jx_v2` | `AntShelfJxV2Record` | `ant_shelf_jxhj_home_FG_ED14_sd` | 金选货架 V2 FG_ED14 | | `ant_shelf_wj` | `job_ant_shelf_wj` | `AntShelfWjRecord` | `ant_shelf_wjlc_FG_ED20_sd` | 稳健货架 FG_ED20 | | `ant_high_end_product_master` | `job_ant_high_end_product_master` | `AntHighEndProductMasterRecord` | `ant_vip_fund_data_FG_ED16_sd` | 高端产品主数据 FG_ED16 | | `ant_high_end_nav_history` | `job_ant_high_end_nav_history` | `AntHighEndNavHistoryRecord` | `ant_vip_fund_nav_FG_ED17_sd` | 高端产品历史净值 FG_ED17 | | `ant_weekly_treasure` | `job_ant_weekly_treasure` | `AntWeeklyTreasureRecord` | `ant_shelf_zzb_FG_ED18_sd` | 周周宝数据 FG_ED18 | | `ant_app_post_interact` | `job_ant_app_post_interact` | `AntAppPostInteractRecord` | `ant_discuss_post_publish_records_FG_ED19_sd` | APP 发帖互动 FG_ED19 | | `ant_discuss_post_report` | `job_mms_report` | `ReportRecordEd21` | `ant_discuss_post_report_records_FG_ED21_sd` | 讨论区竞品/举报 FG_ED21 | | `ant_shelf_all_fund` | `job_ant_all_fund` | `AntShelfAllFundRecord` | `ant_fund_query_FG_01_sd` | 全量基金 FG_01 | | `tencent_shelf` | `job_tencent_shelf` | `TencentShelfRecord` | `tencent_shelf_wjlc_FG_ED02_sd` | 腾安理财通货架 wjlc | | `tencent_kol` | `job_tencent_kol` | `TencentKOLRecord` | `tencent_kol_FG_ED07_sd` | 腾安 KOL | | `tencent_kol_hold` | `job_tencent_kol_hold` | `TencentKOLHoldRecord` | `tencent_kol_hold_FG_ED08_sd` | 腾安 KOL 持仓 | | `tencent_post` | `job_tencent_post` | `TencentPostRecord` | `tencent_discuss_post_FG_ED05_sd` | 腾安帖子(讨论区)FG_ED05;`isdate=False`,滚动前 24h12min ~ 后 12min | | `tencent_post_reply` | `job_tencent_post_reply` | `TencentPostReplyRecord` | `tencent_discuss_post_reply_FG_ED22_sd` | 腾安帖子评论 FG_ED22;与 ED05 同 cron/传参/策略 | | `tencent_hqj` | `job_tencent_hqj` | `TencentHQJRecord` | `tencent_shelf_hqj_FG_ED03_sd` | 理财通活期+ FG_ED03 | | `tencent_traffic_map` | `job_tencent_traffic_map` | `TencentTrafficMapRecord` | `tencent_shelf_traffic_map_FG_ED15_sd` | 腾安流量地图 FG_ED15 | | `tiantian_fund_hot` | `job_tiantian_fund_hot` | `TiantianFundHotRecord` | `tiantian_fund_query_FG_TT01_sd` | 天天基金热门 FG_TT01 | | `ant_kol_expert_operation_fg10` | `job_ant_kol_expert_operation_fg10` | `AntKolExpertOperationFG10Record` | `ant_KOL_expert_operation_FG_10_sd` | 蚂蚁达人操作 FG_10 | #### 养基宝(`provider_name='yangjibao'`) | 数据源键 | `job_id` | ORM 模型 | MySQL 表名 | |----------|----------|----------|------------| | `yjb_sector_list` | `job_yjb`(同一 Job 内顺序写入) | `YJBSectorListRecord` | `sector_list_yjb` | | `yjb_fund_detail_list` | 同上 | `YJBFundDetailListRecord` | `fund_list_yjb` | #### NTB 牛头帮(`provider_name='radar_newbanker'`) | 数据源键 | `job_id` | ORM 模型 | MySQL 表名 | |----------|----------|----------|------------| | `radar_alipay_hotfund_stat` | `job_radar_hotfund_stat_1`~`_4`(四次定时,写入同一表) | `AlipayHotfundStatRecord` | `ant_discuss_hot_ntb` | #### 道乐(`provider_name='daole'`) | 数据源键 | `job_id` | ORM 模型 | MySQL 表名 | |----------|----------|----------|------------| | `daole_ant_shelf` | `job_daole_ant_shelf` | `DaoleAntShelfRecord` | `ant_shelf_v1_dl` | | `daole_ant_forum_articles` | `job_daole_ant_forum_articles` | `DaoleAntForumArticlesRecord` | `ant_discuss_post_dl` | | `daole_jd_expert_fund` / `daole_jd_expert_list` / `daole_jd_operate` | `job_daole_jd_expert` | `DaoleJdExpertFundRecord` / `DaoleJdExpertListRecord` / `DaoleJdOperateRecord` | `jingdong_kol_hold_dl` / `jingdong_kol_dl` / `jingdong_kol_operate_dl` | | `daole_jd_jjdrb` | `job_daole_jd_jjdrb` | `DaoleJdJjdrbRecord` | `jingdong_kol_tab_dl` | > **京东文件接口 `date` 规则**(勿与支付宝 KOL 混淆): > - `job_daole_jd_expert`(`jdExpertFund` / `jdExpertList` / `jdOperate`):定时 = **昨天**;补救 = **传入日**;每日 10:20;先 `checkFileReady` 再三接口共用分页。 > - `job_daole_jd_jjdrb`(`jdJjdrb` 基金达人榜单):定时 = **当天**;补救 = **传入日**;每日 10:20;独立 Job,date 规则与三文件不同。 > 请求体示例:`{"date":"2026-06-16","channel":"uadfmafs","index":0}`。建表 DDL:`folder_migrate/sql/jingdong_jd_dl_tables.sql`。触发命令见 `docs/通用/接口命令说明.md`。 | `daole_zfb_expert_operation` | `job_daole_zfb_expert_operation` | `DaoleZfbExpertOperationRecord` | `ant_KOL_key_operate_dl` | | `daole_zfb_expert_operation_day` 等 6 项 | `job_daole_zfb_kol` | `DaoleZfbExpertOperationDayRecord` 等 | `ant_KOL_expert_operation_day_dl` 等(见下表) | **`job_daole_zfb_kol` 六接口与表名对照** | 数据源键 | 接口路径后缀 | MySQL 表名 | |----------|--------------|------------| | `daole_zfb_expert_operation_day` | `expertOperationDay` | `ant_KOL_expert_operation_day_dl` | | `daole_zfb_expert_article` | `expertArticle` | `ant_KOL_expert_article_dl` | | `daole_zfb_fund_list` | `fundList` | `ant_KOL_fund_list_dl` | | `daole_zfb_expert_list` | `expertList` | `ant_KOL_expert_list_dl` | | `daole_zfb_expert_detail` | `expertDetail` | `ant_KOL_expert_detail_dl` | | `daole_zfb_expert_fund` | `expertFund` | `ant_KOL_expert_fund_dl` | > 完整 URL:`{DAOLE_AI_SYNC_BASE_URL}/sz2/leshu_dev/sync/file/{后缀}`。建表 DDL:`folder_migrate/sql/ant_KOL_zfb_dl_tables.sql`。 > **道乐 POST `date`**:定时(每日 10:00)为触发日**前 1 天(昨日)**;`debug_date` / 区间补救为**传入日**。京东三接口另需先调 `checkFileReady`,再按 `index=0..sliceCount-1` 分页拉取。请求体示例:`{"date":"2026-06-01","channel":"uadfmafs","index":0}`。数据源配置 **`isdate=True`**(表示按 date 业务日;实际请求由 `interface_path` 路由至文件接口)。 > **字段来源**:除 `batch_time`、`created_at`、`updated_at` 为程序追加外,其余列均来自道乐文件接口返回。触发命令见 `docs/通用/接口命令说明.md` 中 `job_daole_zfb_kol`。 #### 博彦(`provider_name='boyan'`) | 数据源键 | `job_id` | ORM 模型 | MySQL 表名 | |----------|----------|----------|------------| | `boyan_ant_key` | `job_boyan_ant_gaineggs`(同一 Job 内先 key 后 compete) | `BoYanAntKeyRecord` | `ant_gain_egg_key_by` | | `boyan_ant_compete` | 同上 | `BoYanAntCompeteRecord` | `ant_gain_egg_compete_by` | | `boyan_tencent_key` | `job_boyan_tencent_gaineggs` | `BoYanTencentKeyRecord` | `tencent_gain_egg_key_by` | | `boyan_tencent_compete` | 同上 | `BoYanTencentCompeteRecord` | `tencent_gain_egg_compete_by` | | `boyan_dongbo_index_change` | `job_boyan_dongbo`(同一 Job;batch_time 快照覆盖) | `BoYanDongboIndexChangeRecord` | `index_list_ths` | | `boyan_dongbo_fund_bet` | 同上(基金码 `fund_info.is_focus=1`,接口分批) | `BoYanDongboFundBetRecord` | `fund_list_ths` | #### 财跃(`provider_name='caiyue'`) | 数据源键 | `job_id` | ORM 模型 | MySQL 表名 | |----------|----------|----------|------------| | `caiyue_telegram_detail` | `job_caiyue_telegram` | `CaiyueTelegramDetailRecord` | `info_telegram_interpretation_cy` | > 列表接口 `typeCode=all`(不调用类型统计);详情默认 `INSERT IGNORE`;`CAIYUE_SKIP_EXISTING_DETAIL=true` 时跳过库中已有 ID 的详情请求。细则见 `docs/财跃/财跃供应商数据同步需求文档.md`。 #### 思迪 MMS(`provider_name='sidi_mms'`) > 与现有 `sidi` 供应商的区别:接口域名为 `mms.thinkive.com`,鉴权需先调 `/apis/base/u/login` 获取 token(蚂蚁/腾安各有独立账号),不使用固定 `Sidi_Authorization`。 > 接口 `POST /apis/da/view/topic/opType/page`;每个 Job 在同一时间窗口内依次拉取后合并写入下表。请求 `param` 含 `platName` 与 `opType`。`job_mms_ant_publish` 依次请求:蚂蚁财富 `TOPIC_PUBLISH`、蚂蚁财富 `TOPIC_REPLY`、小红书 `TOPIC_REPLY`;`job_mms_tencent_publish` 依次请求:腾讯理财通 `TOPIC_PUBLISH`、`TOPIC_REPLY`。`opType` / `platName` 字段由接口返回落库。 | 数据源键 `datasource_name` | `job_id` | ORM 模型 | MySQL 表名 | 接口/业务简称 | |---------------------------|----------|----------|------------|---------------| | `mms_ant_publish` | `job_mms_ant_publish` | `AntPublishRecordMms` | `ant_discuss_post_publish_records_sd` | 蚂蚁发布+回复+小红书回复(pageSize=100) | | `mms_tencent_publish` | `job_mms_tencent_publish` | `TencentPublishRecordMms` | `tencent_discuss_post_publish_records_sd` | 腾安发布+回复(pageSize=20) | > **张数统计**:当前 `models/sync_table_models.py` 中同步落库物理表共 **49** 张(含 FG_ED21 新表;旧表 `ant_discuss_post_report_records_sd` 保留模型未删除)。 ### 写入策略(通用开关) 每个 Job 均支持通过 `.env` 配置主键/唯一索引冲突时的写入行为,key 格式:`JOB_{NAME}_WRITE_STRATEGY`。 | 策略值 | 行为 | 日志(INFO) | |--------|------|------| | `ignore`(**默认**) | `INSERT IGNORE` —— 冲突跳过,保留原值不变 | 获取 / 去重后 / 批次内重复 / **新增** / **跳过** | | `update` | `INSERT ... ON DUPLICATE KEY UPDATE` —— 冲突时更新全部非键字段 | 获取 / 去重后 / 批次内重复 / **新增** / **更新** | > 两种策略均通过 `BaseJob._upsert()` 统一实现,日志均在 INFO 级别输出,可在运行日志中看到每次写入的详细统计。 ### 启动时是否立即执行(Startup 触发) 注册到 APScheduler 后,每个 Job 是否在**应用启动时额外异步执行一次**,由 `BaseJob.should_run_on_startup()` 决定:默认返回 `run_on_startup`(多数 Job 为 `True`)。需要自定义时,在 Job 子类中覆写该方法即可,**无需修改** `scheduler/scheduler.py`。 **NTB(牛头帮)`job_radar_hotfund_stat_4`**:默认不在启动时执行;仅当启动时刻 **≥** `.env` 中 `JOB_RADAR_HOTFUND_STAT_CRON4`(默认 `22:10`,与当日定时阈值一致)时补偿执行一次,避免在当日该批次应跑时间之后才重启导致漏跑。`job_radar_hotfund_stat_1/2/3` 仍为启动即执行一次。细则见 `docs/牛头帮/牛头帮供应商接入需求文档.md` 第 3.3 节。 --- ## 快速开始(环境准备与启动) ### 1) 环境准备 ```bash python -m venv .venv source .venv/bin/activate pip install -r requirements.txt cp .env.example .env ``` 按 `.env.example` 编辑 `.env`。Windows 激活:`.venv\Scripts\Activate.ps1` 或 `activate.bat`。 ### 2) 启动服务 #### 开发环境 ```bash python run.py ``` 启动后自动建表(若缺)、按各 Job 策略可能立即跑一轮、cron 持续同步;NTB `job_radar_hotfund_stat_4` 的启动触发见上文「启动时是否立即执行」。 #### Linux 服务器 需 **Python 3.9+**,shell 为 **bash**。在项目根目录(与 `run.py` 同级): ```bash python3 -m venv .venv && source .venv/bin/activate pip install -r requirements.txt [ ! -f .env ] && cp .env.example .env ``` 编辑 `.env`(说明见 `.env.example`)。业务日志只看 **`LOG_FILE`**,默认 **`logs/sidi_sync.log`**。`>/dev/null` 只丢终端输出,**不影响**该文件。 前台验证:`python run.py`(占住终端)。**另开一终端**,同样 `cd` 到项目根并 `source .venv/bin/activate` 后: ```bash curl -s http://127.0.0.1:5000/health ``` 将 `5000` 改为你的 **`APP_PORT`**(未在 `.env` 设置时源码默认为 5000)。前台正常后,在原终端 `Ctrl+C` 停掉,再后台启动: ```bash nohup python run.py >/dev/null 2>&1 & ``` `pgrep -af "python run.py"`;停止:`pkill -f "python run.py"`(勿误杀其它进程)。管理接口见 **`docs/通用/接口命令说明.md`**。 #### 生产环境 WSGI(可选) ```bash pip install waitress waitress-serve --host=0.0.0.0 --port=6001 app:create_app ``` 业务日志仍为 **`LOG_FILE`**。 --- ## 配置文件(.env) 主 README 不再维护逐项配置字典,所有环境变量说明统一以根目录 `.env.example` 为准。 建议使用流程: ```bash cp .env.example .env ``` - 按注释填写数据库连接、接口鉴权与各 Job 配置。 - 不同环境只改 `.env`,不要直接改 `.env.example` 的键名。 - 表名/字段变更规范见 `docs/通用/表结构与字段说明.md`。 --- ## HTTP 管理接口 为减少主文档体积,接口路径、触发示例(含 `debug_date` 与区间补救)见 `docs/通用/接口命令说明.md`。 --- ## 新增数据源(扩展指南) ### 情况一:新增数据表,使用已有供应商(Sidi) 1. **新增 ORM 模型**(`models/` 目录下新建或在已有文件中追加) ```python # models/sync_table_models.py(或新建 models/新文件.py) class NewRecord(Base): __tablename__ = 'new_table_sd' uuid = Column(String(64), primary_key=True) # ... 其他字段 ``` 2. **在 `models/__init__.py` 末尾添加导入**(若新建了模型文件) ```python import models.新文件 # noqa: F401, E402 ``` 3. **在 `services/datasources.py` 注册数据源** ```python 'new_datasource': DataSourceConfig( interface_env_key='INTERFACE_ID_NEW_DATASOURCE', isdate=False, table_name='new_table_sd', model_class=NewRecord, provider_name='sidi', ), ``` 4. **新建 Job 类**(`services/jobs/new_sync.py`) ```python from services.jobs.base import BaseJob class NewSyncJob(BaseJob): datasource_name: str = 'new_datasource' job_id: str = 'job_new_datasource' cron_env_key: str = 'JOB_NEW_DATASOURCE_CRON' default_cron: str = '09:00' ``` 5. **在 `services/jobs/registry.py` 注册 Job** ```python from services.jobs.new_sync import NewSyncJob JOB_REGISTRY: list = [ HotfundSyncJob, HotsearchSyncJob, NewSyncJob, # ← 追加 ] ``` 6. **在 `.env` 中添加配置** ```env INTERFACE_ID_NEW_DATASOURCE=接口ID JOB_NEW_DATASOURCE_CRON=09:00 ``` > **无需修改** `scheduler/scheduler.py`、`config/settings.py`、`run.py`。若需自定义「启动时是否立即执行」,在 Job 子类中设置 `run_on_startup` 或覆写 `should_run_on_startup()` 即可。 --- ### 情况二:新增数据表,使用全新供应商接口 在情况一的基础上,额外操作: 1. **新建供应商适配器**(`services/providers/新供应商/client.py`) ```python from services.providers.base import BaseProvider class NewVendorProvider(BaseProvider): def fetch(self, interface_id, time_range, *, isdate=False) -> list[dict]: # 实现具体的 HTTP 请求逻辑 ... ``` 2. **在 `services/providers/__init__.py` 注册** ```python def _build_registry(): from services.providers.sidi.client import SidiProvider from services.providers.新供应商.client import NewVendorProvider return { 'sidi': SidiProvider(), 'new_vendor': NewVendorProvider(), } ``` 3. **在 `DataSourceConfig` 中指定 `provider_name='new_vendor'`** > 注意:`provider_name` 必须与 `services/providers/__init__.py` 中 `_build_registry()` 的注册 key 完全一致;当前项目内置的 key 包括 `sidi`、`yangjibao`、`radar_newbanker`、`daole`、`boyan`、`caiyue`。 --- --- ### 情况三:新增数据表,使用自定义调度频率(非每日一次) **养基宝(已内置)**:`YJBSyncJob` 使用「每小时若干分钟」模式,·**在 `.env` 中配置**: ```env JOB_YJB_CRON_MINUTES=27,57 ``` 含义与 APScheduler `CronTrigger(minute='27,57')` 一致(`hour` 为 `*`,即每个整点小时都会命中这些分钟)。 **其他 Job(自定义)**:在 Job 类中可任选其一: 1. **推荐(可配 .env)**:声明 `cron_hourly_minutes_env_key` + `default_cron_hourly_minutes`,调度器会读取环境变量并构建 `CronTrigger(minute=...)`。 2. **代码组装**:在模块内用函数读取 `.env` 后返回 `CronTrigger(...)`(优先级低于 `cron_hourly_minutes_env_key`)。例如 `AntAppPostInteractSyncJob`(FG_ED19)使用 `JOB_ANT_APP_POST_INTERACT_CRON`(`周几 HH:MM`,默认 `fri 13:00`),`dataDate` 取触发日当日(`BaseJob.get_time_range` 默认全日)。 > 每日一次的 Job 仍使用 `cron_env_key` + `default_cron`(格式 `HH:MM`),与养基宝的 `JOB_YJB_CRON_MINUTES` 是两套配置方式。 --- ## 表结构与字段说明 表结构清单、字段字典、表名/字段修改规范见 `docs/通用/表结构与字段说明.md`。 --- ## 文档修订 | 日期 | 修订内容 | |------|----------| | 2026-05-15 | FG_ED19 数据源与 MySQL 表映射与《表结构与字段说明》对齐校对 | | 2026-06-02 | 新增 FG_ED14(金选货架 V2)、FG_ED20(稳健货架)同步 Job 与表 | | 2026-06-23 | `tencent_discuss_post_FG_ED05_sd`(`job_tencent_post`)滚动窗口改为前 24h12min ~ 后 12min | ### 线上轻量化部署 前端先在 `D:\dev2\前端` 执行: ```bash npm run build ``` 将构建产物放到后端项目目录: ```powershell Copy-Item -Path D:\dev2\前端\dist\* -Destination D:\dev2\ai-intelligent-operation-data\dist -Recurse -Force ``` 然后只启动后端: ```bash python serve.py ``` 访问方式: - 页面:`http://:6001/app/` - API:`http://:6001/api/*` 前端刷新深层路由会回退到 `index.html`,可直接打开 `/app/runs`、`/app/backfill` 等页面。