| 表名 | 说明 |
|---|---|
| hrm_employee | 员工基本信息表 |
| hrm_employee_salary | 员工薪酬档案表 |
| hrm_employee_social_security | 员工社保公积金档案表(绑定方案 + 个人化基数) |
| hrm_social_security_scheme | 社保公积金方案表(比例模板) |
| hrm_tax_rate_config | 个税税率配置表(累进税率) |
设计说明:
- 社保公积金方案:定义各项缴纳比例,可被多个员工复用
- 员工社保公积金档案:员工绑定方案,设置个人化的缴纳基数
- 一个社保方案可以绑定多个员工,一个员工可以有多个社保档案(按生效日期取最新)
重要说明:员工与系统用户通过手机号绑定,userId 是系统用户ID,不是员工ID。
┌─────────────────────────────────────────────────────────────────────────┐
│ 表关联关系图 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ system_users (系统用户表) │
│ ├─ id (用户ID) ←────────────────── userId 字段关联此 ID │
│ ├─ username (用户名,等于手机号) │
│ ├─ mobile (手机号) │
│ └─ nickname (昵称,等于员工姓名) │
│ │
│ hrm_employee (员工表) │
│ ├─ id (员工ID) │
│ ├─ user_id → 关联系统用户ID (system_users.id) ← 绑定关系 │
│ ├─ phone (手机号) → 创建员工时同步创建用户(用户名=手机号) │
│ ├─ deptId (部门ID) │
│ ├─ baseSalary (基本工资,兜底数据) │
│ └─ salaryStructureId │
│ │
│ hrm_employee_salary (员工薪酬档案) │
│ ├─ userId → 关联系统用户ID (system_users.id) │
│ ├─ baseSalary (基本工资) │
│ ├─ performanceSalary (绩效工资) │
│ └─ effectiveDate (生效日期) │
│ │
│ hrm_employee_social_security (员工社保公积金档案) │
│ ├─ userId → 关联系统用户ID (system_users.id) │
│ ├─ socialSecuritySchemeId → 关联社保公积金方案ID │
│ ├─ socialSecurityBase (社保缴纳基数) │
│ ├─ housingFundBase (公积金缴纳基数) │
│ └─ effectiveDate (生效日期) │
│ │
│ hrm_social_security_scheme (社保公积金方案) │
│ ├─ id (方案ID) ←────────────────── socialSecuritySchemeId 关联此 ID │
│ ├─ name (方案名称) │
│ ├─ effectiveDate (生效日期) │
│ ├─ 各险种缴纳比例(养老、医疗、失业、工伤、生育、公积金) │
│ └─ 状态、备注 │
│ │
│ hrm_salary_calculation (薪酬核算记录) │
│ ├─ userId → 关联系统用户ID (system_users.id) │
│ ├─ deptId (部门ID,来自员工表) │
│ └─ 各项薪资和扣除 │
│ │
│ hrm_tax_rate_config (全局个税税率配置) │
│ └─ 7级累进税率表 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
绑定逻辑说明:
user_id 字段userId 都是系统用户IDuserId 关联系统用户获取登录账号信息社保公积金配置说明:
薪酬核算时,各数据的获取优先级:
| 数据项 | 优先来源 | 兜底来源 |
|---|---|---|
| 基本工资 | hrm_employee_salary.baseSalary |
hrm_employee.baseSalary |
| 绩效工资 | hrm_employee_salary.performanceSalary |
0 |
| 各项补贴 | hrm_employee_salary.*Allowance |
0 |
| 社保基数 | hrm_employee_social_security.socialSecurityBase |
基本工资 |
| 公积金基数 | hrm_employee_social_security.housingFundBase |
基本工资 |
| 社保比例 | hrm_social_security_scheme.*PersonalRatio(通过档案的方案ID获取) |
默认比例 |
| 公积金比例 | hrm_social_security_scheme.housingFundPersonalRatio(通过档案的方案ID获取) |
12% |
| 个税税率 | hrm_tax_rate_config 按应纳税所得额匹配 | - |
默认缴纳比例:
| 险种 | 个人比例 |
|---|---|
| 养老保险 | 8% |
| 医疗保险 | 2% |
| 失业保险 | 0.5% |
| 公积金 | 12% |
userId 使用场景:
| 场景 | 使用方式 |
|---|---|
| 创建薪酬档案 | userId = employee.getUserId() |
| 创建社保档案 | userId = employee.getUserId() |
| 薪酬核算记录 | userId = employee.getUserId() |
| 查询薪酬档案 | selectLatestByUserId(userId) |
| 查询社保档案 | selectLatestByUserId(userId) |
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /hrm/salary/social-security-scheme/create | 创建社保公积金方案 |
| PUT | /hrm/salary/social-security-scheme/update | 更新社保公积金方案 |
| DELETE | /hrm/salary/social-security-scheme/delete | 删除社保公积金方案 |
| GET | /hrm/salary/social-security-scheme/get | 获取社保公积金方案 |
| GET | /hrm/salary/social-security-scheme/list | 获取社保公积金方案列表 |
社保公积金方案字段:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | Long | 方案ID |
| name | String | 方案名称 |
| effectiveDate | Date | 生效日期 |
| pensionCompanyRatio | BigDecimal | 养老保险企业缴纳比例(如 0.16 表示 16%) |
| pensionPersonalRatio | BigDecimal | 养老保险个人缴纳比例 |
| medicalCompanyRatio | BigDecimal | 医疗保险企业缴纳比例 |
| medicalPersonalRatio | BigDecimal | 医疗保险个人缴纳比例 |
| unemploymentCompanyRatio | BigDecimal | 失业保险企业缴纳比例 |
| unemploymentPersonalRatio | BigDecimal | 失业保险个人缴纳比例 |
| workInjuryRatio | BigDecimal | 工伤保险企业缴纳比例 |
| maternityRatio | BigDecimal | 生育保险企业缴纳比例 |
| housingFundCompanyRatio | BigDecimal | 公积金企业缴纳比例 |
| housingFundPersonalRatio | BigDecimal | 公积金个人缴纳比例 |
| status | Integer | 状态:0-启用,1-禁用 |
| remark | String | 备注 |
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /hrm/salary/tax-rate-config/create-list | 批量创建个税税率配置 |
| PUT | /hrm/salary/tax-rate-config/update | 更新个税税率配置 |
| DELETE | /hrm/salary/tax-rate-config/delete | 删除个税税率配置 |
| GET | /hrm/salary/tax-rate-config/get | 获取个税税率配置 |
| GET | /hrm/salary/tax-rate-config/list | 获取个税税率配置列表 |
| GET | /hrm/salary/tax-rate-config/get-effective | 获取生效的个税税率配置列表 |
个税税率配置字段:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | Long | 配置ID |
| name | String | 配置名称 |
| effectiveDate | Date | 生效日期 |
| level | Integer | 税率级数(1-7) |
| minAmount | BigDecimal | 应纳税所得额下限(含) |
| maxAmount | BigDecimal | 应纳税所得额上限(不含),0表示无上限 |
| taxRate | BigDecimal | 税率(如 0.03 表示 3%) |
| quickDeduction | BigDecimal | 速算扣除数 |
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /hrm/salary/employee-social-security/create | 创建员工社保公积金档案 |
| PUT | /hrm/salary/employee-social-security/update | 更新员工社保公积金档案 |
| DELETE | /hrm/salary/employee-social-security/delete | 删除员工社保公积金档案 |
| GET | /hrm/salary/employee-social-security/get | 获取员工社保公积金档案 |
| GET | /hrm/salary/employee-social-security/page | 获取员工社保公积金档案分页 |
| GET | /hrm/salary/employee-social-security/list-by-user | 获取员工社保公积金档案历史列表 |
| GET | /hrm/salary/employee-social-security/get-latest | 获取员工最新生效的社保公积金档案 |
员工社保公积金档案字段:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | Long | 档案ID |
| userId | Long | 系统用户ID(关联员工表) |
| userName | String | 员工姓名(响应字段) |
| deptName | String | 部门名称(响应字段) |
| socialSecuritySchemeId | Long | 社保公积金方案ID |
| schemeName | String | 方案名称(响应字段) |
| socialSecurityBase | BigDecimal | 社保缴纳基数(员工个性化设置) |
| housingFundBase | BigDecimal | 公积金缴纳基数(员工个性化设置) |
| effectiveDate | Date | 生效日期 |
| status | Integer | 状态:0-启用,1-禁用 |
| remark | String | 备注 |
<template>
<div class="app-container">
<!-- 表格展示7级累进税率 -->
<el-table :data="list">
<el-table-column label="级数" prop="level" width="80" />
<el-table-column label="应纳税所得额">
<template #default="{ row }">
{{ row.minAmount }} ~ {{ row.maxAmount === 0 ? '∞' : row.maxAmount }}
</template>
</el-table-column>
<el-table-column label="税率" width="100">
<template #default="{ row }">
{{ (row.taxRate * 100).toFixed(0) }}%
</template>
</el-table-column>
<el-table-column label="速算扣除数" prop="quickDeduction" />
<el-table-column label="操作" width="100">
<template #default="{ row }">
<el-button link type="primary" @click="handleEdit(row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
<template>
<div class="app-container">
<!-- 工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" @click="handleAdd">新增方案</el-button>
</el-col>
</el-row>
<!-- 表格 -->
<el-table :data="list">
<el-table-column label="方案名称" prop="name" />
<el-table-column label="生效日期" prop="effectiveDate" />
<el-table-column label="养老(企业)" width="100">
<template #default="{ row }">
{{ (row.pensionCompanyRatio * 100).toFixed(1) }}%
</template>
</el-table-column>
<el-table-column label="养老(个人)" width="100">
<template #default="{ row }">
{{ (row.pensionPersonalRatio * 100).toFixed(1) }}%
</template>
</el-table-column>
<el-table-column label="医疗(个人)" width="100">
<template #default="{ row }">
{{ (row.medicalPersonalRatio * 100).toFixed(1) }}%
</template>
</el-table-column>
<el-table-column label="公积金(个人)" width="100">
<template #default="{ row }">
{{ (row.housingFundPersonalRatio * 100).toFixed(1) }}%
</template>
</el-table-column>
<el-table-column label="状态" prop="status">
<template #default="{ row }">
<el-tag :type="row.status === 0 ? 'success' : 'info'">
{{ row.status === 0 ? '启用' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="150">
<template #default="{ row }">
<el-button link type="primary" @click="handleEdit(row)">编辑</el-button>
<el-button link type="danger" @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 新增/编辑弹窗 -->
<el-dialog :title="title" v-model="open" width="700px">
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
<el-form-item label="方案名称" prop="name">
<el-input v-model="form.name" placeholder="如:2024年南通市标准" />
</el-form-item>
<el-form-item label="生效日期" prop="effectiveDate">
<el-date-picker v-model="form.effectiveDate" type="date" placeholder="选择生效日期" />
</el-form-item>
<el-divider content-position="left">养老保险</el-divider>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="企业比例" prop="pensionCompanyRatio">
<el-input-number v-model="form.pensionCompanyRatio" :min="0" :max="1" :step="0.01" :precision="4" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="个人比例" prop="pensionPersonalRatio">
<el-input-number v-model="form.pensionPersonalRatio" :min="0" :max="1" :step="0.01" :precision="4" />
</el-form-item>
</el-col>
</el-row>
<el-divider content-position="left">医疗保险</el-divider>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="企业比例" prop="medicalCompanyRatio">
<el-input-number v-model="form.medicalCompanyRatio" :min="0" :max="1" :step="0.01" :precision="4" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="个人比例" prop="medicalPersonalRatio">
<el-input-number v-model="form.medicalPersonalRatio" :min="0" :max="1" :step="0.01" :precision="4" />
</el-form-item>
</el-col>
</el-row>
<el-divider content-position="left">失业/工伤/生育保险</el-divider>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="失业企业" prop="unemploymentCompanyRatio">
<el-input-number v-model="form.unemploymentCompanyRatio" :min="0" :max="1" :step="0.001" :precision="4" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="失业个人" prop="unemploymentPersonalRatio">
<el-input-number v-model="form.unemploymentPersonalRatio" :min="0" :max="1" :step="0.001" :precision="4" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="工伤比例" prop="workInjuryRatio">
<el-input-number v-model="form.workInjuryRatio" :min="0" :max="1" :step="0.001" :precision="4" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="生育比例" prop="maternityRatio">
<el-input-number v-model="form.maternityRatio" :min="0" :max="1" :step="0.001" :precision="4" />
</el-form-item>
</el-col>
</el-row>
<el-divider content-position="left">公积金</el-divider>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="企业比例" prop="housingFundCompanyRatio">
<el-input-number v-model="form.housingFundCompanyRatio" :min="0" :max="1" :step="0.01" :precision="4" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="个人比例" prop="housingFundPersonalRatio">
<el-input-number v-model="form.housingFundPersonalRatio" :min="0" :max="1" :step="0.01" :precision="4" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="open = false">取消</el-button>
<el-button type="primary" @click="submitForm">确定</el-button>
</template>
</el-dialog>
</div>
</template>
<script>
export default {
data() {
return {
form: {
id: null,
name: '',
effectiveDate: null,
pensionCompanyRatio: 0.16,
pensionPersonalRatio: 0.08,
medicalCompanyRatio: 0.095,
medicalPersonalRatio: 0.02,
unemploymentCompanyRatio: 0.005,
unemploymentPersonalRatio: 0.005,
workInjuryRatio: 0.005,
maternityRatio: 0.005,
housingFundCompanyRatio: 0.12,
housingFundPersonalRatio: 0.12,
status: 0,
remark: '',
},
rules: {
name: [{ required: true, message: '请输入方案名称', trigger: 'blur' }],
effectiveDate: [{ required: true, message: '请选择生效日期', trigger: 'change' }],
},
list: [],
open: false,
title: '',
}
},
methods: {
getList() {
request.get('/hrm/salary/social-security-scheme/list', { params: { status: 0 } })
.then(res => { this.list = res.data })
},
handleAdd() {
this.resetForm()
this.title = '新增社保公积金方案'
this.open = true
},
handleEdit(row) {
this.resetForm()
this.title = '编辑社保公积金方案'
this.form = { ...row }
this.open = true
},
submitForm() {
this.$refs.formRef.validate(valid => {
if (valid) {
const url = this.form.id
? '/hrm/salary/social-security-scheme/update'
: '/hrm/salary/social-security-scheme/create'
request.post(url, this.form)
.then(() => {
this.$message.success('操作成功')
this.open = false
this.getList()
})
}
})
},
}
}
</script>
<template>
<div class="app-container">
<!-- 搜索栏 -->
<el-form :model="queryParams" ref="queryFormRef" :inline="true">
<el-form-item label="员工姓名" prop="userName">
<el-input v-model="queryParams.userName" placeholder="请输入员工姓名" />
</el-form-item>
<el-form-item label="部门" prop="deptId">
<el-select v-model="queryParams.deptId" placeholder="请选择部门" clearable>
<el-option v-for="dept in deptList" :key="dept.id" :label="dept.name" :value="dept.id" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery">搜索</el-button>
<el-button @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" @click="handleAdd">新增</el-button>
</el-col>
</el-row>
<!-- 表格 -->
<el-table :data="list">
<el-table-column label="员工姓名" prop="userName" />
<el-table-column label="部门" prop="deptName" />
<el-table-column label="社保方案" prop="schemeName" />
<el-table-column label="社保基数" prop="socialSecurityBase" />
<el-table-column label="公积金基数" prop="housingFundBase" />
<el-table-column label="生效日期" prop="effectiveDate" />
<el-table-column label="状态" prop="status">
<template #default="{ row }">
<el-tag :type="row.status === 0 ? 'success' : 'info'">
{{ row.status === 0 ? '启用' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="200">
<template #default="{ row }">
<el-button link type="primary" @click="handleEdit(row)">编辑</el-button>
<el-button link type="primary" @click="handleHistory(row)">历史</el-button>
<el-button link type="danger" @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 新增/编辑弹窗 -->
<el-dialog :title="title" v-model="open" width="500px">
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
<el-form-item label="员工" prop="userId">
<el-select v-model="form.userId" placeholder="请选择员工" filterable>
<el-option v-for="user in userList" :key="user.id" :label="user.name" :value="user.id" />
</el-select>
</el-form-item>
<el-form-item label="社保方案" prop="socialSecuritySchemeId">
<el-select v-model="form.socialSecuritySchemeId" placeholder="请选择社保方案">
<el-option v-for="scheme in schemeList" :key="scheme.id" :label="scheme.name" :value="scheme.id" />
</el-select>
</el-form-item>
<el-divider content-position="left">缴纳基数(员工个性化设置)</el-divider>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="社保基数" prop="socialSecurityBase">
<el-input-number v-model="form.socialSecurityBase" :min="0" :precision="2" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="公积金基数" prop="housingFundBase">
<el-input-number v-model="form.housingFundBase" :min="0" :precision="2" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="生效日期" prop="effectiveDate">
<el-date-picker v-model="form.effectiveDate" type="date" placeholder="选择生效日期" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="open = false">取消</el-button>
<el-button type="primary" @click="submitForm">确定</el-button>
</template>
</el-dialog>
<!-- 历史记录弹窗 -->
<el-dialog title="社保公积金历史记录" v-model="historyOpen" width="600px">
<el-table :data="historyList">
<el-table-column label="社保方案" prop="schemeName" />
<el-table-column label="社保基数" prop="socialSecurityBase" />
<el-table-column label="公积金基数" prop="housingFundBase" />
<el-table-column label="生效日期" prop="effectiveDate" />
<el-table-column label="状态">
<template #default="{ row }">
<el-tag :type="row.status === 0 ? 'success' : 'info'">
{{ row.status === 0 ? '启用' : '禁用' }}
</el-tag>
</template>
</el-table-column>
</el-table>
</el-dialog>
</div>
</template>
<script>
export default {
data() {
return {
queryParams: {
userName: '',
deptId: null,
},
form: {
id: null,
userId: null,
socialSecuritySchemeId: null,
socialSecurityBase: 0,
housingFundBase: 0,
effectiveDate: null,
remark: '',
},
rules: {
userId: [{ required: true, message: '请选择员工', trigger: 'change' }],
socialSecuritySchemeId: [{ required: true, message: '请选择社保方案', trigger: 'change' }],
socialSecurityBase: [{ required: true, message: '请输入社保基数', trigger: 'blur' }],
housingFundBase: [{ required: true, message: '请输入公积金基数', trigger: 'blur' }],
effectiveDate: [{ required: true, message: '请选择生效日期', trigger: 'change' }],
},
list: [],
userList: [],
schemeList: [],
deptList: [],
historyList: [],
open: false,
historyOpen: false,
title: '',
}
},
created() {
this.getList()
this.getSchemeList()
},
methods: {
getSchemeList() {
request.get('/hrm/salary/social-security-scheme/list', { params: { status: 0 } })
.then(res => { this.schemeList = res.data })
},
getList() {
request.get('/hrm/salary/employee-social-security/page', { params: this.queryParams })
.then(res => {
this.list = res.data.list
this.total = res.data.total
})
},
handleAdd() {
this.resetForm()
this.title = '新增员工社保公积金档案'
this.open = true
},
handleEdit(row) {
this.resetForm()
this.title = '编辑员工社保公积金档案'
this.form = { ...row }
this.open = true
},
handleHistory(row) {
request.get('/hrm/salary/employee-social-security/list-by-user', { params: { userId: row.userId } })
.then(res => {
this.historyList = res.data
this.historyOpen = true
})
},
submitForm() {
this.$refs.formRef.validate(valid => {
if (valid) {
const url = this.form.id
? '/hrm/salary/employee-social-security/update'
: '/hrm/salary/employee-social-security/create'
request.post(url, this.form)
.then(() => {
this.$message.success('操作成功')
this.open = false
this.getList()
})
}
})
},
handleDelete(row) {
this.$confirm('确认删除该档案?', '提示').then(() => {
request.delete('/hrm/salary/employee-social-security/delete', { params: { id: row.id } })
.then(() => {
this.$message.success('删除成功')
this.getList()
})
})
},
}
}
</script>
每个员工使用其绑定方案中的缴纳比例计算:
社保扣除 = 社保基数 × (养老保险个人比例 + 医疗保险个人比例 + 失业保险个人比例)
socialSecurityBasesocialSecuritySchemeId 获取方案的比例每个员工使用其绑定方案中的缴纳比例计算:
公积金扣除 = 公积金基数 × 公积金个人比例
housingFundBasesocialSecuritySchemeId 获取方案的比例应纳税所得额 = 应发工资 - 社保扣除 - 公积金扣除 - 5000(起征点)
个税 = 应纳税所得额 × 税率 - 速算扣除数
假设员工:
- 基本工资:10000 元
- 绩效工资:2000 元
- 社保基数:10000 元
- 公积金基数:10000 元
按 2024 年标准计算:
应发工资 = 10000 + 2000 = 12000 元
社保扣除 = 10000 × (8% + 2% + 0.5%) = 1050 元
公积金扣除 = 10000 × 12% = 1200 元
应纳税所得额 = 12000 - 1050 - 1200 - 5000 = 4750 元
个税 = 4750 × 3% - 0 = 142.5 元
实发工资 = 12000 - 1050 - 1200 - 142.5 = 9607.5 元