648 lines
19 KiB
Go
648 lines
19 KiB
Go
package controllers
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"server/models"
|
|
|
|
"github.com/beego/beego/v2/client/orm"
|
|
)
|
|
|
|
const scBoardMaxDays = 62
|
|
|
|
// scEntryView 看板单元格里的排班信息。
|
|
type scEntryView struct {
|
|
ID uint64 `json:"id"`
|
|
PlanID uint64 `json:"plan_id"`
|
|
ShiftID uint64 `json:"shift_id"`
|
|
ShiftName string `json:"shift_name"`
|
|
ShortName string `json:"short_name"`
|
|
Color string `json:"color"`
|
|
WorkStart string `json:"work_start"`
|
|
WorkEnd string `json:"work_end"`
|
|
Source int8 `json:"source"`
|
|
Note string `json:"note"`
|
|
}
|
|
|
|
type scBoardRow struct {
|
|
ID uint `json:"id"`
|
|
Name string `json:"name"`
|
|
Account string `json:"account"`
|
|
OrgID uint64 `json:"org_id"`
|
|
OrgName string `json:"org_name"`
|
|
Position string `json:"position"`
|
|
Days map[string]scEntryView `json:"days"`
|
|
}
|
|
|
|
type scDateCell struct {
|
|
Date string `json:"date"`
|
|
Weekday int `json:"weekday"`
|
|
IsToday bool `json:"is_today"`
|
|
}
|
|
|
|
func (c *BackendOaSchedulingController) scEntryQuery(tid uint64) orm.QuerySeter {
|
|
return models.Orm.QueryTable(new(models.OaScheduleEntry)).
|
|
Filter("tid", tid).
|
|
Filter("is_deleted", 0)
|
|
}
|
|
|
|
// Board 排班看板:按员工 × 日期返回区间内的排班矩阵。
|
|
// GET /backend/oa/scheduling/board?start_date=&end_date=&org_id=&include_child=1&keyword=
|
|
func (c *BackendOaSchedulingController) Board() {
|
|
tid, _, _, ok := c.scOperator()
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
startDate := strings.TrimSpace(c.GetString("start_date"))
|
|
endDate := strings.TrimSpace(c.GetString("end_date"))
|
|
if startDate == "" || endDate == "" {
|
|
// 默认展示本周(周一至周日)
|
|
now := time.Now()
|
|
weekday := int(now.Weekday())
|
|
if weekday == 0 {
|
|
weekday = 7
|
|
}
|
|
monday := now.AddDate(0, 0, -(weekday - 1))
|
|
startDate = monday.Format("2006-01-02")
|
|
endDate = monday.AddDate(0, 0, 6).Format("2006-01-02")
|
|
}
|
|
dates, msg := scDateSpan(startDate, endDate)
|
|
if msg != "" {
|
|
c.scFail(400, 400, msg)
|
|
return
|
|
}
|
|
if len(dates) > scBoardMaxDays {
|
|
c.scFail(400, 400, fmt.Sprintf("看板一次最多展示 %d 天", scBoardMaxDays))
|
|
return
|
|
}
|
|
|
|
orgID := uint64(0)
|
|
if raw := strings.TrimSpace(c.GetString("org_id")); raw != "" {
|
|
if parsed, err := strconvParseUint(raw); err == nil {
|
|
orgID = parsed
|
|
}
|
|
}
|
|
includeChild := c.GetString("include_child") != "0"
|
|
keyword := strings.TrimSpace(c.GetString("keyword"))
|
|
onlyScheduled := c.GetString("only_scheduled") == "1"
|
|
shiftFilter := uint64(0)
|
|
if raw := strings.TrimSpace(c.GetString("shift_id")); raw != "" {
|
|
if parsed, err := strconvParseUint(raw); err == nil {
|
|
shiftFilter = parsed
|
|
}
|
|
}
|
|
|
|
employees, err := c.scEmployeesByOrg(tid, orgID, includeChild)
|
|
if err != nil {
|
|
c.scFail(500, 500, "查询员工失败: "+err.Error())
|
|
return
|
|
}
|
|
|
|
index := c.scLoadOrgIndex(tid)
|
|
rows := make([]scBoardRow, 0, len(employees))
|
|
employeeIDs := make([]uint64, 0, len(employees))
|
|
for _, employee := range employees {
|
|
if keyword != "" && !strings.Contains(employee.Name, keyword) && !strings.Contains(employee.Account, keyword) {
|
|
continue
|
|
}
|
|
orgID := scEmployeeDepartmentID(employee)
|
|
position := ""
|
|
if employee.Position != nil {
|
|
position = *employee.Position
|
|
}
|
|
rows = append(rows, scBoardRow{
|
|
ID: employee.ID,
|
|
Name: employee.Name,
|
|
Account: employee.Account,
|
|
OrgID: orgID,
|
|
OrgName: index.name(orgID),
|
|
Position: position,
|
|
Days: map[string]scEntryView{},
|
|
})
|
|
employeeIDs = append(employeeIDs, uint64(employee.ID))
|
|
}
|
|
|
|
truncated := false
|
|
const boardMaxEmployees = 500
|
|
if len(rows) > boardMaxEmployees {
|
|
rows = rows[:boardMaxEmployees]
|
|
employeeIDs = employeeIDs[:boardMaxEmployees]
|
|
truncated = true
|
|
}
|
|
|
|
if len(employeeIDs) > 0 {
|
|
var entries []models.OaScheduleEntry
|
|
_, err = c.scEntryQuery(tid).
|
|
Filter("employee_id__in", employeeIDs).
|
|
Filter("schedule_date__gte", dates[0]).
|
|
Filter("schedule_date__lte", dates[len(dates)-1]).
|
|
All(&entries)
|
|
if err != nil && err != orm.ErrNoRows {
|
|
c.scFail(500, 500, "查询排班失败: "+err.Error())
|
|
return
|
|
}
|
|
|
|
rowIndex := map[uint]*scBoardRow{}
|
|
for i := range rows {
|
|
rowIndex[rows[i].ID] = &rows[i]
|
|
}
|
|
for _, entry := range entries {
|
|
if shiftFilter > 0 && entry.ShiftID != shiftFilter {
|
|
continue
|
|
}
|
|
row, hit := rowIndex[entry.EmployeeID]
|
|
if !hit {
|
|
continue
|
|
}
|
|
row.Days[normalizeOaDate(entry.ScheduleDate)] = scEntryView{
|
|
ID: entry.ID,
|
|
PlanID: entry.PlanID,
|
|
ShiftID: entry.ShiftID,
|
|
ShiftName: entry.ShiftName,
|
|
ShortName: entry.ShortName,
|
|
Color: entry.Color,
|
|
WorkStart: entry.WorkStart,
|
|
WorkEnd: entry.WorkEnd,
|
|
Source: entry.Source,
|
|
Note: entry.Note,
|
|
}
|
|
}
|
|
}
|
|
|
|
if onlyScheduled {
|
|
filtered := rows[:0]
|
|
for _, row := range rows {
|
|
if len(row.Days) > 0 {
|
|
filtered = append(filtered, row)
|
|
}
|
|
}
|
|
rows = filtered
|
|
}
|
|
|
|
today := time.Now().Format("2006-01-02")
|
|
dateCells := make([]scDateCell, 0, len(dates))
|
|
for _, date := range dates {
|
|
parsed, _ := scParseDate(date)
|
|
weekday := int(parsed.Weekday())
|
|
if weekday == 0 {
|
|
weekday = 7
|
|
}
|
|
dateCells = append(dateCells, scDateCell{Date: date, Weekday: weekday, IsToday: date == today})
|
|
}
|
|
|
|
// 汇总:排班人次、临时调配数、未排班人数、今日在岗、班次分布
|
|
entryCount, tempCount, unassigned := 0, 0, 0
|
|
todayOnDuty := 0
|
|
shiftCounter := map[string]int{}
|
|
shiftColor := map[string]string{}
|
|
for _, row := range rows {
|
|
if len(row.Days) == 0 {
|
|
unassigned++
|
|
}
|
|
for date, entry := range row.Days {
|
|
entryCount++
|
|
if entry.Source == models.OaEntrySourceTemp {
|
|
tempCount++
|
|
}
|
|
if date == today {
|
|
todayOnDuty++
|
|
}
|
|
name := entry.ShiftName
|
|
if name == "" {
|
|
name = "未命名班次"
|
|
}
|
|
shiftCounter[name]++
|
|
shiftColor[name] = entry.Color
|
|
}
|
|
}
|
|
shiftDist := make([]map[string]interface{}, 0, len(shiftCounter))
|
|
for name, count := range shiftCounter {
|
|
shiftDist = append(shiftDist, map[string]interface{}{
|
|
"name": name,
|
|
"count": count,
|
|
"color": shiftColor[name],
|
|
})
|
|
}
|
|
sort.Slice(shiftDist, func(i, j int) bool {
|
|
return shiftDist[i]["count"].(int) > shiftDist[j]["count"].(int)
|
|
})
|
|
|
|
var shifts []models.OaShift
|
|
_, _ = c.shiftQuery(tid).Filter("status", 1).OrderBy("sort", "id").All(&shifts)
|
|
if shifts == nil {
|
|
shifts = []models.OaShift{}
|
|
}
|
|
|
|
c.scOK(map[string]interface{}{
|
|
"start_date": startDate,
|
|
"end_date": endDate,
|
|
"dates": dateCells,
|
|
"employees": rows,
|
|
"shifts": shifts,
|
|
"truncated": truncated,
|
|
"summary": map[string]interface{}{
|
|
"employee_count": len(rows),
|
|
"entry_count": entryCount,
|
|
"temp_count": tempCount,
|
|
"unassigned": unassigned,
|
|
"today_on_duty": todayOnDuty,
|
|
"shift_dist": shiftDist,
|
|
},
|
|
})
|
|
}
|
|
|
|
func strconvParseUint(value string) (uint64, error) {
|
|
return strconv.ParseUint(strings.TrimSpace(value), 10, 64)
|
|
}
|
|
|
|
// scEntryTargets 解析临时排班/批量清除的目标员工与日期。
|
|
func (c *BackendOaSchedulingController) scEntryTargets(tid uint64, body map[string]interface{}) ([]models.BackendEmployee, []string, string) {
|
|
employeeIDs := scUintSlice(body, "employee_ids")
|
|
if len(employeeIDs) == 0 {
|
|
return nil, nil, "请选择员工"
|
|
}
|
|
if len(employeeIDs) > scPlanMaxEmployees {
|
|
return nil, nil, fmt.Sprintf("单次最多操作 %d 名员工", scPlanMaxEmployees)
|
|
}
|
|
|
|
startDate := scStringValue(body, "start_date", "schedule_date", "date")
|
|
endDate := scStringValue(body, "end_date")
|
|
if endDate == "" {
|
|
endDate = startDate
|
|
}
|
|
dates, msg := scDateSpan(startDate, endDate)
|
|
if msg != "" {
|
|
return nil, nil, msg
|
|
}
|
|
workdays := scWorkdays(dates, scWeekdaySet(body))
|
|
if len(workdays) == 0 {
|
|
return nil, nil, "所选日期区间内没有符合条件的日期"
|
|
}
|
|
|
|
var employees []models.BackendEmployee
|
|
_, err := c.scBaseEmployeeQuery(tid).Filter("id__in", employeeIDs).All(&employees)
|
|
if err != nil && err != orm.ErrNoRows {
|
|
return nil, nil, "查询员工失败: " + err.Error()
|
|
}
|
|
if len(employees) == 0 {
|
|
return nil, nil, "员工不存在或已离职"
|
|
}
|
|
if len(employees)*len(workdays) > scPlanMaxEntries {
|
|
return nil, nil, fmt.Sprintf("本次将产生 %d 条排班,超过单次 %d 条上限", len(employees)*len(workdays), scPlanMaxEntries)
|
|
}
|
|
return employees, workdays, ""
|
|
}
|
|
|
|
// SaveEntries 临时排班:给一个或多个员工在指定日期/星期安排班次。
|
|
// shift_id=0 时表示清除这些日期的排班。
|
|
// POST /backend/oa/scheduling/entries/save
|
|
func (c *BackendOaSchedulingController) SaveEntries() {
|
|
tid, uid, username, ok := c.scOperator()
|
|
if !ok {
|
|
return
|
|
}
|
|
body := c.scBody()
|
|
employees, workdays, msg := c.scEntryTargets(tid, body)
|
|
if msg != "" {
|
|
c.scFail(400, 400, msg)
|
|
return
|
|
}
|
|
|
|
shiftID := scUintValue(body, "shift_id")
|
|
var shift *models.OaShift
|
|
if shiftID > 0 {
|
|
loaded, found := c.scLoadShift(tid, shiftID)
|
|
if !found {
|
|
c.scFail(400, 400, "班次不存在或已停用")
|
|
return
|
|
}
|
|
shift = loaded
|
|
}
|
|
|
|
note := scStringValue(body, "note")
|
|
overwrite := true
|
|
if _, has := body["overwrite"]; has {
|
|
overwrite = scBoolValue(body, "overwrite")
|
|
}
|
|
|
|
empIDs := make([]uint64, 0, len(employees))
|
|
for _, employee := range employees {
|
|
empIDs = append(empIDs, uint64(employee.ID))
|
|
}
|
|
var existing []models.OaScheduleEntry
|
|
_, err := c.scEntryQuery(tid).
|
|
Filter("employee_id__in", empIDs).
|
|
Filter("schedule_date__gte", workdays[0]).
|
|
Filter("schedule_date__lte", workdays[len(workdays)-1]).
|
|
All(&existing)
|
|
if err != nil && err != orm.ErrNoRows {
|
|
c.scFail(500, 500, "查询已有排班失败: "+err.Error())
|
|
return
|
|
}
|
|
existingMap := map[string]*models.OaScheduleEntry{}
|
|
for i := range existing {
|
|
key := fmt.Sprintf("%d|%s", existing[i].EmployeeID, normalizeOaDate(existing[i].ScheduleDate))
|
|
existingMap[key] = &existing[i]
|
|
}
|
|
|
|
index := c.scLoadOrgIndex(tid)
|
|
stats := map[string]int{"created": 0, "updated": 0, "cleared": 0, "skipped": 0}
|
|
now := time.Now()
|
|
|
|
for _, employee := range employees {
|
|
orgID := scEmployeeDepartmentID(employee)
|
|
position := ""
|
|
if employee.Position != nil {
|
|
position = *employee.Position
|
|
}
|
|
for _, date := range workdays {
|
|
key := fmt.Sprintf("%d|%s", employee.ID, date)
|
|
row := existingMap[key]
|
|
|
|
// shift_id=0:清除排班
|
|
if shift == nil {
|
|
if row == nil {
|
|
stats["skipped"]++
|
|
continue
|
|
}
|
|
oldShiftID, oldShiftName := row.ShiftID, row.ShiftName
|
|
row.IsDeleted = row.ID
|
|
row.DeleteTime = &now
|
|
if _, updateErr := models.Orm.Update(row, "is_deleted", "delete_time"); updateErr != nil {
|
|
c.scFail(500, 500, "清除排班失败: "+updateErr.Error())
|
|
return
|
|
}
|
|
c.scWriteChange(models.Orm, tid, row.ID, row.EmployeeID, row.EmployeeName, date,
|
|
models.OaChangeClear, oldShiftID, oldShiftName, 0, "", note, uid, username)
|
|
delete(existingMap, key)
|
|
stats["cleared"]++
|
|
continue
|
|
}
|
|
|
|
if row != nil {
|
|
if !overwrite {
|
|
stats["skipped"]++
|
|
continue
|
|
}
|
|
oldShiftID, oldShiftName := row.ShiftID, row.ShiftName
|
|
row.ShiftID = shift.ID
|
|
row.ShiftName = shift.Name
|
|
row.ShortName = shift.ShortName
|
|
row.Color = shift.Color
|
|
row.WorkStart = shift.WorkStart
|
|
row.WorkEnd = shift.WorkEnd
|
|
row.Source = models.OaEntrySourceTemp
|
|
row.Note = note
|
|
row.CreatedBy = uid
|
|
row.CreatedByName = username
|
|
if _, updateErr := models.Orm.Update(row); updateErr != nil {
|
|
c.scFail(500, 500, "调整排班失败: "+updateErr.Error())
|
|
return
|
|
}
|
|
c.scWriteChange(models.Orm, tid, row.ID, row.EmployeeID, row.EmployeeName, date,
|
|
models.OaChangeShift, oldShiftID, oldShiftName, shift.ID, shift.Name, note, uid, username)
|
|
stats["updated"]++
|
|
continue
|
|
}
|
|
|
|
entry := &models.OaScheduleEntry{
|
|
Tid: int(tid),
|
|
EmployeeID: employee.ID,
|
|
EmployeeName: employee.Name,
|
|
EmployeeAccount: employee.Account,
|
|
OrgID: orgID,
|
|
OrgName: index.name(orgID),
|
|
Position: position,
|
|
ScheduleDate: date,
|
|
ShiftID: shift.ID,
|
|
ShiftName: shift.Name,
|
|
ShortName: shift.ShortName,
|
|
Color: shift.Color,
|
|
WorkStart: shift.WorkStart,
|
|
WorkEnd: shift.WorkEnd,
|
|
Source: models.OaEntrySourceTemp,
|
|
Note: note,
|
|
CreatedBy: uid,
|
|
CreatedByName: username,
|
|
}
|
|
if _, insertErr := models.Orm.Insert(entry); insertErr != nil {
|
|
c.scFail(500, 500, "新增排班失败: "+insertErr.Error())
|
|
return
|
|
}
|
|
existingMap[key] = entry
|
|
c.scWriteChange(models.Orm, tid, entry.ID, entry.EmployeeID, entry.EmployeeName, date,
|
|
models.OaChangeManual, 0, "", shift.ID, shift.Name, note, uid, username)
|
|
stats["created"]++
|
|
}
|
|
}
|
|
|
|
stats["entry_count"] = stats["created"] + stats["updated"] + stats["cleared"]
|
|
c.scOK(stats)
|
|
}
|
|
|
|
// SwapEntry 单条调班:把某个员工某天的班次换成另一个班次。
|
|
// POST /backend/oa/scheduling/entries/swap/:id
|
|
func (c *BackendOaSchedulingController) SwapEntry() {
|
|
tid, uid, username, ok := c.scOperator()
|
|
if !ok {
|
|
return
|
|
}
|
|
id, valid := c.scPathID(":id")
|
|
if !valid {
|
|
c.scFail(400, 400, "无效的排班ID")
|
|
return
|
|
}
|
|
body := c.scBody()
|
|
shiftID := scUintValue(body, "shift_id")
|
|
if shiftID == 0 {
|
|
c.scFail(400, 400, "请选择调换后的班次")
|
|
return
|
|
}
|
|
shift, found := c.scLoadShift(tid, shiftID)
|
|
if !found {
|
|
c.scFail(400, 400, "班次不存在或已停用")
|
|
return
|
|
}
|
|
|
|
var entry models.OaScheduleEntry
|
|
if err := c.scEntryQuery(tid).Filter("id", id).One(&entry); err != nil {
|
|
c.scFail(404, 404, "排班记录不存在")
|
|
return
|
|
}
|
|
if entry.ShiftID == shift.ID {
|
|
c.scFail(400, 400, "该排班已经是目标班次")
|
|
return
|
|
}
|
|
|
|
reason := scStringValue(body, "reason")
|
|
oldShiftID, oldShiftName := entry.ShiftID, entry.ShiftName
|
|
entry.ShiftID = shift.ID
|
|
entry.ShiftName = shift.Name
|
|
entry.ShortName = shift.ShortName
|
|
entry.Color = shift.Color
|
|
entry.WorkStart = shift.WorkStart
|
|
entry.WorkEnd = shift.WorkEnd
|
|
entry.Source = models.OaEntrySourceTemp
|
|
if reason != "" {
|
|
entry.Note = reason
|
|
}
|
|
if _, err := models.Orm.Update(&entry); err != nil {
|
|
c.scFail(500, 500, "调班失败: "+err.Error())
|
|
return
|
|
}
|
|
c.scWriteChange(models.Orm, tid, entry.ID, entry.EmployeeID, entry.EmployeeName, normalizeOaDate(entry.ScheduleDate),
|
|
models.OaChangeShift, oldShiftID, oldShiftName, shift.ID, shift.Name, reason, uid, username)
|
|
|
|
c.scOK(map[string]interface{}{"entry": entry, "old_shift_name": oldShiftName})
|
|
}
|
|
|
|
// ClearEntries 批量清除排班:按明细 ID,或按员工 + 日期区间。
|
|
// POST /backend/oa/scheduling/entries/clear
|
|
func (c *BackendOaSchedulingController) ClearEntries() {
|
|
tid, uid, username, ok := c.scOperator()
|
|
if !ok {
|
|
return
|
|
}
|
|
body := c.scBody()
|
|
entryIDs := scUintSlice(body, "entry_ids")
|
|
|
|
var entries []models.OaScheduleEntry
|
|
if len(entryIDs) > 0 {
|
|
_, err := c.scEntryQuery(tid).Filter("id__in", entryIDs).All(&entries)
|
|
if err != nil && err != orm.ErrNoRows {
|
|
c.scFail(500, 500, "查询排班失败: "+err.Error())
|
|
return
|
|
}
|
|
} else {
|
|
employees, workdays, msg := c.scEntryTargets(tid, body)
|
|
if msg != "" {
|
|
c.scFail(400, 400, msg)
|
|
return
|
|
}
|
|
empIDs := make([]uint64, 0, len(employees))
|
|
for _, employee := range employees {
|
|
empIDs = append(empIDs, uint64(employee.ID))
|
|
}
|
|
_, err := c.scEntryQuery(tid).
|
|
Filter("employee_id__in", empIDs).
|
|
Filter("schedule_date__in", workdays).
|
|
All(&entries)
|
|
if err != nil && err != orm.ErrNoRows {
|
|
c.scFail(500, 500, "查询排班失败: "+err.Error())
|
|
return
|
|
}
|
|
}
|
|
|
|
if len(entries) == 0 {
|
|
c.scOK(map[string]interface{}{"cleared": 0})
|
|
return
|
|
}
|
|
if len(entries) > scPlanMaxEntries {
|
|
c.scFail(400, 400, fmt.Sprintf("单次最多清除 %d 条排班", scPlanMaxEntries))
|
|
return
|
|
}
|
|
|
|
reason := scStringValue(body, "reason")
|
|
now := time.Now()
|
|
tx, err := orm.NewOrm().Begin()
|
|
if err != nil {
|
|
c.scFail(500, 500, "清除排班失败: "+err.Error())
|
|
return
|
|
}
|
|
for i := range entries {
|
|
entry := &entries[i]
|
|
oldShiftID, oldShiftName := entry.ShiftID, entry.ShiftName
|
|
entry.IsDeleted = entry.ID
|
|
entry.DeleteTime = &now
|
|
if _, err := tx.Update(entry, "is_deleted", "delete_time"); err != nil {
|
|
_ = tx.Rollback()
|
|
c.scFail(500, 500, "清除排班失败: "+err.Error())
|
|
return
|
|
}
|
|
c.scWriteChange(tx, tid, entry.ID, entry.EmployeeID, entry.EmployeeName, normalizeOaDate(entry.ScheduleDate),
|
|
models.OaChangeClear, oldShiftID, oldShiftName, 0, "", reason, uid, username)
|
|
}
|
|
if err := tx.Commit(); err != nil {
|
|
c.scFail(500, 500, "清除排班失败: "+err.Error())
|
|
return
|
|
}
|
|
|
|
c.scOK(map[string]interface{}{"cleared": len(entries)})
|
|
}
|
|
|
|
// ChangeList 排班变更记录(最近 100 条),用于追溯临时调配。
|
|
// GET /backend/oa/scheduling/changes?start_date=&end_date=&employee_id=
|
|
func (c *BackendOaSchedulingController) ChangeList() {
|
|
tid, _, _, ok := c.scOperator()
|
|
if !ok {
|
|
return
|
|
}
|
|
qs := models.Orm.QueryTable(new(models.OaScheduleChange)).Filter("tid", tid)
|
|
if start := strings.TrimSpace(c.GetString("start_date")); isValidOaDate(start) {
|
|
qs = qs.Filter("schedule_date__gte", start)
|
|
}
|
|
if end := strings.TrimSpace(c.GetString("end_date")); isValidOaDate(end) {
|
|
qs = qs.Filter("schedule_date__lte", end)
|
|
}
|
|
if raw := strings.TrimSpace(c.GetString("employee_id")); raw != "" {
|
|
if employeeID, err := strconvParseUint(raw); err == nil {
|
|
qs = qs.Filter("employee_id", employeeID)
|
|
}
|
|
}
|
|
var rows []models.OaScheduleChange
|
|
_, err := qs.OrderBy("-id").Limit(100).All(&rows)
|
|
if err != nil && err != orm.ErrNoRows {
|
|
c.scFail(500, 500, "查询变更记录失败: "+err.Error())
|
|
return
|
|
}
|
|
if rows == nil {
|
|
rows = []models.OaScheduleChange{}
|
|
}
|
|
for i := range rows {
|
|
rows[i].ScheduleDate = normalizeOaDate(rows[i].ScheduleDate)
|
|
}
|
|
c.scOK(map[string]interface{}{"list": rows})
|
|
}
|
|
|
|
// ScheduleEmployeeOptions 排班可选员工(供“指定员工”范围选择)。
|
|
// GET /backend/oa/scheduling/employees?org_id=&include_child=1&keyword=
|
|
func (c *BackendOaSchedulingController) ScheduleEmployeeOptions() {
|
|
tid, _, _, ok := c.scOperator()
|
|
if !ok {
|
|
return
|
|
}
|
|
orgID := uint64(0)
|
|
if raw := strings.TrimSpace(c.GetString("org_id")); raw != "" {
|
|
if parsed, err := strconvParseUint(raw); err == nil {
|
|
orgID = parsed
|
|
}
|
|
}
|
|
includeChild := c.GetString("include_child") != "0"
|
|
keyword := strings.TrimSpace(c.GetString("keyword"))
|
|
|
|
employees, err := c.scEmployeesByOrg(tid, orgID, includeChild)
|
|
if err != nil {
|
|
c.scFail(500, 500, "查询员工失败: "+err.Error())
|
|
return
|
|
}
|
|
list := c.scEmployeeDTOs(tid, employees)
|
|
if keyword != "" {
|
|
filtered := make([]schedulingEmployee, 0, len(list))
|
|
for _, item := range list {
|
|
if strings.Contains(item.Name, keyword) || strings.Contains(item.Account, keyword) {
|
|
filtered = append(filtered, item)
|
|
}
|
|
}
|
|
list = filtered
|
|
}
|
|
if list == nil {
|
|
list = []schedulingEmployee{}
|
|
}
|
|
c.scOK(map[string]interface{}{"list": list, "total": len(list)})
|
|
}
|