add static combine job filter

This commit is contained in:
geekgeekrun
2025-10-26 18:00:35 +08:00
parent 36aef1c16d
commit 57cf631b86
8 changed files with 393 additions and 61 deletions

View File

@@ -124,3 +124,17 @@ export function checkAnyCombineBossRecommendFilterHasCondition(value) {
return !!value[k]?.length
})
}
export function formatStaticCombineFilters(staticCombineRecommendJobFilterConditions) {
const result = staticCombineRecommendJobFilterConditions.map((condition) => {
return {
salaryList: condition.salary ? [condition.salary] : [],
experienceList: condition.experience ? [condition.experience] : [],
degreeList: condition.degree ? [condition.degree] : [],
scaleList: condition.scale ? [condition.scale] : [],
industryList: condition.industry ? [condition.industry] : []
}
})
result.unshift(null)
return result
}

View File

@@ -1,4 +1,5 @@
{
"combineRecommendJobFilterType": 1,
"anyCombineRecommendJobFilter": {
"salaryList": [],
"experienceList": [],
@@ -6,6 +7,7 @@
"scaleList": [],
"industryList": []
},
"staticCombineRecommendJobFilterConditions": [],
"isSkipEmptyConditionForCombineRecommendJobFilter": false,
"expectJobRegExpStr": "",
"jobNotMatchStrategy": 1,

View File

@@ -15,12 +15,21 @@ import { readConfigFile, writeStorageFile, ensureConfigFileExist, readStorageFil
import {
calculateTotalCombinations,
combineFiltersWithConstraintsGenerator,
checkAnyCombineBossRecommendFilterHasCondition
checkAnyCombineBossRecommendFilterHasCondition,
formatStaticCombineFilters,
} from './combineCalculator.mjs'
import { default as jobFilterConditions } from './internal-config/job-filter-conditions-20241002.json'
import { default as rawIndustryFilterExemption } from './internal-config/job-filter-industry-filter-exemption-20241002.json'
import { ChatStartupFrom } from '@geekgeekrun/sqlite-plugin/dist/entity/ChatStartupLog'
import { MarkAsNotSuitReason, MarkAsNotSuitOp, StrategyScopeOptionWhenMarkJobNotMatch, SalaryCalculateWay, JobDetailRegExpMatchLogic, JobSource } from '@geekgeekrun/sqlite-plugin/dist/enums'
import {
MarkAsNotSuitReason,
MarkAsNotSuitOp,
StrategyScopeOptionWhenMarkJobNotMatch,
SalaryCalculateWay,
JobDetailRegExpMatchLogic,
JobSource,
CombineRecommendJobFilterType
} from '@geekgeekrun/sqlite-plugin/dist/enums'
import {
activeDescList,
RECOMMEND_JOB_ENTRY_SELECTOR,
@@ -85,8 +94,10 @@ const bossCookies = readStorageFile('boss-cookies.json')
const bossLocalStorage = readStorageFile('boss-local-storage.json')
const targetCompanyList = readConfigFile('target-company-list.json').filter(it => !!it.trim());
const combineRecommendJobFilterType = readConfigFile('boss.json').combineRecommendJobFilterType ?? CombineRecommendJobFilterType.ANY_COMBINE
const anyCombineRecommendJobFilter = readConfigFile('boss.json').anyCombineRecommendJobFilter
const staticCombineRecommendJobFilterConditions = readConfigFile('boss.json').staticCombineRecommendJobFilterConditions ?? []
let isSkipEmptyConditionForCombineRecommendJobFilter = readConfigFile('boss.json').isSkipEmptyConditionForCombineRecommendJobFilter
if (!checkAnyCombineBossRecommendFilterHasCondition(anyCombineRecommendJobFilter)) {
isSkipEmptyConditionForCombineRecommendJobFilter = false
@@ -662,11 +673,13 @@ async function toRecommendPage (hooks) {
}
}
const filterConditions =
combineRecommendJobFilterType === CombineRecommendJobFilterType.STATIC_COMBINE
? formatStaticCombineFilters(staticCombineRecommendJobFilterConditions)
: combineFiltersWithConstraintsGenerator(anyCombineRecommendJobFilter)
let expectJobList
iterateFilterCondition: for (
const filterCondition of combineFiltersWithConstraintsGenerator(
anyCombineRecommendJobFilter
)
const filterCondition of filterConditions
) {
findInCurrentFilterCondition: while(true) {
await sleepWithRandomDelay(2500)
@@ -691,11 +704,17 @@ async function toRecommendPage (hooks) {
break
}
}
if (
isSkipEmptyConditionForCombineRecommendJobFilter &&
Object.keys(filterCondition).length &&
Object.keys(filterCondition).every(k => !filterCondition[k]?.length)
(
combineRecommendJobFilterType === CombineRecommendJobFilterType.STATIC_COMBINE && filterCondition === null
)
||
(
combineRecommendJobFilterType === CombineRecommendJobFilterType.ANY_COMBINE &&
isSkipEmptyConditionForCombineRecommendJobFilter &&
Object.keys(filterCondition).length &&
Object.keys(filterCondition).every(k => !filterCondition[k]?.length)
)
) {
sleep(4000)
continue iterateFilterCondition

View File

@@ -34,3 +34,8 @@ export enum JobSource {
recommend = 2,
search = 3,
}
export enum CombineRecommendJobFilterType {
ANY_COMBINE = 1,
STATIC_COMBINE = 2,
}

View File

@@ -159,6 +159,13 @@ export default function initIpc() {
if (hasOwn(payload, 'jobSourceList')) {
bossConfig.jobSourceList = payload.jobSourceList
}
if (hasOwn(payload, 'combineRecommendJobFilterType')) {
bossConfig.combineRecommendJobFilterType = payload.combineRecommendJobFilterType
}
if (hasOwn(payload, 'staticCombineRecommendJobFilterConditions')) {
bossConfig.staticCombineRecommendJobFilterConditions =
payload.staticCombineRecommendJobFilterConditions
}
promiseArr.push(writeConfigFile('boss.json', bossConfig))

View File

@@ -0,0 +1,224 @@
<template>
<div>
<el-button p-0 h-auto flex size="small" type="text" :icon="Plus" @click="addCondition"
>添加条件</el-button
>
<div class="job-combo-filter" mt-8px>
<el-table size="small" :data="props.modelValue" border :style="{ maxWidth: '100%' }">
<template #empty>
<div lh-1.5em>
列表中没有条件将仅使用默认的初始空条件为您筛选职位<br />
你可以点击表格左上角<el-button
p-0
h-auto
size="small"
type="text"
:icon="Plus"
@click="addCondition"
>添加条件</el-button
>按钮添加更多筛选条件
</div>
</template>
<el-table-column :resizable="false" label="" :width="80">
<template #default="{ $index: index }">
<div
:style="{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: '2px',
height: 'fit-content'
}"
>
<el-button
:disabled="index <= 0"
style="margin: 0"
circle
size="small"
:icon="ArrowUp"
@click="moveConditionUp(index)"
/>
<el-button
:disabled="index >= modelValue?.length - 1"
style="margin: 0"
circle
size="small"
:icon="ArrowDown"
@click="moveConditionDown(index)"
/>
<el-button
style="margin: 0"
circle
size="small"
:icon="Delete"
@click="removeCondition(index)"
/>
</div>
</template>
</el-table-column>
<el-table-column :resizable="false" label="薪资待遇" prop="salary">
<template #default="{ row }">
<el-select
v-model="row.salary"
:disabled="row.___itemType === 'empty-condition-placeholder'"
clearable
size="small"
>
<el-option
v-for="it in conditions.salaryList.filter((it) => it.code !== 0)"
:key="it.code"
:value="it.code"
:label="it.name"
/>
</el-select>
</template>
</el-table-column>
<el-table-column :resizable="false" label="工作经验" prop="experience">
<template #default="{ row }">
<el-select
v-model="row.experience"
:disabled="row.___itemType === 'empty-condition-placeholder'"
clearable
size="small"
>
<el-option
v-for="it in conditions.experienceList.filter((it) => it.code !== 0)"
:key="it.code"
:value="it.code"
:label="it.name"
/>
</el-select>
</template>
</el-table-column>
<el-table-column :resizable="false" label="学历要求" prop="degree">
<template #default="{ row }">
<el-select
v-model="row.degree"
:disabled="row.___itemType === 'empty-condition-placeholder'"
clearable
size="small"
>
<el-option
v-for="it in conditions.degreeList.filter((it) => it.code !== 0)"
:key="it.code"
:value="it.code"
:label="it.name"
/>
</el-select>
</template>
</el-table-column>
<el-table-column :resizable="false" label="公司行业" :width="200" prop="industry">
<template #default="{ row }">
<el-select
v-model="row.industry"
:disabled="row.___itemType === 'empty-condition-placeholder'"
clearable
size="small"
>
<el-option-group
v-for="group in industryFilterExemption"
:key="group.code"
:label="group.name"
>
<el-option
v-for="item in group.subLevelModelList"
:key="item.code"
:label="item.name"
:value="item.code"
/>
</el-option-group>
</el-select>
</template>
</el-table-column>
<el-table-column :resizable="false" label="公司规模" prop="scale">
<template #default="{ row }">
<el-select
v-model="row.scaleList"
:disabled="row.___itemType === 'empty-condition-placeholder'"
clearable
size="small"
>
<el-option
v-for="it in conditions.scaleList.filter((it) => it.code !== 0)"
:key="it.code"
:value="it.code"
:label="it.name"
/>
</el-select>
</template>
</el-table-column>
</el-table>
</div>
</div>
</template>
<script lang="ts" setup>
import conditions from '@geekgeekrun/geek-auto-start-chat-with-boss/internal-config/job-filter-conditions-20241002.json'
import industryFilterExemption from '@geekgeekrun/geek-auto-start-chat-with-boss/internal-config/job-filter-industry-filter-exemption-20241002.json'
import { ArrowUp, ArrowDown, Delete, Plus } from '@element-plus/icons-vue'
import { PropType } from 'vue'
const props = defineProps({
modelValue: {
type: Array as PropType<
Array<{
salary: number | null
experience: number | null
degree: number | null
industry: number | null
scale: number | null
}>
>,
default: () => []
},
isSkipEmptyConditionForCombineRecommendJobFilter: {
type: Boolean
}
})
function getNewConditionItem() {
return {
salary: null,
experience: null,
degree: null,
industry: null,
scale: null
}
}
function addCondition() {
props.modelValue?.push(getNewConditionItem())
// gtagRenderer('resume_work_exp_added')
}
function moveConditionUp(index) {
;[props.modelValue[index], props.modelValue[index - 1]] = [
props.modelValue[index - 1],
props.modelValue[index]
]
// gtagRenderer('resume_work_exp_moved_up')
}
function moveConditionDown(index) {
;[props.modelValue[index], props.modelValue[index + 1]] = [
props.modelValue[index + 1],
props.modelValue[index]
]
// gtagRenderer('resume_work_exp_moved_down')
}
function removeCondition(index) {
props.modelValue?.splice(index, 1)
// gtagRenderer('resume_work_exp_removed')
}
</script>
<style lang="scss" scoped>
.job-combo-filter {
display: flex;
width: 100%;
gap: 10px;
.filter-item {
flex: 1;
}
}
</style>

View File

@@ -49,60 +49,91 @@
/>
</div>
</el-form-item>
<el-form-item prop="filter" mt18px>
<div font-size-16px>
你希望Boss直聘为你展示什么样的职位
<el-tooltip
effect="light"
placement="bottom-start"
@show="gtagRenderer('tooltip_show_about_wrongly_mark_not_suit')"
>
<template #content>
<ul m0 line-height-1.5em w-540px pl2em>
<li>
这个配置决定了下面列表中会展示哪些职位查找职位时会在Boss直聘页面上自动组合下方设置的筛选条件对应UI<br />
<img h-270px src="../resources/intro-of-job-filter-auto-change.png" />
</li>
<li color-orange>
建议不要筛选过多条件否则最终组合数会成为一个很大的数字这将在当前职位中尝试太多筛选条件不能及时进入下一个职位且会增加命中风控的概率
</li>
</ul>
</template>
<el-button type="text" font-size-12px
><span><QuestionFilled w-1em h-1em mr2px /></span
>这个配置是如何工作的</el-button
<el-form-item prop="filter" mt18px mb0px w-full>
<div flex-1>
<div font-size-16px>
你希望Boss直聘为你筛选出什么样的职位
<el-tooltip
effect="light"
placement="bottom-start"
@show="gtagRenderer('tooltip_show_about_wrongly_mark_not_suit')"
>
</el-tooltip>
<template #content>
<ul m0 line-height-1.5em w-540px pl2em>
<li>
这个配置决定了下面列表中会展示哪些职位查找职位时会在Boss直聘页面上自动组合下方设置的筛选条件对应UI<br />
<img h-270px src="../resources/intro-of-job-filter-auto-change.png" />
</li>
<li color-orange>
建议不要筛选过多条件否则最终组合数会成为一个很大的数字这将在当前职位中尝试太多筛选条件不能及时进入下一个职位且会增加命中风控的概率
</li>
</ul>
</template>
<el-button type="text" font-size-12px
><span><QuestionFilled w-1em h-1em mr2px /></span
>这个配置是如何工作的</el-button
>
</el-tooltip>
</div>
<div>
<div>
<div font-size-12px>筛选条件遍历方式</div>
<el-select v-model="formContent.combineRecommendJobFilterType" w-320px>
<el-option
v-for="op in combineRecommendJobFilterTypeOptions"
:key="op.value"
:value="op.value"
:label="op.name"
>{{ op.name }}</el-option
>
</el-select>
</div>
<div
v-if="
formContent.combineRecommendJobFilterType ===
CombineRecommendJobFilterType.STATIC_COMBINE
"
mt8px
>
<StaticCombineBossRecommendFilter
v-model="formContent.staticCombineRecommendJobFilterConditions"
/>
</div>
<div v-else mt8px>
<AnyCombineBossRecommendFilter
v-model="formContent.anyCombineRecommendJobFilter"
/>
<div mb0>
<el-checkbox
v-if="anyCombineBossRecommendFilterHasCondition"
v-model="formContent.isSkipEmptyConditionForCombineRecommendJobFilter"
@change="
(v) => {
gtagRenderer('is_skip_empty_condition_4crjf_changed', { v })
}
"
>
<span font-size-12px>跳过初始空条件,直接使用设置的条件查找职位</span>
</el-checkbox>
<el-checkbox v-else :model-value="false" disabled>
<span font-size-12px>跳过初始空条件,直接使用设置的条件查找职位</span>
</el-checkbox>
</div>
</div>
</div>
<div font-size-12px>
当前组合条件数:{{
currentAnyCombineRecommendJobFilterCombinationCount.toLocaleString()
}}
<span
v-if="currentAnyCombineRecommendJobFilterCombinationCount >= 5"
class="color-orange"
>不建议选择太多组合条件 -
否则将在当前职位中尝试太多筛选条件,不能及时进入下一个职位,且会增加命中风控的概率</span
>
</div>
</div>
<AnyCombineBossRecommendFilter v-model="formContent.anyCombineRecommendJobFilter" />
</el-form-item>
<el-form-item prop="filter" mb0>
<el-checkbox
v-if="anyCombineBossRecommendFilterHasCondition"
v-model="formContent.isSkipEmptyConditionForCombineRecommendJobFilter"
@change="
(v) => {
gtagRenderer('is_skip_empty_condition_4crjf_changed', { v })
}
"
>
<span font-size-12px>跳过初始空条件直接使用设置的条件查找职位</span>
</el-checkbox>
<el-checkbox v-else :model-value="false" disabled>
<span font-size-12px>跳过初始空条件直接使用设置的条件查找职位</span>
</el-checkbox>
</el-form-item>
<div font-size-12px mt10px>
当前组合条件数{{
currentAnyCombineRecommendJobFilterCombinationCount.toLocaleString()
}}
<span
v-if="currentAnyCombineRecommendJobFilterCombinationCount >= 5"
class="color-orange"
>不建议选择太多组合条件 -
否则将在当前职位中尝试太多筛选条件不能及时进入下一个职位且会增加命中风控的概率</span
>
</div>
</el-card>
<!-- <el-form-item
label="钉钉机器人 AccessToken用于记录开聊请勿使用公司内部群"
@@ -926,6 +957,7 @@ import { ElForm, ElMessage } from 'element-plus'
import { QuestionFilled } from '@element-plus/icons-vue'
import { useRouter } from 'vue-router'
import AnyCombineBossRecommendFilter from '@renderer/features/AnyCombineBossRecommendFilter/index.vue'
import StaticCombineBossRecommendFilter from '@renderer/features/StaticCombineBossRecommendFilter/index.vue'
import { activeDescList } from '@geekgeekrun/geek-auto-start-chat-with-boss/constant.mjs'
import {
calculateTotalCombinations,
@@ -935,6 +967,7 @@ import { gtagRenderer as baseGtagRenderer } from '@renderer/utils/gtag'
import defaultTargetCompanyListConf from '@geekgeekrun/geek-auto-start-chat-with-boss/default-config-file/target-company-list.json'
import { ArrowDown } from '@element-plus/icons-vue'
import {
CombineRecommendJobFilterType,
MarkAsNotSuitOp,
StrategyScopeOptionWhenMarkJobNotMatch,
SalaryCalculateWay,
@@ -960,6 +993,8 @@ const formContent = ref({
dingtalkRobotAccessToken: '',
expectCompanies: '',
anyCombineRecommendJobFilter: {},
combineRecommendJobFilterType: 1,
staticCombineRecommendJobFilterConditions: [],
expectJobNameRegExpStr: '',
expectJobTypeRegExpStr: '',
expectJobDescRegExpStr: '',
@@ -1001,6 +1036,15 @@ const anyCombineBossRecommendFilterHasCondition = computed(() => {
})
const currentAnyCombineRecommendJobFilterCombinationCount = computed(() => {
if (
formContent.value.combineRecommendJobFilterType === CombineRecommendJobFilterType.STATIC_COMBINE
) {
const count = formContent.value.staticCombineRecommendJobFilterConditions.length
if (!count) {
return 1
}
return count
}
return calculateTotalCombinations(
formContent.value.anyCombineRecommendJobFilter,
anyCombineBossRecommendFilterHasCondition.value
@@ -1047,6 +1091,12 @@ electron.ipcRenderer.invoke('fetch-config-file-content').then((res) => {
deep: true
}
)
formContent.value.combineRecommendJobFilterType =
res.config['boss.json']?.combineRecommendJobFilterType ?? 1
formContent.value.staticCombineRecommendJobFilterConditions = res.config['boss.json']
?.staticCombineRecommendJobFilterConditions?.length
? res.config['boss.json'].staticCombineRecommendJobFilterConditions
: []
if (
res.config['boss.json']?.expectJobRegExpStr &&
typeof res.config['boss.json']?.expectJobNameRegExpStr === 'undefined' &&
@@ -1574,6 +1624,17 @@ function formatJobSourceFormValueToConfig(formValue = []) {
return it
})
}
const combineRecommendJobFilterTypeOptions = [
{
name: '使用自由组合条件进行遍历',
value: 1
},
{
name: '使用固定组合条件进行遍历',
value: 2
}
]
</script>
<style scoped lang="scss">

View File

@@ -1,6 +1,6 @@
<template>
<div class="flex h100vh">
<div class="flex flex-col w200px pt30px pl30px aside-nav of-hidden">
<div class="flex flex-col min-w200px w200px pt30px pl30px aside-nav of-hidden">
<div class="nav-list flex-1 of-auto">
<RouterLink to="./GeekAutoStartChatWithBoss">
Boss炸弹