Skip to content

薪资管理 API

薪资管理模块提供完整的员工薪资核算、发放、查询和统计功能。管理员可以进行薪资的创建、更新、发放和统计分析,员工可以查询自己的薪资明细。

功能概述

  • 薪资核算:支持多维度薪资计算,包括基础工资、绩效工资、津贴、奖金等
  • 自动计算:自动计算实发工资,支持各项扣款
  • 状态管理:支持薪资状态管理(未确认/已发放)
  • 统计分析:提供薪资统计分析功能
  • 权限控制:管理员管理所有员工薪资,员工仅查看自己薪资

管理员接口

创建薪资明细

为员工创建指定月份的薪资明细记录。

  • URL: /api/v1/admin/salaries
  • 方法: POST
  • 内容类型: application/json
  • 认证: 需要管理员权限

请求参数

字段类型必填描述示例
employee_iduint员工ID1
monthstring薪资月份(格式: 2024-01)"2024-01"
statusstring薪资状态(0-未确认, 1-已发放)"0"
payment_datestring发放日期(格式: 2024-02-05)"2024-02-05"
category_iduint薪资分类ID1
basic_salaryfloat64基础工资(元)12000.00
seniority_payfloat64工龄工资(元)800.00
performance_payfloat64绩效工资(元)5000.00
overtime_payfloat64加班费(元)500.00
bonusfloat64奖金(元)1000.00
allowancefloat64津贴(元)300.00
attendance_deductionfloat64考勤扣款(元)200.00
personal_insurancefloat64个人保险(元)1200.00
personal_taxfloat64个人所得税(元)600.00
housing_fundfloat64住房公积金(元)800.00
other_deductionsfloat64其他扣除(元)100.00
remarksstring备注"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
  • 认证: 需要管理员权限

请求参数

参数类型必填描述示例
iduint薪资明细ID1

响应示例

成功响应 (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
  • 认证: 需要管理员权限

请求参数

路径参数

参数类型必填描述示例
iduint薪资明细ID1

请求体参数

字段类型必填描述示例
statusstring薪资状态(0-未确认, 1-已发放)"1"
payment_datestring发放日期"2024-02-05"
basic_salaryfloat64基础工资(元)12500.00
remarksstring备注"调整后的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
  • 认证: 需要管理员权限

请求参数

参数类型必填描述示例
iduint薪资明细ID1

响应示例

成功响应 (200)

json
{
  "code": 200,
  "message": "薪资明细删除成功",
  "data": null,
  "timestamp": "2024-01-01T12:00:00Z"
}

获取薪资明细列表

获取薪资明细列表,支持多种过滤条件和分页。

  • URL: /api/v1/admin/salaries
  • 方法: GET
  • 认证: 需要管理员权限

请求参数

参数类型必填描述示例
pageint页码,默认11
page_sizeint每页数量,默认1020
employee_iduint员工ID1
monthstring薪资月份(格式: 2024-01)"2024-01"
statusstring薪资状态"已确认"
start_monthstring开始月份"2024-01"
end_monthstring结束月份"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
  • 认证: 需要管理员权限

请求参数

字段类型必填描述示例
idsuint[]薪资明细ID列表[1, 2, 3]
statusstring薪资状态(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
  • 认证: 需要管理员权限

请求参数

参数类型必填描述示例
monthstring薪资月份(格式: 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
  • 认证: 需要员工权限

请求参数

参数类型必填描述示例
pageint页码,默认11
page_sizeint每页数量,默认1010
employee_iduint员工ID(管理员接口)1
monthstring薪资月份(格式: 2024-01)"2024-01"
statusstring薪资状态"已确认"
start_monthstring开始月份"2024-01"
end_monthstring结束月份"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
  • 认证: 需要员工权限

请求参数

参数类型必填描述示例
monthstring薪资月份(格式: 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()

相关链接

基于 MIT 许可发布