薪资管理 API
薪资管理模块提供完整的员工薪资核算、发放、查询和统计功能。管理员可以进行薪资的创建、更新、发放和统计分析,员工可以查询自己的薪资明细。
功能概述
- 薪资核算:支持多维度薪资计算,包括基础工资、绩效工资、津贴、奖金等
- 自动计算:自动计算实发工资,支持各项扣款
- 状态管理:支持薪资状态管理(未确认/已发放)
- 统计分析:提供薪资统计分析功能
- 权限控制:管理员管理所有员工薪资,员工仅查看自己薪资
管理员接口
创建薪资明细
为员工创建指定月份的薪资明细记录。
- URL:
/api/v1/admin/salaries - 方法:
POST - 内容类型:
application/json - 认证: 需要管理员权限
请求参数
| 字段 | 类型 | 必填 | 描述 | 示例 |
|---|---|---|---|---|
| employee_id | uint | 是 | 员工ID | 1 |
| month | string | 是 | 薪资月份(格式: 2024-01) | "2024-01" |
| status | string | 否 | 薪资状态(0-未确认, 1-已发放) | "0" |
| payment_date | string | 否 | 发放日期(格式: 2024-02-05) | "2024-02-05" |
| category_id | uint | 否 | 薪资分类ID | 1 |
| basic_salary | float64 | 否 | 基础工资(元) | 12000.00 |
| seniority_pay | float64 | 否 | 工龄工资(元) | 800.00 |
| performance_pay | float64 | 否 | 绩效工资(元) | 5000.00 |
| overtime_pay | float64 | 否 | 加班费(元) | 500.00 |
| bonus | float64 | 否 | 奖金(元) | 1000.00 |
| allowance | float64 | 否 | 津贴(元) | 300.00 |
| attendance_deduction | float64 | 否 | 考勤扣款(元) | 200.00 |
| personal_insurance | float64 | 否 | 个人保险(元) | 1200.00 |
| personal_tax | float64 | 否 | 个人所得税(元) | 600.00 |
| housing_fund | float64 | 否 | 住房公积金(元) | 800.00 |
| other_deductions | float64 | 否 | 其他扣除(元) | 100.00 |
| remarks | string | 否 | 备注 | "2024年1月薪资" |
请求示例
json
{
"employee_id": 1,
"month": "2024-01",
"status": "0",
"payment_date": "2024-02-05",
"category_id": 1,
"basic_salary": 12000.00,
"seniority_pay": 800.00,
"performance_pay": 5000.00,
"overtime_pay": 500.00,
"bonus": 1000.00,
"allowance": 300.00,
"attendance_deduction": 200.00,
"personal_insurance": 1200.00,
"personal_tax": 600.00,
"housing_fund": 800.00,
"other_deductions": 100.00,
"remarks": "2024年1月薪资"
}响应示例
成功响应 (200)
json
{
"success": true,
"message": "薪资明细创建成功",
"data": {
"id": 1,
"employee_id": 1,
"employee_name": "张三",
"month": "2024年01月",
"net_salary": 15800.00,
"status": "0",
"payment_date": "2024-02-05T00:00:00Z",
"income": {
"basic_salary": 12000.00,
"seniority_pay": 800.00,
"performance_pay": 5000.00,
"overtime_pay": 500.00,
"bonus": 1000.00,
"allowance": 300.00,
"total": 19600.00
},
"deductions": {
"attendance_deduction": 200.00,
"personal_insurance": 1200.00,
"personal_tax": 600.00,
"housing_fund": 800.00,
"other_deductions": 100.00,
"total": 2900.00
},
"summary": {
"total_income": 19600.00,
"total_deductions": 2900.00,
"net_salary": 16700.00
},
"remarks": "2024年1月薪资",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}
}获取薪资明细详情
根据ID获取指定薪资明细的详细信息。
- URL:
/api/v1/admin/salaries/{id} - 方法:
GET - 认证: 需要管理员权限
请求参数
| 参数 | 类型 | 必填 | 描述 | 示例 |
|---|---|---|---|---|
| id | uint | 是 | 薪资明细ID | 1 |
响应示例
成功响应 (200)
json
{
"code": 200,
"message": "获取薪资明细成功",
"data": {
"id": 1,
"employee_id": 1,
"employee_name": "张三",
"month": "2024年01月",
"net_salary": 15800.00,
"status": "已确认",
"payment_date": "2024-02-05T00:00:00Z",
"income": {
"basic_salary": 12000.00,
"seniority_pay": 800.00,
"performance_pay": 5000.00,
"overtime_pay": 500.00,
"bonus": 1000.00,
"allowance": 300.00,
"total": 19600.00
},
"deductions": {
"attendance_deduction": 200.00,
"personal_insurance": 1200.00,
"personal_tax": 600.00,
"housing_fund": 800.00,
"other_deductions": 100.00,
"total": 2900.00
},
"summary": {
"total_income": 19600.00,
"total_deductions": 2900.00,
"net_salary": 16700.00
},
"remarks": "2024年1月薪资",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
},
"timestamp": "2024-01-01T12:00:00Z"
}更新薪资明细
更新指定薪资明细的信息。
- URL:
/api/v1/admin/salaries/{id} - 方法:
PUT - 内容类型:
application/json - 认证: 需要管理员权限
请求参数
路径参数
| 参数 | 类型 | 必填 | 描述 | 示例 |
|---|---|---|---|---|
| id | uint | 是 | 薪资明细ID | 1 |
请求体参数
| 字段 | 类型 | 必填 | 描述 | 示例 |
|---|---|---|---|---|
| status | string | 否 | 薪资状态(0-未确认, 1-已发放) | "1" |
| payment_date | string | 否 | 发放日期 | "2024-02-05" |
| basic_salary | float64 | 否 | 基础工资(元) | 12500.00 |
| remarks | string | 否 | 备注 | "调整后的2024年1月薪资" |
请求示例
json
{
"status": "1",
"payment_date": "2024-02-05",
"basic_salary": 12500.00,
"remarks": "调整后的2024年1月薪资"
}响应示例
成功响应 (200)
json
{
"code": 200,
"message": "薪资明细更新成功",
"data": {
"id": 1,
"employee_id": 1,
"employee_name": "张三",
"month": "2024年01月",
"net_salary": 17000.00,
"status": "已发放",
"payment_date": "2024-02-05T00:00:00Z",
"income": {
"basic_salary": 12500.00,
"seniority_pay": 800.00,
"performance_pay": 5000.00,
"overtime_pay": 500.00,
"bonus": 1000.00,
"allowance": 300.00,
"total": 20100.00
},
"deductions": {
"attendance_deduction": 200.00,
"personal_insurance": 1200.00,
"personal_tax": 600.00,
"housing_fund": 800.00,
"other_deductions": 100.00,
"total": 2900.00
},
"summary": {
"total_income": 20100.00,
"total_deductions": 2900.00,
"net_salary": 17200.00
},
"remarks": "调整后的2024年1月薪资",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T12:45:00Z"
},
"timestamp": "2024-01-01T12:00:00Z"
}删除薪资明细
删除指定的薪资明细记录。
- URL:
/api/v1/admin/salaries/{id} - 方法:
DELETE - 认证: 需要管理员权限
请求参数
| 参数 | 类型 | 必填 | 描述 | 示例 |
|---|---|---|---|---|
| id | uint | 是 | 薪资明细ID | 1 |
响应示例
成功响应 (200)
json
{
"code": 200,
"message": "薪资明细删除成功",
"data": null,
"timestamp": "2024-01-01T12:00:00Z"
}获取薪资明细列表
获取薪资明细列表,支持多种过滤条件和分页。
- URL:
/api/v1/admin/salaries - 方法:
GET - 认证: 需要管理员权限
请求参数
| 参数 | 类型 | 必填 | 描述 | 示例 |
|---|---|---|---|---|
| page | int | 否 | 页码,默认1 | 1 |
| page_size | int | 否 | 每页数量,默认10 | 20 |
| employee_id | uint | 否 | 员工ID | 1 |
| month | string | 否 | 薪资月份(格式: 2024-01) | "2024-01" |
| status | string | 否 | 薪资状态 | "已确认" |
| start_month | string | 否 | 开始月份 | "2024-01" |
| end_month | string | 否 | 结束月份 | "2024-12" |
请求示例
GET /api/v1/admin/salaries?page=1&page_size=20&employee_id=1&start_month=2024-01&end_month=2024-06响应示例
成功响应 (200)
json
{
"success": true,
"message": "获取薪资列表成功",
"data": {
"salaries": [
{
"id": 1,
"employee_id": 1,
"employee_name": "张三",
"month": "2024年01月",
"net_salary": 15800.00,
"status": "已发放",
"payment_date": "2024-02-05T00:00:00Z",
"income": {
"basic_salary": 12000.00,
"seniority_pay": 800.00,
"performance_pay": 5000.00,
"overtime_pay": 500.00,
"bonus": 1000.00,
"allowance": 300.00,
"total": 19600.00
},
"deductions": {
"attendance_deduction": 200.00,
"personal_insurance": 1200.00,
"personal_tax": 600.00,
"housing_fund": 800.00,
"other_deductions": 100.00,
"total": 2900.00
},
"summary": {
"total_income": 19600.00,
"total_deductions": 2900.00,
"net_salary": 16700.00
},
"remarks": "2024年1月薪资",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}
],
"pagination": {
"current_page": 1,
"page_size": 20,
"total_items": 1,
"total_pages": 1,
"has_next": false,
"has_prev": false
}
},
"timestamp": "2024-01-01T12:00:00Z"
}批量更新薪资状态
批量更新多个薪资明细的状态。
- URL:
/api/v1/admin/salaries/batch-status - 方法:
PATCH - 内容类型:
application/json - 认证: 需要管理员权限
请求参数
| 字段 | 类型 | 必填 | 描述 | 示例 |
|---|---|---|---|---|
| ids | uint[] | 是 | 薪资明细ID列表 | [1, 2, 3] |
| status | string | 是 | 薪资状态(0-未确认, 1-已发放) | "1" |
请求示例
json
{
"ids": [1, 2, 3],
"status": "1"
}响应示例
成功响应 (200)
json
{
"code": 200,
"message": "批量更新薪资状态成功",
"data": null,
"timestamp": "2024-01-01T12:00:00Z"
}获取薪资统计信息
获取薪资相关的统计信息。
- URL:
/api/v1/admin/salaries/stats - 方法:
GET - 认证: 需要管理员权限
请求参数
| 参数 | 类型 | 必填 | 描述 | 示例 |
|---|---|---|---|---|
| month | string | 否 | 薪资月份(格式: 2024-01,为空表示所有月份) | "2024-01" |
响应示例
成功响应 (200)
json
{
"code": 200,
"message": "获取薪资统计信息成功",
"data": {
"total_employees": 100,
"total_salaries": 1580000,
"avg_salary": 15800.00,
"max_salary": 25000.00,
"min_salary": 8000.00
},
"timestamp": "2024-01-01T12:00:00Z"
}员工接口
查询自己的薪资明细列表
员工查询自己的薪资明细列表。
- URL:
/api/v1/employees/salaries - 方法:
GET - 认证: 需要员工权限
请求参数
| 参数 | 类型 | 必填 | 描述 | 示例 |
|---|---|---|---|---|
| page | int | 否 | 页码,默认1 | 1 |
| page_size | int | 否 | 每页数量,默认10 | 10 |
| employee_id | uint | 否 | 员工ID(管理员接口) | 1 |
| month | string | 否 | 薪资月份(格式: 2024-01) | "2024-01" |
| status | string | 否 | 薪资状态 | "已确认" |
| start_month | string | 否 | 开始月份 | "2024-01" |
| end_month | string | 否 | 结束月份 | "2024-12" |
请求示例
GET /api/v1/employees/salaries?page=1&page_size=12&start_month=2024-01&end_month=2024-12响应示例
成功响应 (200)
json
{
"success": true,
"message": "获取薪资列表成功",
"data": {
"salaries": [
{
"id": 1,
"employee_id": 1,
"employee_name": "张三",
"month": "2024年01月",
"net_salary": 15800.00,
"status": "已发放",
"payment_date": "2024-02-05T00:00:00Z",
"income": {
"basic_salary": 12000.00,
"seniority_pay": 800.00,
"performance_pay": 5000.00,
"overtime_pay": 500.00,
"bonus": 1000.00,
"allowance": 300.00,
"total": 19600.00
},
"deductions": {
"attendance_deduction": 200.00,
"personal_insurance": 1200.00,
"personal_tax": 600.00,
"housing_fund": 800.00,
"other_deductions": 100.00,
"total": 2900.00
},
"summary": {
"total_income": 19600.00,
"total_deductions": 2900.00,
"net_salary": 16700.00
},
"remarks": "2024年1月薪资",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}
],
"pagination": {
"current_page": 1,
"page_size": 12,
"total_items": 12,
"total_pages": 1,
"has_next": false,
"has_prev": false
}
},
"timestamp": "2024-01-01T12:00:00Z"
}查询某月薪资明细
员工查询自己指定月份的薪资明细。
- URL:
/api/v1/employees/salaries/{month} - 方法:
GET - 认证: 需要员工权限
请求参数
| 参数 | 类型 | 必填 | 描述 | 示例 |
|---|---|---|---|---|
| month | string | 是 | 薪资月份(格式: 2024-01) | "2024-01" |
请求示例
GET /api/v1/employees/salaries/2024-01响应示例
成功响应 (200)
json
{
"code": 200,
"message": "获取薪资明细成功",
"data": {
"id": 1,
"employee_id": 1,
"employee_name": "张三",
"month": "2024年01月",
"net_salary": 15800.00,
"status": "已发放",
"payment_date": "2024-02-05T00:00:00Z",
"income": {
"basic_salary": 12000.00,
"seniority_pay": 800.00,
"performance_pay": 5000.00,
"overtime_pay": 500.00,
"bonus": 1000.00,
"allowance": 300.00,
"total": 19600.00
},
"deductions": {
"attendance_deduction": 200.00,
"personal_insurance": 1200.00,
"personal_tax": 600.00,
"housing_fund": 800.00,
"other_deductions": 100.00,
"total": 2900.00
},
"summary": {
"total_income": 19600.00,
"total_deductions": 2900.00,
"net_salary": 16700.00
},
"remarks": "2024年1月薪资",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
},
"timestamp": "2024-01-01T12:00:00Z"
}数据模型
SalaryResponse
typescript
interface SalaryResponse {
id: number; // 薪资明细ID
employee_id: number; // 员工ID
employee_name: string; // 员工姓名
month: string; // 薪资月份(格式: 2024年01月)
net_salary: number; // 实发工资
status: string; // 薪资状态
payment_date?: string; // 发放时间
income: IncomeDetail; // 收入明细
deductions: DeductionDetail; // 扣除明细
summary: SalarySummary; // 汇总信息
remarks: string; // 备注
created_at: string; // 创建时间
updated_at: string; // 更新时间
}IncomeDetail
typescript
interface IncomeDetail {
basic_salary: number; // 基础工资
seniority_pay: number; // 工龄工资
performance_pay: number; // 绩效工资
overtime_pay: number; // 加班费
bonus: number; // 奖金
allowance: number; // 津贴
total: number; // 收入总计
}DeductionDetail
typescript
interface DeductionDetail {
attendance_deduction: number; // 考勤扣款
personal_insurance: number; // 个人保险
personal_tax: number; // 个人所得税
housing_fund: number; // 住房公积金
other_deductions: number; // 其他扣除
total: number; // 扣除总计
}SalarySummary
typescript
interface SalarySummary {
total_income: number; // 总收入
total_deductions: number; // 总扣除
net_salary: number; // 实发工资
}SalaryStatsResponse
typescript
interface SalaryStatsResponse {
total_employees: number; // 总员工数
total_salaries: number; // 薪资总额(分)
avg_salary: number; // 平均薪资
max_salary: number; // 最高薪资
min_salary: number; // 最低薪资
}错误代码
| 错误代码 | 描述 | 解决方案 |
|---|---|---|
| 400 | 请求参数错误 | 检查请求参数格式和必填字段 |
| 401 | 未认证 | 检查 JWT token 是否有效 |
| 403 | 无权限 | 确认用户具有相应权限 |
| 404 | 薪资明细不存在 | 检查薪资明细ID是否正确 |
| 409 | 薪资明细已存在 | 检查是否重复创建同一员工同一月份的薪资 |
| 500 | 服务器内部错误 | 联系系统管理员 |
薪资状态说明
| 状态值 | 描述 | 说明 |
|---|---|---|
| 0 | 未确认 | 薪资已核算但未确认发放 |
| 1 | 已发放 | 薪资已确认并发放 |
金额说明
- 所有金额在数据库中以分为单位存储,避免浮点数精度问题
- API 接口使用元为单位进行交互,后端自动进行元分转换
- 实发工资 = 总收入 - 总扣除,系统自动计算和验证
集成示例
JavaScript 薪资查询组件
javascript
import React, { useState, useEffect } from 'react';
const SalaryManagement = () => {
const [salaries, setSalaries] = useState([]);
const [loading, setLoading] = useState(false);
const [selectedMonth, setSelectedMonth] = useState('');
const [stats, setStats] = useState(null);
const fetchSalaries = async (month = '') => {
setLoading(true);
try {
const params = new URLSearchParams();
if (month) params.append('month', month);
const response = await fetch(`/api/v1/employees/salaries?${params}`, {
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`
}
});
const data = await response.json();
if (data.code === 200) {
setSalaries(data.data.salaries);
}
} catch (error) {
console.error('获取薪资列表失败:', error);
} finally {
setLoading(false);
}
};
const fetchSalaryByMonth = async (month) => {
try {
const response = await fetch(`/api/v1/employees/salaries/${month}`, {
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`
}
});
const data = await response.json();
if (data.code === 200) {
return data.data;
}
} catch (error) {
console.error('获取薪资明细失败:', error);
}
};
const formatCurrency = (amount) => {
return new Intl.NumberFormat('zh-CN', {
style: 'currency',
currency: 'CNY'
}).format(amount);
};
const formatDate = (dateString) => {
return new Date(dateString).toLocaleDateString('zh-CN');
};
useEffect(() => {
fetchSalaries();
}, []);
const handleMonthChange = (e) => {
const month = e.target.value;
setSelectedMonth(month);
fetchSalaries(month);
};
return (
<div className="salary-management">
<h2>薪资查询</h2>
<div className="filters">
<div className="filter-item">
<label>选择月份:</label>
<input
type="month"
value={selectedMonth}
onChange={handleMonthChange}
/>
</div>
</div>
{loading ? (
<div className="loading">加载中...</div>
) : (
<div className="salary-list">
{salaries.map(salary => (
<div key={salary.id} className="salary-item">
<div className="salary-header">
<h3>{salary.month}</h3>
<span className={`status ${salary.status === '已发放' ? 'paid' : 'pending'}`}>
{salary.status}
</span>
</div>
<div className="salary-summary">
<div className="summary-item">
<label>实发工资:</label>
<span className="amount">{formatCurrency(salary.net_salary)}</span>
</div>
{salary.payment_date && (
<div className="summary-item">
<label>发放日期:</label>
<span>{formatDate(salary.payment_date)}</span>
</div>
)}
</div>
<div className="salary-details">
<div className="income-section">
<h4>收入明细</h4>
<div className="detail-grid">
<div className="detail-item">
<span>基础工资:</span>
<span>{formatCurrency(salary.income.basic_salary)}</span>
</div>
<div className="detail-item">
<span>绩效工资:</span>
<span>{formatCurrency(salary.income.performance_pay)}</span>
</div>
<div className="detail-item">
<span>加班费:</span>
<span>{formatCurrency(salary.income.overtime_pay)}</span>
</div>
<div className="detail-item">
<span>奖金:</span>
<span>{formatCurrency(salary.income.bonus)}</span>
</div>
<div className="detail-item total">
<span>收入合计:</span>
<span>{formatCurrency(salary.income.total)}</span>
</div>
</div>
</div>
<div className="deductions-section">
<h4>扣除明细</h4>
<div className="detail-grid">
<div className="detail-item">
<span>考勤扣款:</span>
<span>{formatCurrency(salary.deductions.attendance_deduction)}</span>
</div>
<div className="detail-item">
<span>个人保险:</span>
<span>{formatCurrency(salary.deductions.personal_insurance)}</span>
</div>
<div className="detail-item">
<span>个人所得税:</span>
<span>{formatCurrency(salary.deductions.personal_tax)}</span>
</div>
<div className="detail-item">
<span>住房公积金:</span>
<span>{formatCurrency(salary.deductions.housing_fund)}</span>
</div>
<div className="detail-item total">
<span>扣除合计:</span>
<span>{formatCurrency(salary.deductions.total)}</span>
</div>
</div>
</div>
</div>
{salary.remarks && (
<div className="remarks">
<strong>备注:</strong> {salary.remarks}
</div>
)}
</div>
))}
</div>
)}
<style jsx>{`
.salary-management {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.filters {
margin-bottom: 20px;
padding: 15px;
background: #f5f5f5;
border-radius: 8px;
}
.filter-item {
display: flex;
align-items: center;
gap: 10px;
}
.filter-item label {
font-weight: bold;
}
.filter-item input {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
}
.salary-item {
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
background: white;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.salary-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.salary-header h3 {
margin: 0;
color: #333;
}
.status {
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: bold;
}
.status.paid {
background: #d4edda;
color: #155724;
}
.status.pending {
background: #fff3cd;
color: #856404;
}
.salary-summary {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
padding: 15px;
background: #f8f9fa;
border-radius: 6px;
}
.summary-item {
display: flex;
flex-direction: column;
align-items: center;
}
.summary-item .amount {
font-size: 18px;
font-weight: bold;
color: #28a745;
}
.salary-details {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-bottom: 15px;
}
.income-section, .deductions-section {
padding: 15px;
background: #fafafa;
border-radius: 6px;
}
.income-section h4, .deductions-section h4 {
margin: 0 0 10px 0;
color: #495057;
}
.detail-grid {
display: flex;
flex-direction: column;
gap: 8px;
}
.detail-item {
display: flex;
justify-content: space-between;
padding: 5px 0;
}
.detail-item.total {
border-top: 1px solid #dee2e6;
padding-top: 10px;
margin-top: 5px;
font-weight: bold;
}
.remarks {
padding: 10px;
background: #e9ecef;
border-radius: 4px;
font-size: 14px;
}
.loading {
text-align: center;
padding: 40px;
color: #6c757d;
}
`}</style>
</div>
);
};
export default SalaryManagement;Python 薪资管理脚本
python
import requests
import csv
from datetime import datetime
from typing import List, Optional, Dict, Any
class SalaryManager:
def __init__(self, base_url: str, admin_token: str, employee_token: str):
self.base_url = base_url
self.admin_token = admin_token
self.employee_token = employee_token
self.admin_headers = {
'Authorization': f'Bearer {admin_token}',
'Content-Type': 'application/json'
}
self.employee_headers = {
'Authorization': f'Bearer {employee_token}',
'Content-Type': 'application/json'
}
def create_salary(self, salary_data: Dict[str, Any]) -> Dict[str, Any]:
"""创建薪资明细"""
response = requests.post(
f'{self.base_url}/api/v1/admin/salaries',
json=salary_data,
headers=self.admin_headers
)
response.raise_for_status()
return response.json()
def get_salary_list(self, employee_id: Optional[int] = None,
month: Optional[str] = None,
status: Optional[str] = None,
page: int = 1, page_size: int = 20) -> Dict[str, Any]:
"""获取薪资列表"""
params = {
'page': page,
'page_size': page_size
}
if employee_id:
params['employee_id'] = employee_id
if month:
params['month'] = month
if status:
params['status'] = status
response = requests.get(
f'{self.base_url}/api/v1/admin/salaries',
params=params,
headers=self.admin_headers
)
response.raise_for_status()
return response.json()
def update_salary(self, salary_id: int, update_data: Dict[str, Any]) -> Dict[str, Any]:
"""更新薪资明细"""
response = requests.put(
f'{self.base_url}/api/v1/admin/salaries/{salary_id}',
json=update_data,
headers=self.admin_headers
)
response.raise_for_status()
return response.json()
def batch_update_salary_status(self, salary_ids: List[int], status: str) -> Dict[str, Any]:
"""批量更新薪资状态"""
response = requests.patch(
f'{self.base_url}/api/v1/admin/salaries/batch-status',
json={'ids': salary_ids, 'status': status},
headers=self.admin_headers
)
response.raise_for_status()
return response.json()
def get_salary_stats(self, month: Optional[str] = None) -> Dict[str, Any]:
"""获取薪资统计"""
params = {}
if month:
params['month'] = month
response = requests.get(
f'{self.base_url}/api/v1/admin/salaries/stats',
params=params,
headers=self.admin_headers
)
response.raise_for_status()
return response.json()
def get_employee_salary_list(self, month: Optional[str] = None,
start_month: Optional[str] = None,
end_month: Optional[str] = None) -> Dict[str, Any]:
"""员工获取自己的薪资列表"""
params = {}
if month:
params['month'] = month
if start_month:
params['start_month'] = start_month
if end_month:
params['end_month'] = end_month
response = requests.get(
f'{self.base_url}/api/v1/employees/salaries',
params=params,
headers=self.employee_headers
)
response.raise_for_status()
return response.json()
def get_employee_salary_by_month(self, month: str) -> Dict[str, Any]:
"""员工获取某月薪资明细"""
response = requests.get(
f'{self.base_url}/api/v1/employees/salaries/{month}',
headers=self.employee_headers
)
response.raise_for_status()
return response.json()
def export_salaries_to_csv(self, filename: str, month: Optional[str] = None):
"""导出薪资数据到CSV文件"""
all_salaries = []
page = 1
while True:
data = self.get_salary_list(month=month, page=page, page_size=100)
salaries = data['data']['salaries']
if not salaries:
break
all_salaries.extend(salaries)
page += 1
# 写入CSV文件
with open(filename, 'w', newline='', encoding='utf-8') as csvfile:
if not all_salaries:
print("没有找到薪资数据")
return
fieldnames = [
'id', 'employee_name', 'month', 'net_salary', 'status', 'payment_date',
'basic_salary', 'performance_pay', 'overtime_pay', 'bonus',
'personal_insurance', 'personal_tax', 'housing_fund', 'remarks'
]
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
for salary in all_salaries:
writer.writerow({
'id': salary['id'],
'employee_name': salary['employee_name'],
'month': salary['month'],
'net_salary': salary['net_salary'],
'status': salary['status'],
'payment_date': salary.get('payment_date', ''),
'basic_salary': salary['income']['basic_salary'],
'performance_pay': salary['income']['performance_pay'],
'overtime_pay': salary['income']['overtime_pay'],
'bonus': salary['income']['bonus'],
'personal_insurance': salary['deductions']['personal_insurance'],
'personal_tax': salary['deductions']['personal_tax'],
'housing_fund': salary['deductions']['housing_fund'],
'remarks': salary['remarks']
})
print(f"已导出 {len(all_salaries)} 条薪资记录到 {filename}")
def generate_monthly_salary_report(self, month: str) -> str:
"""生成月度薪资报告"""
stats = self.get_salary_stats(month)
salaries_data = self.get_salary_list(month=month, page_size=1000)
report = f"""
# {month} 月度薪资报告
## 统计概览
- 总员工数: {stats['data']['total_employees']}
- 薪资总额: ¥{stats['data']['total_salaries'] / 100:,.2f}
- 平均薪资: ¥{stats['data']['avg_salary']:,.2f}
- 最高薪资: ¥{stats['data']['max_salary']:,.2f}
- 最低薪资: ¥{stats['data']['min_salary']:,.2f}
## 发放状态统计
"""
status_count = {}
for salary in salaries_data['data']['salaries']:
status = salary['status']
status_count[status] = status_count.get(status, 0) + 1
for status, count in status_count.items():
report += f"- {status}: {count} 人\n"
return report
# 使用示例
def main():
# 初始化薪资管理器
salary_manager = SalaryManager(
base_url='http://localhost:8080',
admin_token='your_admin_token',
employee_token='your_employee_token'
)
# 管理员操作
print("=== 管理员操作 ===")
# 创建薪资明细
salary_data = {
'employee_id': 1,
'month': '2024-01',
'basic_salary': 12000.00,
'performance_pay': 5000.00,
'personal_insurance': 1200.00,
'personal_tax': 600.00,
'remarks': '2024年1月薪资'
}
try:
result = salary_manager.create_salary(salary_data)
print(f"创建薪资成功: {result['message']}")
except Exception as e:
print(f"创建薪资失败: {e}")
# 获取薪资统计
try:
stats = salary_manager.get_salary_stats('2024-01')
print(f"2024年1月薪资统计:")
print(f" 总员工数: {stats['data']['total_employees']}")
print(f" 平均薪资: ¥{stats['data']['avg_salary']:,.2f}")
except Exception as e:
print(f"获取统计失败: {e}")
# 员工操作
print("\n=== 员工操作 ===")
# 员工查询自己的薪资
try:
my_salaries = salary_manager.get_employee_salary_list('2024-01')
print(f"我的薪资记录数: {len(my_salaries['data']['salaries'])}")
if my_salaries['data']['salaries']:
salary = my_salaries['data']['salaries'][0]
print(f" 月份: {salary['month']}")
print(f" 实发工资: ¥{salary['net_salary']:,.2f}")
print(f" 状态: {salary['status']}")
except Exception as e:
print(f"查询薪资失败: {e}")
# 导出薪资数据
try:
salary_manager.export_salaries_to_csv('salaries_2024.csv', '2024-01')
print("薪资数据导出完成")
except Exception as e:
print(f"导出失败: {e}")
# 生成月度报告
try:
report = salary_manager.generate_monthly_salary_report('2024-01')
print(report)
except Exception as e:
print(f"生成报告失败: {e}")
if __name__ == '__main__':
main()