feat: add attendance APIs

This commit is contained in:
richjohnrambo
2026-08-31 13:27:12 +08:00
parent 9f647de4d0
commit cb7dc36cad
10 changed files with 1749 additions and 12 deletions
+125 -4
View File
@@ -915,6 +915,9 @@ func (a *adapter) CreateDb(reset bool) error {
if err = createIMOrganizationTables(tx); err != nil {
return err
}
if err = createIMAttendanceTables(tx); err != nil {
return err
}
if err = createIMMeetingTables(tx); err != nil {
return err
}
@@ -1142,10 +1145,11 @@ func createIMWorkbenchTables(tx *sql.Tx) error {
`INSERT INTO im_workbench_apps(code,name,route,sort_order,unread_key,enabled,permission)
VALUES
('organization','组织架构','/organization',10,'organization',1,'enabled'),
('meetings','会议通知','/meetings',20,'meetings',1,'enabled'),
('daily_reports','日报','/reports/daily',30,'daily_reports',1,'enabled'),
('monthly_reports','月报','/reports/monthly',40,'monthly_reports',1,'enabled'),
('executive_mailbox','总经理信箱','/executive-mailbox',50,'executive_mailbox',1,'enabled')
('attendance','打卡','/attendance',20,'attendance',1,'enabled'),
('meetings','会议通知','/meetings',30,'meetings',1,'enabled'),
('daily_reports','日报','/reports/daily',40,'daily_reports',1,'enabled'),
('monthly_reports','月报','/reports/monthly',50,'monthly_reports',1,'enabled'),
('executive_mailbox','总经理信箱','/executive-mailbox',60,'executive_mailbox',1,'enabled')
ON DUPLICATE KEY UPDATE
name=VALUES(name),
route=VALUES(route),
@@ -1231,6 +1235,123 @@ func ensureIMOrganizationVisibilityTables(exec sqlExecutor) error {
return err
}
func createIMAttendanceTables(tx *sql.Tx) error {
statements := []string{
`CREATE TABLE im_attendance_rules(
id BIGINT NOT NULL AUTO_INCREMENT,
rule_id VARCHAR(64) NOT NULL,
name VARCHAR(128) NOT NULL,
workday TINYINT NOT NULL DEFAULT 1,
location_required TINYINT NOT NULL DEFAULT 0,
wifi_required TINYINT NOT NULL DEFAULT 0,
allow_outside_checkin TINYINT NOT NULL DEFAULT 1,
appeal_allowed TINYINT NOT NULL DEFAULT 1,
effective_from DATE,
effective_to DATE,
state VARCHAR(16) NOT NULL DEFAULT 'active',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY(id),
UNIQUE INDEX uk_im_attendance_rules_rule(rule_id),
INDEX idx_im_attendance_rules_state(state,effective_from,effective_to)
)`,
`CREATE TABLE im_attendance_shifts(
id BIGINT NOT NULL AUTO_INCREMENT,
shift_id VARCHAR(64) NOT NULL,
rule_id VARCHAR(64) NOT NULL,
name VARCHAR(128) NOT NULL,
work_start TIME NOT NULL,
work_end TIME NOT NULL,
checkin_start TIME NOT NULL,
checkout_end TIME NOT NULL,
state VARCHAR(16) NOT NULL DEFAULT 'active',
sort_order INT NOT NULL DEFAULT 0,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY(id),
UNIQUE INDEX uk_im_attendance_shifts_shift(shift_id),
INDEX idx_im_attendance_shifts_rule(rule_id,state,sort_order)
)`,
`CREATE TABLE im_attendance_records(
id BIGINT NOT NULL AUTO_INCREMENT,
record_id VARCHAR(64) NOT NULL,
user_id BIGINT NOT NULL,
record_date DATE NOT NULL,
rule_id VARCHAR(64) NOT NULL,
shift_id VARCHAR(64) NOT NULL,
status VARCHAR(32) NOT NULL DEFAULT 'not_checked',
exceptions JSON,
appeal_status VARCHAR(32) NOT NULL DEFAULT '',
version INT NOT NULL DEFAULT 1,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY(id),
UNIQUE INDEX uk_im_attendance_records_record(record_id),
UNIQUE INDEX uk_im_attendance_records_user_date_shift(user_id,record_date,shift_id),
INDEX idx_im_attendance_records_user_id(user_id,id DESC),
INDEX idx_im_attendance_records_date_status(record_date,status,id DESC)
)`,
`CREATE TABLE im_attendance_checkins(
id BIGINT NOT NULL AUTO_INCREMENT,
checkin_id VARCHAR(64) NOT NULL,
record_id VARCHAR(64) NOT NULL,
user_id BIGINT NOT NULL,
record_date DATE NOT NULL,
action VARCHAR(32) NOT NULL,
rule_id VARCHAR(64) NOT NULL,
shift_id VARCHAR(64) NOT NULL,
client_time DATETIME(3) NOT NULL,
server_time DATETIME(3) NOT NULL,
result VARCHAR(32) NOT NULL,
location JSON,
wifi JSON,
device JSON,
idempotency_key VARCHAR(128) NOT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY(id),
UNIQUE INDEX uk_im_attendance_checkins_checkin(checkin_id),
UNIQUE INDEX uk_im_attendance_checkins_idempotency(user_id,idempotency_key),
INDEX idx_im_attendance_checkins_record(record_id,id)
)`,
`CREATE TABLE im_attendance_appeals(
id BIGINT NOT NULL AUTO_INCREMENT,
appeal_id VARCHAR(64) NOT NULL,
record_id VARCHAR(64) NOT NULL,
user_id BIGINT NOT NULL,
appeal_type VARCHAR(64) NOT NULL,
reason VARCHAR(512) NOT NULL,
target_action VARCHAR(32) NOT NULL DEFAULT '',
target_time DATETIME(3),
attachment_file_ids JSON,
status VARCHAR(32) NOT NULL DEFAULT 'pending',
version INT NOT NULL DEFAULT 1,
idempotency_key VARCHAR(128) NOT NULL,
submitted_at DATETIME(3) NOT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY(id),
UNIQUE INDEX uk_im_attendance_appeals_appeal(appeal_id),
UNIQUE INDEX uk_im_attendance_appeals_idempotency(user_id,idempotency_key),
INDEX idx_im_attendance_appeals_record(record_id),
INDEX idx_im_attendance_appeals_user_status(user_id,status,id DESC)
)`,
}
for _, stmt := range statements {
if _, err := tx.Exec(stmt); err != nil {
return err
}
}
if _, err := tx.Exec(`INSERT INTO im_attendance_rules(rule_id,name,workday,location_required,wifi_required,allow_outside_checkin,appeal_allowed)
VALUES('att_rule_default','默认考勤规则',1,0,0,1,1)
ON DUPLICATE KEY UPDATE updated_at=updated_at`); err != nil {
return err
}
_, err := tx.Exec(`INSERT INTO im_attendance_shifts(shift_id,rule_id,name,work_start,work_end,checkin_start,checkout_end,sort_order)
VALUES('shift_default','att_rule_default','默认班次','09:00:00','18:00:00','08:30:00','20:00:00',10)
ON DUPLICATE KEY UPDATE updated_at=updated_at`)
return err
}
func createIMMeetingTables(tx *sql.Tx) error {
statements := []string{
`CREATE TABLE im_meetings(