Files
yunzerwebsiteallinone/go/controllers/backend_oa_scheduling_scope.go
T
2026-10-26 08:54:34 +08:00

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
}