初始化

This commit is contained in:
zk
2026-06-02 17:44:03 +08:00
commit 30e6a6e2a5
34 changed files with 1692 additions and 0 deletions
+33
View File
@@ -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]]`,保持文档与实际代码一致。
+104
View File
@@ -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 按标准库 → 第三方库 → 项目内部 顺序排列
+139
View File
@@ -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_statusPG
```
pending → cleaning → cleaned
→ discarded
cleaning 超时 → pending(僵尸恢复)
```
### bg_company.statusMySQL
```
0(待完善) → 3(补充中) → 1(已完善)
→ 4(补充失败)
3 超时 → 0(僵尸恢复)
```
## 9️⃣ 与其他项目的关系
- **与 back-endJava)的关系**:共享 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