add logic support customize job no active time range. add standalone logic to handle no active job. fix logic of set tempTargetJobIndexToCheckDetail

This commit is contained in:
geekgeekrun
2025-05-03 03:31:39 +08:00
parent 67e4b94f9e
commit 043d782a05
8 changed files with 300 additions and 108 deletions

View File

@@ -0,0 +1,3 @@
export const activeDescList = [
'', "半年前活跃","近半年活跃","5月内活跃","4月内活跃","3月内活跃","2月内活跃","本月活跃","2周内活跃","本周活跃","3日内活跃","昨日活跃","今日活跃","刚刚活跃"
]

View File

@@ -8,6 +8,8 @@
},
"expectJobRegExpStr": "",
"jobNotMatchStrategy": 1,
"jobNotActiveStrategy": 1,
"markAsNotActiveSelectedTimeRange": 7,
"autoReminder": {
"throttleIntervalMinutes": 10,
"rechatLimitDay": 21,

View File

@@ -17,6 +17,7 @@ import { default as jobFilterConditions } from './internal-config/job-filter-con
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 } from '@geekgeekrun/sqlite-plugin/dist/enums'
import { activeDescList } from './constant.mjs'
const jobFilterConditionsMapByCode = {}
Object.values(jobFilterConditions).forEach(arr => {
@@ -80,6 +81,23 @@ const targetCompanyList = readConfigFile('target-company-list.json').filter(it =
const anyCombineRecommendJobFilter = readConfigFile('boss.json').anyCombineRecommendJobFilter
const expectJobRegExpStr = readConfigFile('boss.json').expectJobRegExpStr
const jobNotMatchStrategy = readConfigFile('boss.json').jobNotMatchStrategy ?? MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS
const markAsNotActiveSelectedTimeRange = (() => {
let n = readConfigFile('boss.json').markAsNotActiveSelectedTimeRange
if (
typeof n !== 'number' || isNaN(parseInt(n)) || n >= activeDescList.length || n < 0
) {
n = 7
}
return n
})()
const jobNotActiveStrategy = (() => {
let value = readConfigFile('boss.json').jobNotActiveStrategy ?? MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS
if (markAsNotActiveSelectedTimeRange === 0) {
value = MarkAsNotSuitOp.NO_OP
}
return value
})()
let {
expectJobNameRegExpStr,
expectJobTypeRegExpStr,
@@ -122,6 +140,12 @@ async function markJobAsNotSuitInRecommendPage (reasonCode) {
const result = {}
const notSuitableFeedbackButtonProxy = await page.$('.job-detail-box .job-detail-operate .not-suitable')
if (notSuitableFeedbackButtonProxy) {
await notSuitableFeedbackButtonProxy.evaluate(el => {
el.scrollIntoView({
block: 'center'
})
})
await sleep(200)
await notSuitableFeedbackButtonProxy.click()
const rawReasonResData = (await (await page.waitForResponse(
response => {
@@ -492,18 +516,24 @@ async function toRecommendPage (hooks) {
let hasReachLastPage = false
let targetJobIndex = -1
let targetJobData
function getTempTargetJobIndexToCheckDetail () {
return jobListData.findIndex(it =>
!blockBossNotNewChat.has(it.encryptBossId) &&
!blockBossNotActive.has(it.encryptBossId) &&
!blockJobNotSuit.has(it.encryptJobId) &&
(
enableCompanyAllowList ?
[...expectCompanySet].find(
name => it.brandName?.toLowerCase?.()?.includes(name.toLowerCase())
)
:
true
)
)
}
continueFind: while (targetJobIndex < 0 && !hasReachLastPage) {
// when disable company allow list, we will believe that the first one in the list is your expect job.
let tempTargetJobIndexToCheckDetail = enableCompanyAllowList ? jobListData.findIndex(
it => !blockBossNotNewChat.has(it.encryptBossId)
&& !blockBossNotActive.has(it.encryptBossId)
&& [...expectCompanySet].find(
name => it.brandName.includes(name)
)
&& !blockJobNotSuit.has(it.encryptJobId)
) : jobListData.findIndex(
it => !blockBossNotNewChat.has(it.encryptBossId) && !blockBossNotActive.has(it.encryptBossId) && !blockJobNotSuit.has(it.encryptJobId)
)
let tempTargetJobIndexToCheckDetail = getTempTargetJobIndexToCheckDetail()
while (tempTargetJobIndexToCheckDetail < 0 && !hasReachLastPage) {
// fetch new
const recommendJobListElBBox = await recommendJobListElProxy.boundingBox()
@@ -538,12 +568,7 @@ async function toRecommendPage (hooks) {
document.querySelector('.page-jobs-main')?.__vue__?.jobList
`
)
tempTargetJobIndexToCheckDetail = jobListData.findIndex(it =>
!blockBossNotNewChat.has(it.encryptBossId) &&
!blockBossNotActive.has(it.encryptBossId) &&
[...expectCompanySet].find(name => it.brandName?.toLowerCase?.()?.includes(name.toLowerCase())) &&
!blockJobNotSuit.has(it.encryptJobId)
)
tempTargetJobIndexToCheckDetail = getTempTargetJobIndexToCheckDetail()
}
if (tempTargetJobIndexToCheckDetail < 0 && hasReachLastPage) {
@@ -593,31 +618,44 @@ async function toRecommendPage (hooks) {
// 刚刚活跃 // 今日活跃 // 昨日活跃 // 3日内活跃 // 本周活跃 // 2周内活跃
// 本月活跃 // 2月内活跃 // 3月内活跃 // 4月内活跃 // 5月内活跃 // 近半年活跃 // 半年前活跃
//#endregion
if ([
'本月活跃',
'2月内活跃',
'3月内活跃',
'4月内活跃',
'5月内活跃',
'近半年活跃',
'半年前活跃',
].includes(targetJobData.bossInfo.activeTimeDesc)) {
const indexOfActiveText = activeDescList.indexOf(targetJobData.bossInfo.activeTimeDesc)
if (
markAsNotActiveSelectedTimeRange > 0 &&
indexOfActiveText > 0 && indexOfActiveText <= markAsNotActiveSelectedTimeRange
) {
blockBossNotActive.add(targetJobData.jobInfo.encryptUserId)
// click prevent recommend button
try {
const { chosenReasonInUi } = await markJobAsNotSuitInRecommendPage(MarkAsNotSuitReason.BOSS_INACTIVE)
await hooks.jobMarkedAsNotSuit.promise(
targetJobData,
{
markFrom: ChatStartupFrom.AutoFromRecommendList,
markReason: MarkAsNotSuitReason.BOSS_INACTIVE,
extInfo: {
bossActiveTimeDesc: targetJobData.bossInfo.activeTimeDesc,
chosenReasonInUi
if (jobNotActiveStrategy === MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS) {
try {
const { chosenReasonInUi } = await markJobAsNotSuitInRecommendPage(MarkAsNotSuitReason.BOSS_INACTIVE)
await hooks.jobMarkedAsNotSuit.promise(
targetJobData,
{
markFrom: ChatStartupFrom.AutoFromRecommendList,
markReason: MarkAsNotSuitReason.BOSS_INACTIVE,
extInfo: {
bossActiveTimeDesc: targetJobData.bossInfo.activeTimeDesc,
chosenReasonInUi
},
markOp: MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS
}
}
)
} catch {
)
} catch {
}
}
else if (jobNotActiveStrategy === MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL) {
try {
await hooks.jobMarkedAsNotSuit.promise(
targetJobData,
{
markFrom: ChatStartupFrom.AutoFromRecommendList,
markReason: MarkAsNotSuitReason.BOSS_INACTIVE,
extInfo: null,
markOp: MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL
}
)
} catch {
}
}
continue continueFind
}
@@ -801,7 +839,9 @@ export async function mainLoop (hooks) {
await page.bringToFront()
await hooks.mainFlowWillLaunch?.callAsync({
jobNotMatchStrategy,
blockJobNotSuit
jobNotActiveStrategy,
blockJobNotSuit,
blockBossNotActive,
})
await toRecommendPage(hooks)
// goto search

View File

@@ -26,7 +26,7 @@ import { saveChatStartupRecord, saveJobInfoFromRecommendPage, saveMarkAsNotSuitR
import { UpdateChatStartupLogTable1729182577167 } from "./migrations/1729182577167-UpdateChatStartupLogTable";
import minimist from 'minimist'
import { UpdateBossInfoTable1732032381304 } from "./migrations/1732032381304-UpdateBossInfoTable";
import { MarkAsNotSuitOp } from "./enums";
import { MarkAsNotSuitOp, MarkAsNotSuitReason } from "./enums";
import { AddColumnForMarkAsNotSuitLog1746092370665 } from "./migrations/1746092370665-AddColumnForMarkAsNotSuitLog";
export function initDb(dbFilePath) {
@@ -104,12 +104,41 @@ export default class SqlitePlugin {
"SqlitePlugin",
async ({
jobNotMatchStrategy,
blockJobNotSuit
jobNotActiveStrategy,
blockJobNotSuit,
blockBossNotActive,
}) => {
if (jobNotMatchStrategy === MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL) {
if (
jobNotMatchStrategy === MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL ||
jobNotActiveStrategy === MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL
) {
const ds = await this.initPromise;
const last7DayMarkRecords = (await getNotSuitMarkRecordsInLastSomeDays(ds, 7) ?? []).map(it => it.encryptJobId);
last7DayMarkRecords.forEach(id => blockJobNotSuit.add(id))
const last7DayMarkRecords = (await getNotSuitMarkRecordsInLastSomeDays(ds, 7) ?? []);
if (jobNotMatchStrategy === MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL) {
last7DayMarkRecords
.filter(it =>
[
MarkAsNotSuitReason.JOB_NOT_SUIT,
MarkAsNotSuitReason.USER_MANUAL_OPERATION_WITH_UNKNOWN_REASON
].includes(it.markReason)
)
.map(
it => it.encryptJobId
)
.forEach(
id => blockJobNotSuit.add(id)
)
}
if (jobNotActiveStrategy === MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL) {
last7DayMarkRecords
.filter(it => it.markReason === MarkAsNotSuitReason.BOSS_INACTIVE)
.map(
it => it.encryptJobId
)
.forEach(
id => blockJobNotSuit.add(id)
)
}
}
}
);

View File

@@ -98,6 +98,12 @@ export default function initIpc() {
if (hasOwn(payload, 'jobNotMatchStrategy')) {
bossConfig.jobNotMatchStrategy = payload.jobNotMatchStrategy
}
if (hasOwn(payload, 'markAsNotActiveSelectedTimeRange')) {
bossConfig.markAsNotActiveSelectedTimeRange = payload.markAsNotActiveSelectedTimeRange
}
if (hasOwn(payload, 'jobNotActiveStrategy')) {
bossConfig.jobNotActiveStrategy = payload.jobNotActiveStrategy
}
if (hasOwn(payload, 'autoReminder')) {
bossConfig.autoReminder = payload.autoReminder
}

View File

@@ -8,8 +8,9 @@ export let mainWindow: BrowserWindow | null = null
export function createMainWindow(): BrowserWindow {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 1024,
width: 1280,
height: 720,
minWidth: 1024,
show: false,
autoHideMenuBar: true,
...(process.platform === 'linux'

View File

@@ -155,69 +155,115 @@
</el-form-item>
</div>
</div>
<div
:style="{
display: 'grid',
gridTemplateColumns: '500px 1fr',
gap: '5px',
width: '100%',
alignItems: 'end'
}"
>
<el-form-item>
<div>
当查看职位详情后发现职位不满足如上设置的条件时
<el-tooltip
effect="light"
placement="bottom-start"
@show="gtagRenderer('tooltip_show_about_mark_not_suit_intro')"
>
<template #content>
<ol m0 line-height-1.5em w-400px pl2em>
<li>
如果查找到的职位职位名称职位类型职位描述与如上正则不匹配则这个职位将被标记为不合适
</li>
<li>
如果查找到的职位活跃时间为本月活跃或更往前的时间则这个职位将被标记为不合适
</li>
<li>
如有错误标记请在左侧<a
href="javascript:void(0)"
style="color: var(--el-color-primary)"
@click.prevent="
() => {
gtagRenderer('click_view_mansr_from_boss_b_tooltip')
$router.push('/main-layout/MarkAsNotSuitRecord')
}
"
>标记不合适</a
>记录中找到相关记录手动对这些职位发起会话
</li>
</ol>
</template>
<el-button type="text" font-size-12px
><span><QuestionFilled w-1em h-1em mr2px /></span>标记不合适机制</el-button
>
</el-tooltip>
</div>
<el-select
v-model="formContent.jobNotMatchStrategy"
@change="(value) => gtagRenderer('job_not_match_strategy_changed', { value })"
>
<el-option
v-for="op in strategyOptionWhenCurrentJobNotMatch"
:key="op.value"
:label="op.name"
:value="op.value"
>{{ op.name }}</el-option
>
</el-select>
<div>
<el-form-item mb0>
查看职位详情后发现当前职位名称/类型/描述不符合如上设置的投递条件将要执行的操作
</el-form-item>
<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-400px pl2em>
<li>
如有错误标记请在左侧<a
href="javascript:void(0)"
style="color: var(--el-color-primary)"
@click.prevent="
() => {
gtagRenderer('click_view_mansr_from_boss_b_tooltip')
$router.push('/main-layout/MarkAsNotSuitRecord')
}
"
>标记不合适</a
>记录中找到相关记录来查看职位详情或手动对这些职位发起会话
</li>
</ul>
</template>
<!-- <el-button type="text" font-size-12px
><span><QuestionFilled w-1em h-1em mr2px /></span
>职位被错误标记为不合适了如何处理</el-button
> -->
</el-tooltip>
<div
:style="{
display: 'grid',
gridTemplateColumns: '1.25fr 0.75fr',
gap: '10px 0',
width: '100%',
alignItems: 'end'
}"
>
<el-form-item>
<el-select
v-model="formContent.jobNotMatchStrategy"
@change="(value) => gtagRenderer('job_not_match_strategy_changed', { value })"
>
<el-option
v-for="op in strategyOptionWhenCurrentJobNotMatch"
:key="op.value"
:label="op.name"
:value="op.value"
>{{ op.name }}</el-option
>
</el-select>
</el-form-item>
<div />
</div>
</div>
<div>
<el-form-item mb0> 查看职位详情后发现当前职位不活跃时将要执行的操作 </el-form-item>
<div
:style="{
display: 'grid',
gridTemplateColumns: '1.25fr 0.75fr',
gap: '10px 0',
width: '100%',
alignItems: 'end'
}"
>
<el-form-item>
<div font-size-12px>认为职位不活跃的时间范围</div>
<el-slider
v-model="formContent.markAsNotActiveSelectedTimeRange"
:marks="noActiveDefinitionMarks"
:max="10"
:step="1"
pl40px
pr20px
class="no-active-definition-text-slider"
:format-tooltip="
(v) =>
typeof noActiveDefinitionMarks[v] === 'string'
? noActiveDefinitionMarks[v]
: noActiveDefinitionMarks[v]?.label
"
@change="(value) => gtagRenderer('job_not_active_time_range_changed', { value })"
/>
</el-form-item>
<div />
<el-form-item v-if="formContent.markAsNotActiveSelectedTimeRange > 0" mb0>
<div font-size-12px>发现当前职位不活跃时:</div>
<el-select
v-model="formContent.jobNotActiveStrategy"
@change="(value) => gtagRenderer('job_not_active_strategy_changed', { value })"
>
<el-option
v-for="op in strategyOptionWhenCurrentJobNotMatch"
:key="op.value"
:label="op.name"
:value="op.value"
>{{ op.name }}</el-option
>
</el-select>
</el-form-item>
</div>
</div>
<el-form-item
label="职位备选筛选条件当前求职期望无合适职位时自动更改Boss筛选条件查找新工作"
prop="filter"
mt20px
mb0
>
<AnyCombineBossRecommendFilter v-model="formContent.anyCombineRecommendJobFilter" />
@@ -244,6 +290,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 { activeDescList } from '@geekgeekrun/geek-auto-start-chat-with-boss/constant.mjs'
import { calculateTotalCombinations } from '@geekgeekrun/geek-auto-start-chat-with-boss/combineCalculator.mjs'
import { gtagRenderer } from '@renderer/utils/gtag'
import defaultTargetCompanyListConf from '@geekgeekrun/geek-auto-start-chat-with-boss/default-config-file/target-company-list.json'
@@ -260,7 +307,9 @@ const formContent = ref({
expectJobNameRegExpStr: '',
expectJobTypeRegExpStr: '',
expectJobDescRegExpStr: '',
jobNotMatchStrategy: MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS
jobNotMatchStrategy: MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS,
jobNotActiveStrategy: MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS,
markAsNotActiveSelectedTimeRange: 7
})
const currentAnyCombineRecommendJobFilterCombinationCount = computed(() => {
@@ -311,6 +360,17 @@ electron.ipcRenderer.invoke('fetch-config-file-content').then((res) => {
.includes(res.config['boss.json'].jobNotMatchStrategy)
? res.config['boss.json'].jobNotMatchStrategy
: MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS
formContent.value.markAsNotActiveSelectedTimeRange = isNaN(
parseInt(String(res.config['boss.json'].markAsNotActiveSelectedTimeRange))
)
? 7
: parseInt(String(res.config['boss.json'].markAsNotActiveSelectedTimeRange))
formContent.value.jobNotActiveStrategy = strategyOptionWhenCurrentJobNotMatch
.map((it) => it.value)
.includes(res.config['boss.json'].jobNotActiveStrategy)
? res.config['boss.json'].jobNotActiveStrategy
: MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS
})
const formRules = {
@@ -531,17 +591,39 @@ const strategyOptionWhenCurrentJobNotMatch = [
value: MarkAsNotSuitOp.NO_OP
}
]
const noActiveDefinitionMarks = computed(() => {
let arr = [...activeDescList]
arr.shift()
arr.unshift('不处理“职位不活跃”')
arr = arr.map((it, index) => {
if (index <= formContent.value.markAsNotActiveSelectedTimeRange) {
if (formContent.value.markAsNotActiveSelectedTimeRange > 0 && index === 0) {
return it
}
return {
style: {
color: '#1989FA',
fontWeight: 700
},
label: it
}
}
return it
})
return arr
})
</script>
<style scoped lang="scss">
.form-wrap {
margin: 0 auto;
max-width: 1000px;
max-height: 100vh;
overflow: auto;
padding-left: 20px;
padding-right: 20px;
:deep(.el-form) {
max-width: 1000px;
margin: 0 auto;
padding-top: 8px;
}
.last-form-item {
@@ -551,6 +633,35 @@ const strategyOptionWhenCurrentJobNotMatch = [
}
}
}
.no-active-definition-text-slider {
::v-deep(.el-slider__marks-text) {
font-size: 10px;
margin-top: 5px;
&:nth-child(2n-1) {
margin-top: 20px;
&::before {
content: '';
bottom: calc(100% - 14px);
z-index: -1;
left: 50%;
transform: translateX(-50%);
position: absolute;
display: block;
width: 2px;
height: 26px;
background-image: linear-gradient(var(--el-slider-runway-bg-color), transparent);
}
}
}
::v-deep(.el-slider__stop) {
box-shadow: 0 0 0 2px rgba(52, 137, 255, 0.3);
}
::v-deep(.el-slider__button) {
transform: rotate(45deg);
border-radius: 10px 10px 0px 10px;
--el-slider-button-size: 18px;
}
}
</style>
<style lang="scss">

View File

@@ -483,13 +483,13 @@ async function handleTestEffectClicked() {
<style scoped lang="scss">
.form-wrap {
margin: 0 auto;
max-width: 1000px;
max-height: 100vh;
overflow: auto;
padding-left: 20px;
padding-right: 20px;
:deep(.el-form) {
margin: 0 auto;
max-width: 1000px;
padding-top: 8px;
}
.last-form-item {