初始化
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
---
|
||||
inclusion: always
|
||||
---
|
||||
|
||||
# 项目规范执行指引
|
||||
|
||||
## 方案讨论前
|
||||
|
||||
必读 `#[[file:.kiro/steering/项目结构说明.md]]`,全面了解:
|
||||
- 项目分层结构:`config` → `core` → `ai` → `models` → `services` → `scheduler`
|
||||
- 双数据源架构(PG 源库 + MySQL 业务库)
|
||||
- 定时任务清单和清洗流程
|
||||
- 并发模型(asyncio 协程 + Semaphore 信号量)
|
||||
- 与 back-end、offerpie_python_ai 的关系
|
||||
|
||||
方案讨论时:
|
||||
- 优先给出简洁的方案思路(涉及哪些模块、新增内容放在哪、核心流程概要),不要一开始就铺开所有细节
|
||||
- 用户明确要求时,再给出详细的方案流程
|
||||
- 说明与现有模块的关系
|
||||
|
||||
## 开发方案输出前 / 写代码前
|
||||
|
||||
必读 `#[[file:.kiro/steering/代码开发风格文档.md]]`,严格遵守:
|
||||
- 命名约定、类型注解规范
|
||||
- 双数据源使用规范(PgSession / MysqlSession)
|
||||
- 异步规范(Semaphore、Lock、gather)
|
||||
- AI 调用规范(model_config 引用、ai_tool 封装)
|
||||
- 日志规范(批次汇总 + 单条关键结果)
|
||||
- 配置规范(所有可调参数走 settings)
|
||||
|
||||
## 写完代码后
|
||||
|
||||
涉及新增文件、新增模块或目录结构变更时,必须同步更新 `#[[file:.kiro/steering/项目结构说明.md]]`,保持文档与实际代码一致。
|
||||
@@ -0,0 +1,104 @@
|
||||
---
|
||||
inclusion: manual
|
||||
---
|
||||
|
||||
# 代码开发风格文档
|
||||
|
||||
本项目为 Python 3.12 纯后台定时任务服务,基于 asyncio + SQLAlchemy + APScheduler,应用主目录为 `app/`。
|
||||
|
||||
## 项目结构
|
||||
|
||||
- `app/config/` — 配置层:Pydantic Settings 统一配置
|
||||
- `app/core/` — 核心基础设施:双数据源连接、日志
|
||||
- `app/ai/` — AI 能力层:LLM 模型枚举、场景配置、Prompt 模板
|
||||
- `app/models/` — ORM 模型层:分 pg/ 和 mysql/ 两个子包
|
||||
- `app/services/` — 业务逻辑层:清洗、补充、恢复、下架等异步服务
|
||||
- `app/scheduler/` — 调度层:APScheduler 定时任务注册
|
||||
|
||||
## 命名约定
|
||||
|
||||
### 文件命名
|
||||
- 全部小写,下划线分隔,如 `job_clean_service.py`、`dict_cache_service.py`
|
||||
- ORM 模型文件与表名对应(去掉 `bg_` 或 `app_` 前缀),如 `job.py` 对应 `bg_job`
|
||||
|
||||
### 类命名
|
||||
- ORM 模型用 PascalCase 业务名,无后缀,如 `Job`、`Company`、`AppJobData`
|
||||
- 字典缓存用 `DictCacheService`
|
||||
- 枚举类以大写命名,如 `LLM`
|
||||
- 场景配置类以 `Model` 结尾,如 `JobCleanModel`、`CompanyCleanModel`
|
||||
|
||||
### 变量与函数命名
|
||||
- 函数和变量使用 snake_case,如 `run_job_clean`、`data_id`
|
||||
- 私有函数以单下划线开头,如 `_do_clean`、`_build_user_message`
|
||||
- 常量使用全大写下划线,如 `JOB_STRUCTURE_SYSTEM`
|
||||
- 全局单例小写,如 `dict_cache`、`_id_gen`
|
||||
|
||||
## 类型注解
|
||||
|
||||
- 所有函数参数和返回值必须有类型注解
|
||||
- ORM 模型字段使用 `Mapped[T]` + `mapped_column()` 声明
|
||||
- 可选字段使用 `Optional[T]` 或 `T | None`
|
||||
- 集合类型使用 `list[T]`、`dict[K, V]`(Python 3.12 内置泛型)
|
||||
|
||||
## 注释规范
|
||||
|
||||
- 模块级注释使用文件顶部的 docstring,说明模块用途
|
||||
- 函数注释使用 docstring,简洁描述功能
|
||||
- 复杂逻辑用行内注释 `#` 说明
|
||||
- ORM 模型字段通过 `comment` 参数说明含义
|
||||
|
||||
## 数据库使用规范
|
||||
|
||||
### 双数据源
|
||||
- PostgreSQL 会话通过 `PgSession()` 获取,用于 `app_job_data` 表操作
|
||||
- MySQL 会话通过 `MysqlSession()` 获取,用于业务表操作
|
||||
- 每次操作用 `async with XxxSession() as session` 获取会话,手动 `await session.commit()`
|
||||
|
||||
### SQL 风格
|
||||
- 简单操作使用 `sqlalchemy.text()` 原生 SQL,保持直观
|
||||
- 批量插入使用 `insert(Model).values(...)` 或 `insert(Model), [dict...]`
|
||||
- 锁定使用 `FOR UPDATE SKIP LOCKED`(PG)防阻塞
|
||||
|
||||
### 不做分布式事务
|
||||
- PG 和 MySQL 独立操作,不跨库事务
|
||||
- 失败靠僵尸恢复机制重试
|
||||
|
||||
## 异步规范
|
||||
|
||||
- 所有数据库操作、AI 调用使用 `async/await`
|
||||
- 并发控制使用 `asyncio.Semaphore`
|
||||
- 互斥操作使用 `asyncio.Lock()`(如公司查找或创建)
|
||||
- 批量任务使用 `asyncio.gather(*tasks, return_exceptions=True)`
|
||||
|
||||
## AI 调用规范
|
||||
|
||||
- 业务代码从 `app.ai.model_config` 引用场景配置类,不直接使用 `LLM` 枚举
|
||||
- AI 调用统一通过 `app.services.ai_tool.ai_chat_json()` 封装(异步调用 + JSON 清洗 + 解析)
|
||||
- AI 调用失败不影响主流程(第二次/第三次 AI 调用独立 try-catch)
|
||||
- 修改模型或参数只需改 `model_config.py`
|
||||
|
||||
## 日志规范
|
||||
|
||||
- 使用 `from app.core.logger import log`
|
||||
- 批次任务:记录锁定数量和完成汇总
|
||||
- 单条处理:记录关键结果(入库成功/丢弃/跳过),格式 `[id=xxx] 描述`
|
||||
- 异常:`log.error` 记录异常详情
|
||||
- 非关键失败:`log.warning` 记录但不中断
|
||||
|
||||
## 配置规范
|
||||
|
||||
- 所有可调参数放在 `settings.py`,通过 `.env` 文件覆盖
|
||||
- 运行时参数(batch_size、concurrency、interval、expire_days)必须可配置
|
||||
- 数据库密码通过 `urllib.parse.quote` 编码后拼入 URL
|
||||
|
||||
## 代码格式规范
|
||||
|
||||
### 紧凑风格
|
||||
- 避免过度换行,保持代码紧凑易读
|
||||
- f-string 拼接优先写在一行
|
||||
- 方法参数列表较多时,可适当换行但保持紧凑
|
||||
|
||||
### 模块组织
|
||||
- 每个 service 文件对外暴露一个 `run_xxx()` 异步函数作为入口
|
||||
- 内部逻辑拆分为 `_xxx` 私有函数
|
||||
- import 按标准库 → 第三方库 → 项目内部 顺序排列
|
||||
@@ -0,0 +1,139 @@
|
||||
---
|
||||
inclusion: manual
|
||||
---
|
||||
|
||||
# OfferPie Job Cleaner 项目结构说明
|
||||
|
||||
## 1️⃣ 项目整体层次
|
||||
```
|
||||
offerpie_job_cleaner/
|
||||
│
|
||||
├─ .env / .env.prod # 环境变量配置(dev/prod)
|
||||
├─ requirements.txt # Python 依赖清单
|
||||
│
|
||||
└─ app/ # 应用主目录
|
||||
├─ main.py # 入口:初始化双数据源 → 加载字典缓存 → 启动调度器 → 等待关闭信号
|
||||
│
|
||||
├─ config/ # **配置层**
|
||||
│ └─ settings.py # Pydantic Settings 统一配置(PG、MySQL、AI供应商、清洗参数、下架参数、日志)
|
||||
│
|
||||
├─ core/ # **核心基础设施层**
|
||||
│ ├─ database.py # 双数据源:PgSession()(PostgreSQL)+ MysqlSession()(MySQL)
|
||||
│ └─ logger.py # Loguru 日志配置(控制台+文件,按天轮转保留30天)
|
||||
│
|
||||
├─ ai/ # **AI 能力层**
|
||||
│ ├─ models.py # LLM 模型枚举(LLM.DOUBAO_SEED_LITE、DEEPSEEK_V4_FLASH 等)
|
||||
│ ├─ model_config.py # AI 模型场景配置(JobCleanModel、CompanyCleanModel)
|
||||
│ └─ prompts.py # 各步骤 Prompt 模板(结构化提取、专业匹配、技能提取、公司补充)
|
||||
│
|
||||
├─ models/ # **ORM 模型层**
|
||||
│ ├─ pg/ # PostgreSQL 模型
|
||||
│ │ └─ app_job_data.py # 爬虫原始数据表(app_job_data)
|
||||
│ └─ mysql/ # MySQL 模型
|
||||
│ ├─ job.py # 岗位表(bg_job)
|
||||
│ ├─ company.py # 公司表(bg_company)
|
||||
│ ├─ skill_tag.py # 技能标签表(bg_skill_tag)
|
||||
│ └─ relations.py # 关联表(bg_job_region_relation、bg_job_skill_tag_relation)
|
||||
│
|
||||
├─ services/ # **业务逻辑层**
|
||||
│ ├─ job_clean_service.py # 岗位清洗主逻辑(协程+信号量并发,三次AI调用)
|
||||
│ ├─ company_clean_service.py # 公司数据补充(协程+信号量并发,AI补充公司信息)
|
||||
│ ├─ dict_cache_service.py # 字典数据缓存(启动时从MySQL加载岗位分类/行业/专业/地区)
|
||||
│ ├─ zombie_recover_service.py # 僵尸恢复(PG岗位超时重置 + MySQL公司超时重置)
|
||||
│ ├─ job_expire_service.py # 岗位下架(create_time超N天的岗位标记失效)
|
||||
│ └─ ai_tool.py # AI 调用工具封装(异步调用+JSON清洗+解析)
|
||||
│
|
||||
└─ scheduler/ # **调度层**
|
||||
└─ tasks.py # APScheduler 定时任务注册(岗位清洗/公司补充/僵尸恢复/岗位下架)
|
||||
```
|
||||
|
||||
## 2️⃣ 各层模块职责
|
||||
| 层级 | 主要职责 | 关键类/文件 |
|
||||
|------|----------|-------------|
|
||||
| **config** | 统一配置管理,双数据源连接参数、AI供应商、清洗/下架参数 | `Settings`(pg_*、db_*、volcengine_*、clean_*、company_*、job_expire_days) |
|
||||
| **core** | 核心基础设施:双数据库连接池、日志 | `database.py`(PgSession/MysqlSession 函数)、`logger.py`(loguru) |
|
||||
| **ai** | AI 模型管理 + Prompt 模板 | `LLM` 枚举、`JobCleanModel`/`CompanyCleanModel`(场景配置)、`prompts.py`(4个prompt) |
|
||||
| **models** | SQLAlchemy ORM 模型,分 pg/ 和 mysql/ 两个子包 | `AppJobData`(PG)、`Job`/`Company`/`SkillTag`/`Relations`(MySQL) |
|
||||
| **services** | 业务逻辑实现,全部异步协程 | 岗位清洗、公司补充、字典缓存、僵尸恢复、岗位下架、AI工具 |
|
||||
| **scheduler** | APScheduler 定时任务注册和触发 | `tasks.py`(5个定时任务) |
|
||||
|
||||
## 3️⃣ 技术栈
|
||||
| 类别 | 技术选型 | 说明 |
|
||||
|------|----------|------|
|
||||
| **运行时** | Python 3.12 + asyncio | 纯后台定时任务,无 HTTP 服务 |
|
||||
| **ORM** | SQLAlchemy 2.0 (asyncio) | 双引擎:asyncpg(PG) + asyncmy(MySQL) |
|
||||
| **AI/LLM** | LangChain-OpenAI | 兼容 OpenAI 协议,火山引擎豆包模型 |
|
||||
| **调度** | APScheduler (AsyncIOScheduler) | 轻量异步定时任务调度 |
|
||||
| **配置** | Pydantic Settings + python-dotenv | 类型安全的 .env 配置管理 |
|
||||
| **日志** | Loguru | 控制台+文件日志,按天轮转 |
|
||||
| **ID生成** | snowflake-id | 雪花算法分布式ID |
|
||||
|
||||
## 4️⃣ 双数据源架构
|
||||
| 数据源 | 用途 | 地址 |
|
||||
|--------|------|------|
|
||||
| **PostgreSQL** | 爬虫原始数据(app_job_data),只读写这一张表 | 本地 192.168.31.51:5432 |
|
||||
| **MySQL** | 业务库(bg_job、bg_company、字典表等) | 生产 |
|
||||
|
||||
- PG:读取待清洗数据 → 更新清洗状态
|
||||
- MySQL:写入清洗后的业务数据 + 读取字典缓存
|
||||
|
||||
## 5️⃣ 定时任务清单
|
||||
| 任务 | 频率 | 数据源 | 描述 |
|
||||
|------|------|--------|------|
|
||||
| 岗位清洗 | 每3分钟 | PG→MySQL | 批量锁定 pending → 协程并发AI清洗 → 写入 bg_job |
|
||||
| 公司补充 | 每5分钟 | MySQL | 锁定 status=0 → 协程并发AI补充 → 回填 bg_company |
|
||||
| 岗位僵尸恢复 | 每30分钟 | PG | cleaning 超时10分钟 → 重置 pending |
|
||||
| 公司僵尸恢复 | 每小时 | MySQL | status=3 超时10分钟 → 重置 0 |
|
||||
| 岗位下架 | 每天凌晨2点 | MySQL | create_time 超 N 天 → status=2 |
|
||||
|
||||
## 6️⃣ 清洗流程(三次AI调用)
|
||||
```
|
||||
app_job_data (PG, pending)
|
||||
↓ 批量锁定 → cleaning
|
||||
↓
|
||||
第一次AI:结构化提取(岗位名/薪资/学历/分类/地区/公司...)
|
||||
↓ valid=false → discarded
|
||||
↓ valid=true → 去重 → 创建公司 → 写入 bg_job
|
||||
↓
|
||||
第二次AI:专业匹配(requiredMajorIds + majorSensitivity)
|
||||
↓ 失败不影响
|
||||
↓
|
||||
第三次AI:技能提取(INSERT IGNORE bg_skill_tag → 写关联表)
|
||||
↓ 失败不影响
|
||||
↓
|
||||
更新 PG: clean_status = cleaned
|
||||
```
|
||||
|
||||
## 7️⃣ 并发模型
|
||||
- **asyncio 协程 + Semaphore 信号量**
|
||||
- 批量从数据库捞 N 条(batch_size),所有协程同时启动
|
||||
- 信号量限制同时调 AI 的并发数(concurrency),做完一条立刻补一条
|
||||
- 公司创建用 `asyncio.Lock()` 防并发重复插入
|
||||
|
||||
## 8️⃣ 状态机
|
||||
|
||||
### app_job_data.clean_status(PG)
|
||||
```
|
||||
pending → cleaning → cleaned
|
||||
→ discarded
|
||||
cleaning 超时 → pending(僵尸恢复)
|
||||
```
|
||||
|
||||
### bg_company.status(MySQL)
|
||||
```
|
||||
0(待完善) → 3(补充中) → 1(已完善)
|
||||
→ 4(补充失败)
|
||||
3 超时 → 0(僵尸恢复)
|
||||
```
|
||||
|
||||
## 9️⃣ 与其他项目的关系
|
||||
- **与 back-end(Java)的关系**:共享 MySQL 业务库,清洗逻辑从 Java 迁移至此项目
|
||||
- **与 offerpie_python_ai 的关系**:独立部署,AI 封装风格一致(LLM枚举 + model_config),但不共享代码
|
||||
- **数据流向**:爬虫 → PG(app_job_data) → 本项目清洗 → MySQL(bg_job等) → Java/Python AI 服务使用
|
||||
|
||||
## 🔟 构建与运行
|
||||
- **虚拟环境**:`.venv` 目录管理
|
||||
- **依赖安装**:`pip install -r requirements.txt`
|
||||
- **启动**:`python -m app.main`
|
||||
- **环境切换**:通过 `.env` / `.env.prod` 控制,ENV 环境变量指定
|
||||
- **部署**:本地非公网环境运行,无需 Docker/Nginx
|
||||
Reference in New Issue
Block a user