基于springboot 以及vue前后端分离架构的求职招聘系统设计与实现

news2025/2/15 14:43:22

基于springboot 以及vue前后端分离架构的求职招聘系统设计与实现

随着互联网技术的飞速发展,求职招聘行业也在不断发生变革。传统的求职招聘方式往往存在着信息不对称、效率低下、交易成本高等问题,导致企业的招聘成本增加,求职者的体验下降。为了应对这一挑战,我们设计并实现了一套基于Java和Vue的求职招聘系统,旨在提供一个高效、便捷且用户友好的求职招聘平台。本博客将详细介绍该系统的设计与实现过程,并分析其功能、架构及技术优势。

1. 系统设计目标

该求职招聘系统主要目标是解决传统求职招聘过程中的种种痛点,提升用户体验,优化招聘流程。具体目标包括:

信息对称性:解决传统招聘中信息不对称的问题,提供求职者和招聘方之间的透明沟通渠道。
降低交易成本:通过在线简历投递和职位搜索等功能,简化招聘流程,降低双方的时间和经济成本。
智能化推荐:通过职位推荐算法和简历匹配功能,帮助求职者找到最合适的职位,提升招聘效率。
用户友好的交互体验:设计简洁、直观的界面,提升用户的交互体验和满意度。

2. 技术选型

2.1 后端技术

系统后端采用了Spring Boot框架来构建,利用其简洁、快速、易于集成的特点,快速实现了系统的核心功能。Spring Boot 的主要优势体现在:

快速开发:借助Spring Boot的自动配置和约定大于配置的思想,可以大大降低开发工作量。
高扩展性:Spring Boot 支持多种中间件集成,能够在后期方便地进行功能扩展。
性能稳定:Spring Boot 基于Spring框架,具有高效且稳定的性能,适合处理高并发请求。
系统数据存储部分采用MySQL数据库进行管理,MySQL作为一款高效的关系型数据库,能够稳定存储大量数据,满足系统对数据存取的需求。

2.2 前端技术

前端部分采用了Vue.js框架来构建。Vue.js 是一款轻量级的前端框架,具有以下优势:

易上手:Vue.js的语法简洁直观,适合快速开发。
灵活性高:Vue.js支持单页应用(SPA)开发,能够提供流畅的用户体验。
生态丰富:Vue.js有着成熟的插件生态,可以方便地进行功能扩展。
前端与后端通过RESTful API进行数据交互,保证了系统的高效性与扩展性。

3. 系统架构

本求职招聘系统采用了分层架构模式,具体包括:

3.1 服务层(Spring Boot)

后端服务层采用Spring Boot实现,负责处理前端请求并进行业务逻辑处理。服务层包括多个功能模块,如用户管理、简历管理、职位管理、推荐系统等。

用户管理:负责用户的注册、登录、个人信息管理等。
职位管理:处理职位的发布、修改、删除、搜索等功能。
简历管理:支持简历的发布、查看、修改等功能。
职位推荐:通过职位的点击量、用户兴趣等信息,向求职者推荐合适的职位。

package com.gk.study.controller;

import com.gk.study.common.APIResponse;
import com.gk.study.common.ResponeCode;
import com.gk.study.entity.Thing;
import com.gk.study.permission.Access;
import com.gk.study.permission.AccessLevel;
import com.gk.study.service.ThingService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.UUID;

@RestController
@RequestMapping("/thing")
public class ThingController {

    private final static Logger logger = LoggerFactory.getLogger(ThingController.class);

    @Autowired
    ThingService service;

    @Value("${File.uploadPath}")
    private String uploadPath;

    @RequestMapping(value = "/list", method = RequestMethod.GET)
    public APIResponse list(String keyword, String sort, String c, String tag){
        List<Thing> list =  service.getThingList(keyword, sort, c, tag);

        return new APIResponse(ResponeCode.SUCCESS, "查询成功", list);
    }

    @RequestMapping(value = "/detail", method = RequestMethod.GET)
    public APIResponse detail(String id){
        Thing thing =  service.getThingById(id);

        return new APIResponse(ResponeCode.SUCCESS, "查询成功", thing);
    }

    @Access(level = AccessLevel.ADMIN)
    @RequestMapping(value = "/create", method = RequestMethod.POST)
    @Transactional
    public APIResponse create(Thing thing) throws IOException {
        String url = saveThing(thing);
        if(!StringUtils.isEmpty(url)) {
            thing.cover = url;
        }

        service.createThing(thing);
        return new APIResponse(ResponeCode.SUCCESS, "创建成功");
    }

    @Access(level = AccessLevel.ADMIN)
    @RequestMapping(value = "/delete", method = RequestMethod.POST)
    public APIResponse delete(String ids){
        System.out.println("ids===" + ids);
        // 批量删除
        String[] arr = ids.split(",");
        for (String id : arr) {
            service.deleteThing(id);
        }
        return new APIResponse(ResponeCode.SUCCESS, "删除成功");
    }

    @Access(level = AccessLevel.ADMIN)
    @RequestMapping(value = "/update", method = RequestMethod.POST)
    @Transactional
    public APIResponse update(Thing thing) throws IOException {
        System.out.println(thing);
        String url = saveThing(thing);
        if(!StringUtils.isEmpty(url)) {
            thing.cover = url;
        }

        service.updateThing(thing);
        return new APIResponse(ResponeCode.SUCCESS, "更新成功");
    }

    public String saveThing(Thing thing) throws IOException {
        MultipartFile file = thing.getImageFile();
        String newFileName = null;
        if(file !=null && !file.isEmpty()) {

            // 存文件
            String oldFileName = file.getOriginalFilename();
            String randomStr = UUID.randomUUID().toString();
            newFileName = randomStr + oldFileName.substring(oldFileName.lastIndexOf("."));
            String filePath = uploadPath + File.separator + "image" + File.separator + newFileName;
            File destFile = new File(filePath);
            if(!destFile.getParentFile().exists()){
                destFile.getParentFile().mkdirs();
            }
            file.transferTo(destFile);
        }
        if(!StringUtils.isEmpty(newFileName)) {
            thing.cover = newFileName;
        }
        return newFileName;
    }


    @RequestMapping(value = "/listUserThingApi", method = RequestMethod.GET)
    public APIResponse listUserThingApi(String userId){
        List<Thing> list =  service.getUserThing(userId);

        return new APIResponse(ResponeCode.SUCCESS, "查询成功", list);
    }

}

3.2 前端层(Vue.js)

前端负责实现用户界面的展示和用户交互功能。用户通过前端界面完成注册、登录、简历上传、职位搜索和投递等操作。前端通过发送HTTP请求与后端进行数据交互,获取职位信息、简历信息以及其他相关数据。

<template>
  <div class="detail">
    <Header/>

    <div class="detail-content">
      <div class="detail-content-top">
        <div style="position: relative;">
          <div class="thing-infos-view">
            <div class="thing-infos">

              <div class="thing-info-box">
                <div class="thing-state">
                  <span class="state hidden-sm">岗位状态</span>
                  <span>正常</span>
                </div>
                <h1 class="thing-name">{{ detailData.title }}</h1>
                <span>
                  <span class="a-price-symbol"></span>
                  <span class="a-price">{{ detailData.salary }}</span>
                </span>
                <div class="translators flex-view" style="">
                  <span>所属公司:</span>
                  <span class="name">{{ detailData.company_title }}</span>
                </div>
                <div class="translators flex-view" style="">
                  <span>工作地点:</span>
                  <span class="name">{{ detailData.location }}</span>
                </div>
                <div class="translators flex-view" style="">
                  <span>学历要求:</span>
                  <span class="name">{{ detailData.education }}</span>
                </div>
                <div class="translators flex-view" style="">
                  <span>经验要求:</span>
                  <span class="name">{{ detailData.work_expe }}</span>
                </div>
                <div class="translators flex-view" style="">
                  <span>岗位描述:</span>
                  <span class="name">{{ detailData.description }}</span>
                </div>

                <a-popconfirm title="确定投递?" ok-text="" cancel-text="" @confirm="handleOrder(detailData)">
                  <button class="buy-btn">
                    <img :src="AddIcon"/>
                    <span>投递简历</span>
                  </button>
                </a-popconfirm>

              </div>
            </div>

            <div class="thing-counts hidden-sm">
              <div class="count-item flex-view pointer" @click="addToWish()">
                <div class="count-img">
                  <img :src="WantIcon">
                </div>
                <div class="count-box flex-view">
                  <div class="count-text-box">
                    <span class="count-title">加入心愿单</span>
                  </div>
                  <div class="count-num-box">
                    <span class="num-text">{{ detailData.wishCount }}</span>
                  </div>
                </div>
              </div>
              <div class="count-item flex-view pointer" @click="collect()">
                <div class="count-img">
                  <img :src="RecommendIcon">
                </div>
                <div class="count-box flex-view">
                  <div class="count-text-box">
                    <span class="count-title">收藏</span>
                  </div>
                  <div class="count-num-box">
                    <span class="num-text">{{ detailData.collectCount }}</span>
                  </div>
                </div>
              </div>
              <div class="count-item flex-view" @click="share()">
                <div class="count-img">
                  <img :src="ShareIcon">
                </div>
                <div class="count-box flex-view">
                  <div class="count-text-box">
                    <span class="count-title">分享</span>
                  </div>
                  <div class="count-num-box">
                    <span class="num-text"></span>
                    <img :src="WeiboShareIcon" class="mg-l">
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
      <div class="detail-content-bottom">
        <div class="thing-content-view flex-view">
          <div class="main-content">
            <div class="order-view main-tab">
          <span class="tab"
                :class="selectTabIndex===index? 'tab-select':''"
                v-for="(item,index) in tabData"
                :key="index"
                @click="selectTab(index)">
            {{ item }}
          </span>
              <span :style="{left: tabUnderLeft + 'px'}" class="tab-underline"></span>
            </div>

            <!--简介-->
            <div class="thing-intro" :class="selectTabIndex <= 0? '':'hide'">
              <p class="text" style="">{{ detailData.description }}</p>
            </div>

            <!--评论-->
            <div class="thing-comment" :class="selectTabIndex > 0? '':'hide'">
              <div class="title">发表新的评论</div>
              <div class="publish flex-view">
                <img :src="AvatarIcon" class="mine-img">
                <input placeholder="说点什么..." class="content-input" ref="commentRef">
                <button class="send-btn" @click="sendComment()">发送</button>
              </div>
              <div class="tab-view flex-view">
                <div class="count-text">共有{{ commentData.length }}条评论</div>
                <div class="tab-box flex-view" v-if="commentData.length > 0">
                  <span :class="sortIndex === 0? 'tab-select': ''" @click="sortCommentList('recent')">最新</span>
                  <div class="line"></div>
                  <span :class="sortIndex === 1? 'tab-select': ''" @click="sortCommentList('hot')">热门</span>
                </div>
              </div>
              <div class="comments-list">
                <div class="comment-item" v-for="item in commentData">
                  <div class="flex-item flex-view">
                    <img :src="AvatarIcon" class="avator">
                    <div class="person">
                      <div class="name">{{ item.username }}</div>
                      <div class="time">{{ item.commentTime }}</div>
                    </div>
                    <div class="float-right">
                      <span @click="like(item.id)">推荐</span>
                      <span class="num">{{ item.likeCount }}</span>
                    </div>
                  </div>
                  <p class="comment-content">{{ item.content }}</p>
                </div>
                <div class="infinite-loading-container">
                  <div class="infinite-status-prompt" style="">
                    <div slot="no-results" class="no-results">
                      <div></div>
                      <p>没有更多了</p>
                    </div>
                  </div>
                </div>
              </div>
            </div>

          </div>
          <div class="recommend" style="">
            <div class="title">热门推荐</div>
            <div class="things">
              <div class="sub-li" v-for="item in recommendData" @click="handleDetail(item)">
                <a class="job-info" target="_blank">
                  <div class="sub-li-top">
                    <div class="sub-li-info">
                      <p class="name">{{ item.title }}</p>
                    </div>
                    <p class="salary">{{ item.salary }}</p>
                  </div>
                  <p class="job-text">
                    <span>{{ item.location }}</span>
                    <span>{{ item.work_expe }}</span>
                    <span>{{ item.education }}</span>
                  </p>
                </a>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>

    <Footer/>
  </div>
</template>
<script setup>
import {message} from "ant-design-vue";
import Header from '/@/views/index/components/header.vue'
import Footer from '/@/views/index/components/footer.vue'
import AddIcon from '/@/assets/images/add.svg';
import WantIcon from '/@/assets/images/want-read-hover.svg';
import RecommendIcon from '/@/assets/images/recommend-hover.svg';
import ShareIcon from '/@/assets/images/share-icon.svg';
import WeiboShareIcon from '/@/assets/images/wb-share.svg';
import AvatarIcon from '/@/assets/images/avatar.jpg';
import {
  detailApi,
  listApi as listThingList,
} from '/@/api/thing'
import {listThingCommentsApi, createApi as createCommentApi, likeApi} from '/@/api/comment'
import {wishApi} from '/@/api/thingWish'
import {collectApi} from '/@/api/thingCollect'
import {BASE_URL} from "/@/store/constants";
import {useRoute, useRouter} from "vue-router/dist/vue-router";
import {useUserStore} from "/@/store";
import {getFormatTime} from "/@/utils";

import {
  detailApi as resumeDetailApi
} from '/@/api/resume'
import {
  createApi as createPostApi
} from '/@/api/post'

const router = useRouter()
const route = useRoute()
const userStore = useUserStore();


let thingId = ref('')
let detailData = ref({})
let tabUnderLeft = ref(6)
let tabData = ref(['评论'])
let selectTabIndex = ref(1)

let commentData = ref([])
let recommendData = ref([])
let sortIndex = ref(0)
let order = ref('recent') // 默认排序最新

let commentRef = ref()

onMounted(()=>{
  thingId.value = route.query.id.trim()
  getThingDetail()
  getRecommendThing()
  getCommentList()
})

const selectTab =(index)=> {
  selectTabIndex.value = index
  tabUnderLeft.value = 6 + 54 * index
}

const getThingDetail =()=> {
  detailApi({id: thingId.value}).then(res => {
    detailData.value = res.data
    detailData.value.cover = BASE_URL + '/api/staticfiles/image/' + detailData.value.cover
  }).catch(err => {
    message.error('获取详情失败')
  })
}
const addToWish =()=> {
  let userId = userStore.user_id
  if (userId) {
    wishApi({thingId: thingId.value, userId: userId}).then(res => {
      message.success(res.msg)
      getThingDetail()
    }).catch(err => {
      console.log('操作失败')
    })
  } else {
    message.warn('请先登录')
  }
}
const collect =()=> {
  let userId = userStore.user_id
  if (userId) {
    collectApi({thingId: thingId.value, userId: userId}).then(res => {
      message.success(res.msg)
      getThingDetail()
    }).catch(err => {
      console.log('收藏失败')
    })
  } else {
    message.warn('请先登录')
  }
}
const share =()=> {
  let content = '分享一个非常好玩的网站 ' + window.location.href
  let shareHref = 'http://service.weibo.com/share/share.php?title=' + content
  window.open(shareHref)
}
const handleOrder =(detailData)=> {
  console.log(detailData)
  const userId = userStore.user_id

  if (!userId) {
    message.warn("请先登录")
    return
  }

  // 获取简历
  resumeDetailApi({
    userId: userId
  }).then(res => {
    console.log(res.data)
    let resumeId = res.data.id
    let companyId = detailData.companyId
    let thingId = detailData.id
    console.log(resumeId, companyId, userId)
    createPostApi({
      userId: userId,
      companyId: companyId,
      resumeId: resumeId,
      thingId: thingId
    }).then(res => {
      message.success("投递成功")
    }).catch(err =>{
      message.success(err.msg || "投递失败")
    })
  }).catch(err => {
    message.warn("请完善简历")
    router.push({name: 'resumeEditView'})
  })
}
const getRecommendThing =()=> {
  listThingList({sort: 'recommend'}).then(res => {
    res.data.forEach((item, index) => {
      if (item.cover) {
        item.cover = BASE_URL + '/api/staticfiles/image/' + item.cover
      }
    })
    console.log(res)
    recommendData.value = res.data.slice(0, 6)
  }).catch(err => {
    console.log(err)
  })
}
const handleDetail =(item)=> {
  // 跳转新页面
  let text = router.resolve({name: 'detail', query: {id: item.id}})
  window.open(text.href, '_blank')
}
const sendComment =()=> {
  console.log(commentRef.value)
  let text = commentRef.value.value.trim()
  console.log(text)
  if (text.length <= 0) {
    return
  }
  commentRef.value.value = ''
  let userId = userStore.user_id
  if (userId) {
    createCommentApi({content: text, thingId: thingId.value, userId: userId}).then(res => {
      getCommentList()
    }).catch(err => {
      console.log(err)
    })
  } else {
    message.warn('请先登录!')
    router.push({name: 'login'})
  }
}
const like =(commentId)=> {
  likeApi({id: commentId}).then(res => {
    getCommentList()
  }).catch(err => {
    console.log(err)
  })
}
const getCommentList =()=> {
  listThingCommentsApi({thingId: thingId.value, order: order.value}).then(res => {
    res.data.forEach(item => {
      item.commentTime = getFormatTime(item.commentTime, true)
    })
    commentData.value = res.data
  }).catch(err => {
    console.log(err)
  })
}
const sortCommentList =(sortType)=> {
  if (sortType === 'recent') {
    sortIndex.value = 0
  } else {
    sortIndex.value = 1
  }
  order.value = sortType
  getCommentList()
}

</script>
<style scoped lang="less">

.hide {
  display: none;
}

.detail-content {
  display: flex;
  flex-direction: column;
  width: 1100px;
  margin: 4px auto;
}

.flex-view {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
}

.hidden-lg {
  display: none !important;
}

.thing-infos-view {
  display: flex;
  margin: 89px 0 40px;
  overflow: hidden;

  .thing-infos {
    -webkit-box-flex: 1;
    -ms-flex: 1;
    flex: 1;
    display: flex;
  }

  .mobile-share-box {
    height: 38px;
    background: transparent;
    padding: 0 16px;
    margin: 12px 0;
    font-size: 0;
    -webkit-box-align: center;
    -ms-flex-align: center;
    align-items: center;
    -webkit-box-pack: justify;
    -ms-flex-pack: justify;
    justify-content: space-between;

    .state {
      width: 64px;
      height: 24px;
      line-height: 24px;
      background: rgba(70, 132, 226, .1);
      border-radius: 2px;
      font-weight: 500;
      font-size: 12px;
      color: #4684e2;
      text-align: center;
    }

    .share-img {
      background: #fff;
      width: 38px;
      height: 38px;
      border-radius: 50%;
      text-align: center;

      img {
        position: relative;
        top: 4px;
        width: 24px;
      }
    }
  }

  .thing-img-box {
    -webkit-box-flex: 0;
    -ms-flex: 0 0 235px;
    flex: 0 0 235px;
    margin: 0 40px 0 0;

    img {
      width: 200px;
      height: 186px;
      display: block;
    }
  }

  .thing-info-box {
    text-align: left;
    padding: 0;
    margin: 0;
  }

  .thing-state {
    height: 26px;
    line-height: 26px;

    .state {
      font-weight: 500;
      color: #4684e2;
      background: rgba(70, 132, 226, .1);
      border-radius: 2px;
      padding: 5px 8px;
      margin-right: 16px;
    }

    span {
      font-size: 14px;
      color: #152844;
    }
  }

  .thing-name {
    line-height: 32px;
    margin: 16px 0;
    color: #0F1111!important;
    font-size: 15px!important;
    font-weight: 400!important;
    font-style: normal!important;
    text-transform: none!important;
    text-decoration: none!important;
  }

  .translators, .authors {
    line-height: 18px;
    font-size: 14px;
    margin: 8px 0;
    -webkit-box-align: start;
    -ms-flex-align: start;
    align-items: flex-start;
    -webkit-box-pack: start;
    -ms-flex-pack: start;
    justify-content: flex-start;

    .name {
      color: #315c9e;
      white-space: normal;
    }
  }

  .tags {
    position: absolute;
    bottom: 20px;
    margin-top: 16px;

    .category-box {
      color: #152844;
      font-size: 14px;

      .title {
        color: #787878;
      }
    }
  }

  .thing-counts {
    -webkit-box-flex: 0;
    -ms-flex: 0 0 235px;
    flex: 0 0 235px;
    margin-left: 20px;
  }

  .pointer {
    cursor: pointer;
  }

  .count-item {
    height: 64px;
    -webkit-box-align: center;
    -ms-flex-align: center;
    align-items: center;
    cursor: pointer;
  }

  .count-img {
    -webkit-box-flex: 0;
    -ms-flex: 0 0 32px;
    flex: 0 0 32px;
    margin-right: 24px;
    font-size: 0;

    img {
      width: 100%;
      display: block;
    }
  }

  .count-box {
    position: relative;
    border-bottom: 1px solid #cedce4;
    -webkit-box-align: center;
    -ms-flex-align: center;
    align-items: center;
    -webkit-box-pack: justify;
    -ms-flex-pack: justify;
    justify-content: space-between;
    -webkit-box-flex: 1;
    -ms-flex: 1;
    flex: 1;
    height: 100%;
  }

  .count-text-box {
    font-size: 0;

    .count-title {
      color: #152844;
      font-weight: 600;
      font-size: 16px;
      line-height: 18px;
      display: block;
      height: 18px;
    }
  }

  .count-num-box {
    font-weight: 600;
    font-size: 20px;
    line-height: 24px;
    color: #152844;
  }
}

.buy-btn {
  cursor: pointer;
  display: block;
  background: #4684e2;
  border-radius: 4px;
  text-align: center;
  color: #fff;
  font-size: 14px;
  height: 36px;
  line-height: 36px;
  width: 110px;
  outline: none;
  border: none;
  margin-top: 18px;
}

.buy-btn img {
  width: 12px;
  margin-right: 2px;
  vertical-align: middle;
}

.buy-btn span {
  vertical-align: middle;
}

.buy-way {
  overflow: hidden;

  .title {
    font-weight: 600;
    font-size: 18px;
    height: 26px;
    line-height: 26px;
    color: #152844;
    margin-bottom: 12px;
  }
}

.thing-content-view {
  margin-top: 40px;
  padding-bottom: 50px;
}

.main-content {
  -webkit-box-flex: 1;
  -ms-flex: 1;
  flex: 1;

  .text {
    color: #484848;
    font-size: 16px;
    line-height: 26px;
    padding-left: 12px;
    margin: 11px 0;
    white-space: pre-wrap;
  }
}

.main-tab {
  border-bottom: 1px solid #cedce4;
}

.order-view {
  position: relative;
  color: #6c6c6c;
  font-size: 14px;
  line-height: 40px;

  .title {
    margin-right: 8px;
  }

  .tab {
    margin-right: 20px;
    cursor: pointer;
    color: #5f77a6;
    font-size: 16px;
    cursor: pointer;
  }

  .tab-select {
    color: #152844;
    font-weight: 600;
  }

  .tab-underline {
    position: absolute;
    bottom: 0;
    left: 84px;
    width: 16px;
    height: 4px;
    background: #4684e2;
    -webkit-transition: left .3s;
    transition: left .3s;
  }
}

.recommend {
  -webkit-box-flex: 0;
  -ms-flex: 0 0 235px;
  flex: 0 0 235px;
  margin-left: 20px;

  .title {
    font-weight: 600;
    font-size: 18px;
    line-height: 26px;
    color: #152844;
    margin-bottom: 12px;
  }

  .things {
    border-top: 1px solid #cedce4;
    display: flex;
    flex-direction: column;
    gap:24px;
    padding-top: 24px;

    .sub-li {
      background-color: #f6fbfb;
      height: 120px;
      overflow: hidden;
      transition: all .2s linear;
      display: block;
      width: 260px;
      font-size: 0;
      padding: 16px 20px;
      box-sizing: border-box;

      .job-info {
        padding: 16px 20px;
        box-sizing: border-box;
      }

      .sub-li-top {
        margin-bottom: 12px;
        display: flex;
        width: 100%;
        align-items: center;

        .name {
          color: #222;
          font-size: 16px;
          font-weight: 500;
          line-height: 22px;
          transition: all .2s linear;
          position: relative;
          max-width: 200px;
          margin-right: 8px;
          overflow: hidden;
          text-overflow: ellipsis;
          white-space: nowrap;
        }

        .salary {
          font-size: 16px;
          font-weight: 500;
          color: #fe574a;
          line-height: 22px;
          flex: none;
        }
      }

      .sub-li-info {
        display: flex;
        align-items: center;
        flex-wrap: wrap;
        height: 22px;
        overflow: hidden;
        flex: 1;
      }

      .job-text {
        white-space: normal;
        padding-right: 0;
        height: 22px;
        line-height: 22px;
        overflow: hidden;
        word-break: break-all;
        max-width: none;

        span {
          display: inline-block;
          height: 18px;
          font-size: 13px;
          font-weight: 400;
          color: #666;
          line-height: 18px;
          padding-right: 20px;
          border-radius: 4px;
          background: #f8f8f8;
        }
      }
    }

  }
}

.flex-view {
  display: flex;
}

.thing-comment {
  .title {
    font-weight: 600;
    font-size: 14px;
    line-height: 22px;
    height: 22px;
    color: #152844;
    margin: 24px 0 12px;
  }

  .publish {
    -webkit-box-align: center;
    -ms-flex-align: center;
    align-items: center;

    .mine-img {
      -webkit-box-flex: 0;
      -ms-flex: 0 0 40px;
      flex: 0 0 40px;
      margin-right: 12px;
      border-radius: 50%;
      width: 40px;
      height: 40px;
    }

    .content-input {
      -webkit-box-flex: 1;
      -ms-flex: 1;
      flex: 1;
      background: #f6f9fb;
      border-radius: 4px;
      height: 32px;
      line-height: 32px;
      color: #484848;
      padding: 5px 12px;
      white-space: nowrap;
      outline: none;
      border: 0px;
    }

    .send-btn {
      margin-left: 10px;
      background: #4684e2;
      border-radius: 4px;
      -webkit-box-flex: 0;
      -ms-flex: 0 0 80px;
      flex: 0 0 80px;
      color: #fff;
      font-size: 14px;
      text-align: center;
      height: 32px;
      line-height: 32px;
      outline: none;
      border: 0px;
      cursor: pointer;
    }
  }

  .tab-view {
    -webkit-box-pack: justify;
    -ms-flex-pack: justify;
    justify-content: space-between;
    font-size: 14px;
    -webkit-box-align: center;
    -ms-flex-align: center;
    align-items: center;
    margin: 24px 0;

    .count-text {
      color: #484848;
      float: left;
    }

    .tab-box {
      color: #5f77a6;
      -webkit-box-align: center;
      -ms-flex-align: center;
      align-items: center;

      .tab-select {
        color: #152844;
      }

      span {
        cursor: pointer;
      }
    }

    .line {
      width: 1px;
      height: 12px;
      margin: 0 12px;
      background: #cedce4;
    }
  }
}


.comments-list {
  .comment-item {
    .flex-item {
      -webkit-box-align: center;
      -ms-flex-align: center;
      align-items: center;
      padding-top: 16px;

      .avator {
        -webkit-box-flex: 0;
        -ms-flex: 0 0 40px;
        flex: 0 0 40px;
        width: 40px;
        height: 40px;
        margin-right: 12px;
        border-radius: 50%;
        cursor: pointer;
      }

      .person {
        -webkit-box-flex: 1;
        -ms-flex: 1;
        flex: 1;
      }

      .name {
        color: #152844;
        font-weight: 600;
        font-size: 14px;
        line-height: 22px;
        height: 22px;
        cursor: pointer;
      }

      .time {
        color: #5f77a6;
        font-size: 12px;
        line-height: 16px;
        height: 16px;
        margin-top: 2px;
      }

      .float-right {
        color: #4684e2;
        font-size: 14px;
        float: right;

        span {
          margin-left: 19px;
          cursor: pointer;
        }

        .num {
          color: #152844;
          margin-left: 6px;
          cursor: auto;
        }
      }
    }
  }
}

.comment-content {
  margin-top: 8px;
  color: #484848;
  font-size: 14px;
  line-height: 22px;
  padding-bottom: 16px;
  border-bottom: 1px dashed #cedce4;
  margin-left: 52px;
  overflow: hidden;
  word-break: break-word;
}

.infinite-loading-container {
  clear: both;
  text-align: center;
}

.a-price-symbol {
  top: -0.5em;
  font-size: 12px;
}
.a-price {
  color: #0F1111;
  font-size:21px;
}
</style>

3.3 数据层(MySQL)

系统的数据层使用MySQL数据库进行数据存储和管理。MySQL数据库负责存储用户、职位、简历、申请记录等数据。系统采用了ORM框架(如JPA或MyBatis)来简化数据库操作。

4. 主要功能

4.1 用户注册与登录

用户可以通过邮箱或手机号码注册账号,并通过验证码进行身份验证。登录后,用户可以访问自己的个人信息页面,修改个人资料,查看历史投递记录等。

4.2 简历发布与查看

求职者可以在系统中创建个人简历,上传个人信息、教育背景、工作经历等,招聘方可以查看并筛选简历。

4.3 职位发布与搜索

招聘方可以发布职位信息,设置职位要求和工作地点等。求职者则可以通过关键词、职位类别等进行职位搜索,快速找到适合自己的职位。

4.4 在线投递简历

求职者可以直接在系统中在线投递简历给招聘方,简化了传统求职过程中投递简历的繁琐步骤。

4.5 职位推荐

系统基于用户行为(如点击量、投递情况等)提供职位推荐功能,帮助求职者找到更匹配的职位。

5. 系统测试与验证

在系统开发完成后,我们进行了全面的功能测试和性能测试。测试内容包括:

功能测试:验证系统的各项功能是否正常运行,包括用户注册、职位发布、简历投递等功能。
性能测试:通过模拟高并发用户请求,验证系统的性能和稳定性,确保在大流量情况下系统依然能保持良好的响应速度。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

8. 结论

基于Java的求职招聘系统在设计和实现过程中充分考虑了用户体验和系统性能,成功解决了传统求职招聘中的许多痛点。通过系统的使用,求职者能够更高效地找到合适的职位,招聘方也能够更加便捷地筛选和招聘人才。未来,我们将继续优化系统功能,为用户提供更加智能化、个性化的服务,推动求职招聘行业的发展。

如有遇到问题可以找小编沟通交流哦。另外小编帮忙辅导大课作业,学生毕设等。不限于MapReduce, MySQL, python,java,大数据,模型训练等。 hadoop hdfs yarn spark Django flask flink kafka flume datax sqoop seatunnel echart可视化 机器学习等
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2298711.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring Boot整合协同过滤算法,实现个性化推荐

1. 引言 在这篇文章中&#xff0c;我们将展示如何使用 Spring Boot 框架与 协同过滤算法 相结合来构建一个简单的推荐系统。推荐系统广泛应用于电商、电影推荐、社交平台等领域。协同过滤算法通过分析用户行为&#xff0c;找出相似的用户或者物品&#xff0c;从而实现个性化推荐…

自己部署 DeepSeek 助力 Vue 开发:打造丝滑的时间线(Timeline )

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 自己…

光谱相机在天文学领域的应用

天体成分分析 恒星成分研究&#xff1a;恒星的光谱包含了其大气中各种元素的吸收和发射线特征。通过光谱相机精确测量这些谱线&#xff0c;天文学家能确定恒星大气中氢、氦、碳、氮、氧等元素的含量。如对太阳的光谱分析发现&#xff0c;太阳大气中氢元素占比约 71%&#xff0…

深度卷积神经网络实战海洋动物图像识别

本文采用深度卷积神经网络作为核心算法框架&#xff0c;结合PyQt5构建用户界面&#xff0c;使用Python3进行开发。YOLOv11以其高效的特征提取能力&#xff0c;在多个图像分类任务中展现出卓越性能。本研究针对5种海洋动物数据集进行训练和优化&#xff0c;该数据集包含丰富的海…

MySQL-mysql zip安装包配置教程

网上的教程有很多&#xff0c;基本上大同小异。但是安装软件有时就可能因为一个细节安装失败。我也是综合了很多个教程才安装好的&#xff0c;所以本教程可能也不是普遍适合的。 安装环境&#xff1a;win11 1、下载zip安装包&#xff1a; MySQL8.0 For Windows zip包下载地址…

ECP在Successfactors中paylisp越南语乱码问题

导读 pyalisp:ECP中显示工资单有两种方式&#xff0c;一种是PE51&#xff0c;一种是hrform&#xff0c;PE51就是划线的那种&#xff0c; 海外使用的比较多&#xff0c;国内基本没人使用&#xff0c;hrform就是pdf&#xff0c;可以编辑pdf&#xff0c;这个国内相对使用的人 比…

PDF另存为图片的一个方法

说明 有时需要把PDF的每一页另存为图片。用Devexpress可以很方便的完成这个功能。 窗体上放置一个PdfViewer。 然后循环每一页 for (int i 1; i < pdfViewer1.PageCount; i) 调用 chg_pdf_to_bmp函数获得图片并保存 chg_pdf_to_bmp中调用了PdfViewer的CreateBitmap函数…

本地部署DeepSeek集成VSCode创建自己的AI助手

文章目录 安装Ollama和CodeGPT安装Ollama安装CodeGPT 下载并配置DeepSeek模型下载聊天模型&#xff08;deepseek-r1:1.5b&#xff09;下载自动补全模型&#xff08;deepseek-coder:1.3b&#xff09; 使用DeepSeek进行编程辅助配置CodeGPT使用DeepSeek模型开始使用AI助手 ✍️相…

无人机雨季应急救灾技术详解

无人机在雨季应急救灾中发挥着至关重要的作用&#xff0c;其凭借机动灵活、反应迅速、高效安全等特点&#xff0c;为救灾工作提供了强有力的技术支撑。以下是对无人机雨季应急救灾技术的详细解析&#xff1a; 一、无人机在雨季应急救灾中的应用场景 1. 灾情侦查与监测 无人机…

DeepSeek本地化部署【window下安装】【linux下安装】

一、window 本地安装指导 1.1、下载window安装包 https://ollama.com/download/OllamaSetup.exe 1.2、点击下载好的安装包进行安装 检测安装是否成功&#xff1a; C:\Users\admin>ollama -v ollama version is 0.5.7有上面的输出&#xff0c;则证明已经安装成功。 配置…

Ae:常见的光照控件和材质控件

在 After Effects中&#xff0c;几种模拟效果都有类似的光照控件和材质控件&#xff0c;比如&#xff0c;焦散、卡片动画、碎片等。 光照控件和材质控件允许用户模拟不同光源、阴影和高光效果&#xff0c;控制表面反射特性&#xff0c;从而实现真实的光照和反射模拟。适用于材质…

【鸿蒙开发】第三十章 应用稳定性-检测、分析、优化、运维汇总

目录​​​​​​​ 1 概述 2 使用Asan检测内存错误 2.1 背景 2.2 原理概述 2.3 使用约束 2.4 配置参数 2.4.1 在app.json5中配置环境变量 2.4.2 在Run/Debug Configurations中配置环境变量 2.5 Asan使能 方式一 方式二 运行ASan 2.6 ASan异常检测类型 heap-buf…

Linux软件编程:IO编程

IO&#xff08;linux输入输出&#xff09; 1. IO概念&#xff1a; I&#xff1a;输入 O&#xff1a;输出 Linux 一切皆是文件 屏幕 -> /dev/tty 磁盘 -> /dev/sda 网卡 键盘 -> /dev/event 鼠标-> /dev/mice 都是一个文件 2. IO操作的对象&#xff1a; 文件 3. 文…

javaEE2

maven 搭建 前后端交互 HTML servlet 后台和数据库交互 servlet jdbc 未来 servlet-->springmvc jdbc-->mybatis-->mybatisplus/jpa javaee-->spring-->springboot SERVLET tomcat ~Apache 服务 Apache(音译为阿帕奇)是世界上使用排名第一的Web服务器…

2025最新深度学习pytorch完整配置:conda/jupyter/vscode

从今天开始&#xff0c;开始一个新的专栏&#xff0c;更新深度学习相关的内容&#xff0c;从入门到精通&#xff0c;首先的首先是关于环境的配置指南&#xff1a;工欲善其事必先利其器&#xff01; PyTorch 是由 Facebook&#xff08;现 Meta&#xff09;开发的 开源深度学习框…

华为小艺助手接入DeepSeek,升级鸿蒙HarmonyOS NEXT即可体验

小艺助手接入DeepSeek的背景与意义 随着人工智能技术的不断发展&#xff0c;大模型成为推动智能交互升级的关键力量。DeepSeek在自然语言处理等领域具有出色的表现&#xff0c;其模型在语言理解、生成等方面展现出强大的能力。华为小艺助手接入DeepSeek&#xff0c;旨在借助其先…

Git 查看修改记录 二

Git 查看修改记录 二 续接 Git 查看一个文件的修改记录 一 一、修改 A.txt 修改 A.txt number6执行命令 git add . git commit -a -m "修改 number6" # git commit -a -m "修改 number6" 执行 输出如下 # $ git commit -a -m "修改 number6"…

【STM32】增量型旋钮编码器

1.增量型旋钮编码器原理 该编码器有A&#xff0c;B两相&#xff0c;当顺时针旋转时 B相会提前A相90度&#xff08;匀速转的时候&#xff09;&#xff0c;也就是A相上升沿时&#xff0c;B相对应高电平&#xff0c;计数器会1&#xff0c;A相下降沿时&#xff0c;B相为低电平时&…

电动汽车电池监测平台系统设计(论文+源码+图纸)

1总体设计 本次基于单片机的电池监测平台系统设计&#xff0c;其整个系统架构如图2.1所示&#xff0c;其采用STC89C52单片机作为控制器&#xff0c;结合ACS712电流传感器、TLC1543模数转换器、LCD液晶、DS18B20温度传感器构成整个系统&#xff0c;在功能上可以实现电压、电流、…

DeepSeek助力:打造属于你的GPTs智能AI助手

文章目录 一、环境准备1.安装必要的工具和库2. 选择合适的开发语言 二、核心技术选型1. 选择适合的AI框架 三、功能实现1. 文本生成与对话交互2. 代码生成与自动补全3. 数据分析与报告生成 四、案例实战1. 搭建一个简单的聊天机器人2. 创建一个代码生成器 五、总结与展望1. 当前…