221 lines
6.3 KiB
Go
221 lines
6.3 KiB
Go
package business
|
|
|
|
import (
|
|
"context"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/go-kratos/kratos/v3/errors"
|
|
)
|
|
|
|
var ErrGoalInput = errors.BadRequest("SW_GOAL_INPUT", "目标须为非负整数或留空,可修改近6个月及下月目标")
|
|
|
|
type GoalUsecase struct {
|
|
repo GoalRepo
|
|
performance *PerformanceUsecase
|
|
now func() time.Time
|
|
}
|
|
|
|
func (uc *GoalUsecase) Mine(ctx context.Context) (*GoalMonth, error) {
|
|
actor, _, err := uc.performance.teams.accounts.actor(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
now := uc.now().In(portraitLocation)
|
|
month := now.Format("2006-01")
|
|
goals, err := uc.repo.List(ctx, month, month)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var goal *PerformanceGoal
|
|
for _, g := range goals {
|
|
if g.Kind == "person" && g.TargetID == uint64(actor.UserID) {
|
|
goal = g
|
|
break
|
|
}
|
|
}
|
|
if goal == nil {
|
|
return nil, nil
|
|
}
|
|
_, owners, _, _, err := uc.performance.Target(ctx, "person", uint64(actor.UserID))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
data, err := uc.performance.query(ctx, PerformanceFilter{Year: now.Year(), BeginMonth: int(now.Month()), EndMonth: int(now.Month())}, owners)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !data.Available {
|
|
return nil, ErrWorkbenchUnavailable
|
|
}
|
|
for _, row := range data.Rows {
|
|
if row.Member.ID == actor.UserID {
|
|
return &GoalMonth{Month: month, Goal: goal, Actual: row.Total, Available: true}, nil
|
|
}
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func NewGoalUsecase(repo GoalRepo, performance *PerformanceUsecase) *GoalUsecase {
|
|
return &GoalUsecase{repo: repo, performance: performance, now: time.Now}
|
|
}
|
|
func goalEditable(month string, now time.Time) bool {
|
|
current := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, portraitLocation)
|
|
return month >= current.AddDate(0, -5, 0).Format("2006-01") && month <= current.AddDate(0, 1, 0).Format("2006-01")
|
|
}
|
|
func (uc *GoalUsecase) Save(ctx context.Context, goal *PerformanceGoal) error {
|
|
if goal == nil {
|
|
return ErrGoalInput
|
|
}
|
|
_, _, _, editable, err := uc.performance.Target(ctx, goal.Kind, goal.TargetID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !editable {
|
|
return ErrTeamScope
|
|
}
|
|
month, err := time.ParseInLocation("2006-01", goal.Month, portraitLocation)
|
|
if err != nil || month.Format("2006-01") != goal.Month || !goalEditable(goal.Month, uc.now().In(portraitLocation)) {
|
|
return ErrGoalInput
|
|
}
|
|
for _, value := range []*int64{goal.Open, goal.Effective, goal.Amount} {
|
|
if value != nil && (*value < 0 || *value > MaxSettingsInteger) {
|
|
return ErrGoalInput
|
|
}
|
|
}
|
|
actor, _, err := uc.performance.teams.accounts.actor(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
goal.UpdatedBy = actor.UserID
|
|
return uc.repo.Save(ctx, goal)
|
|
}
|
|
func goalStatus(goal *PerformanceGoal, actual PerformanceMetrics, month, current string) (string, int) {
|
|
if goal == nil || goal.Open == nil && goal.Effective == nil && goal.Amount == nil {
|
|
return "unset", 0
|
|
}
|
|
missing := 0
|
|
if goal.Open != nil && actual.Open < *goal.Open {
|
|
missing++
|
|
}
|
|
if goal.Effective != nil && actual.Effective < *goal.Effective {
|
|
missing++
|
|
}
|
|
if goal.Amount != nil {
|
|
pay, _ := portraitNumber(actual.Pay)
|
|
threshold, _ := portraitNumber(strconv.FormatInt(*goal.Amount, 10))
|
|
if pay.Cmp(threshold) < 0 {
|
|
missing++
|
|
}
|
|
}
|
|
if month == current {
|
|
return "ongoing", missing
|
|
}
|
|
if month > current {
|
|
return "future", missing
|
|
}
|
|
if missing == 0 {
|
|
return "achieved", 0
|
|
}
|
|
return "missing", missing
|
|
}
|
|
func (uc *GoalUsecase) History(ctx context.Context, kind string, id uint64) (*GoalPage, error) {
|
|
_, owners, name, editable, err := uc.performance.Target(ctx, kind, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
now := uc.now().In(portraitLocation)
|
|
current := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, portraitLocation)
|
|
begin := current.AddDate(0, -5, 0)
|
|
end := current.AddDate(0, 1, 0)
|
|
goals, err := uc.repo.List(ctx, begin.Format("2006-01"), end.Format("2006-01"))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
byMonth := map[string]*PerformanceGoal{}
|
|
for _, g := range goals {
|
|
if g.Kind == kind && g.TargetID == id {
|
|
byMonth[g.Month] = g
|
|
}
|
|
}
|
|
out := &GoalPage{Kind: kind, TargetID: id, Name: name, CanEdit: editable, Months: []GoalMonth{}, MemberGoals: map[string]PerformanceMetrics{}}
|
|
if kind == "group" {
|
|
view, err := uc.performance.teams.View(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
members := map[uint64]bool{}
|
|
for _, group := range view.Groups {
|
|
if group.ID == id {
|
|
for _, member := range group.MemberIDs {
|
|
members[uint64(member)] = true
|
|
}
|
|
}
|
|
}
|
|
for _, goal := range goals {
|
|
if goal.Kind != "person" || !members[goal.TargetID] {
|
|
continue
|
|
}
|
|
value := PerformanceMetrics{Pay: "0.00"}
|
|
if goal.Open != nil {
|
|
value.Open = *goal.Open
|
|
}
|
|
if goal.Effective != nil {
|
|
value.Effective = *goal.Effective
|
|
}
|
|
if goal.Amount != nil {
|
|
value.Pay = strconv.FormatInt(*goal.Amount, 10)
|
|
}
|
|
out.MemberGoals[goal.Month] = addPerformance(out.MemberGoals[goal.Month], value)
|
|
}
|
|
}
|
|
// At most two year queries supply the complete history; no query per row.
|
|
actuals := map[string]PerformanceMetrics{}
|
|
available := true
|
|
for year := begin.Year(); year <= end.Year(); year++ {
|
|
from, to := 1, 12
|
|
if year == begin.Year() {
|
|
from = int(begin.Month())
|
|
}
|
|
if year == end.Year() {
|
|
to = int(end.Month())
|
|
}
|
|
data, err := uc.performance.query(ctx, PerformanceFilter{Year: year, BeginMonth: from, EndMonth: to}, owners)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
available = available && data.Available
|
|
var values map[string]PerformanceMetrics
|
|
if kind == "person" {
|
|
for _, row := range data.Rows {
|
|
if uint64(row.Member.ID) == id {
|
|
values = row.Periods
|
|
}
|
|
}
|
|
} else {
|
|
for _, group := range data.Groups {
|
|
if group.Group.ID == id {
|
|
values = group.Periods
|
|
}
|
|
}
|
|
}
|
|
for key, value := range values {
|
|
month := key[:7]
|
|
actuals[month] = addPerformance(actuals[month], value)
|
|
}
|
|
}
|
|
for month := end; !month.Before(begin); month = month.AddDate(0, -1, 0) {
|
|
key := month.Format("2006-01")
|
|
actual := actuals[key]
|
|
if actual.Pay == "" {
|
|
actual.Pay = "0.00"
|
|
}
|
|
status, missing := goalStatus(byMonth[key], actual, key, current.Format("2006-01"))
|
|
if !available {
|
|
status = "unavailable"
|
|
}
|
|
out.Months = append(out.Months, GoalMonth{Month: key, Goal: byMonth[key], Actual: actual, Status: status, Missing: missing, CanEdit: editable && goalEditable(key, now), Available: available})
|
|
}
|
|
return out, nil
|
|
}
|