Update monitoring PF competitor tracking

This commit is contained in:
Grendgi
2026-06-05 12:31:52 +03:00
parent 6966e6810c
commit 7a4d03c905
4 changed files with 156 additions and 80 deletions

View File

@@ -33,6 +33,8 @@ func (s Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
s.accessMe(w, r)
case path == "/api/v1/summary" && r.Method == http.MethodGet:
s.summary(w, r)
case path == "/api/v1/team/overview" && r.Method == http.MethodGet:
s.teamOverview(w, r)
case path == "/api/v1/employees":
s.employees(w, r)
case strings.HasPrefix(path, "/api/v1/employees/"):
@@ -86,6 +88,7 @@ func (s Server) accessMe(w http.ResponseWriter, r *http.Request) {
"is_admin": isAdmin(r),
"portal_user_id": nullablePlain(portalID),
"telegram_linked": emp != nil && emp.TGChatID != nil && *emp.TGChatID != "",
"can_view_team": canViewTeam(r),
"employee": emp,
"telegram_bot_username": nullablePlain(botUsername),
"telegram_start_command": command,
@@ -110,6 +113,19 @@ func (s Server) summary(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, out)
}
func (s Server) teamOverview(w http.ResponseWriter, r *http.Request) {
if !canViewTeam(r) {
writeError(w, http.StatusNotFound, "not found")
return
}
items, err := s.App.TeamOverview(r.Context(), subordinatePortalIDs(r), isAdmin(r))
if err != nil {
writeError(w, http.StatusInternalServerError, err.Error())
return
}
writeJSON(w, http.StatusOK, items)
}
func (s Server) employees(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
@@ -352,45 +368,13 @@ func (s Server) listingItem(w http.ResponseWriter, r *http.Request, path string)
writeError(w, http.StatusMethodNotAllowed, "method not allowed")
return
}
deleted, err := s.App.DeleteListing(r.Context(), emp.ID, id)
if err != nil {
if err := s.App.DeleteListing(r.Context(), emp.ID, id); err != nil {
writeError(w, http.StatusNotFound, "listing not found")
return
}
if deleted.OwnerChatID != nil && *deleted.OwnerChatID != "" {
_ = s.App.TG.SendMessage(r.Context(), *deleted.OwnerChatID, formatDeletedListingMessage(deleted))
}
w.WriteHeader(http.StatusNoContent)
}
func formatDeletedListingMessage(deleted *DeletedListing) string {
listing := deleted.Listing
title := "без названия"
if listing.Title != nil && *listing.Title != "" {
title = *listing.Title
}
price := "—"
if listing.CurrentPrice != nil {
currency := "AED"
if listing.Currency != nil && *listing.Currency != "" {
currency = *listing.Currency
}
price = strconv.FormatFloat(*listing.CurrentPrice, 'f', 0, 64) + " " + currency
}
permit := "—"
if listing.PermitNumber != nil && *listing.PermitNumber != "" {
permit = *listing.PermitNumber
}
return "🏠 <b>" + deleted.ProjectTitle + "</b>\n" +
"Тип: " + deleted.ProjectDeal + " · Изменений: 1\n" +
"——————————\n" +
"🗑️ <b>Удален конкурент</b> — " + listing.Source + "\n" +
title + "\n" +
"Последняя цена: " + price + "\n" +
"Permit: <code>" + permit + "</code>\n" +
listing.URL
}
func (s Server) requireEmployee(w http.ResponseWriter, r *http.Request) (*Employee, bool) {
emp, err := s.App.CurrentEmployee(r.Context(), portalUserID(r), true)
if errors.Is(err, ErrTelegramRequired) {
@@ -412,6 +396,26 @@ func isAdmin(r *http.Request) bool {
return r.Header.Get("X-User-Is-Admin") == "1"
}
func canViewTeam(r *http.Request) bool {
return isAdmin(r) || r.Header.Get("X-User-Is-Department-Head") == "1"
}
func subordinatePortalIDs(r *http.Request) []string {
raw := strings.TrimSpace(r.Header.Get("X-User-Subordinates"))
if raw == "" {
return []string{}
}
parts := strings.Split(raw, ",")
out := make([]string, 0, len(parts))
for _, part := range parts {
id := strings.TrimSpace(part)
if id != "" {
out = append(out, id)
}
}
return out
}
func nullablePlain(value string) *string {
if strings.TrimSpace(value) == "" {
return nil