194 lines
6.5 KiB
Go
194 lines
6.5 KiB
Go
package repository
|
|
|
|
import (
|
|
"errors"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"hyapp-admin-server/internal/appctx"
|
|
"hyapp-admin-server/internal/model"
|
|
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/clause"
|
|
)
|
|
|
|
var databiPeriodMonthPattern = regexp.MustCompile(`^\d{4}-(0[1-9]|1[0-2])$`)
|
|
|
|
// ListMoneyScopeOperators 返回所有配置过财务/数据范围的后台用户及其范围;
|
|
// 社交 BI 的"运营人员"就是这批人,不再依赖固定 team_id 约定。
|
|
func (s *Store) ListMoneyScopeOperators() ([]model.User, map[uint][]model.UserMoneyScope, error) {
|
|
var scopes []model.UserMoneyScope
|
|
if err := s.db.Order("user_id ASC, app_code ASC, region_id ASC").Find(&scopes).Error; err != nil {
|
|
return nil, nil, err
|
|
}
|
|
scopesByUser := map[uint][]model.UserMoneyScope{}
|
|
userIDs := make([]uint, 0, len(scopes))
|
|
for _, scope := range scopes {
|
|
if _, ok := scopesByUser[scope.UserID]; !ok {
|
|
userIDs = append(userIDs, scope.UserID)
|
|
}
|
|
scopesByUser[scope.UserID] = append(scopesByUser[scope.UserID], scope)
|
|
}
|
|
if len(userIDs) == 0 {
|
|
return []model.User{}, scopesByUser, nil
|
|
}
|
|
var users []model.User
|
|
if err := s.db.Where("id IN ?", userIDs).Order("id ASC").Find(&users).Error; err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return users, scopesByUser, nil
|
|
}
|
|
|
|
func (s *Store) ListDatabiKpiTargets(periodMonths []string, userIDs []uint) ([]model.DatabiKpiTarget, error) {
|
|
query := s.db.Model(&model.DatabiKpiTarget{})
|
|
months := normalizeDatabiPeriodMonths(periodMonths)
|
|
if len(months) > 0 {
|
|
query = query.Where("period_month IN ?", months)
|
|
}
|
|
if len(userIDs) > 0 {
|
|
query = query.Where("user_id IN ?", userIDs)
|
|
}
|
|
var targets []model.DatabiKpiTarget
|
|
err := query.Order("user_id ASC, app_code ASC, region_id ASC, period_month ASC").Find(&targets).Error
|
|
return targets, err
|
|
}
|
|
|
|
// UpsertDatabiKpiTargets 逐条 upsert 目标;月目标和日目标同时为 0 视为清除该条目标。
|
|
func (s *Store) UpsertDatabiKpiTargets(targets []model.DatabiKpiTarget) error {
|
|
normalized, removals, err := normalizeDatabiKpiTargets(targets)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return s.db.Transaction(func(tx *gorm.DB) error {
|
|
for _, target := range removals {
|
|
if err := tx.Where(
|
|
"user_id = ? AND app_code = ? AND region_id = ? AND period_month = ?",
|
|
target.UserID, target.AppCode, target.RegionID, target.PeriodMonth,
|
|
).Delete(&model.DatabiKpiTarget{}).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if len(normalized) == 0 {
|
|
return nil
|
|
}
|
|
return tx.Clauses(clause.OnConflict{
|
|
Columns: []clause.Column{{Name: "user_id"}, {Name: "app_code"}, {Name: "region_id"}, {Name: "period_month"}},
|
|
DoUpdates: clause.AssignmentColumns([]string{
|
|
"target_usd_minor",
|
|
"daily_target_usd_minor",
|
|
"updated_at_ms",
|
|
}),
|
|
}).Create(&normalized).Error
|
|
})
|
|
}
|
|
|
|
func (s *Store) ListDatabiAppKpiTargets(periodMonths []string) ([]model.DatabiAppKpiTarget, error) {
|
|
query := s.db.Model(&model.DatabiAppKpiTarget{})
|
|
months := normalizeDatabiPeriodMonths(periodMonths)
|
|
if len(months) > 0 {
|
|
query = query.Where("period_month IN ?", months)
|
|
}
|
|
var targets []model.DatabiAppKpiTarget
|
|
err := query.Order("app_code ASC, period_month ASC").Find(&targets).Error
|
|
return targets, err
|
|
}
|
|
|
|
// UpsertDatabiAppKpiTargets 逐条 upsert App 级目标;月目标和日目标同时为 0 视为清除。
|
|
func (s *Store) UpsertDatabiAppKpiTargets(targets []model.DatabiAppKpiTarget) error {
|
|
seen := map[string]struct{}{}
|
|
upserts := make([]model.DatabiAppKpiTarget, 0, len(targets))
|
|
removals := make([]model.DatabiAppKpiTarget, 0)
|
|
for _, target := range targets {
|
|
target.AppCode = appctx.Normalize(target.AppCode)
|
|
target.PeriodMonth = strings.TrimSpace(target.PeriodMonth)
|
|
if target.AppCode == "" {
|
|
return errors.New("app kpi target app code is required")
|
|
}
|
|
if !databiPeriodMonthPattern.MatchString(target.PeriodMonth) {
|
|
return errors.New("app kpi target period month must be formatted as YYYY-MM")
|
|
}
|
|
if target.TargetUSDMinor < 0 || target.DailyTargetUSDMinor < 0 {
|
|
return errors.New("app kpi target amounts must not be negative")
|
|
}
|
|
key := target.AppCode + ":" + target.PeriodMonth
|
|
if _, ok := seen[key]; ok {
|
|
continue
|
|
}
|
|
seen[key] = struct{}{}
|
|
if target.TargetUSDMinor == 0 && target.DailyTargetUSDMinor == 0 {
|
|
removals = append(removals, target)
|
|
continue
|
|
}
|
|
target.ID = 0
|
|
upserts = append(upserts, target)
|
|
}
|
|
return s.db.Transaction(func(tx *gorm.DB) error {
|
|
for _, target := range removals {
|
|
if err := tx.Where("app_code = ? AND period_month = ?", target.AppCode, target.PeriodMonth).
|
|
Delete(&model.DatabiAppKpiTarget{}).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if len(upserts) == 0 {
|
|
return nil
|
|
}
|
|
return tx.Clauses(clause.OnConflict{
|
|
Columns: []clause.Column{{Name: "app_code"}, {Name: "period_month"}},
|
|
DoUpdates: clause.AssignmentColumns([]string{
|
|
"target_usd_minor",
|
|
"daily_target_usd_minor",
|
|
"updated_at_ms",
|
|
}),
|
|
}).Create(&upserts).Error
|
|
})
|
|
}
|
|
|
|
func normalizeDatabiKpiTargets(targets []model.DatabiKpiTarget) ([]model.DatabiKpiTarget, []model.DatabiKpiTarget, error) {
|
|
seen := map[string]struct{}{}
|
|
upserts := make([]model.DatabiKpiTarget, 0, len(targets))
|
|
removals := make([]model.DatabiKpiTarget, 0)
|
|
for _, target := range targets {
|
|
target.AppCode = appctx.Normalize(target.AppCode)
|
|
target.PeriodMonth = strings.TrimSpace(target.PeriodMonth)
|
|
if target.UserID == 0 || target.AppCode == "" || target.RegionID < 0 {
|
|
return nil, nil, errors.New("kpi target user, app code and region id are required")
|
|
}
|
|
if !databiPeriodMonthPattern.MatchString(target.PeriodMonth) {
|
|
return nil, nil, errors.New("kpi target period month must be formatted as YYYY-MM")
|
|
}
|
|
if target.TargetUSDMinor < 0 || target.DailyTargetUSDMinor < 0 {
|
|
return nil, nil, errors.New("kpi target amounts must not be negative")
|
|
}
|
|
key := strconv.FormatUint(uint64(target.UserID), 10) + ":" + target.AppCode + ":" + strconv.FormatInt(target.RegionID, 10) + ":" + target.PeriodMonth
|
|
if _, ok := seen[key]; ok {
|
|
continue
|
|
}
|
|
seen[key] = struct{}{}
|
|
if target.TargetUSDMinor == 0 && target.DailyTargetUSDMinor == 0 {
|
|
removals = append(removals, target)
|
|
continue
|
|
}
|
|
target.ID = 0
|
|
upserts = append(upserts, target)
|
|
}
|
|
return upserts, removals, nil
|
|
}
|
|
|
|
func normalizeDatabiPeriodMonths(periodMonths []string) []string {
|
|
out := make([]string, 0, len(periodMonths))
|
|
seen := map[string]struct{}{}
|
|
for _, month := range periodMonths {
|
|
month = strings.TrimSpace(month)
|
|
if month == "" || !databiPeriodMonthPattern.MatchString(month) {
|
|
continue
|
|
}
|
|
if _, ok := seen[month]; ok {
|
|
continue
|
|
}
|
|
seen[month] = struct{}{}
|
|
out = append(out, month)
|
|
}
|
|
return out
|
|
}
|