招聘推送(Job Feed)
招聘推送负责把一条已发布的职位,变成 Discord 频道里的一张格式化卡片。它位于 youteacher_discord 服务内部,由一个小型应用服务(JobFeedService)加一对 HTTP 路由组成。
职位如何进来
职位通过一个 ingest 端点进入:POST /discord/v1/job-feed/ingest。上游的 job 服务在职位发布时,应带着服务凭据调用它。请求体会逐字段校验——jobId、title、city、country、一个合法的 url,以及可选的 salaryMin / salaryMax / currency / salaryPeriod / jobType / description / benefits——校验通过后才进入后续处理。
职位去往哪里
一个 Discord 频道通过一条配置记录来订阅推送:一个 channelId,加上可选的 countries 与 cities 过滤。职位进来时,服务会遍历每一个已配置的频道,逐个频道判断这条职位是否该出现在该频道。
匹配规则是「默认全放行」:国家或城市列表为空表示「不限制」,因此没有过滤条件的频道会收到全部职位。列表非空则收窄为精确匹配——职位的 country 必须在该频道的国家列表里、city 在城市列表里,职位才能通过。
不重复推送同一条职位
去重的键同时按「职位」和「频道」——键是 jobId:channelId。同一条职位仍可到达两个不同频道,但同一个频道永不会收到同一条职位两次。已推送集合只在成功推送后才标记,因此一次发送失败不会白白吃掉这条职位;发布失败是记录日志、而非抛出,因此一个坏频道不会拖垮其它频道。
Discord 卡片
每条职位渲染成一个 Discord embed。标题链接到完整职位页。地点(城市, 国家)和格式化后的薪资构成开头一行,其后是职位描述的截断片段。结构化字段承载相同信息——地点、薪资、工作类型、福利——每一项都只在有值时才出现。薪资格式化会把最小/最大值合成一个区间(或单值加 +),附上货币,并把发薪周期映射为 /month、/year、/hour。工作类型被规范为展示形式(Full-time、Part-time、Contract、Internship)。所有自由文本都会折叠空白并做长度上限截断,使卡片保持在 Discord 的限制之内。
Owner 控制与默认值
频道配置通过 PUT /discord/v1/job-feed/config 写入,并加了守卫:只有 admin 用户能改动它。在此之上,可以在启动时从环境设置里自动注册一个默认频道:若环境中存在默认频道 id,服务会在进程启动时以「无过滤」注册它,使推送开箱即用,无需手动调用配置接口。
当前形态
按服务自身注释,配置与去重集合都保存在内存中,是一种 MVP 取舍——持久化(落数据库)是注释里点明的下一步。因此一次重启会同时清空频道配置(除环境播种的默认频道外)和「已推送过什么」的记录。