241 lines
6.9 KiB
C++
241 lines
6.9 KiB
C++
|
|
#ifndef ATTENDANCE_SYSTEM_APP_HPP
|
|||
|
|
#define ATTENDANCE_SYSTEM_APP_HPP
|
|||
|
|
|
|||
|
|
#include <string>
|
|||
|
|
#include <vector>
|
|||
|
|
#include <map>
|
|||
|
|
#include <ctime>
|
|||
|
|
#include <memory>
|
|||
|
|
#include <functional>
|
|||
|
|
#include <algorithm>
|
|||
|
|
#include <sstream>
|
|||
|
|
#include <iomanip>
|
|||
|
|
#include <stdexcept>
|
|||
|
|
#include <cassert>
|
|||
|
|
|
|||
|
|
// ============================================================
|
|||
|
|
// 常量与枚举
|
|||
|
|
// ============================================================
|
|||
|
|
|
|||
|
|
/// 打卡类型
|
|||
|
|
enum class CheckType {
|
|||
|
|
Unknown,
|
|||
|
|
ClockIn, // 上班打卡
|
|||
|
|
ClockOut // 下班打卡
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/// 异常标记
|
|||
|
|
enum class AbnormalFlag {
|
|||
|
|
None,
|
|||
|
|
Late, // 迟到
|
|||
|
|
EarlyLeave, // 早退
|
|||
|
|
Absenteeism // 旷工
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/// 报表输出格式
|
|||
|
|
enum class ReportFormat {
|
|||
|
|
Excel,
|
|||
|
|
PDF
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/// 预警发送状态
|
|||
|
|
enum class AlertStatus {
|
|||
|
|
Pending,
|
|||
|
|
Sent,
|
|||
|
|
Failed,
|
|||
|
|
Read
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/// 角色类型(RBAC)
|
|||
|
|
enum class RoleType {
|
|||
|
|
Employee,
|
|||
|
|
DeptManager,
|
|||
|
|
HRAdmin,
|
|||
|
|
SystemAdmin
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// ============================================================
|
|||
|
|
// 数据结构定义
|
|||
|
|
// ============================================================
|
|||
|
|
|
|||
|
|
/// 员工信息
|
|||
|
|
struct Employee {
|
|||
|
|
std::string employeeId;
|
|||
|
|
std::string name;
|
|||
|
|
std::string department;
|
|||
|
|
std::string position;
|
|||
|
|
std::string shiftId;
|
|||
|
|
std::string phone;
|
|||
|
|
RoleType role{RoleType::Employee};
|
|||
|
|
|
|||
|
|
// 脱敏手机号显示(后4位明文)
|
|||
|
|
std::string maskedPhone() const {
|
|||
|
|
if (phone.length() != 11) return "***";
|
|||
|
|
return "*******" + phone.substr(7);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/// 排班规则
|
|||
|
|
struct ShiftRule {
|
|||
|
|
std::string shiftId;
|
|||
|
|
std::string shiftName;
|
|||
|
|
int startHour{9}; // 上班小时
|
|||
|
|
int startMin{0}; // 上班分钟
|
|||
|
|
int endHour{18}; // 下班小时
|
|||
|
|
int endMin{0}; // 下班分钟
|
|||
|
|
int flexWindow{30}; // 弹性时间窗口(分钟)
|
|||
|
|
int graceLate{15}; // 迟到宽限分钟
|
|||
|
|
|
|||
|
|
/// 获取上班时间(分钟偏移量)
|
|||
|
|
int workStartMinutes() const { return startHour * 60 + startMin; }
|
|||
|
|
|
|||
|
|
/// 获取下班时间(分钟偏移量)
|
|||
|
|
int workEndMinutes() const { return endHour * 60 + endMin; }
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/// 打卡流水记录
|
|||
|
|
struct CheckInRecord {
|
|||
|
|
std::string recordId;
|
|||
|
|
std::string employeeId;
|
|||
|
|
std::time_t timestamp{0};
|
|||
|
|
CheckType type{CheckType::Unknown};
|
|||
|
|
std::string deviceId;
|
|||
|
|
std::string latitude;
|
|||
|
|
std::string longitude;
|
|||
|
|
bool verified{false};
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/// 考勤核算结果(按天)
|
|||
|
|
struct DailyAttendance {
|
|||
|
|
std::string dateStr;
|
|||
|
|
std::string employeeId;
|
|||
|
|
int expectedMinutes{0}; // 应出勤分钟
|
|||
|
|
int actualMinutes{0}; // 实际出勤分钟
|
|||
|
|
int overtimeMinutes{0}; // 加班分钟
|
|||
|
|
int leaveDeduction{0}; // 调休抵扣分钟
|
|||
|
|
AbnormalFlag abnormal{AbnormalFlag::None};
|
|||
|
|
|
|||
|
|
/// 有效出勤率(%)
|
|||
|
|
double attendanceRate() const {
|
|||
|
|
if (expectedMinutes == 0) return 100.0;
|
|||
|
|
return 100.0 * actualMinutes / expectedMinutes;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 是否异常
|
|||
|
|
bool hasAbnormal() const { return abnormal != AbnormalFlag::None; }
|
|||
|
|
|
|||
|
|
std::string abnormalStr() const {
|
|||
|
|
switch (abnormal) {
|
|||
|
|
case AbnormalFlag::Late: return "迟到";
|
|||
|
|
case AbnormalFlag::EarlyLeave: return "早退";
|
|||
|
|
case AbnormalFlag::Absenteeism: return "旷工";
|
|||
|
|
default: return "正常";
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/// 异常预警日志
|
|||
|
|
struct AlertLog {
|
|||
|
|
std::string alertId;
|
|||
|
|
std::string employeeId;
|
|||
|
|
AbnormalFlag type{AbnormalFlag::None};
|
|||
|
|
std::time_t occurTime{0};
|
|||
|
|
std::string channel; // 通知渠道:站内信/短信/企业微信
|
|||
|
|
AlertStatus status{AlertStatus::Pending};
|
|||
|
|
int retryCount{0};
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/// 报表任务
|
|||
|
|
struct ReportTask {
|
|||
|
|
std::string taskId;
|
|||
|
|
std::string requester;
|
|||
|
|
std::string deptId;
|
|||
|
|
std::string dateRangeBegin;
|
|||
|
|
std::string dateRangeEnd;
|
|||
|
|
ReportFormat format{ReportFormat::Excel};
|
|||
|
|
std::string status{"Pending"}; // Pending / Running / Done / Failed
|
|||
|
|
std::string downloadUrl;
|
|||
|
|
std::time_t createTime{0};
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/// 权限配置(RBAC)
|
|||
|
|
struct Permission {
|
|||
|
|
std::string resource; // 资源路径,如 "/api/v1/reports"
|
|||
|
|
std::string action; // 操作:READ / WRITE / DELETE
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// ============================================================
|
|||
|
|
// 业务逻辑类
|
|||
|
|
// ============================================================
|
|||
|
|
|
|||
|
|
class AttendanceService {
|
|||
|
|
public:
|
|||
|
|
AttendanceService();
|
|||
|
|
|
|||
|
|
// ---- 员工管理 ----
|
|||
|
|
void addEmployee(const Employee& emp);
|
|||
|
|
const Employee* findEmployee(const std::string& id) const;
|
|||
|
|
std::vector<Employee> getEmployeesByDept(const std::string& dept) const;
|
|||
|
|
|
|||
|
|
// ---- 排班管理 ----
|
|||
|
|
void addShiftRule(const ShiftRule& rule);
|
|||
|
|
const ShiftRule* findShift(const std::string& shiftId) const;
|
|||
|
|
|
|||
|
|
// ---- 打卡处理 ----
|
|||
|
|
CheckInRecord processCheckIn(const std::string& employeeId,
|
|||
|
|
CheckType type,
|
|||
|
|
const std::string& deviceId,
|
|||
|
|
std::time_t timestamp);
|
|||
|
|
|
|||
|
|
std::vector<CheckInRecord> getRecords(const std::string& employeeId) const;
|
|||
|
|
|
|||
|
|
// ---- 考勤核算 ----
|
|||
|
|
DailyAttendance calculateDaily(const std::string& employeeId,
|
|||
|
|
const std::string& dateStr);
|
|||
|
|
|
|||
|
|
std::vector<DailyAttendance> generateReport(const std::string& deptId,
|
|||
|
|
const std::string& dateBegin,
|
|||
|
|
const std::string& dateEnd);
|
|||
|
|
|
|||
|
|
// ---- 异常预警 ----
|
|||
|
|
AlertLog createAlert(const std::string& employeeId, AbnormalFlag type);
|
|||
|
|
std::vector<AlertLog> getAlerts(const std::string& employeeId) const;
|
|||
|
|
|
|||
|
|
// ---- 报表任务 ----
|
|||
|
|
ReportTask createReportTask(const std::string& requester,
|
|||
|
|
const std::string& deptId,
|
|||
|
|
const std::string& dateBegin,
|
|||
|
|
const std::string& dateEnd,
|
|||
|
|
ReportFormat fmt);
|
|||
|
|
void completeTask(const std::string& taskId, const std::string& url);
|
|||
|
|
|
|||
|
|
// ---- 工具 ----
|
|||
|
|
static std::string timeToString(std::time_t t);
|
|||
|
|
static std::string currentDateStr();
|
|||
|
|
static std::time_t parseDate(const std::string& dateStr);
|
|||
|
|
|
|||
|
|
private:
|
|||
|
|
std::map<std::string, Employee> employees_;
|
|||
|
|
std::map<std::string, ShiftRule> shifts_;
|
|||
|
|
std::vector<CheckInRecord> records_;
|
|||
|
|
std::vector<AlertLog> alerts_;
|
|||
|
|
std::vector<ReportTask> tasks_;
|
|||
|
|
int idCounter_{0};
|
|||
|
|
|
|||
|
|
std::string nextId();
|
|||
|
|
int minutesFromMidnight(std::time_t t) const;
|
|||
|
|
bool isSameDate(std::time_t t, const std::string& dateStr) const;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// ============================================================
|
|||
|
|
// 工具函数声明
|
|||
|
|
// ============================================================
|
|||
|
|
|
|||
|
|
/// 脱敏工具:将敏感字符串中间部分替换为 '*'
|
|||
|
|
std::string maskSensitive(const std::string& input, size_t visibleHead = 2, size_t visibleTail = 2);
|
|||
|
|
|
|||
|
|
/// 格式化输出一行分隔线
|
|||
|
|
void printSeparator(char ch = '=', size_t count = 60);
|
|||
|
|
|
|||
|
|
#endif // ATTENDANCE_SYSTEM_APP_HPP
|