mirror of
https://github.com/larksuite/cli.git
synced 2026-07-03 14:02:43 +08:00
Add semi-plain text (simple) format for OKR content I/O, and a new `+patch` shortcut for incremental updates to objectives and key results.
292 lines
9.5 KiB
Go
292 lines
9.5 KiB
Go
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
||
// SPDX-License-Identifier: MIT
|
||
|
||
package okr
|
||
|
||
// RespAlignment 对齐关系
|
||
type RespAlignment struct {
|
||
ID string `json:"id"`
|
||
CreateTime string `json:"create_time"`
|
||
UpdateTime string `json:"update_time"`
|
||
FromOwner RespOwner `json:"from_owner"`
|
||
ToOwner RespOwner `json:"to_owner"`
|
||
FromEntityType string `json:"from_entity_type"`
|
||
FromEntityID string `json:"from_entity_id"`
|
||
ToEntityType string `json:"to_entity_type"`
|
||
ToEntityID string `json:"to_entity_id"`
|
||
}
|
||
|
||
// RespCategory 分类
|
||
type RespCategory struct {
|
||
ID string `json:"id"`
|
||
CreateTime string `json:"create_time"`
|
||
UpdateTime string `json:"update_time"`
|
||
CategoryType string `json:"category_type"`
|
||
Enabled *bool `json:"enabled,omitempty"`
|
||
Color *string `json:"color,omitempty"`
|
||
Name CategoryName `json:"name"`
|
||
}
|
||
|
||
// RespCycle 周期
|
||
type RespCycle struct {
|
||
ID string `json:"id"`
|
||
StartTime string `json:"start_time"`
|
||
EndTime string `json:"end_time"`
|
||
CycleStatus *string `json:"cycle_status,omitempty"`
|
||
}
|
||
|
||
// RespIndicator 指标
|
||
type RespIndicator struct {
|
||
ID string `json:"id"`
|
||
CreateTime string `json:"create_time"`
|
||
UpdateTime string `json:"update_time"`
|
||
Owner RespOwner `json:"owner"`
|
||
EntityType *string `json:"entity_type,omitempty"`
|
||
EntityID *string `json:"entity_id,omitempty"`
|
||
IndicatorStatus *string `json:"indicator_status,omitempty"`
|
||
StatusCalculateType *string `json:"status_calculate_type,omitempty"`
|
||
StartValue *float64 `json:"start_value,omitempty"`
|
||
TargetValue *float64 `json:"target_value,omitempty"`
|
||
CurrentValue *float64 `json:"current_value,omitempty"`
|
||
CurrentValueCalculateType *string `json:"current_value_calculate_type,omitempty"`
|
||
Unit *RespIndicatorUnit `json:"unit,omitempty"`
|
||
}
|
||
|
||
// RespIndicatorUnit 指标单位
|
||
type RespIndicatorUnit struct {
|
||
UnitType *string `json:"unit_type,omitempty"`
|
||
UnitValue *string `json:"unit_value,omitempty"`
|
||
}
|
||
|
||
// RespKeyResult 关键结果
|
||
type RespKeyResult struct {
|
||
ID string `json:"id"`
|
||
CreateTime string `json:"create_time"`
|
||
UpdateTime string `json:"update_time"`
|
||
Owner RespOwner `json:"owner"`
|
||
ObjectiveID string `json:"objective_id"`
|
||
Position *int32 `json:"position,omitempty"`
|
||
Content *string `json:"content,omitempty"`
|
||
Score *float64 `json:"score,omitempty"`
|
||
Weight *float64 `json:"weight,omitempty"`
|
||
Deadline *string `json:"deadline,omitempty"`
|
||
}
|
||
|
||
// RespObjective 目标
|
||
type RespObjective struct {
|
||
ID string `json:"id"`
|
||
CreateTime string `json:"create_time"`
|
||
UpdateTime string `json:"update_time"`
|
||
Owner RespOwner `json:"owner"`
|
||
CycleID string `json:"cycle_id"`
|
||
Position *int32 `json:"position,omitempty"`
|
||
Content *string `json:"content,omitempty"`
|
||
Score *float64 `json:"score,omitempty"`
|
||
Notes *string `json:"notes,omitempty"`
|
||
Weight *float64 `json:"weight,omitempty"`
|
||
Deadline *string `json:"deadline,omitempty"`
|
||
CategoryID *string `json:"category_id,omitempty"`
|
||
KeyResults []RespKeyResult `json:"key_results,omitempty"`
|
||
}
|
||
|
||
// RespOwner OKR 所有者
|
||
type RespOwner struct {
|
||
OwnerType string `json:"owner_type"`
|
||
UserID *string `json:"user_id,omitempty"`
|
||
}
|
||
|
||
// ProgressStatus 进展状态
|
||
type ProgressStatus int32
|
||
|
||
const (
|
||
ProgressStatusNormal ProgressStatus = 0 // 正常
|
||
ProgressStatusOverdue ProgressStatus = 1 // 逾期
|
||
ProgressStatusDone ProgressStatus = 2 // 已完成
|
||
)
|
||
|
||
// ParseProgressStatus parses a progress status string into ProgressStatus.
|
||
// Accepts "normal", "overdue", "done" or their numeric values "0", "1", "2".
|
||
func ParseProgressStatus(s string) (ProgressStatus, bool) {
|
||
switch s {
|
||
case "normal", "0":
|
||
return ProgressStatusNormal, true
|
||
case "overdue", "1":
|
||
return ProgressStatusOverdue, true
|
||
case "done", "2":
|
||
return ProgressStatusDone, true
|
||
default:
|
||
return 0, false
|
||
}
|
||
}
|
||
|
||
// String returns a human-readable name for ProgressStatus.
|
||
func (s ProgressStatus) String() string {
|
||
switch s {
|
||
case ProgressStatusNormal:
|
||
return "normal"
|
||
case ProgressStatusOverdue:
|
||
return "overdue"
|
||
case ProgressStatusDone:
|
||
return "done"
|
||
default:
|
||
return ""
|
||
}
|
||
}
|
||
|
||
// RespProgressRate 进度率(面向用户的响应格式,Status 为可读字符串)
|
||
type RespProgressRate struct {
|
||
Percent *float64 `json:"percent,omitempty"`
|
||
Status *string `json:"status,omitempty"`
|
||
}
|
||
|
||
// RespProgress 进展记录
|
||
type RespProgress struct {
|
||
ID string `json:"progress_id"`
|
||
ModifyTime string `json:"modify_time"`
|
||
CreateTime *string `json:"create_time,omitempty"`
|
||
Content *string `json:"content,omitempty"`
|
||
ProgressRate *RespProgressRate `json:"progress_rate,omitempty"`
|
||
}
|
||
|
||
// ========== Simple-style response types (semi-plain text format) ==========
|
||
|
||
// RespKeyResultSimple is KeyResult response with SemiPlainContent instead of ContentBlock JSON string.
|
||
type RespKeyResultSimple struct {
|
||
ID string `json:"id"`
|
||
CreateTime string `json:"create_time"`
|
||
UpdateTime string `json:"update_time"`
|
||
Owner RespOwner `json:"owner"`
|
||
ObjectiveID string `json:"objective_id"`
|
||
Position *int32 `json:"position,omitempty"`
|
||
Content *SemiPlainContent `json:"content,omitempty"`
|
||
Score *float64 `json:"score,omitempty"`
|
||
Weight *float64 `json:"weight,omitempty"`
|
||
Deadline *string `json:"deadline,omitempty"`
|
||
}
|
||
|
||
// RespObjectiveSimple is Objective response with SemiPlainContent instead of ContentBlock JSON string.
|
||
type RespObjectiveSimple struct {
|
||
ID string `json:"id"`
|
||
CreateTime string `json:"create_time"`
|
||
UpdateTime string `json:"update_time"`
|
||
Owner RespOwner `json:"owner"`
|
||
CycleID string `json:"cycle_id"`
|
||
Position *int32 `json:"position,omitempty"`
|
||
Content *SemiPlainContent `json:"content,omitempty"`
|
||
Score *float64 `json:"score,omitempty"`
|
||
Notes *SemiPlainContent `json:"notes,omitempty"`
|
||
Weight *float64 `json:"weight,omitempty"`
|
||
Deadline *string `json:"deadline,omitempty"`
|
||
CategoryID *string `json:"category_id,omitempty"`
|
||
KeyResults []RespKeyResultSimple `json:"key_results,omitempty"`
|
||
}
|
||
|
||
// RespProgressSimple is Progress response with SemiPlainContent instead of ContentBlock JSON string.
|
||
type RespProgressSimple struct {
|
||
ID string `json:"progress_id"`
|
||
ModifyTime string `json:"modify_time"`
|
||
CreateTime *string `json:"create_time,omitempty"`
|
||
Content *SemiPlainContent `json:"content,omitempty"`
|
||
ProgressRate *RespProgressRate `json:"progress_rate,omitempty"`
|
||
}
|
||
|
||
// ToSimple converts KeyResult to RespKeyResultSimple.
|
||
func (k *KeyResult) ToSimple() *RespKeyResultSimple {
|
||
if k == nil {
|
||
return nil
|
||
}
|
||
result := &RespKeyResultSimple{
|
||
ID: k.ID,
|
||
CreateTime: formatTimestamp(k.CreateTime),
|
||
UpdateTime: formatTimestamp(k.UpdateTime),
|
||
Owner: *k.Owner.ToResp(),
|
||
ObjectiveID: k.ObjectiveID,
|
||
Position: k.Position,
|
||
Score: k.Score,
|
||
Weight: k.Weight,
|
||
}
|
||
if k.Deadline != nil {
|
||
d := formatTimestamp(*k.Deadline)
|
||
result.Deadline = &d
|
||
}
|
||
result.Content = k.Content.ToSemiPlain()
|
||
return result
|
||
}
|
||
|
||
// ToSimple converts Objective to RespObjectiveSimple.
|
||
func (o *Objective) ToSimple() *RespObjectiveSimple {
|
||
if o == nil {
|
||
return nil
|
||
}
|
||
result := &RespObjectiveSimple{
|
||
ID: o.ID,
|
||
CreateTime: formatTimestamp(o.CreateTime),
|
||
UpdateTime: formatTimestamp(o.UpdateTime),
|
||
Owner: *o.Owner.ToResp(),
|
||
CycleID: o.CycleID,
|
||
Position: o.Position,
|
||
Score: o.Score,
|
||
Weight: o.Weight,
|
||
CategoryID: o.CategoryID,
|
||
}
|
||
if o.Deadline != nil {
|
||
d := formatTimestamp(*o.Deadline)
|
||
result.Deadline = &d
|
||
}
|
||
result.Content = o.Content.ToSemiPlain()
|
||
result.Notes = o.Notes.ToSemiPlain()
|
||
return result
|
||
}
|
||
|
||
// ToSimple converts ProgressV1 to RespProgressSimple.
|
||
func (p *ProgressV1) ToSimple() *RespProgressSimple {
|
||
if p == nil {
|
||
return nil
|
||
}
|
||
resp := &RespProgressSimple{
|
||
ID: p.ID,
|
||
ModifyTime: formatTimestamp(p.ModifyTime),
|
||
}
|
||
if p.ProgressRate != nil {
|
||
resp.ProgressRate = &RespProgressRate{
|
||
Percent: p.ProgressRate.Percent,
|
||
}
|
||
if p.ProgressRate.Status != nil {
|
||
s := ProgressStatus(*p.ProgressRate.Status).String()
|
||
if s != "" {
|
||
resp.ProgressRate.Status = &s
|
||
}
|
||
}
|
||
}
|
||
if p.Content != nil {
|
||
resp.Content = p.Content.ToV2().ToSemiPlain()
|
||
}
|
||
return resp
|
||
}
|
||
|
||
// ToSimple converts Progress to RespProgressSimple.
|
||
func (p *Progress) ToSimple() *RespProgressSimple {
|
||
if p == nil {
|
||
return nil
|
||
}
|
||
createTime := formatTimestamp(p.CreateTime)
|
||
resp := &RespProgressSimple{
|
||
ID: p.ID,
|
||
ModifyTime: formatTimestamp(p.UpdateTime),
|
||
CreateTime: &createTime,
|
||
}
|
||
if p.ProgressRate != nil {
|
||
resp.ProgressRate = &RespProgressRate{
|
||
Percent: p.ProgressRate.ProgressPercent,
|
||
}
|
||
if p.ProgressRate.ProgressStatus != nil {
|
||
s := ProgressStatus(*p.ProgressRate.ProgressStatus).String()
|
||
if s != "" {
|
||
resp.ProgressRate.Status = &s
|
||
}
|
||
}
|
||
}
|
||
resp.Content = p.Content.ToSemiPlain()
|
||
return resp
|
||
}
|