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

207 lines
5.9 KiB
Go

package controllers
import (
"strconv"
"strings"
"server/models"
"github.com/beego/beego/v2/client/orm"
)
// 租户端排班模板:只读调用,不允许修改平台模板。
//
// 调用模板时会把模板的每日规则转成方案周期的 pattern 快照,并把模板里的
// 班次按「名称 + 上下班时间」匹配到租户自己的班次;匹配不到则自动创建,
// 这样租户拿到方案后就能像普通方案一样自由修改。
func schedTplTypeLabel(value int8) string {
return schedTplTypeText(value)
}
func (c *BackendOaSchedulingController) schedTplQuery() orm.QuerySeter {
return models.Orm.QueryTable(new(models.PlatformSchedTpl)).
Filter("delete_time__isnull", true)
}
func (c *BackendOaSchedulingController) schedTplDays(templateID uint64) []models.PlatformSchedTplDay {
rows := []models.PlatformSchedTplDay{}
_, err := models.Orm.QueryTable(new(models.PlatformSchedTplDay)).
Filter("template_id", templateID).
OrderBy("day_index").All(&rows)
if err != nil && err != orm.ErrNoRows {
return []models.PlatformSchedTplDay{}
}
return rows
}
func (c *BackendOaSchedulingController) schedTplByID(id uint64) (*models.PlatformSchedTpl, bool) {
var row models.PlatformSchedTpl
if err := c.schedTplQuery().Filter("id", id).One(&row); err != nil {
return nil, false
}
return &row, true
}
func schedTplView(row *models.PlatformSchedTpl, days []models.PlatformSchedTplDay) map[string]interface{} {
view := map[string]interface{}{
"id": row.ID,
"code": row.Code,
"name": row.Name,
"template_type": row.TemplateType,
"template_type_text": schedTplTypeLabel(row.TemplateType),
"align_mode": row.AlignMode,
"cycle_days": row.CycleDays,
"week_offset": row.WeekOffset,
"month_rest_days": row.MonthRestDays,
"month_rest_weekday": row.MonthRestWeekday,
"summary": row.Summary,
}
if days != nil {
view["days"] = days
}
return view
}
// TemplateList 租户端可调用的排班模板(只返回启用的模板)。
// GET /backend/oa/scheduling/templates?keyword=
func (c *BackendOaSchedulingController) TemplateList() {
if _, _, _, ok := c.scOperator(); !ok {
return
}
qs := c.schedTplQuery().Filter("status", 1)
if keyword := strings.TrimSpace(c.GetString("keyword")); keyword != "" {
qs = qs.Filter("name__icontains", keyword)
}
var rows []models.PlatformSchedTpl
if _, err := qs.OrderBy("sort", "id").All(&rows); err != nil && err != orm.ErrNoRows {
c.scFail(500, 500, "查询排班模板失败: "+err.Error())
return
}
list := make([]map[string]interface{}, 0, len(rows))
for i := range rows {
list = append(list, schedTplView(&rows[i], c.schedTplDays(rows[i].ID)))
}
c.scOK(map[string]interface{}{"list": list, "total": len(list)})
}
// TemplateApply 调用模板:返回可直接用于方案的周期规则,并把模板班次
// 匹配到租户班次(不存在时自动创建)。
// POST /backend/oa/scheduling/templates/apply body: {"template_id": 1}
func (c *BackendOaSchedulingController) TemplateApply() {
tid, _, _, ok := c.scOperator()
if !ok {
return
}
id := scUintValue(c.scBody(), "template_id")
if id == 0 {
if raw := strings.TrimSpace(c.GetString("template_id")); raw != "" {
id, _ = strconv.ParseUint(raw, 10, 64)
}
}
if id == 0 {
c.scFail(400, 400, "请选择排班模板")
return
}
tpl, found := c.schedTplByID(id)
if !found {
c.scFail(404, 404, "排班模板不存在")
return
}
if tpl.Status != 1 {
c.scFail(400, 400, "该模板已停用")
return
}
var shifts []models.OaShift
if _, err := c.shiftQuery(tid).OrderBy("sort", "id").All(&shifts); err != nil && err != orm.ErrNoRows {
c.scFail(500, 500, "查询班次失败: "+err.Error())
return
}
shiftMap := make(map[string]*models.OaShift, len(shifts))
maxSort := 0
for i := range shifts {
shift := &shifts[i]
shiftMap[schedShiftKey(shift.Name, shift.WorkStart, shift.WorkEnd)] = shift
if shift.Sort > maxSort {
maxSort = shift.Sort
}
}
patterns := []scPatternDay{}
createdShifts := 0
matchedShifts := 0
for _, day := range c.schedTplDays(tpl.ID) {
if day.DayType != models.SchedTplDayWork {
patterns = append(patterns, scPatternDay{DayIndex: day.DayIndex, DayType: models.SchedTplDayRest})
continue
}
key := schedShiftKey(day.ShiftName, day.WorkStart, day.WorkEnd)
shift, hit := shiftMap[key]
if hit {
matchedShifts++
} else {
maxSort += 10
shift = &models.OaShift{
Tid: int(tid),
Name: day.ShiftName,
ShortName: day.ShiftShort,
Color: day.Color,
WorkStart: day.WorkStart,
WorkEnd: day.WorkEnd,
RestStart: day.RestStart,
RestEnd: day.RestEnd,
WorkHours: day.WorkHours,
IsDefault: 0,
Status: 1,
Sort: maxSort,
IsDeleted: 0,
}
if shift.ShortName == "" {
shift.ShortName = shift.Name
}
if shift.Color == "" {
shift.Color = "#409EFF"
}
if _, err := models.Orm.Insert(shift); err != nil {
c.scFail(500, 500, "创建班次失败: "+err.Error())
return
}
shiftMap[key] = shift
createdShifts++
}
patterns = append(patterns, scPatternDay{
DayIndex: day.DayIndex,
DayType: models.SchedTplDayWork,
ShiftID: shift.ID,
ShiftName: shift.Name,
ShortName: shift.ShortName,
Color: shift.Color,
WorkStart: shift.WorkStart,
WorkEnd: shift.WorkEnd,
RestStart: shift.RestStart,
RestEnd: shift.RestEnd,
WorkHours: shift.WorkHours,
})
}
if len(patterns) == 0 {
c.scFail(400, 400, "该模板没有配置排班规则")
return
}
c.scOK(map[string]interface{}{
"template": schedTplView(tpl, nil),
"pattern": patterns,
"days": patterns,
"created_shift": createdShifts,
"matched_shift": matchedShifts,
})
}
func schedShiftKey(name, workStart, workEnd string) string {
return strings.TrimSpace(name) + "|" + strings.TrimSpace(workStart) + "|" + strings.TrimSpace(workEnd)
}