197 lines
5.6 KiB
Go
197 lines
5.6 KiB
Go
package controllers
|
|
|
|
import (
|
|
"strconv"
|
|
"strings"
|
|
|
|
"server/models"
|
|
|
|
"github.com/beego/beego/v2/client/orm"
|
|
)
|
|
|
|
// schedulingEmployee 排班看板/方案预览里的员工视图。
|
|
// Department 保留组织 ID 字符串,DepartmentName 供前端直接展示。
|
|
type schedulingEmployee struct {
|
|
ID uint `json:"id"`
|
|
Name string `json:"name"`
|
|
Account string `json:"account"`
|
|
Department string `json:"department"`
|
|
DepartmentName string `json:"department_name"`
|
|
Position string `json:"position"`
|
|
Phone string `json:"phone"`
|
|
AccountStatus int8 `json:"account_status"`
|
|
}
|
|
|
|
// scOrgIndex 租户组织架构的内存索引,用于把“部门/小组”展开成部门子树,
|
|
// 以及给员工补齐部门名称。一次请求只加载一次,避免 N+1 查询。
|
|
type scOrgIndex struct {
|
|
rows map[uint64]models.BackendOrganization
|
|
children map[uint64][]uint64
|
|
names map[uint64]string
|
|
}
|
|
|
|
func (c *BackendOaSchedulingController) scLoadOrgIndex(tid uint64) *scOrgIndex {
|
|
index := &scOrgIndex{
|
|
rows: map[uint64]models.BackendOrganization{},
|
|
children: map[uint64][]uint64{},
|
|
names: map[uint64]string{},
|
|
}
|
|
var rows []models.BackendOrganization
|
|
_, err := models.Orm.QueryTable(new(models.BackendOrganization)).
|
|
Filter("tid", tid).
|
|
Filter("delete_time__isnull", true).
|
|
All(&rows)
|
|
if err != nil {
|
|
return index
|
|
}
|
|
for _, row := range rows {
|
|
index.rows[row.ID] = row
|
|
index.names[row.ID] = row.OrgName
|
|
index.children[row.ParentID] = append(index.children[row.ParentID], row.ID)
|
|
}
|
|
return index
|
|
}
|
|
|
|
// subtreeIDs 返回节点自身及其全部下级组织 ID。
|
|
func (index *scOrgIndex) subtreeIDs(root uint64) map[uint64]bool {
|
|
result := map[uint64]bool{}
|
|
if root == 0 {
|
|
return result
|
|
}
|
|
queue := []uint64{root}
|
|
for len(queue) > 0 {
|
|
current := queue[0]
|
|
queue = queue[1:]
|
|
if result[current] {
|
|
continue
|
|
}
|
|
result[current] = true
|
|
queue = append(queue, index.children[current]...)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (index *scOrgIndex) name(id uint64) string {
|
|
if name, ok := index.names[id]; ok {
|
|
return name
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// scBaseEmployeeQuery 排班可选员工:未删除、非离职。
|
|
func (c *BackendOaSchedulingController) scBaseEmployeeQuery(tid uint64) orm.QuerySeter {
|
|
return models.Orm.QueryTable(new(models.BackendEmployee)).
|
|
Filter("tid", tid).
|
|
Filter("delete_time__isnull", true).
|
|
Exclude("account_status", 2)
|
|
}
|
|
|
|
func scEmployeeDepartmentID(row models.BackendEmployee) uint64 {
|
|
if row.Department == nil {
|
|
return 0
|
|
}
|
|
id, err := strconv.ParseUint(strings.TrimSpace(*row.Department), 10, 64)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
return id
|
|
}
|
|
|
|
func (c *BackendOaSchedulingController) scEmployeeDTOs(tid uint64, rows []models.BackendEmployee) []schedulingEmployee {
|
|
index := c.scLoadOrgIndex(tid)
|
|
result := make([]schedulingEmployee, 0, len(rows))
|
|
for _, row := range rows {
|
|
orgID := scEmployeeDepartmentID(row)
|
|
position := ""
|
|
if row.Position != nil {
|
|
position = *row.Position
|
|
}
|
|
phone := ""
|
|
if row.Phone != nil {
|
|
phone = *row.Phone
|
|
}
|
|
result = append(result, schedulingEmployee{
|
|
ID: row.ID,
|
|
Name: row.Name,
|
|
Account: row.Account,
|
|
Department: strconv.FormatUint(orgID, 10),
|
|
DepartmentName: index.name(orgID),
|
|
Position: position,
|
|
Phone: phone,
|
|
AccountStatus: row.AccountStatus,
|
|
})
|
|
}
|
|
return result
|
|
}
|
|
|
|
// scEmployeesByOrg 按组织节点取员工;includeChild=true 时包含全部下级组织,
|
|
// 这样选中“公司/部门/小组”任意层级都能覆盖到该范围内的所有人。
|
|
func (c *BackendOaSchedulingController) scEmployeesByOrg(tid, orgID uint64, includeChild bool) ([]models.BackendEmployee, error) {
|
|
qs := c.scBaseEmployeeQuery(tid)
|
|
if orgID > 0 {
|
|
allowed := map[uint64]bool{orgID: true}
|
|
if includeChild {
|
|
allowed = c.scLoadOrgIndex(tid).subtreeIDs(orgID)
|
|
}
|
|
values := make([]string, 0, len(allowed))
|
|
for id := range allowed {
|
|
values = append(values, strconv.FormatUint(id, 10))
|
|
}
|
|
qs = qs.Filter("department__in", values)
|
|
}
|
|
var rows []models.BackendEmployee
|
|
_, err := qs.OrderBy("department", "id").All(&rows)
|
|
if err != nil && err != orm.ErrNoRows {
|
|
return nil, err
|
|
}
|
|
return rows, nil
|
|
}
|
|
|
|
// scEmployeesByIDs 按员工 ID 取员工,保持传入顺序由前端自行处理。
|
|
func (c *BackendOaSchedulingController) scEmployeesByIDs(tid uint64, ids []uint64) ([]models.BackendEmployee, error) {
|
|
if len(ids) == 0 {
|
|
return nil, nil
|
|
}
|
|
var rows []models.BackendEmployee
|
|
_, err := c.scBaseEmployeeQuery(tid).Filter("id__in", ids).All(&rows)
|
|
if err != nil && err != orm.ErrNoRows {
|
|
return nil, err
|
|
}
|
|
return rows, nil
|
|
}
|
|
|
|
// scResolveEmployees 把方案范围(公司 / 部门小组 / 指定员工)展开成员工列表。
|
|
func (c *BackendOaSchedulingController) scResolveEmployees(tid uint64, scopeType int8, orgID uint64, includeChild bool, employeeIDs []uint64) ([]models.BackendEmployee, error) {
|
|
switch scopeType {
|
|
case models.OaScopeEmployees:
|
|
return c.scEmployeesByIDs(tid, employeeIDs)
|
|
case models.OaScopeCompany:
|
|
return c.scEmployeesByOrg(tid, 0, false)
|
|
default:
|
|
return c.scEmployeesByOrg(tid, orgID, includeChild)
|
|
}
|
|
}
|
|
|
|
// scWorkdays 从日期区间中筛出命中的星期(1=周一 ... 7=周日)。
|
|
// 未选择任何星期表示每天。
|
|
func scWorkdays(dates []string, weekdays map[int]bool) []string {
|
|
if len(weekdays) == 0 {
|
|
return dates
|
|
}
|
|
result := make([]string, 0, len(dates))
|
|
for _, date := range dates {
|
|
parsed, ok := scParseDate(date)
|
|
if !ok {
|
|
continue
|
|
}
|
|
weekday := int(parsed.Weekday())
|
|
if weekday == 0 {
|
|
weekday = 7
|
|
}
|
|
if weekdays[weekday] {
|
|
result = append(result, date)
|
|
}
|
|
}
|
|
return result
|
|
}
|