Files
offerpai_browser_plug/.kiro/steering/project-guidelines.md
T

12 KiB
Raw Blame History

inclusion
inclusion
always

项目开发规范与注意事项

技术栈

  • Plasmo + React + TypeScript
  • 样式:SCSS

项目结构

src/
├── api/
│   ├── aiApi.ts               # AI后端接口(Python后端,简历优化/AI生成等)
│   ├── dataApi.ts             # 数据后端接口(Java后端,简历CRUD/岗位/会员等)
│   └── request.ts             # 请求封装(axios实例、拦截器、Token注入)
├── background/
│   └── index.ts               # 插件后台脚本(Service Worker
├── components/
│   ├── SidebarPanel.tsx       # 侧边栏面板组件(主操作界面,自动填写入口,字段进度展示)
│   └── SidebarPanel.scss      # 侧边栏样式
├── contents/
│   └── sidebar.tsx            # Content Script 入口(注入侧边栏到页面)
├── handlers/
│   ├── handleAutoFillCommon.ts  # 通用模式自动填写(不依赖任何CSS类名,纯DOM差异对比适配所有网站)
│   └── handleAutoFillBeisen.ts  # 北森模式自动填写(针对北森Phoenix UI的特殊处理,含级联选择器等)
├── lib/
│   ├── types.ts               # 所有共享类型定义(简历接口、表单标签、匹配字段、UI组件库配置、经历区块类型)
│   ├── constants.ts           # 常量数据(JOB_FORM_LABELS标签数组、EXPERIENCE_SECTION_CONFIGS经历配置、UI_LIB_PICKER_CONFIGS组件库配置)
│   ├── dom.ts                 # DOM工具(extractDomStructure提取DOM树、detectPageLanguage语言检测、isJobApplicationForm表单页判断、buildSelector选择器生成、snapshotElementsInRange/diffSnapshots/findTopLevelNewElements DOM快照差异对比)
│   ├── formMatcher.ts         # 表单字段匹配(matchFormFields全页面匹配、matchFormFieldsInRange范围内匹配、matchMainFields非经历区域匹配、findNearestInput查找input
│   ├── labelFinder.ts         # 标签定位(findLabelForInput通过form-item容器+DOM逆向遍历两遍策略识别input对应的标签文字)
│   ├── pickerDetector.ts      # 选择器识别(detectPickerField检测字段是否为选择器类型,通过UI组件库类名/提示文字/主动点击DOM差异对比三种方式)
│   ├── pickerFill.ts          # 选择器选项匹配与点击(fuzzyMatchScore模糊匹配、clickBestOptionInDropdown弹出层内选项点击、findAndClickOptionInVisiblePopups弹出层搜索)
│   ├── datePicker.ts          # 日期选择器填写(fillDatePicker日期填写、detectNavButtons按钮探测、navigateToYearMonth导航、tryFillMonthPanel月份面板)
│   ├── autofill.ts            # 自动填写核心(forceSetValue强制写值、fillMatchedField统一填写入口、fillPickerField选择器填写含DOM差异对比、fillSearchPickerField搜索型选择器、fillTimePeriodField/fillTimeSingleField时间字段、fillYearMonthPicker年月选择器)
│   ├── experienceSection.ts   # 经历区块识别与段数管理(locateExperienceSections定位5大经历、expandExperienceSections补足段数、countExistingSegments段数校正、relocateSegmentContainer重定位、sortExperienceByTime时间排序)
│   ├── fillStats.ts           # 填写统计(scanPageFields按大标题分组扫描所有字段状态、extractNonResumeFields提取非简历格式字段、printFieldStats打印统计)
│   ├── formStyle.ts           # 表单样式(setFieldHighlight字段背景高亮、isRequiredField必填检测)
│   ├── resumeDataHelper.ts    # 简历数据工具(getResumeFieldValue根据section/index/field获取简历值)
│   ├── resumeUpload.ts        # 简历上传(detectAndUploadResume检测上传按钮并从OSS下载注入文件)
│   └── channelBridge.ts       # 跨域通信桥(panelBridge跨域读写数据,用于Content Script和网页端通信)
├── utils/
│   ├── auth.ts                # 认证工具(getMemberStatus会员状态查询、getCustomizeResume定制简历获取)
│   ├── cookie.ts              # Cookie工具(getCookieValue从浏览器获取Cookie值)
│   ├── delay.ts               # 延时工具(delay统一延时函数,按等级管理:low/mid/high/max
│   └── storage.ts             # 存储工具(chrome.storage的get/set/remove封装)
└── assets/
    ├── icon.png               # 插件图标
    └── logo-offerpai.png      # OfferPai logo

编码规范

  • 页面结构和ts的常量变量和方法都要加中文注释
  • 重要逻辑加【注意】标记防止误删(如fillPickerField的DOM差异对比逻辑)

注意事项

  • 项目接口有Java后端和Python AI后端两种,如果是联调加入新接口,一定要确认有没有说是哪个后端的
  • fillPickerField中的DOM差异对比弹出层检测是核心机制,不要简化或删除
  • detectPickerField的方式3(主动点击检测DOM变化)是自建组件选择器识别的关键,不要删除
  • UI_LIB_PICKER_CONFIGS里不要加容易误匹配的配置(如之前Beisen Phoenix被误匹配到新东方自建网站)

填充操作核心规范

  • 所有字段填充必须走 fillMatchedField 统一入口,禁止在外部自行编写填充逻辑
  • 选择器类型字段(下拉、日期、月份、级联等)绝对不能用 forceSetValue 直接写值,这类 input 通常是受控组件,直接写值无效
  • 选择器类型检测必须走 detectPickerField 统一入口pickerDetector.ts),禁止在外部自行判断字段是否为选择器
    • detectPickerField 内部已封装三种检测方式:UI组件库类名匹配 → 提示文字检测 → 主动点击DOM差异对比
    • 不要自行通过 readonly、class 等属性简单判断是否为选择器,这样会漏判
  • 时间字段(开始时间/结束时间/起止时间)如果没有找到 placeholder="年"/"月" 的下拉输入框,必须标记 isPicker=truefillMatchedFieldfillPickerFieldfillDatePickertryFillMonthPanel 已封装的完整链路
  • 禁止在新增代码中自行编写选择器展开、月份点击、日期导航等操作逻辑,必须先查看项目中已封装的方法并直接引用:
    • 选择器类型检测 → pickerDetector.tsdetectPickerField
    • 日期/月份选择器操作 → datePicker.tsfillDatePicker / tryFillMonthPanel / navigateToYearMonth / clickDayCell
    • 下拉选项匹配与点击 → pickerFill.tsclickBestOptionInDropdown / findAndClickOptionInVisiblePopups
    • 选择器展开与DOM差异检测 → autofill.ts 中的 fillPickerField
    • 单选按钮点击 → autofill.ts 中的 fillRadioField
    • 搜索型选择器 → autofill.ts 中的 fillSearchPickerField
    • 年月下拉选择器 → autofill.ts 中的 fillYearMonthPicker
  • datePicker.ts 中的按钮探测逻辑(detectNavButtons:逐个点击观察年月变化)是通用日期选择器适配的核心,不要简化或删除

经历段落定位核心规范

  • experienceSection.ts 中的段落容器定位逻辑(DOM 差异对比 + 特征重建)是5大经历区域填写的核心,不可擅自修改或简化
  • 核心原理:
    1. 点击添加按钮前后,用 snapshotElementsInRange / diffSnapshots / findTopLevelNewElementsdom.ts)对比 DOM 快照差异
    2. 找到新增的顶层容器(一段经历的完整 DOM),记住它的 tag + className 前缀作为段容器特征
    3. 同时保存新增段容器的 DOM 引用(lastNewContainerEl
    4. 所有段添加完后,从 lastNewContainerEl.parentElement 出发,在父级 children 中匹配所有同特征兄弟容器
    5. 按 DOM 顺序重建 segmentRanges 数组,确保每段经历的 containerElement 精确对应
  • 禁止用 input 差集(beforeInputs)来定位段容器——React 重渲染会导致原始段的 input 节点被重建,差集不可靠
  • 禁止依赖 titleElement 存活状态来做重建——React 重渲染可能使 titleElement 脱离 DOM
  • 对于初始有1段且不需要添加的经历:走 detectFirstSegment 的 fallback 逻辑(无同级兄弟时 containerElement 可能为 nullmatchFormFieldsInRange 有 fallback 用 startElement/endElement 范围搜索)

下拉选择器弹出层搜索核心规范

  • 弹出层检测不依赖任何固定类名,全部走 DOM 差异对比(点击前后全页面快照对比新增/新可见元素)
  • 禁止使用 POPUP_SELECTORS 或任何固定 CSS 选择器来检测弹出层,必须走全页面可见性差异对比(offsetHeight > 0 && offsetWidth > 0
  • pickerDetector.ts 方式3(主动点击检测)的核心约束
    • 检测到弹出层后 禁止关闭弹出层(不调用 dismissPopup
    • 将弹出层 DOM 引用保存到 field.pickerDropdownElement
    • fillPickerField 步骤0 直接在已有弹出层内搜索,避免二次点击触发 toggle 关闭
    • 弹出层由用户选择后自动关闭,无需手动干预
  • 选项搜索必须限定在检测到的弹出层元素内,禁止全页面搜:
    • findAndClickOptionInVisiblePopups(fillValue, labelText, popupEl?) 没有 popupEl 时直接返回 false,不做全页面搜
    • fillPickerField 步骤0 用 field.pickerDropdownElementpickerDetector 保存的引用)在弹出层内搜索
    • fillPickerField 步骤4 在 DOM 差异检测到的 dropdownEl 内搜索
    • fillSearchPickerField 轮询检测到的 newPopupEl 传给 clickBestOptionInDropdownfindAndClickOptionInVisiblePopups
  • fillPickerField 步骤0pickerDetector 可能已展开弹出层(toggle 冲突问题),所以在步骤1之前先尝试在当前页面搜索匹配选项,避免二次点击关闭弹出层。不可删除此步骤
  • isAsyncDropdown 字段的特殊处理FormLabelItem.isAsyncDropdown = true):
    • 接口异步下拉字段(如学校、专业)输入值后需轮询等待接口返回数据(最多3秒)
    • 弹出层检测阈值为 ≥1 个可见子元素(普通字段为 ≥2)
    • 检测到弹出层后优先用 clickBestOptionInDropdown(newPopupEl) 在弹出层内直接做文字匹配
    • 禁止对 isAsyncDropdown 字段走 tryClickDropdownListItem 全页面搜(选项数可能为1,全页面搜会被其他同规格元素干扰)
  • pickerFill.tsgroupBySpec / findVisibleListGroups 的同规格标签组阈值为 ≥2(不可改回 ≥3,否则只有2个选项的下拉列表会漏匹配)
  • clickBestOptionInDropdown 是在指定弹出层 DOM 内递归搜所有可见叶子文字节点做模糊匹配的核心方法,不可简化其递归逻辑

延时调用规范

  • 项目中所有延时调用必须且只能使用 src/utils/delay.ts 中导出的 delay 函数
  • 禁止在任何文件中自行编写 setTimeout / new Promise(resolve => setTimeout(...)) 等延时逻辑
  • delay 函数接收延时等级参数,统一管理延时时长:
    • delay("low") — 低延时(微等待,如点击后极短暂停)
    • delay("mid") — 中延时(等待 DOM 更新、弹出层渲染等)
    • delay("high") — 高延时(等待接口返回、搜索结果、动画完成等)
    • delay("max") — 特殊超长延时(谨慎使用)
  • 具体毫秒数只在 src/utils/delay.ts 中统一设置,调用方只使用等级名称
  • 如需调整全局延时策略,只修改 delay.ts 中的 DELAY_MS 常量即可

代码组织与可维护性规范

  • 修改或添加新逻辑时,必须完整查看逻辑相关的所有引用和引入,理解上下游调用关系后再动手
  • 只要能实现,新逻辑优先单独抽出为独立方法,通过返回值向调用方提供所需结果,保持项目的解耦性和高可维护性
  • 禁止将新增逻辑直接内联到已有的大方法中导致方法膨胀、职责混乱
  • 独立方法应有清晰的中文注释说明:用途、入参、返回值、安全退化行为
  • 调用独立方法的位置需加注释说明为什么调用、结果如何使用