207 lines
5.9 KiB
Go
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)
|
|
}
|