132 lines
5.1 KiB
Go
132 lines
5.1 KiB
Go
// Copyright 2026 Alibaba Group
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package smart
|
|
|
|
import (
|
|
"time"
|
|
|
|
apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors"
|
|
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/shortcut"
|
|
)
|
|
|
|
// DueToday: list MY todos whose planFinishDate falls within today.
|
|
//
|
|
// Unlike +overdue (which locally keeps cards whose dueTime is already in the
|
|
// past), this shortcut filters SERVER-SIDE: it passes the today window as
|
|
// planFinishDateStart / planFinishDateEnd (epoch millis) to
|
|
// get_user_todos_in_current_org, so the backend returns only todos due today.
|
|
//
|
|
// Steps:
|
|
//
|
|
// 1. compute the [today 00:00, tomorrow 00:00) window in the local timezone,
|
|
// as epoch millis;
|
|
//
|
|
// 2. list my todos via get_user_todos_in_current_org with
|
|
// planFinishDateStart = today 00:00 ms, planFinishDateEnd = tomorrow 00:00
|
|
// ms, roleTypes=["executor"] (mirroring +overdue), pageNum/pageSize as
|
|
// strings (mirroring helpers.todo list);
|
|
//
|
|
// 3. project each card to a clean {title, status, priority, creator,
|
|
// planFinishDate, taskId} shape and print via rt.Output so it honours
|
|
// --format/--jq/--fields.
|
|
//
|
|
// Read-only: it never mutates any todo, it only lists and projects.
|
|
//
|
|
// dws todo +due-today
|
|
var DueToday = shortcut.Shortcut{
|
|
Service: "todo",
|
|
Command: "+due-today",
|
|
Product: "todo",
|
|
Description: "列出我今天到期的待办",
|
|
Intent: "当你想快速看清自己今天(planFinishDate 落在今天 00:00 到次日 00:00 之间)到期的待办、方便安排一天的工作时使用;" +
|
|
"内部按今天的本地时间窗,把 planFinishDateStart=今天0点、planFinishDateEnd=次日0点(毫秒时间戳)传给 get_user_todos_in_current_org 做服务端过滤," +
|
|
"默认拉取你作为执行人(executor)的待办,可用 --role-types 覆盖角色范围,最后只打印这些今天到期待办的标题、状态、优先级、创建人、到期时间和任务 ID。" +
|
|
"这与 +overdue(已过期)不同:+overdue 看的是已经过了截止时间的待办,本命令看的是今天当天到期的待办。" +
|
|
"这是纯只读操作,只做列表与投影,不会修改或完成任何待办;若今天没有到期的待办则返回错误提示。",
|
|
Risk: shortcut.RiskRead,
|
|
Flags: []shortcut.Flag{
|
|
{
|
|
Name: "role-types",
|
|
Type: shortcut.FlagString,
|
|
Desc: "覆盖默认角色范围,逗号分隔,取值 creator/executor/participant;不传则默认 executor",
|
|
},
|
|
},
|
|
Tips: []string{
|
|
`dws todo +due-today`,
|
|
`dws todo +due-today --role-types creator,executor`,
|
|
},
|
|
Execute: func(rt *shortcut.RuntimeContext) error {
|
|
// Step 1 — compute [today 00:00, tomorrow 00:00) in the local timezone.
|
|
now := time.Now()
|
|
loc := now.Location()
|
|
startOfDay := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, loc)
|
|
startOfNextDay := startOfDay.AddDate(0, 0, 1)
|
|
startMs := startOfDay.UnixMilli()
|
|
endMs := startOfNextDay.UnixMilli()
|
|
|
|
// Step 2 — build params. roleTypes defaults to executor (like +overdue);
|
|
// --role-types overrides it (validated like helpers.parseRoleTypes). The
|
|
// today window is passed as planFinishDateStart / planFinishDateEnd so the
|
|
// backend filters server-side.
|
|
roleTypes := []string{"executor"}
|
|
if rt.Changed("role-types") {
|
|
parsed, err := parseRelatedRoleTypes(rt.Str("role-types"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(parsed) > 0 {
|
|
roleTypes = parsed
|
|
}
|
|
}
|
|
|
|
params := map[string]any{
|
|
"roleTypes": roleTypes,
|
|
"planFinishDateStart": startMs,
|
|
"planFinishDateEnd": endMs,
|
|
}
|
|
|
|
// List ALL matching cards across pages so a todo due today beyond the
|
|
// first page is not silently dropped.
|
|
cards, err := shortcutListAllTodoCards(rt, params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Step 3 — project the cards. The server already filtered to today's
|
|
// window; we keep a defensive local guard against any planFinishDate that
|
|
// leaks outside [startMs, endMs).
|
|
results := make([]map[string]any, 0, len(cards))
|
|
for _, m := range cards {
|
|
if due, ok := shortcutOverdueDueTime(m); ok { // reused from overdue.go
|
|
if due < startMs || due >= endMs {
|
|
continue
|
|
}
|
|
}
|
|
taskID := shortcutRelatedTaskID(m) // reused from related_tasks.go
|
|
results = append(results, shortcutRelatedProject(m, taskID)) // reused from related_tasks.go
|
|
}
|
|
|
|
if len(results) == 0 {
|
|
return apperrors.NewValidation("今天没有到期的待办")
|
|
}
|
|
|
|
// Step 4 — print the projected list.
|
|
return rt.Output(map[string]any{"count": len(results), "tasks": results})
|
|
},
|
|
}
|
|
|
|
func init() {
|
|
shortcut.Register(DueToday)
|
|
}
|